Enables recurring scheduled event completion
Extends scheduled event completion to support recurring events. The changes introduce the concept of an "occurrence" for recurring events, allowing users to mark individual instances of a recurring event as complete. This involves: - Adding recurrence information to the ScheduledEvent model. - Modifying completion logic to handle recurring events with daily/weekly frequencies - Updating UI to display recurrence information and mark individual occurrences as complete.
This commit is contained in:
parent
8381d43268
commit
706ceea84b
4 changed files with 147 additions and 46 deletions
|
|
@ -200,10 +200,10 @@ async function onToggleLike(note: FeedPost) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle scheduled event completion toggle
|
// Handle scheduled event completion toggle
|
||||||
async function onToggleComplete(event: ScheduledEvent) {
|
async function onToggleComplete(event: ScheduledEvent, occurrence?: string) {
|
||||||
console.log('🎯 NostrFeed: onToggleComplete called for event:', event.title)
|
console.log('🎯 NostrFeed: onToggleComplete called for event:', event.title, 'occurrence:', occurrence)
|
||||||
try {
|
try {
|
||||||
await toggleComplete(event)
|
await toggleComplete(event, occurrence)
|
||||||
console.log('✅ NostrFeed: toggleComplete succeeded')
|
console.log('✅ NostrFeed: toggleComplete succeeded')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ NostrFeed: Failed to toggle event completion:', error)
|
console.error('❌ NostrFeed: Failed to toggle event completion:', error)
|
||||||
|
|
|
||||||
|
|
@ -21,12 +21,12 @@ import type { ScheduledEvent, EventCompletion } from '../services/ScheduledEvent
|
||||||
interface Props {
|
interface Props {
|
||||||
event: ScheduledEvent
|
event: ScheduledEvent
|
||||||
getDisplayName: (pubkey: string) => string
|
getDisplayName: (pubkey: string) => string
|
||||||
getCompletion: (eventAddress: string) => EventCompletion | undefined
|
getCompletion: (eventAddress: string, occurrence?: string) => EventCompletion | undefined
|
||||||
adminPubkeys?: string[]
|
adminPubkeys?: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Emits {
|
interface Emits {
|
||||||
(e: 'toggle-complete', event: ScheduledEvent): void
|
(e: 'toggle-complete', event: ScheduledEvent, occurrence?: string): void
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
|
@ -41,11 +41,20 @@ const showConfirmDialog = ref(false)
|
||||||
// Event address for tracking completion
|
// Event address for tracking completion
|
||||||
const eventAddress = computed(() => `31922:${props.event.pubkey}:${props.event.dTag}`)
|
const eventAddress = computed(() => `31922:${props.event.pubkey}:${props.event.dTag}`)
|
||||||
|
|
||||||
|
// Check if this is a recurring event
|
||||||
|
const isRecurring = computed(() => !!props.event.recurrence)
|
||||||
|
|
||||||
|
// For recurring events, occurrence is today's date. For non-recurring, it's undefined.
|
||||||
|
const occurrence = computed(() => {
|
||||||
|
if (!isRecurring.value) return undefined
|
||||||
|
return new Date().toISOString().split('T')[0] // YYYY-MM-DD
|
||||||
|
})
|
||||||
|
|
||||||
// Check if this is an admin event
|
// Check if this is an admin event
|
||||||
const isAdminEvent = computed(() => props.adminPubkeys.includes(props.event.pubkey))
|
const isAdminEvent = computed(() => props.adminPubkeys.includes(props.event.pubkey))
|
||||||
|
|
||||||
// Check if event is completed - call function directly
|
// Check if event is completed - call function with occurrence for recurring events
|
||||||
const isCompleted = computed(() => props.getCompletion(eventAddress.value)?.completed || false)
|
const isCompleted = computed(() => props.getCompletion(eventAddress.value, occurrence.value)?.completed || false)
|
||||||
|
|
||||||
// Check if event is completable (task type)
|
// Check if event is completable (task type)
|
||||||
const isCompletable = computed(() => props.event.eventType === 'task')
|
const isCompletable = computed(() => props.event.eventType === 'task')
|
||||||
|
|
@ -109,8 +118,8 @@ function handleMarkComplete() {
|
||||||
|
|
||||||
// Confirm and execute mark complete
|
// Confirm and execute mark complete
|
||||||
function confirmMarkComplete() {
|
function confirmMarkComplete() {
|
||||||
console.log('✅ Confirmed mark complete for event:', props.event.title)
|
console.log('✅ Confirmed mark complete for event:', props.event.title, 'occurrence:', occurrence.value)
|
||||||
emit('toggle-complete', props.event)
|
emit('toggle-complete', props.event, occurrence.value)
|
||||||
showConfirmDialog.value = false
|
showConfirmDialog.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -152,8 +161,13 @@ function cancelMarkComplete() {
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<!-- Completed Badge with completer name -->
|
<!-- Completed Badge with completer name -->
|
||||||
<Badge v-if="isCompletable && isCompleted && getCompletion(eventAddress)" variant="secondary" class="text-xs">
|
<Badge v-if="isCompletable && isCompleted && getCompletion(eventAddress, occurrence)" variant="secondary" class="text-xs">
|
||||||
✓ {{ getDisplayName(getCompletion(eventAddress)!.pubkey) }}
|
✓ {{ getDisplayName(getCompletion(eventAddress, occurrence)!.pubkey) }}
|
||||||
|
</Badge>
|
||||||
|
|
||||||
|
<!-- Recurring Badge -->
|
||||||
|
<Badge v-if="isRecurring" variant="outline" class="text-xs">
|
||||||
|
🔄
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
<!-- Admin Badge -->
|
<!-- Admin Badge -->
|
||||||
|
|
@ -192,9 +206,9 @@ function cancelMarkComplete() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Completion info (only for completable events) -->
|
<!-- Completion info (only for completable events) -->
|
||||||
<div v-if="isCompletable && isCompleted && getCompletion(eventAddress)" class="text-xs text-muted-foreground mb-3">
|
<div v-if="isCompletable && isCompleted && getCompletion(eventAddress, occurrence)" class="text-xs text-muted-foreground mb-3">
|
||||||
✓ Completed by {{ getDisplayName(getCompletion(eventAddress)!.pubkey) }}
|
✓ Completed by {{ getDisplayName(getCompletion(eventAddress, occurrence)!.pubkey) }}
|
||||||
<span v-if="getCompletion(eventAddress)!.notes"> - {{ getCompletion(eventAddress)!.notes }}</span>
|
<span v-if="getCompletion(eventAddress, occurrence)!.notes"> - {{ getCompletion(eventAddress, occurrence)!.notes }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Author (if not admin) -->
|
<!-- Author (if not admin) -->
|
||||||
|
|
|
||||||
|
|
@ -56,10 +56,10 @@ export function useScheduledEvents() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle completion status of an event
|
* Toggle completion status of an event (optionally for a specific occurrence)
|
||||||
*/
|
*/
|
||||||
const toggleComplete = async (event: ScheduledEvent, notes: string = ''): Promise<void> => {
|
const toggleComplete = async (event: ScheduledEvent, occurrence?: string, notes: string = ''): Promise<void> => {
|
||||||
console.log('🔧 useScheduledEvents: toggleComplete called for event:', event.title)
|
console.log('🔧 useScheduledEvents: toggleComplete called for event:', event.title, 'occurrence:', occurrence)
|
||||||
|
|
||||||
if (!scheduledEventService) {
|
if (!scheduledEventService) {
|
||||||
console.error('❌ useScheduledEvents: Scheduled event service not available')
|
console.error('❌ useScheduledEvents: Scheduled event service not available')
|
||||||
|
|
@ -69,16 +69,16 @@ export function useScheduledEvents() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const eventAddress = `31922:${event.pubkey}:${event.dTag}`
|
const eventAddress = `31922:${event.pubkey}:${event.dTag}`
|
||||||
const currentlyCompleted = scheduledEventService.isCompleted(eventAddress)
|
const currentlyCompleted = scheduledEventService.isCompleted(eventAddress, occurrence)
|
||||||
console.log('📊 useScheduledEvents: Current completion status:', currentlyCompleted)
|
console.log('📊 useScheduledEvents: Current completion status:', currentlyCompleted)
|
||||||
|
|
||||||
if (currentlyCompleted) {
|
if (currentlyCompleted) {
|
||||||
console.log('⬇️ useScheduledEvents: Marking as incomplete...')
|
console.log('⬇️ useScheduledEvents: Marking as incomplete...')
|
||||||
await scheduledEventService.uncompleteEvent(event)
|
await scheduledEventService.uncompleteEvent(event, occurrence)
|
||||||
toast.success('Event marked as incomplete')
|
toast.success('Event marked as incomplete')
|
||||||
} else {
|
} else {
|
||||||
console.log('⬆️ useScheduledEvents: Marking as complete...')
|
console.log('⬆️ useScheduledEvents: Marking as complete...')
|
||||||
await scheduledEventService.completeEvent(event, notes)
|
await scheduledEventService.completeEvent(event, notes, occurrence)
|
||||||
toast.success('Event completed!')
|
toast.success('Event completed!')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,12 @@ import { injectService, SERVICE_TOKENS } from '@/core/di-container'
|
||||||
import { finalizeEvent, type EventTemplate } from 'nostr-tools'
|
import { finalizeEvent, type EventTemplate } from 'nostr-tools'
|
||||||
import type { Event as NostrEvent } from 'nostr-tools'
|
import type { Event as NostrEvent } from 'nostr-tools'
|
||||||
|
|
||||||
|
export interface RecurrencePattern {
|
||||||
|
frequency: 'daily' | 'weekly'
|
||||||
|
dayOfWeek?: string // For weekly: 'monday', 'tuesday', etc.
|
||||||
|
endDate?: string // ISO date string - when to stop recurring (optional)
|
||||||
|
}
|
||||||
|
|
||||||
export interface ScheduledEvent {
|
export interface ScheduledEvent {
|
||||||
id: string
|
id: string
|
||||||
pubkey: string
|
pubkey: string
|
||||||
|
|
@ -19,11 +25,13 @@ export interface ScheduledEvent {
|
||||||
participants?: Array<{ pubkey: string; type?: string }> // 'required', 'optional', 'organizer'
|
participants?: Array<{ pubkey: string; type?: string }> // 'required', 'optional', 'organizer'
|
||||||
content: string
|
content: string
|
||||||
tags: string[][]
|
tags: string[][]
|
||||||
|
recurrence?: RecurrencePattern // Optional: for recurring events
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventCompletion {
|
export interface EventCompletion {
|
||||||
id: string
|
id: string
|
||||||
eventAddress: string // "31922:pubkey:d-tag"
|
eventAddress: string // "31922:pubkey:d-tag"
|
||||||
|
occurrence?: string // ISO date string for the specific occurrence (YYYY-MM-DD)
|
||||||
pubkey: string // Who completed it
|
pubkey: string // Who completed it
|
||||||
created_at: number
|
created_at: number
|
||||||
completed: boolean
|
completed: boolean
|
||||||
|
|
@ -87,6 +95,20 @@ export class ScheduledEventService extends BaseService {
|
||||||
type: tag[3] // 'required', 'optional', 'organizer'
|
type: tag[3] // 'required', 'optional', 'organizer'
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// Parse recurrence tags
|
||||||
|
const recurrenceFreq = event.tags.find(tag => tag[0] === 'recurrence')?.[1] as 'daily' | 'weekly' | undefined
|
||||||
|
const recurrenceDayOfWeek = event.tags.find(tag => tag[0] === 'recurrence-day')?.[1]
|
||||||
|
const recurrenceEndDate = event.tags.find(tag => tag[0] === 'recurrence-end')?.[1]
|
||||||
|
|
||||||
|
let recurrence: RecurrencePattern | undefined
|
||||||
|
if (recurrenceFreq === 'daily' || recurrenceFreq === 'weekly') {
|
||||||
|
recurrence = {
|
||||||
|
frequency: recurrenceFreq,
|
||||||
|
dayOfWeek: recurrenceDayOfWeek,
|
||||||
|
endDate: recurrenceEndDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!start) {
|
if (!start) {
|
||||||
console.warn('Scheduled event missing start date:', event.id)
|
console.warn('Scheduled event missing start date:', event.id)
|
||||||
return
|
return
|
||||||
|
|
@ -109,7 +131,8 @@ export class ScheduledEventService extends BaseService {
|
||||||
eventType,
|
eventType,
|
||||||
participants: participants.length > 0 ? participants : undefined,
|
participants: participants.length > 0 ? participants : undefined,
|
||||||
content: event.content,
|
content: event.content,
|
||||||
tags: event.tags
|
tags: event.tags,
|
||||||
|
recurrence
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store or update the event (replaceable by d-tag)
|
// Store or update the event (replaceable by d-tag)
|
||||||
|
|
@ -138,9 +161,11 @@ export class ScheduledEventService extends BaseService {
|
||||||
const completed = event.tags.find(tag => tag[0] === 'completed')?.[1] === 'true'
|
const completed = event.tags.find(tag => tag[0] === 'completed')?.[1] === 'true'
|
||||||
const completedAtTag = event.tags.find(tag => tag[0] === 'completed_at')?.[1]
|
const completedAtTag = event.tags.find(tag => tag[0] === 'completed_at')?.[1]
|
||||||
const completedAt = completedAtTag ? parseInt(completedAtTag) : undefined
|
const completedAt = completedAtTag ? parseInt(completedAtTag) : undefined
|
||||||
|
const occurrence = event.tags.find(tag => tag[0] === 'occurrence')?.[1] // ISO date string
|
||||||
|
|
||||||
console.log('📋 Completion details:', {
|
console.log('📋 Completion details:', {
|
||||||
aTag,
|
aTag,
|
||||||
|
occurrence,
|
||||||
completed,
|
completed,
|
||||||
pubkey: event.pubkey,
|
pubkey: event.pubkey,
|
||||||
eventId: event.id
|
eventId: event.id
|
||||||
|
|
@ -149,6 +174,7 @@ export class ScheduledEventService extends BaseService {
|
||||||
const completion: EventCompletion = {
|
const completion: EventCompletion = {
|
||||||
id: event.id,
|
id: event.id,
|
||||||
eventAddress: aTag,
|
eventAddress: aTag,
|
||||||
|
occurrence,
|
||||||
pubkey: event.pubkey,
|
pubkey: event.pubkey,
|
||||||
created_at: event.created_at,
|
created_at: event.created_at,
|
||||||
completed,
|
completed,
|
||||||
|
|
@ -157,12 +183,15 @@ export class ScheduledEventService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store completion (most recent one wins)
|
// Store completion (most recent one wins)
|
||||||
const existing = this._completions.get(aTag)
|
// For recurring events, include occurrence in the key: "eventAddress:occurrence"
|
||||||
|
// For non-recurring, just use eventAddress
|
||||||
|
const completionKey = occurrence ? `${aTag}:${occurrence}` : aTag
|
||||||
|
const existing = this._completions.get(completionKey)
|
||||||
if (!existing || event.created_at > existing.created_at) {
|
if (!existing || event.created_at > existing.created_at) {
|
||||||
this._completions.set(aTag, completion)
|
this._completions.set(completionKey, completion)
|
||||||
console.log('✅ Stored completion for:', aTag, '- completed:', completed)
|
console.log('✅ Stored completion for:', completionKey, '- completed:', completed)
|
||||||
} else {
|
} else {
|
||||||
console.log('⏭️ Skipped older completion for:', aTag)
|
console.log('⏭️ Skipped older completion for:', completionKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -189,12 +218,55 @@ export class ScheduledEventService extends BaseService {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a recurring event occurs on a specific date
|
||||||
|
*/
|
||||||
|
private doesRecurringEventOccurOnDate(event: ScheduledEvent, targetDate: string): boolean {
|
||||||
|
if (!event.recurrence) return false
|
||||||
|
|
||||||
|
const target = new Date(targetDate)
|
||||||
|
const eventStart = new Date(event.start.split('T')[0]) // Get date part only
|
||||||
|
|
||||||
|
// Check if target date is before the event start date
|
||||||
|
if (target < eventStart) return false
|
||||||
|
|
||||||
|
// Check if target date is after the event end date (if specified)
|
||||||
|
if (event.recurrence.endDate) {
|
||||||
|
const endDate = new Date(event.recurrence.endDate)
|
||||||
|
if (target > endDate) return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check frequency-specific rules
|
||||||
|
if (event.recurrence.frequency === 'daily') {
|
||||||
|
// Daily events occur every day within the range
|
||||||
|
return true
|
||||||
|
} else if (event.recurrence.frequency === 'weekly') {
|
||||||
|
// Weekly events occur on specific day of week
|
||||||
|
const targetDayOfWeek = target.toLocaleDateString('en-US', { weekday: 'long' }).toLowerCase()
|
||||||
|
const eventDayOfWeek = event.recurrence.dayOfWeek?.toLowerCase()
|
||||||
|
return targetDayOfWeek === eventDayOfWeek
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get events for today, optionally filtered by user participation
|
* Get events for today, optionally filtered by user participation
|
||||||
*/
|
*/
|
||||||
getTodaysEvents(userPubkey?: string): ScheduledEvent[] {
|
getTodaysEvents(userPubkey?: string): ScheduledEvent[] {
|
||||||
const today = new Date().toISOString().split('T')[0]
|
const today = new Date().toISOString().split('T')[0]
|
||||||
let events = this.getEventsForDate(today)
|
|
||||||
|
// Get one-time events for today
|
||||||
|
const oneTimeEvents = this.getEventsForDate(today)
|
||||||
|
|
||||||
|
// Get all events and check for recurring events that occur today
|
||||||
|
const allEvents = this.getScheduledEvents()
|
||||||
|
const recurringEventsToday = allEvents.filter(event =>
|
||||||
|
event.recurrence && this.doesRecurringEventOccurOnDate(event, today)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Combine one-time and recurring events
|
||||||
|
let events = [...oneTimeEvents, ...recurringEventsToday]
|
||||||
|
|
||||||
// Filter events based on participation (if user pubkey provided)
|
// Filter events based on participation (if user pubkey provided)
|
||||||
if (userPubkey) {
|
if (userPubkey) {
|
||||||
|
|
@ -217,24 +289,25 @@ export class ScheduledEventService extends BaseService {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get completion status for an event
|
* Get completion status for an event (optionally for a specific occurrence)
|
||||||
*/
|
*/
|
||||||
getCompletion(eventAddress: string): EventCompletion | undefined {
|
getCompletion(eventAddress: string, occurrence?: string): EventCompletion | undefined {
|
||||||
return this._completions.get(eventAddress)
|
const completionKey = occurrence ? `${eventAddress}:${occurrence}` : eventAddress
|
||||||
|
return this._completions.get(completionKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an event is completed
|
* Check if an event is completed (optionally for a specific occurrence)
|
||||||
*/
|
*/
|
||||||
isCompleted(eventAddress: string): boolean {
|
isCompleted(eventAddress: string, occurrence?: string): boolean {
|
||||||
const completion = this.getCompletion(eventAddress)
|
const completion = this.getCompletion(eventAddress, occurrence)
|
||||||
return completion?.completed || false
|
return completion?.completed || false
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark an event as complete
|
* Mark an event as complete (optionally for a specific occurrence)
|
||||||
*/
|
*/
|
||||||
async completeEvent(event: ScheduledEvent, notes: string = ''): Promise<void> {
|
async completeEvent(event: ScheduledEvent, notes: string = '', occurrence?: string): Promise<void> {
|
||||||
if (!this.authService?.isAuthenticated?.value) {
|
if (!this.authService?.isAuthenticated?.value) {
|
||||||
throw new Error('Must be authenticated to complete events')
|
throw new Error('Must be authenticated to complete events')
|
||||||
}
|
}
|
||||||
|
|
@ -254,15 +327,22 @@ export class ScheduledEventService extends BaseService {
|
||||||
const eventAddress = `31922:${event.pubkey}:${event.dTag}`
|
const eventAddress = `31922:${event.pubkey}:${event.dTag}`
|
||||||
|
|
||||||
// Create RSVP/completion event (NIP-52)
|
// Create RSVP/completion event (NIP-52)
|
||||||
|
const tags: string[][] = [
|
||||||
|
['a', eventAddress],
|
||||||
|
['status', 'accepted'],
|
||||||
|
['completed', 'true'],
|
||||||
|
['completed_at', Math.floor(Date.now() / 1000).toString()]
|
||||||
|
]
|
||||||
|
|
||||||
|
// Add occurrence tag if provided (for recurring events)
|
||||||
|
if (occurrence) {
|
||||||
|
tags.push(['occurrence', occurrence])
|
||||||
|
}
|
||||||
|
|
||||||
const eventTemplate: EventTemplate = {
|
const eventTemplate: EventTemplate = {
|
||||||
kind: 31925, // Calendar Event RSVP
|
kind: 31925, // Calendar Event RSVP
|
||||||
content: notes,
|
content: notes,
|
||||||
tags: [
|
tags,
|
||||||
['a', eventAddress],
|
|
||||||
['status', 'accepted'],
|
|
||||||
['completed', 'true'],
|
|
||||||
['completed_at', Math.floor(Date.now() / 1000).toString()]
|
|
||||||
],
|
|
||||||
created_at: Math.floor(Date.now() / 1000)
|
created_at: Math.floor(Date.now() / 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -290,7 +370,7 @@ export class ScheduledEventService extends BaseService {
|
||||||
/**
|
/**
|
||||||
* Uncomplete an event (publish new RSVP with completed=false)
|
* Uncomplete an event (publish new RSVP with completed=false)
|
||||||
*/
|
*/
|
||||||
async uncompleteEvent(event: ScheduledEvent): Promise<void> {
|
async uncompleteEvent(event: ScheduledEvent, occurrence?: string): Promise<void> {
|
||||||
if (!this.authService?.isAuthenticated?.value) {
|
if (!this.authService?.isAuthenticated?.value) {
|
||||||
throw new Error('Must be authenticated to uncomplete events')
|
throw new Error('Must be authenticated to uncomplete events')
|
||||||
}
|
}
|
||||||
|
|
@ -310,14 +390,21 @@ export class ScheduledEventService extends BaseService {
|
||||||
const eventAddress = `31922:${event.pubkey}:${event.dTag}`
|
const eventAddress = `31922:${event.pubkey}:${event.dTag}`
|
||||||
|
|
||||||
// Create RSVP event with completed=false
|
// Create RSVP event with completed=false
|
||||||
|
const tags: string[][] = [
|
||||||
|
['a', eventAddress],
|
||||||
|
['status', 'tentative'],
|
||||||
|
['completed', 'false']
|
||||||
|
]
|
||||||
|
|
||||||
|
// Add occurrence tag if provided (for recurring events)
|
||||||
|
if (occurrence) {
|
||||||
|
tags.push(['occurrence', occurrence])
|
||||||
|
}
|
||||||
|
|
||||||
const eventTemplate: EventTemplate = {
|
const eventTemplate: EventTemplate = {
|
||||||
kind: 31925,
|
kind: 31925,
|
||||||
content: '',
|
content: '',
|
||||||
tags: [
|
tags,
|
||||||
['a', eventAddress],
|
|
||||||
['status', 'tentative'],
|
|
||||||
['completed', 'false']
|
|
||||||
],
|
|
||||||
created_at: Math.floor(Date.now() / 1000)
|
created_at: Math.floor(Date.now() / 1000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue