Browse Source

minor navigation bug-fixes

imwald
Silberengel 5 months ago
parent
commit
1c78d41c66
  1. 109
      src/PageManager.tsx
  2. 2
      src/components/SearchBar/index.tsx
  3. 187
      src/pages/primary/DiscussionsPage/CreateThreadDialog.tsx
  4. 118
      src/pages/primary/DiscussionsPage/index.tsx

109
src/PageManager.tsx

@ -614,8 +614,23 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { @@ -614,8 +614,23 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
const pushSecondaryPage = (url: string, index?: number) => {
console.log('pushSecondaryPage called with:', url)
setSecondaryStack((prevStack) => {
console.log('Current secondary stack length:', prevStack.length)
// For relay pages, clear the stack and start fresh to avoid confusion
if (url.startsWith('/relays/')) {
console.log('Clearing stack for relay navigation')
const { newStack, newItem } = pushNewPageToStack([], url, maxStackSize, 0)
console.log('New stack length:', newStack.length, 'New item:', !!newItem)
if (newItem) {
window.history.pushState({ index: newItem.index, url }, '', url)
}
return newStack
}
if (isCurrentPage(prevStack, url)) {
console.log('Page already exists, scrolling to top')
const currentItem = prevStack[prevStack.length - 1]
if (currentItem?.ref?.current) {
currentItem.ref.current.scrollToTop('instant')
@ -623,7 +638,9 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { @@ -623,7 +638,9 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
return prevStack
}
console.log('Creating new page for URL:', url)
const { newStack, newItem } = pushNewPageToStack(prevStack, url, maxStackSize, index)
console.log('New stack length:', newStack.length, 'New item:', !!newItem)
if (newItem) {
window.history.pushState({ index: newItem.index, url }, '', url)
}
@ -696,16 +713,26 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { @@ -696,16 +713,26 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
) : (
<>
{!!secondaryStack.length &&
secondaryStack.map((item, index) => (
<div
key={item.index}
style={{
display: index === secondaryStack.length - 1 ? 'block' : 'none'
}}
>
{item.component}
</div>
))}
secondaryStack.map((item, index) => {
const isLast = index === secondaryStack.length - 1
console.log('Rendering secondary stack item:', {
index,
isLast,
url: item.url,
hasComponent: !!item.component,
display: isLast ? 'block' : 'none'
})
return (
<div
key={item.index}
style={{
display: isLast ? 'block' : 'none'
}}
>
{item.component}
</div>
)
})}
{primaryPages.map(({ name, element, props }) => (
<div
key={name}
@ -756,13 +783,40 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) { @@ -756,13 +783,40 @@ export function PageManager({ maxStackSize = 5 }: { maxStackSize?: number }) {
}}
>
<Sidebar />
<MainContentArea
primaryPages={primaryPages}
currentPrimaryPage={currentPrimaryPage}
primaryNoteView={primaryNoteView}
primaryViewType={primaryViewType}
goBack={goBack}
/>
{secondaryStack.length > 0 ? (
// Show secondary pages when there are any in the stack
<div className="flex-1 overflow-auto">
{secondaryStack.map((item, index) => {
const isLast = index === secondaryStack.length - 1
console.log('Rendering desktop secondary stack item:', {
index,
isLast,
url: item.url,
hasComponent: !!item.component,
display: isLast ? 'block' : 'none'
})
return (
<div
key={item.index}
style={{
display: isLast ? 'block' : 'none'
}}
>
{item.component}
</div>
)
})}
</div>
) : (
// Show primary pages when no secondary pages
<MainContentArea
primaryPages={primaryPages}
currentPrimaryPage={currentPrimaryPage}
primaryNoteView={primaryNoteView}
primaryViewType={primaryViewType}
goBack={goBack}
/>
)}
</div>
</div>
<TooManyRelaysAlertDialog />
@ -807,19 +861,36 @@ function isCurrentPage(stack: TStackItem[], url: string) { @@ -807,19 +861,36 @@ function isCurrentPage(stack: TStackItem[], url: string) {
const currentPage = stack[stack.length - 1]
if (!currentPage) return false
console.log('isCurrentPage check:', { currentUrl: currentPage.url, newUrl: url, match: currentPage.url === url })
return currentPage.url === url
}
function findAndCreateComponent(url: string, index: number) {
const path = url.split('?')[0].split('#')[0]
console.log('findAndCreateComponent called with:', { url, path, routes: routes.length })
for (const { matcher, element } of routes) {
const match = matcher(path)
console.log('Trying route matcher, match result:', !!match)
if (!match) continue
if (!element) return {}
if (!element) {
console.log('No element for this route')
return {}
}
const ref = createRef<TPageRef>()
return { component: cloneElement(element, { ...match.params, index, ref } as any), ref }
// Decode URL parameters for relay pages
const params = { ...match.params }
if (params.url && typeof params.url === 'string') {
params.url = decodeURIComponent(params.url)
console.log('Decoded URL parameter:', params.url)
}
console.log('Creating component with params:', params)
return { component: cloneElement(element, { ...params, index, ref } as any), ref }
}
console.log('No matching route found for:', path)
return {}
}

2
src/components/SearchBar/index.tsx

@ -282,7 +282,7 @@ const SearchBar = forwardRef< @@ -282,7 +282,7 @@ const SearchBar = forwardRef<
className={cn(
'bg-surface-background rounded-b-lg shadow-lg z-50',
isSmallScreen
? 'fixed top-12 inset-x-0'
? 'absolute top-full -translate-y-1 inset-x-0 pt-1'
: 'absolute top-full -translate-y-1 inset-x-0 pt-1 '
)}
onMouseDown={(e) => e.preventDefault()}

187
src/pages/primary/DiscussionsPage/CreateThreadDialog.tsx

@ -9,7 +9,8 @@ import { Slider } from '@/components/ui/slider' @@ -9,7 +9,8 @@ import { Slider } from '@/components/ui/slider'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import { Checkbox } from '@/components/ui/checkbox'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Hash, X, Users, Code, Coins, Newspaper, BookOpen, Scroll, Cpu, Trophy, Film, Heart, TrendingUp, Utensils, MapPin, Home, PawPrint, Shirt, Image, Zap, Settings, Book, Network, Car, Eye, Edit3 } from 'lucide-react'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
import { Hash, X, Users, Code, Coins, Newspaper, BookOpen, Scroll, Cpu, Trophy, Film, Heart, TrendingUp, Utensils, MapPin, Home, PawPrint, Shirt, Image, Zap, Settings, Book, Network, Car, Eye, Edit3, ChevronDown, Check } from 'lucide-react'
import { useState, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { useNostr } from '@/providers/NostrProvider'
@ -114,19 +115,20 @@ export default function CreateThreadDialog({ @@ -114,19 +115,20 @@ export default function CreateThreadDialog({
const [minPow, setMinPow] = useState(0)
const [showAdvancedOptions, setShowAdvancedOptions] = useState(false)
const [isLoadingRelays, setIsLoadingRelays] = useState(true)
const [isTopicSelectorOpen, setIsTopicSelectorOpen] = useState(false)
// Readings options state
const [isReadingGroup, setIsReadingGroup] = useState(false)
const [author, setAuthor] = useState('')
const [subject, setSubject] = useState('')
const [showReadingsPanel, setShowReadingsPanel] = useState(false)
// Create combined topics list (predefined + dynamic)
// Create combined topics list (predefined + dynamic) with hierarchy
const allAvailableTopics = useMemo(() => {
const combined = [...DISCUSSION_TOPICS]
if (dynamicTopics) {
// Add dynamic main topics
// Add dynamic main topics first
dynamicTopics.mainTopics.forEach(dynamicTopic => {
combined.push({
id: dynamicTopic.id,
@ -135,13 +137,56 @@ export default function CreateThreadDialog({ @@ -135,13 +137,56 @@ export default function CreateThreadDialog({
})
})
// Add dynamic subtopics
// Add dynamic subtopics grouped under their main topics
dynamicTopics.subtopics.forEach(dynamicTopic => {
combined.push({
id: dynamicTopic.id,
label: `${dynamicTopic.label} (${dynamicTopic.count}) 📌`,
icon: Hash // Use Hash icon for dynamic topics
})
// Try to find a related main topic
const predefinedMainTopic = DISCUSSION_TOPICS.find(pt =>
dynamicTopic.id.toLowerCase().includes(pt.id.toLowerCase()) ||
pt.id.toLowerCase().includes(dynamicTopic.id.toLowerCase())
)
const relatedDynamicMainTopic = dynamicTopics.mainTopics.find(dt =>
dynamicTopic.id.toLowerCase().includes(dt.id.toLowerCase()) ||
dt.id.toLowerCase().includes(dynamicTopic.id.toLowerCase())
)
const parentTopic = predefinedMainTopic?.id || relatedDynamicMainTopic?.id
if (parentTopic) {
// Find the index of the parent topic and insert after it
const parentIndex = combined.findIndex(topic => topic.id === parentTopic)
if (parentIndex !== -1) {
combined.splice(parentIndex + 1, 0, {
id: dynamicTopic.id,
label: ` └─ ${dynamicTopic.label} (${dynamicTopic.count}) 📌`,
icon: Hash // Use Hash icon for dynamic topics
})
} else {
// Fallback: add at the end if parent not found
combined.push({
id: dynamicTopic.id,
label: `${dynamicTopic.label} (${dynamicTopic.count}) 📌`,
icon: Hash // Use Hash icon for dynamic topics
})
}
} else {
// No parent found, group under "General"
const generalIndex = combined.findIndex(topic => topic.id === 'general')
if (generalIndex !== -1) {
combined.splice(generalIndex + 1, 0, {
id: dynamicTopic.id,
label: ` └─ ${dynamicTopic.label} (${dynamicTopic.count}) 📌`,
icon: Hash // Use Hash icon for dynamic topics
})
} else {
// Fallback: add at the end if General not found
combined.push({
id: dynamicTopic.id,
label: `${dynamicTopic.label} (${dynamicTopic.count}) 📌`,
icon: Hash // Use Hash icon for dynamic topics
})
}
}
})
}
@ -270,13 +315,70 @@ export default function CreateThreadDialog({ @@ -270,13 +315,70 @@ export default function CreateThreadDialog({
// Only add topic tag if it's a specific topic (not 'all' or 'general')
if (selectedTopic !== 'all' && selectedTopic !== 'general') {
tags.push(['t', normalizeTopic(selectedTopic)])
// Check if this is a dynamic subtopic
const selectedDynamicTopic = dynamicTopics?.allTopics.find(dt => dt.id === selectedTopic)
if (selectedDynamicTopic?.isSubtopic) {
// For subtopics, we need to find the parent main topic
// First, try to find a predefined main topic that might be related
const predefinedMainTopic = DISCUSSION_TOPICS.find(pt =>
selectedTopic.toLowerCase().includes(pt.id.toLowerCase()) ||
pt.id.toLowerCase().includes(selectedTopic.toLowerCase())
)
if (predefinedMainTopic) {
// Add the predefined main topic first, then the subtopic
tags.push(['t', normalizeTopic(predefinedMainTopic.id)])
tags.push(['t', normalizeTopic(selectedTopic)])
} else {
// If no predefined main topic found, try to find a dynamic main topic
const relatedDynamicMainTopic = dynamicTopics?.mainTopics.find(dt =>
selectedTopic.toLowerCase().includes(dt.id.toLowerCase()) ||
dt.id.toLowerCase().includes(selectedTopic.toLowerCase())
)
if (relatedDynamicMainTopic) {
// Add the dynamic main topic first, then the subtopic
tags.push(['t', normalizeTopic(relatedDynamicMainTopic.id)])
tags.push(['t', normalizeTopic(selectedTopic)])
} else {
// Fallback: just add the subtopic and let the system categorize it under 'general'
// Don't add 'general' as a t-tag since it's the default fallback
tags.push(['t', normalizeTopic(selectedTopic)])
}
}
} else {
// Regular topic (predefined or dynamic main topic)
tags.push(['t', normalizeTopic(selectedTopic)])
}
}
// Add hashtags as t-tags (deduplicate with selectedTopic if it's not 'all' or 'general')
const uniqueHashtags = (selectedTopic !== 'all' && selectedTopic !== 'general')
? hashtags.filter(hashtag => hashtag !== normalizeTopic(selectedTopic))
: hashtags
// Add hashtags as t-tags (deduplicate with selectedTopic and any parent topics)
let uniqueHashtags = hashtags
if (selectedTopic !== 'all' && selectedTopic !== 'general') {
const selectedDynamicTopic = dynamicTopics?.allTopics.find(dt => dt.id === selectedTopic)
if (selectedDynamicTopic?.isSubtopic) {
// For subtopics, deduplicate against both the subtopic and its potential parent
const predefinedMainTopic = DISCUSSION_TOPICS.find(pt =>
selectedTopic.toLowerCase().includes(pt.id.toLowerCase()) ||
pt.id.toLowerCase().includes(selectedTopic.toLowerCase())
)
const relatedDynamicMainTopic = dynamicTopics?.mainTopics.find(dt =>
selectedTopic.toLowerCase().includes(dt.id.toLowerCase()) ||
dt.id.toLowerCase().includes(selectedTopic.toLowerCase())
)
const parentTopic = predefinedMainTopic?.id || relatedDynamicMainTopic?.id
uniqueHashtags = hashtags.filter(hashtag =>
hashtag !== normalizeTopic(selectedTopic) &&
(parentTopic ? hashtag !== normalizeTopic(parentTopic) : true)
)
} else {
// Regular topic
uniqueHashtags = hashtags.filter(hashtag => hashtag !== normalizeTopic(selectedTopic))
}
}
for (const hashtag of uniqueHashtags) {
tags.push(['t', hashtag])
}
@ -395,18 +497,49 @@ export default function CreateThreadDialog({ @@ -395,18 +497,49 @@ export default function CreateThreadDialog({
{/* Topic Selection */}
<div className="space-y-2">
<Label htmlFor="topic">{t('Topic')}</Label>
<select
id="topic"
value={selectedTopic}
onChange={(e) => setSelectedTopic(e.target.value)}
className="w-full px-3 py-2 bg-white dark:bg-gray-800 text-black dark:text-white border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
{allAvailableTopics.map((topic) => (
<option key={topic.id} value={topic.id}>
{topic.label}
</option>
))}
</select>
<Popover open={isTopicSelectorOpen} onOpenChange={setIsTopicSelectorOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
role="combobox"
aria-expanded={isTopicSelectorOpen}
className="w-full justify-between"
>
{selectedTopicInfo?.label || t('Select topic...')}
<ChevronDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent
className="w-[--radix-popover-trigger-width] p-2 z-[10000]"
align="start"
side="bottom"
sideOffset={4}
>
<div className="max-h-60 overflow-y-auto">
{allAvailableTopics.map((topic) => {
const Icon = topic.icon
return (
<div
key={topic.id}
className="flex items-center p-2 hover:bg-accent cursor-pointer rounded"
onClick={() => {
setSelectedTopic(topic.id)
setIsTopicSelectorOpen(false)
}}
>
<Check
className={`mr-2 h-4 w-4 ${
selectedTopic === topic.id ? 'opacity-100' : 'opacity-0'
}`}
/>
<Icon className="mr-2 h-4 w-4" />
{topic.label}
</div>
)
})}
</div>
</PopoverContent>
</Popover>
<p className="text-sm text-muted-foreground">
{t('Threads are organized by topics. Choose a topic that best fits your discussion.')}
</p>

118
src/pages/primary/DiscussionsPage/index.tsx

@ -14,6 +14,7 @@ import client from '@/services/client.service' @@ -14,6 +14,7 @@ import client from '@/services/client.service'
import { DISCUSSION_TOPICS } from './CreateThreadDialog'
import ThreadCard from './ThreadCard'
import CreateThreadDialog from './CreateThreadDialog'
import PrimaryPageLayout from '@/layouts/PrimaryPageLayout'
// Simple event map type
type EventMapEntry = {
@ -271,7 +272,17 @@ function getEnhancedTopicFromTags(allTopics: string[], predefinedTopicIds: strin @@ -271,7 +272,17 @@ function getEnhancedTopicFromTags(allTopics: string[], predefinedTopicIds: strin
return 'general'
}
const DiscussionsPage = forwardRef(() => {
function DiscussionsPageTitlebar() {
const { t } = useTranslation()
return (
<div className="flex items-center gap-2">
<h1 className="text-lg font-semibold">{t('Discussions')}</h1>
</div>
)
}
const DiscussionsPage = forwardRef((_, ref) => {
const { t } = useTranslation()
const { favoriteRelays, blockedRelays } = useFavoriteRelays()
const { pubkey } = useNostr()
@ -625,43 +636,45 @@ const DiscussionsPage = forwardRef(() => { @@ -625,43 +636,45 @@ const DiscussionsPage = forwardRef(() => {
}>()
searchedEntries.forEach((entry) => {
const mainTopic = entry.categorizedTopic
// Initialize main topic group if it doesn't exist
if (!mainTopicGroups.has(mainTopic)) {
mainTopicGroups.set(mainTopic, {
entries: [],
subtopics: new Map()
})
}
const group = mainTopicGroups.get(mainTopic)!
// Check if this entry has any dynamic subtopics
const entrySubtopics = entry.allTopics.filter(topic => {
const dynamicTopic = dynamicTopics.allTopics.find(dt => dt.id === topic && dt.isSubtopic)
return !!dynamicTopic
})
// Debug logging for subtopic detection
// if (entrySubtopics.length > 0) {
// console.log('Found subtopics for entry:', {
// threadId: entry.event.id.substring(0, 8),
// allTopics: entry.allTopics,
// entrySubtopics,
// dynamicTopics: dynamicTopics.allTopics.map(dt => ({ id: dt.id, isSubtopic: dt.isSubtopic }))
// })
// }
if (entrySubtopics.length > 0) {
// Group under the first subtopic found
// This entry has subtopics - group under the main topic with the subtopic
const mainTopic = entry.categorizedTopic
const subtopic = entrySubtopics[0]
// Initialize main topic group if it doesn't exist
if (!mainTopicGroups.has(mainTopic)) {
mainTopicGroups.set(mainTopic, {
entries: [],
subtopics: new Map()
})
}
const group = mainTopicGroups.get(mainTopic)!
// Add to subtopic group
if (!group.subtopics.has(subtopic)) {
group.subtopics.set(subtopic, [])
}
group.subtopics.get(subtopic)!.push(entry)
} else {
// No subtopic, add to main topic
const mainTopic = entry.categorizedTopic
// Initialize main topic group if it doesn't exist
if (!mainTopicGroups.has(mainTopic)) {
mainTopicGroups.set(mainTopic, {
entries: [],
subtopics: new Map()
})
}
const group = mainTopicGroups.get(mainTopic)!
group.entries.push(entry)
}
})
@ -688,16 +701,13 @@ const DiscussionsPage = forwardRef(() => { @@ -688,16 +701,13 @@ const DiscussionsPage = forwardRef(() => {
group.subtopics.forEach((entries) => sortEntries(entries))
})
// Convert to array format for rendering with proper hierarchy
const result: Array<[string, EventMapEntry[], Map<string, EventMapEntry[]>]> = []
mainTopicGroups.forEach((group, mainTopic) => {
// Add main topic with its subtopics
result.push([mainTopic, group.entries, group.subtopics])
})
// Sort groups by most recent activity (newest first)
result.sort(([, aEntries], [, bEntries]) => {
const sortedGroups = new Map<string, { entries: EventMapEntry[], subtopics: Map<string, EventMapEntry[]> }>()
const sortedEntries = Array.from(mainTopicGroups.entries()).sort(([, aGroup], [, bGroup]) => {
const aEntries = aGroup.entries
const bEntries = bGroup.entries
if (aEntries.length === 0 && bEntries.length === 0) return 0
if (aEntries.length === 0) return 1
if (bEntries.length === 0) return -1
@ -716,7 +726,11 @@ const DiscussionsPage = forwardRef(() => { @@ -716,7 +726,11 @@ const DiscussionsPage = forwardRef(() => {
return bMostRecent - aMostRecent // Newest first
})
return result
sortedEntries.forEach(([topic, group]) => {
sortedGroups.set(topic, group)
})
return sortedGroups
}, [searchedEntries, dynamicTopics])
// Handle refresh
@ -772,11 +786,14 @@ const DiscussionsPage = forwardRef(() => { @@ -772,11 +786,14 @@ const DiscussionsPage = forwardRef(() => {
}
return (
<div className="flex flex-col h-full">
{/* Header */}
<div className="flex flex-col gap-4 p-4 border-b">
<PrimaryPageLayout
ref={ref}
pageName="discussions"
titlebar={<DiscussionsPageTitlebar />}
displayScrollToTopButton
>
<div className="flex flex-col gap-4 p-4">
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4">
<h1 className="text-2xl font-bold">{t('Discussions')}</h1>
<button
onClick={() => setShowCreateDialog(true)}
className="px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 w-full sm:w-auto"
@ -845,35 +862,32 @@ const DiscussionsPage = forwardRef(() => { @@ -845,35 +862,32 @@ const DiscussionsPage = forwardRef(() => {
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto p-2 sm:p-4">
<div className="p-2 sm:p-4 pb-20 sm:pb-4">
{loading ? (
<div className="text-center py-8">{t('Loading...')}</div>
) : isSearching ? (
<div className="text-center py-8">{t('Searching...')}</div>
) : (
<div className="space-y-6">
{groupedEvents.map(([topic, events, subtopics]) => {
const topicInfo = availableTopics.find(t => t.topic === topic)
<div className="space-y-6 pb-8">
{Array.from(groupedEvents.entries()).map(([mainTopic, group]) => {
const topicInfo = availableTopics.find(t => t.topic === mainTopic)
const isDynamicMain = topicInfo?.isDynamic && topicInfo?.isMainTopic
const isDynamicSubtopic = topicInfo?.isDynamic && topicInfo?.isSubtopic
return (
<div key={topic} className="space-y-4">
<div key={mainTopic} className="space-y-4">
{/* Main Topic Header */}
<h2 className="text-lg font-semibold mb-3 capitalize flex flex-col sm:flex-row sm:items-center gap-1 sm:gap-2">
<span className="flex items-center gap-2">
{isDynamicMain && <span className="text-orange-500">🔥</span>}
{isDynamicSubtopic && <span className="text-blue-500">📌</span>}
{topic} ({events.length} {events.length === 1 ? t('thread') : t('threads')})
{mainTopic} ({group.entries.length + Array.from(group.subtopics.values()).reduce((sum, events) => sum + events.length, 0)} {group.entries.length + Array.from(group.subtopics.values()).reduce((sum, events) => sum + events.length, 0) === 1 ? t('thread') : t('threads')})
</span>
{isDynamicMain && <span className="text-xs bg-orange-100 dark:bg-orange-900 text-orange-800 dark:text-orange-200 px-2 py-1 rounded w-fit">Main Topic</span>}
{isDynamicSubtopic && <span className="text-xs bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200 px-2 py-1 rounded w-fit">Subtopic</span>}
</h2>
{/* Main Topic Threads */}
{events.length > 0 && (
{group.entries.length > 0 && (
<div className="space-y-3">
{events.map((entry) => (
{group.entries.map((entry) => (
<ThreadCard
key={entry.event.id}
thread={entry.event}
@ -888,9 +902,9 @@ const DiscussionsPage = forwardRef(() => { @@ -888,9 +902,9 @@ const DiscussionsPage = forwardRef(() => {
)}
{/* Subtopic Groups */}
{subtopics.size > 0 && (
{group.subtopics.size > 0 && (
<div className="ml-2 sm:ml-4 space-y-4">
{Array.from(subtopics.entries()).map(([subtopic, subtopicEvents]) => {
{Array.from(group.subtopics.entries()).map(([subtopic, subtopicEvents]) => {
const subtopicInfo = availableTopics.find(t => t.topic === subtopic)
const isSubtopicDynamic = subtopicInfo?.isDynamic && subtopicInfo?.isSubtopic
@ -939,7 +953,7 @@ const DiscussionsPage = forwardRef(() => { @@ -939,7 +953,7 @@ const DiscussionsPage = forwardRef(() => {
onThreadCreated={handleCreateThread}
/>
)}
</div>
</PrimaryPageLayout>
)
})

Loading…
Cancel
Save