refactor(activities): useApprovalState composable
Both the activities-app shell and EventsPage probed isAdmin / autoApprove with slightly different code, both needed by the edit dialog's warning copy. Extract into a single composable so the probe sequence + re-probe-on-auth-flip behavior lives in one place. Also clear editingEvent eagerly when the bottom-nav Create tab fires so a Create tap never inherits a stale Edit selection from a prior close path that didn't run for any reason. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2b376bb244
commit
e540feba44
3 changed files with 61 additions and 41 deletions
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { computed } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { CalendarDays, Map, Heart, Search, Plus } from 'lucide-vue-next'
|
||||
|
|
@ -7,6 +7,7 @@ import AppShell from '@/components/layout/AppShell.vue'
|
|||
import type { BottomTab } from '@/components/layout/BottomNav.vue'
|
||||
import { useAuth } from '@/composables/useAuthService'
|
||||
import { useActivitiesStore } from '@/modules/activities/stores/activities'
|
||||
import { useApprovalState } from '@/modules/activities/composables/useApprovalState'
|
||||
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||
import type { TicketApiService } from '@/modules/activities/services/TicketApiService'
|
||||
import type { CreateEventRequest } from '@/modules/activities/types/ticket'
|
||||
|
|
@ -16,26 +17,7 @@ const route = useRoute()
|
|||
const { t } = useI18n()
|
||||
const { isAuthenticated, currentUser } = useAuth()
|
||||
const activitiesStore = useActivitiesStore()
|
||||
|
||||
// Probe LNbits admin status + extension auto_approve once at auth-ready
|
||||
// so the shell-mounted dialog renders the right warning copy when an
|
||||
// owner edits their own event.
|
||||
const isAdmin = ref(false)
|
||||
const autoApprove = ref(false)
|
||||
async function probeApprovalState() {
|
||||
if (!isAuthenticated.value) return
|
||||
const wallet = currentUser.value?.wallets?.[0]
|
||||
if (!wallet?.inkey) return
|
||||
const ticketApi = injectService(SERVICE_TOKENS.TICKET_API) as TicketApiService
|
||||
autoApprove.value = await ticketApi.getAutoApprove(wallet.inkey)
|
||||
if (wallet.adminkey) {
|
||||
isAdmin.value = await ticketApi.isAdmin(wallet.adminkey)
|
||||
}
|
||||
}
|
||||
onMounted(probeApprovalState)
|
||||
watch(isAuthenticated, (yes) => {
|
||||
if (yes) probeApprovalState()
|
||||
})
|
||||
const { isAdmin, autoApprove } = useApprovalState()
|
||||
|
||||
// Settings dropped — theme/lang/currency now live in the shared profile sheet.
|
||||
// Create lives in the bottom nav (auth-gated): activity creation is a deliberate
|
||||
|
|
@ -47,7 +29,12 @@ const tabs = computed<BottomTab[]>(() => [
|
|||
{
|
||||
name: t('activities.createNew'),
|
||||
icon: Plus,
|
||||
onClick: () => { activitiesStore.showCreateDialog = true },
|
||||
onClick: () => {
|
||||
// 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
|
||||
},
|
||||
disabled: !isAuthenticated.value,
|
||||
},
|
||||
{ name: t('activities.nav.map'), icon: Map, path: '/activities/map' },
|
||||
|
|
|
|||
49
src/modules/activities/composables/useApprovalState.ts
Normal file
49
src/modules/activities/composables/useApprovalState.ts
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import { onMounted, ref, watch } from 'vue'
|
||||
import { useAuth } from '@/composables/useAuthService'
|
||||
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||
import type { TicketApiService } from '../services/TicketApiService'
|
||||
|
||||
/**
|
||||
* Probe the events extension for the caller's approval-flow context:
|
||||
*
|
||||
* - `autoApprove` — is the global `auto_approve` toggle on? Reads the
|
||||
* invoice-key-gated public probe (v1.3.0-aio.5+), so non-admin
|
||||
* wallet holders get an accurate answer.
|
||||
* - `isAdmin` — is the caller an LNbits admin? Tries the admin-only
|
||||
* `/events/all` endpoint; a 200 means yes.
|
||||
*
|
||||
* Both refs default to `false` (the safer assumption for warning UI
|
||||
* — biased toward showing the "edit will go back to pending" copy
|
||||
* when in doubt). Probe re-runs whenever auth flips to authenticated.
|
||||
*
|
||||
* Used by every surface that opens the edit-mode CreateEventDialog
|
||||
* (activities-app/App.vue shell mount, activities EventsPage). Keeps
|
||||
* the probe logic single-source-of-truth.
|
||||
*/
|
||||
export function useApprovalState() {
|
||||
const { isAuthenticated, currentUser } = useAuth()
|
||||
const isAdmin = ref(false)
|
||||
const autoApprove = ref(false)
|
||||
|
||||
async function probe() {
|
||||
if (!isAuthenticated.value) {
|
||||
isAdmin.value = false
|
||||
autoApprove.value = false
|
||||
return
|
||||
}
|
||||
const wallet = currentUser.value?.wallets?.[0]
|
||||
if (!wallet?.inkey) return
|
||||
const ticketApi = injectService(SERVICE_TOKENS.TICKET_API) as TicketApiService
|
||||
autoApprove.value = await ticketApi.getAutoApprove(wallet.inkey)
|
||||
if (wallet.adminkey) {
|
||||
isAdmin.value = await ticketApi.isAdmin(wallet.adminkey)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(probe)
|
||||
watch(isAuthenticated, (yes) => {
|
||||
if (yes) probe()
|
||||
})
|
||||
|
||||
return { isAdmin, autoApprove, probe }
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useEvents } from '../composables/useEvents'
|
||||
import { useApprovalState } from '../composables/useApprovalState'
|
||||
import { useAuth } from '@/composables/useAuthService'
|
||||
import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
||||
|
|
@ -18,6 +19,7 @@ import type { CreateEventRequest, TicketedEvent } from '../types/ticket'
|
|||
|
||||
const { upcomingEvents, pastEvents, isLoading, error, refresh } = useEvents()
|
||||
const { isAuthenticated, userDisplay, currentUser } = useAuth()
|
||||
const { isAdmin, autoApprove } = useApprovalState()
|
||||
|
||||
const showPurchaseDialog = ref(false)
|
||||
const selectedEvent = ref<{
|
||||
|
|
@ -31,13 +33,6 @@ const showEventDialog = ref(false)
|
|||
// `null` ↔ create mode; populated ↔ edit mode.
|
||||
const editingEvent = ref<TicketedEvent | null>(null)
|
||||
|
||||
// Probe once at mount so the dialog can render the "going to pending"
|
||||
// warning accurately. Both probes degrade to safe defaults on failure
|
||||
// (not admin, not auto-approved), which biases the warning toward
|
||||
// being shown when in doubt.
|
||||
const isAdmin = ref(false)
|
||||
const autoApprove = ref(false)
|
||||
|
||||
const myWalletIds = computed(
|
||||
() => new Set((currentUser.value?.wallets ?? []).map((w) => w.id))
|
||||
)
|
||||
|
|
@ -46,17 +41,6 @@ function canEdit(event: TicketedEvent): boolean {
|
|||
return isAuthenticated.value && myWalletIds.value.has(event.wallet)
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
if (!isAuthenticated.value) return
|
||||
const wallet = currentUser.value?.wallets?.[0]
|
||||
if (!wallet?.inkey) return
|
||||
const ticketApi = injectService(SERVICE_TOKENS.TICKET_API) as TicketApiService
|
||||
autoApprove.value = await ticketApi.getAutoApprove(wallet.inkey)
|
||||
if (wallet.adminkey) {
|
||||
isAdmin.value = await ticketApi.isAdmin(wallet.adminkey)
|
||||
}
|
||||
})
|
||||
|
||||
function formatDate(dateStr: string | null | undefined) {
|
||||
if (!dateStr) return 'Date not available'
|
||||
const date = new Date(dateStr)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue