From df7ab30dc53edec8ea798a32b1bd8e4ad76dcf5d Mon Sep 17 00:00:00 2001 From: Padreug Date: Thu, 4 Jun 2026 22:37:03 +0200 Subject: [PATCH 01/10] fix(activities): share filter refs across useActivities consumers useActivityFilters allocated a fresh set of refs on every call, so when activities-app/App.vue (Hosting bottom-nav tab) and ActivitiesPage.vue each invoked useActivities(), they got independent onlyHosting/temporal/etc state. Tapping Hosting toggled the App.vue ref; the page never saw the change. Hoist the filter refs to module scope so every consumer shares the same instance. --- .../composables/useActivityFilters.ts | 37 ++++++------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/src/modules/activities/composables/useActivityFilters.ts b/src/modules/activities/composables/useActivityFilters.ts index 8f9914b..a5ae1da 100644 --- a/src/modules/activities/composables/useActivityFilters.ts +++ b/src/modules/activities/composables/useActivityFilters.ts @@ -8,35 +8,22 @@ import type { ActivityCategory } from '../types/category' import type { TemporalFilter, ActivityFilters } from '../types/filters' import { DEFAULT_FILTERS } from '../types/filters' +// Filter state is hoisted to module scope so every `useActivities()` / +// `useActivityFilters()` call shares the same refs. The bottom-nav +// Hosting tab in activities-app/App.vue and the feed view in +// ActivitiesPage.vue both rely on this — without a shared instance, +// tapping Hosting toggled a private ref the page never saw. +const temporal = ref(DEFAULT_FILTERS.temporal) +const selectedCategories = ref([]) +const selectedDate = ref(undefined) +const onlyOwnedTickets = ref(false) +const onlyHosting = ref(false) +const showPast = ref(false) + /** * Composable for managing activity filter state and applying filters reactively. */ export function useActivityFilters() { - const temporal = ref(DEFAULT_FILTERS.temporal) - const selectedCategories = ref([]) - const selectedDate = ref(undefined) - /** - * When true, the feed is narrowed to activities the current user - * holds at least one paid ticket for. Crossed with the - * `ownedActivityIds` set from useOwnedTickets in useActivities - * (this composable stays free of ticket fetching). - */ - const onlyOwnedTickets = ref(false) - /** - * When true, the feed is narrowed to activities the current user - * is hosting (organizer pubkey matches the signed-in user, or the - * row is a local LNbits draft of theirs). Reads `activity.isMine` - * which `useActivities.tagOwnership()` populates. - */ - const onlyHosting = ref(false) - /** - * When false (default), activities that have already ended are - * hidden from the feed. Toggling on includes them so the user can - * browse past events. The date-picker overrides this — picking a - * specific past date shows that day's activities regardless, - * mirroring how it overrides the temporal pills. - */ - const showPast = ref(false) const filters = computed(() => ({ temporal: temporal.value, From f01d5aa58137202313b59384e0dc0bb3c9ddafb7 Mon Sep 17 00:00:00 2001 From: Padreug Date: Thu, 4 Jun 2026 22:41:35 +0200 Subject: [PATCH 02/10] feat(activities): tailor Hosting tab + host detail view for operators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hosting feed (ActivitiesPage when onlyHosting): - Hide the date picker strip + calendar shortcut and the entire Filters/temporal-pills row; an operator managing their roster doesn't need calendar navigation or temporal narrowing. - Keep the search bar — finding a specific event in a long roster still matters. - Render compact cards via a new `compact` prop on ActivityList + ActivityCard: no hero image, single-line title, no summary, no bookmark, no "Yours" badge (every card is the operator's own), tighter p-3 padding, single-column flex layout. Host detail view (ActivityDetailPage when ownedLnbitsEvent): - Drop the top-bar Scan and Edit buttons. Edit moves into the title row as a prominent filled-primary icon button right of the title; Scan moves into the tickets section. - Render a full-width "Scan tickets" CTA in place of Buy ticket, and hoist it outside the ticketInfo gate so it appears even on hosted events that were published without AIO ticket tags. - Hide BookmarkButton and RSVPButton for the host (favoriting / RSVPing your own event are noise affordances). Detail-page badge row: "Yours" leads the row in the highlighted secondary variant; category and tags drop to outline so the ownership signal stands out. --- .../activities/components/ActivityCard.vue | 49 ++++++--- .../activities/components/ActivityList.vue | 13 ++- .../activities/views/ActivitiesPage.vue | 16 ++- .../activities/views/ActivityDetailPage.vue | 102 ++++++++++-------- 4 files changed, 111 insertions(+), 69 deletions(-) diff --git a/src/modules/activities/components/ActivityCard.vue b/src/modules/activities/components/ActivityCard.vue index 7c156e6..5eb919c 100644 --- a/src/modules/activities/components/ActivityCard.vue +++ b/src/modules/activities/components/ActivityCard.vue @@ -12,6 +12,10 @@ import type { Activity } from '../types/activity' const props = defineProps<{ activity: Activity + /** Render a compact row: no hero image, no summary, single-line + * title, tighter padding. Used by the Hosting view where the + * host already knows what their events look like. */ + compact?: boolean }>() const emit = defineEmits<{ @@ -65,12 +69,12 @@ const isPast = computed(() => { class="overflow-hidden cursor-pointer hover:shadow-lg transition-shadow duration-200 flex flex-col" @click="emit('click', activity)" > - -
+ +
{
- - -
+ + +
{{ categoryLabel }} {{ priceDisplay }} - + Yours @@ -167,26 +174,34 @@ const isPast = computed(() => {
- +
-

+

{{ activity.title }}

- +

{{ activity.summary }}

-
+
diff --git a/src/modules/activities/components/ActivityList.vue b/src/modules/activities/components/ActivityList.vue index 726f09e..1e59110 100644 --- a/src/modules/activities/components/ActivityList.vue +++ b/src/modules/activities/components/ActivityList.vue @@ -7,6 +7,10 @@ import type { Activity } from '../types/activity' defineProps<{ activities: Activity[] isLoading?: boolean + /** Render compact rows instead of full-image cards. Used by the + * Hosting view so an operator can scan their roster of events + * without the visual weight of hero images they already recognize. */ + compact?: boolean }>() const emit = defineEmits<{ @@ -47,12 +51,17 @@ const { t } = useI18n()

- -
+ +
diff --git a/src/modules/activities/views/ActivitiesPage.vue b/src/modules/activities/views/ActivitiesPage.vue index 3687e5a..b820560 100644 --- a/src/modules/activities/views/ActivitiesPage.vue +++ b/src/modules/activities/views/ActivitiesPage.vue @@ -90,8 +90,10 @@ function openCalendar() { -
+ strip so the tabs row stays focused on the primary views. + Hidden in the Hosting view — operators don't need calendar + navigation when they're managing their own roster. --> +
- + past-events toggle + category chips below. Hidden in the + Hosting view — the operator's roster doesn't need them. --> +
@@ -185,10 +188,13 @@ function openCalendar() { {{ error }}
- +
diff --git a/src/modules/activities/views/ActivityDetailPage.vue b/src/modules/activities/views/ActivityDetailPage.vue index 6d46e9c..dfd2d2b 100644 --- a/src/modules/activities/views/ActivityDetailPage.vue +++ b/src/modules/activities/views/ActivityDetailPage.vue @@ -170,36 +170,14 @@ function goToMyTickets() {