Adds date navigation to scheduled events
Implements date navigation for scheduled events, allowing users to view events for different days. This change replaces the static "Today's Events" section with a dynamic date selector. It introduces buttons for navigating to the previous and next days, as well as a "Today" button to return to the current date. A date display shows the selected date, and a message indicates when there are no scheduled events for a given day.
This commit is contained in:
parent
a27a8232f2
commit
0e42318036
3 changed files with 137 additions and 23 deletions
|
|
@ -9,7 +9,7 @@ import {
|
|||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Megaphone, RefreshCw, AlertCircle } from 'lucide-vue-next'
|
||||
import { Megaphone, RefreshCw, AlertCircle, ChevronLeft, ChevronRight } from 'lucide-vue-next'
|
||||
import { useFeed } from '../composables/useFeed'
|
||||
import { useProfiles } from '../composables/useProfiles'
|
||||
import { useReactions } from '../composables/useReactions'
|
||||
|
|
@ -99,10 +99,66 @@ const { getDisplayName, fetchProfiles } = useProfiles()
|
|||
const { getEventReactions, subscribeToReactions, toggleLike } = useReactions()
|
||||
|
||||
// Use scheduled events service
|
||||
const { getTodaysEvents, getCompletion, toggleComplete, allCompletions } = useScheduledEvents()
|
||||
const { getEventsForSpecificDate, getCompletion, toggleComplete, allCompletions } = useScheduledEvents()
|
||||
|
||||
// Get today's scheduled events (reactive)
|
||||
const todaysScheduledEvents = computed(() => getTodaysEvents())
|
||||
// Selected date for viewing events (defaults to today)
|
||||
const selectedDate = ref(new Date().toISOString().split('T')[0])
|
||||
|
||||
// Get scheduled events for the selected date (reactive)
|
||||
const scheduledEventsForDate = computed(() => getEventsForSpecificDate(selectedDate.value))
|
||||
|
||||
// Navigate to previous day
|
||||
function goToPreviousDay() {
|
||||
const date = new Date(selectedDate.value)
|
||||
date.setDate(date.getDate() - 1)
|
||||
selectedDate.value = date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
// Navigate to next day
|
||||
function goToNextDay() {
|
||||
const date = new Date(selectedDate.value)
|
||||
date.setDate(date.getDate() + 1)
|
||||
selectedDate.value = date.toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
// Go back to today
|
||||
function goToToday() {
|
||||
selectedDate.value = new Date().toISOString().split('T')[0]
|
||||
}
|
||||
|
||||
// Check if selected date is today
|
||||
const isToday = computed(() => {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
return selectedDate.value === today
|
||||
})
|
||||
|
||||
// Format date for display
|
||||
const dateDisplayText = computed(() => {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
const yesterday = new Date()
|
||||
yesterday.setDate(yesterday.getDate() - 1)
|
||||
const yesterdayStr = yesterday.toISOString().split('T')[0]
|
||||
const tomorrow = new Date()
|
||||
tomorrow.setDate(tomorrow.getDate() + 1)
|
||||
const tomorrowStr = tomorrow.toISOString().split('T')[0]
|
||||
|
||||
if (selectedDate.value === today) {
|
||||
return "Today's Events"
|
||||
} else if (selectedDate.value === yesterdayStr) {
|
||||
return "Yesterday's Events"
|
||||
} else if (selectedDate.value === tomorrowStr) {
|
||||
return "Tomorrow's Events"
|
||||
} else {
|
||||
// Format as "Events for Mon, Jan 15"
|
||||
const date = new Date(selectedDate.value + 'T00:00:00')
|
||||
const formatted = date.toLocaleDateString('en-US', {
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
})
|
||||
return `Events for ${formatted}`
|
||||
}
|
||||
})
|
||||
|
||||
// Watch for new posts and fetch their profiles and reactions
|
||||
watch(notes, async (newNotes) => {
|
||||
|
|
@ -119,12 +175,12 @@ watch(notes, async (newNotes) => {
|
|||
}, { immediate: true })
|
||||
|
||||
// Watch for scheduled events and fetch profiles for event authors and completers
|
||||
watch(todaysScheduledEvents, async (events) => {
|
||||
watch(scheduledEventsForDate, async (events) => {
|
||||
if (events.length > 0) {
|
||||
const pubkeys = new Set<string>()
|
||||
|
||||
// Add event authors
|
||||
events.forEach(event => {
|
||||
events.forEach((event: ScheduledEvent) => {
|
||||
pubkeys.add(event.pubkey)
|
||||
|
||||
// Add completer pubkey if event is completed
|
||||
|
|
@ -421,14 +477,50 @@ function cancelDelete() {
|
|||
|
||||
<!-- Posts List - Natural flow without internal scrolling -->
|
||||
<div v-else>
|
||||
<!-- Today's Scheduled Events Section -->
|
||||
<div v-if="todaysScheduledEvents.length > 0" class="mb-6 md:mb-8">
|
||||
<h3 class="text-sm font-semibold text-muted-foreground uppercase tracking-wide px-4 md:px-0 mb-3">
|
||||
📅 Today's Events
|
||||
</h3>
|
||||
<div class="md:space-y-3">
|
||||
<!-- Scheduled Events Section with Date Navigation -->
|
||||
<div v-if="scheduledEventsForDate.length > 0 || !isToday" class="mb-6 md:mb-8">
|
||||
<div class="flex items-center justify-between px-4 md:px-0 mb-3">
|
||||
<!-- Left Arrow -->
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
@click="goToPreviousDay"
|
||||
>
|
||||
<ChevronLeft class="h-4 w-4" />
|
||||
</Button>
|
||||
|
||||
<!-- Date Header with Today Button -->
|
||||
<div class="flex items-center gap-2">
|
||||
<h3 class="text-sm font-semibold text-muted-foreground uppercase tracking-wide">
|
||||
📅 {{ dateDisplayText }}
|
||||
</h3>
|
||||
<Button
|
||||
v-if="!isToday"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
class="h-6 text-xs"
|
||||
@click="goToToday"
|
||||
>
|
||||
Today
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Right Arrow -->
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
class="h-8 w-8"
|
||||
@click="goToNextDay"
|
||||
>
|
||||
<ChevronRight class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<!-- Events List or Empty State -->
|
||||
<div v-if="scheduledEventsForDate.length > 0" class="md:space-y-3">
|
||||
<ScheduledEventCard
|
||||
v-for="event in todaysScheduledEvents"
|
||||
v-for="event in scheduledEventsForDate"
|
||||
:key="`${event.pubkey}:${event.dTag}`"
|
||||
:event="event"
|
||||
:get-display-name="getDisplayName"
|
||||
|
|
@ -437,11 +529,14 @@ function cancelDelete() {
|
|||
@toggle-complete="onToggleComplete"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="text-center py-8 text-muted-foreground text-sm px-4">
|
||||
No events scheduled for this day
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Posts Section -->
|
||||
<div v-if="threadedPosts.length > 0" class="md:space-y-4 md:py-4">
|
||||
<h3 v-if="todaysScheduledEvents.length > 0" class="text-sm font-semibold text-muted-foreground uppercase tracking-wide px-4 md:px-0 mb-3 mt-6">
|
||||
<h3 v-if="scheduledEventsForDate.length > 0" class="text-sm font-semibold text-muted-foreground uppercase tracking-wide px-4 md:px-0 mb-3 mt-6">
|
||||
💬 Posts
|
||||
</h3>
|
||||
<ThreadedPost
|
||||
|
|
|
|||
|
|
@ -31,6 +31,15 @@ export function useScheduledEvents() {
|
|||
return scheduledEventService.getEventsForDate(date)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events for a specific date (filtered by current user participation)
|
||||
* @param date - ISO date string (YYYY-MM-DD). Defaults to today.
|
||||
*/
|
||||
const getEventsForSpecificDate = (date?: string): ScheduledEvent[] => {
|
||||
if (!scheduledEventService) return []
|
||||
return scheduledEventService.getEventsForSpecificDate(date, currentUserPubkey.value)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get today's scheduled events (filtered by current user participation)
|
||||
*/
|
||||
|
|
@ -141,6 +150,7 @@ export function useScheduledEvents() {
|
|||
// Methods
|
||||
getScheduledEvents,
|
||||
getEventsForDate,
|
||||
getEventsForSpecificDate,
|
||||
getTodaysEvents,
|
||||
getCompletion,
|
||||
isCompleted,
|
||||
|
|
|
|||
|
|
@ -251,22 +251,24 @@ export class ScheduledEventService extends BaseService {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get events for today, optionally filtered by user participation
|
||||
* Get events for a specific date, optionally filtered by user participation
|
||||
* @param date - ISO date string (YYYY-MM-DD). Defaults to today.
|
||||
* @param userPubkey - Optional user pubkey to filter by participation
|
||||
*/
|
||||
getTodaysEvents(userPubkey?: string): ScheduledEvent[] {
|
||||
const today = new Date().toISOString().split('T')[0]
|
||||
getEventsForSpecificDate(date?: string, userPubkey?: string): ScheduledEvent[] {
|
||||
const targetDate = date || new Date().toISOString().split('T')[0]
|
||||
|
||||
// Get one-time events for today (exclude recurring events to avoid duplicates)
|
||||
const oneTimeEvents = this.getEventsForDate(today).filter(event => !event.recurrence)
|
||||
// Get one-time events for the date (exclude recurring events to avoid duplicates)
|
||||
const oneTimeEvents = this.getEventsForDate(targetDate).filter(event => !event.recurrence)
|
||||
|
||||
// Get all events and check for recurring events that occur today
|
||||
// Get all events and check for recurring events that occur on this date
|
||||
const allEvents = this.getScheduledEvents()
|
||||
const recurringEventsToday = allEvents.filter(event =>
|
||||
event.recurrence && this.doesRecurringEventOccurOnDate(event, today)
|
||||
const recurringEventsOnDate = allEvents.filter(event =>
|
||||
event.recurrence && this.doesRecurringEventOccurOnDate(event, targetDate)
|
||||
)
|
||||
|
||||
// Combine one-time and recurring events
|
||||
let events = [...oneTimeEvents, ...recurringEventsToday]
|
||||
let events = [...oneTimeEvents, ...recurringEventsOnDate]
|
||||
|
||||
// Filter events based on participation (if user pubkey provided)
|
||||
if (userPubkey) {
|
||||
|
|
@ -288,6 +290,13 @@ export class ScheduledEventService extends BaseService {
|
|||
return events
|
||||
}
|
||||
|
||||
/**
|
||||
* Get events for today, optionally filtered by user participation
|
||||
*/
|
||||
getTodaysEvents(userPubkey?: string): ScheduledEvent[] {
|
||||
return this.getEventsForSpecificDate(undefined, userPubkey)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get completion status for an event (optionally for a specific occurrence)
|
||||
*/
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue