feat(forum): add bottom navigation bar
5 tabs at the bottom of the forum standalone: Posts (→ /forum), Spaces, Submit (→ /submit), Search, Alerts Spaces, Search, and Alerts are dimmed and emit a "coming soon" toast on tap pointing at the tracking issue: - Spaces → #31 (NIP-72 communities) - Search → #15 (link aggregator search) - Alerts → #32 (per-standalone notifications, hub aggregation) Mirrors the activities-app bottom-bar pattern (icon + 10px label, fixed bottom, safe-area-aware) and replaces the previous bare forum-app shell which had no way to compose a new submission. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
55324a0501
commit
a694dc2135
1 changed files with 59 additions and 2 deletions
|
|
@ -7,7 +7,9 @@ import { useTheme } from '@/components/theme-provider'
|
||||||
import { toast } from 'vue-sonner'
|
import { toast } from 'vue-sonner'
|
||||||
import { useAuth } from '@/composables/useAuthService'
|
import { useAuth } from '@/composables/useAuthService'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { LogIn } from 'lucide-vue-next'
|
import {
|
||||||
|
LogIn, Newspaper, Hash, SquarePen, Search, Bell,
|
||||||
|
} from 'lucide-vue-next'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -17,8 +19,39 @@ const { isAuthenticated } = useAuth()
|
||||||
|
|
||||||
const showLoginDialog = ref(false)
|
const showLoginDialog = ref(false)
|
||||||
|
|
||||||
|
interface Tab {
|
||||||
|
name: string
|
||||||
|
icon: any
|
||||||
|
path?: string
|
||||||
|
comingSoon?: { issue: number; label: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
const bottomTabs: Tab[] = [
|
||||||
|
{ name: 'Posts', icon: Newspaper, path: '/forum' },
|
||||||
|
{ name: 'Spaces', icon: Hash, comingSoon: { issue: 31, label: 'Spaces' } },
|
||||||
|
{ name: 'Submit', icon: SquarePen, path: '/submit' },
|
||||||
|
{ name: 'Search', icon: Search, comingSoon: { issue: 15, label: 'Search' } },
|
||||||
|
{ name: 'Alerts', icon: Bell, comingSoon: { issue: 32, label: 'Notifications' } },
|
||||||
|
]
|
||||||
|
|
||||||
const isLoginPage = computed(() => route.path === '/login')
|
const isLoginPage = computed(() => route.path === '/login')
|
||||||
|
|
||||||
|
function isActiveTab(tab: Tab): boolean {
|
||||||
|
if (!tab.path) return false
|
||||||
|
if (tab.path === '/forum') return route.path === '/forum' || route.path.startsWith('/submission/')
|
||||||
|
return route.path.startsWith(tab.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onTabClick(tab: Tab) {
|
||||||
|
if (tab.path) {
|
||||||
|
router.push(tab.path)
|
||||||
|
} else if (tab.comingSoon) {
|
||||||
|
toast.info(`${tab.comingSoon.label} — coming soon`, {
|
||||||
|
description: `Tracked on issue #${tab.comingSoon.issue}`,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleLoginSuccess() {
|
async function handleLoginSuccess() {
|
||||||
showLoginDialog.value = false
|
showLoginDialog.value = false
|
||||||
toast.success('Welcome!')
|
toast.success('Welcome!')
|
||||||
|
|
@ -36,9 +69,33 @@ async function handleLoginSuccess() {
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main class="flex-1">
|
<main class="flex-1" :class="{ 'pb-16': !isLoginPage }">
|
||||||
<router-view />
|
<router-view />
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<nav
|
||||||
|
v-if="!isLoginPage"
|
||||||
|
class="fixed bottom-0 left-0 right-0 z-50 border-t bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60"
|
||||||
|
style="padding-bottom: env(safe-area-inset-bottom)"
|
||||||
|
>
|
||||||
|
<div class="flex items-center justify-around h-14 max-w-lg mx-auto">
|
||||||
|
<button
|
||||||
|
v-for="tab in bottomTabs"
|
||||||
|
:key="tab.name"
|
||||||
|
class="flex flex-col items-center justify-center gap-0.5 flex-1 h-full transition-colors"
|
||||||
|
:class="[
|
||||||
|
isActiveTab(tab)
|
||||||
|
? 'text-primary'
|
||||||
|
: 'text-muted-foreground hover:text-foreground',
|
||||||
|
tab.comingSoon ? 'opacity-50' : '',
|
||||||
|
]"
|
||||||
|
@click="onTabClick(tab)"
|
||||||
|
>
|
||||||
|
<component :is="tab.icon" class="w-5 h-5" />
|
||||||
|
<span class="text-[10px] font-medium">{{ tab.name }}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Toaster />
|
<Toaster />
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue