diff --git a/src/activities-app/App.vue b/src/activities-app/App.vue index 829ba78..663fb38 100644 --- a/src/activities-app/App.vue +++ b/src/activities-app/App.vue @@ -3,10 +3,9 @@ import { computed } from 'vue' import { useRoute, useRouter } from 'vue-router' import { useI18n } from 'vue-i18n' import { toast } from 'vue-sonner' -import { CalendarDays, Map, Heart, Search, Plus, Ticket, Megaphone } from 'lucide-vue-next' +import { Home, Map, Heart, Ticket, Megaphone } from 'lucide-vue-next' import AppShell from '@/components/layout/AppShell.vue' import type { BottomTab } from '@/components/layout/BottomNav.vue' -import type { SidebarNavItem } from '@/components/layout/StandaloneMenu.vue' import { useAuth } from '@/composables/useAuthService' import { useActivitiesStore } from '@/modules/activities/stores/activities' import { useActivities } from '@/modules/activities/composables/useActivities' @@ -24,38 +23,71 @@ const activitiesStore = useActivitiesStore() const { isAdmin, autoApprove } = useApprovalState() // Used to merge own LNbits drafts into the activities feed right after // the user creates or edits an event — otherwise the new draft only -// surfaces on the next ActivitiesPage subscribe cycle. -// The hosting filter also lives on the activities composable; the -// sidebar entry below mirrors what the old in-page chip used to do. +// surfaces on the next ActivitiesPage subscribe cycle. `onlyHosting` +// is the feed filter that backs the Hosting bottom-nav tab — tapping +// it toggles the filter on; Home tab toggles it off. const { loadOwnEvents, onlyHosting, toggleHosting } = useActivities() -// Settings dropped — theme/lang/currency now live in the shared profile sheet. -// Create lives in the bottom nav: when logged out, tapping it shows an -// auth-prompt toast (mirroring BookmarkButton/RSVPButton) instead of -// opening the dialog. Per-app placement deliberation tracked at #53. +// True for /activities and its sub-routes (incl. detail pages) but +// not for the routes owned by other tabs (map/favorites). Used by +// both Home and Hosting active-state predicates so the highlight +// only shifts based on the onlyHosting flag while you're in the feed. +function inFeedRoute(): boolean { + if (route.path.startsWith('/activities/map')) return false + if (route.path.startsWith('/activities/favorites')) return false + return route.path === '/activities' || route.path.startsWith('/activities/') +} + const tabs = computed(() => [ - { name: t('activities.nav.feed'), icon: Search, path: '/activities' }, - { name: t('activities.nav.calendar'), icon: CalendarDays, path: '/activities/calendar' }, { - name: t('activities.createNew'), - icon: Plus, + name: t('activities.nav.feed'), + icon: Home, + onClick: () => { + // Tapping Home clears the hosting filter so the feed always + // returns to the unfiltered view, regardless of where the + // user just came from. + if (onlyHosting.value) toggleHosting() + if (route.path !== '/activities') router.push('/activities') + }, + isActive: () => inFeedRoute() && !onlyHosting.value, + }, + { + name: t('activities.filters.myTickets'), + icon: Ticket, + path: '/my-tickets', onClick: () => { if (!isAuthenticated.value) { - toast.info('Log in to create an activity', { + toast.info(t('activities.detail.loginToBuyTickets'), { action: { - label: 'Log in', + label: t('activities.detail.logIn'), onClick: () => router.push('/login'), }, }) return } - // Defensively clear any lingering edit selection so the Create - // tap always opens in Create mode regardless of a prior Edit. - activitiesStore.editingEvent = null - activitiesStore.showCreateDialog = true + router.push('/my-tickets') }, disabled: !isAuthenticated.value, }, + { + name: t('activities.filters.hosting'), + icon: Megaphone, + onClick: () => { + if (!isAuthenticated.value) { + toast.info(t('activities.hosting.loginPrompt', 'Log in to manage your hosted activities'), { + action: { + label: t('activities.favorites.logIn'), + onClick: () => router.push('/login'), + }, + }) + return + } + if (!onlyHosting.value) toggleHosting() + if (route.path !== '/activities') router.push('/activities') + }, + isActive: () => inFeedRoute() && onlyHosting.value, + disabled: !isAuthenticated.value, + }, { name: t('activities.nav.map'), icon: Map, path: '/activities/map' }, { name: t('activities.nav.favorites'), @@ -80,43 +112,8 @@ const tabs = computed(() => [ }, ]) -// Sidebar entries shown to authed users only. "My tickets" routes to -// the dedicated /my-tickets page; "Hosting" toggles the feed filter -// (no dedicated page yet) and lands on /activities so the user can -// see the filtered list. -const sidebarNav = computed(() => { - if (!isAuthenticated.value) return [] - return [ - { - name: t('activities.filters.myTickets', 'My tickets'), - icon: Ticket, - path: '/my-tickets', - isActive: () => route.path.startsWith('/my-tickets'), - }, - { - name: t('activities.filters.hosting', 'Hosting'), - icon: Megaphone, - onClick: () => { - if (!onlyHosting.value) toggleHosting() - if (!route.path.startsWith('/activities')) router.push('/activities') - }, - isActive: () => onlyHosting.value, - }, - ] -}) - -// Feed tab is active for the bare /activities route AND all sub-paths that -// aren't owned by another tab (e.g. /activities/ detail pages). +// Path-based fallback for tabs that don't carry their own `isActive`. function isActive(path: string): boolean { - if (path === '/activities') { - return ( - route.path === '/activities' || - (route.path.startsWith('/activities/') && - !route.path.startsWith('/activities/calendar') && - !route.path.startsWith('/activities/map') && - !route.path.startsWith('/activities/favorites')) - ) - } return route.path.startsWith(path) } @@ -151,7 +148,7 @@ function handleDialogOpenChange(open: boolean) {