feat(activities): edit button on activity detail page
The NIP-52 d-tag we publish for an event is the LNbits event id (set in nostr_publisher.build_nip52_event), so a single fetchMyEvents call can tell us whether the displayed activity belongs to the caller. When it does, show an Edit button next to Bookmark; clicking sets the store's editingEvent and opens the shell-mounted dialog in edit mode. This was the missing surface — users land on /activities/:id when they tap a posting, not on /events. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
345ca073af
commit
b9bca36b50
1 changed files with 57 additions and 7 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { format } from 'date-fns'
|
import { format } from 'date-fns'
|
||||||
|
|
@ -8,13 +8,18 @@ import { Button } from '@/components/ui/button'
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { Separator } from '@/components/ui/separator'
|
import { Separator } from '@/components/ui/separator'
|
||||||
import {
|
import {
|
||||||
Calendar, MapPin, ArrowLeft,
|
Calendar, MapPin, ArrowLeft, Pencil,
|
||||||
} from 'lucide-vue-next'
|
} from 'lucide-vue-next'
|
||||||
import { useActivityDetail } from '../composables/useActivityDetail'
|
import { useActivityDetail } from '../composables/useActivityDetail'
|
||||||
import BookmarkButton from '../components/BookmarkButton.vue'
|
import BookmarkButton from '../components/BookmarkButton.vue'
|
||||||
import RSVPButton from '../components/RSVPButton.vue'
|
import RSVPButton from '../components/RSVPButton.vue'
|
||||||
import OrganizerCard from '../components/OrganizerCard.vue'
|
import OrganizerCard from '../components/OrganizerCard.vue'
|
||||||
import { NIP52_KINDS } from '../types/nip52'
|
import { NIP52_KINDS } from '../types/nip52'
|
||||||
|
import { useAuth } from '@/composables/useAuthService'
|
||||||
|
import { useActivitiesStore } from '../stores/activities'
|
||||||
|
import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||||
|
import type { TicketApiService } from '../services/TicketApiService'
|
||||||
|
import type { TicketedEvent } from '../types/ticket'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
@ -24,6 +29,38 @@ const activityId = route.params.id as string
|
||||||
const { activity, isLoading, error, reload } = useActivityDetail(activityId)
|
const { activity, isLoading, error, reload } = useActivityDetail(activityId)
|
||||||
const { dateLocale } = useDateLocale()
|
const { dateLocale } = useDateLocale()
|
||||||
|
|
||||||
|
// Owner-edit affordance: the NIP-52 d-tag we use for the activity id is
|
||||||
|
// the same as the LNbits event id (set at publish time in
|
||||||
|
// nostr_publisher.build_nip52_event). Look the user's own events up
|
||||||
|
// once and offer an Edit button on a match.
|
||||||
|
const { isAuthenticated, currentUser } = useAuth()
|
||||||
|
const activitiesStore = useActivitiesStore()
|
||||||
|
const ownedLnbitsEvent = ref<TicketedEvent | null>(null)
|
||||||
|
|
||||||
|
async function loadOwnedEvent() {
|
||||||
|
ownedLnbitsEvent.value = null
|
||||||
|
if (!isAuthenticated.value) return
|
||||||
|
const invoiceKey = currentUser.value?.wallets?.[0]?.inkey
|
||||||
|
if (!invoiceKey) return
|
||||||
|
try {
|
||||||
|
const ticketApi = injectService(SERVICE_TOKENS.TICKET_API) as TicketApiService
|
||||||
|
const mine = await ticketApi.fetchMyEvents(invoiceKey)
|
||||||
|
ownedLnbitsEvent.value =
|
||||||
|
(mine as TicketedEvent[]).find((e) => e.id === activityId) ?? null
|
||||||
|
} catch {
|
||||||
|
ownedLnbitsEvent.value = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(loadOwnedEvent)
|
||||||
|
watch(isAuthenticated, () => loadOwnedEvent())
|
||||||
|
|
||||||
|
function openEditDialog() {
|
||||||
|
if (!ownedLnbitsEvent.value) return
|
||||||
|
activitiesStore.editingEvent = ownedLnbitsEvent.value
|
||||||
|
activitiesStore.showCreateDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
const dateDisplay = computed(() => {
|
const dateDisplay = computed(() => {
|
||||||
if (!activity.value) return ''
|
if (!activity.value) return ''
|
||||||
const a = activity.value
|
const a = activity.value
|
||||||
|
|
@ -60,11 +97,24 @@ function goBack() {
|
||||||
<ArrowLeft class="w-4 h-4" />
|
<ArrowLeft class="w-4 h-4" />
|
||||||
Back
|
Back
|
||||||
</Button>
|
</Button>
|
||||||
<BookmarkButton
|
<div class="flex items-center gap-1.5">
|
||||||
v-if="activity"
|
<Button
|
||||||
:pubkey="activity.organizer.pubkey"
|
v-if="ownedLnbitsEvent"
|
||||||
:d-tag="activity.id"
|
variant="ghost"
|
||||||
/>
|
size="sm"
|
||||||
|
class="gap-1.5"
|
||||||
|
@click="openEditDialog"
|
||||||
|
aria-label="Edit event"
|
||||||
|
>
|
||||||
|
<Pencil class="w-4 h-4" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<BookmarkButton
|
||||||
|
v-if="activity"
|
||||||
|
:pubkey="activity.organizer.pubkey"
|
||||||
|
:d-tag="activity.id"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Loading -->
|
<!-- Loading -->
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue