@@ -127,7 +127,6 @@ export const addOrRemoveNotification = async (programDetails) => {
127127 async ( ) => {
128128 const success = await deleteProgramNotification ( existingNotification . id ) ;
129129 if ( success ) {
130- // Notify other tabs via BroadcastChannel after a successful local action
131130 notificationChannel . postMessage ( { type : 'refresh-notifications' } ) ;
132131 guideState . userNotifications = guideState . userNotifications . filter ( n => n . id !== existingNotification . id ) ;
133132 renderNotifications ( ) ;
@@ -165,9 +164,7 @@ export const addOrRemoveNotification = async (programDetails) => {
165164 }
166165 }
167166
168- // --- **FIX 1: Robust lead time calculation** ---
169- // Ensure notificationLeadTime is a valid number, default to 10 if not.
170- const notificationLeadTime = parseInt ( guideState . settings . notificationLeadTime , 10 ) ;
167+ let notificationLeadTime = parseInt ( guideState . settings . notificationLeadTime , 10 ) ;
171168 if ( isNaN ( notificationLeadTime ) ) {
172169 console . warn ( `[NOTIF] Invalid 'notificationLeadTime' in settings: ${ guideState . settings . notificationLeadTime } . Defaulting to 10.` ) ;
173170 notificationLeadTime = 10 ;
@@ -178,7 +175,6 @@ export const addOrRemoveNotification = async (programDetails) => {
178175
179176 const programStartTime = new Date ( programDetails . programStart ) ;
180177
181- // --- **FIX 2: Validate program start time** ---
182178 if ( isNaN ( programStartTime . getTime ( ) ) ) {
183179 console . error ( '[NOTIF_ERROR] The program start time is invalid.' , programDetails . programStart ) ;
184180 showNotification ( 'Cannot set notification due to an invalid program start time.' , true ) ;
@@ -208,8 +204,7 @@ export const addOrRemoveNotification = async (programDetails) => {
208204 const addedNotification = await addProgramNotification ( newNotificationData ) ;
209205 if ( addedNotification ) {
210206 notificationChannel . postMessage ( { type : 'refresh-notifications' } ) ;
211- // Manually add the lead time to the object for immediate correct rendering
212- const completeNotification = { ...addedNotification , status : 'pending' , notificationLeadTime : notificationLeadTime } ;
207+ const completeNotification = { ...addedNotification , status : 'pending' } ;
213208 guideState . userNotifications . push ( completeNotification ) ;
214209
215210 renderNotifications ( ) ;
@@ -223,8 +218,7 @@ export const addOrRemoveNotification = async (programDetails) => {
223218} ;
224219
225220/**
226- * Checks if a given program has ANY notification scheduled (pending, sent, or expired).
227- * This makes the visual indicator in the guide persistent.
221+ * Checks if a given program has ANY notification scheduled.
228222 * @param {object } program - The program object.
229223 * @param {string } channelId - The ID of the channel the program belongs to.
230224 * @returns {object|null } The notification object if found, otherwise null.
@@ -251,17 +245,14 @@ export const renderNotifications = () => {
251245 UIElements . noNotificationsMessage . classList . toggle ( 'hidden' , upcomingNotifications . length > 0 ) ;
252246
253247 notificationListEl . innerHTML = upcomingNotifications . map ( notif => {
254- // --- **FIX 3: Robust rendering to prevent crashes and "undefined" bug** ---
255248 const programStartTime = new Date ( notif . programStart ) ;
256249 const notificationTime = new Date ( notif . scheduledTime ) ;
257250
258- // If dates are invalid, skip rendering this item to prevent crashes.
259251 if ( isNaN ( programStartTime . getTime ( ) ) || isNaN ( notificationTime . getTime ( ) ) ) {
260252 console . error ( '[NOTIF_RENDER] Skipping notification with invalid date:' , notif ) ;
261253 return '' ;
262254 }
263255
264- // Calculate lead time directly from timestamps for accuracy.
265256 const leadTimeMinutes = Math . round ( ( programStartTime . getTime ( ) - notificationTime . getTime ( ) ) / 60000 ) ;
266257
267258 const formattedProgramTime = programStartTime . toLocaleString ( [ ] , { weekday : 'short' , month : 'short' , day : 'numeric' , hour : '2-digit' , minute : '2-digit' , hour12 : false } ) ;
@@ -298,16 +289,21 @@ export const renderPastNotifications = () => {
298289 if ( ! pastNotificationsListEl ) return ;
299290
300291 const now = new Date ( ) ;
292+
293+ // --- **FIX: Correctly filter for past notifications based on the new server logic** ---
294+ // A notification is considered "past" if its scheduled time is in the past,
295+ // regardless of the master 'status', which is now derived on the server.
301296 const pastNotifications = guideState . userNotifications
302- . filter ( n => n . status === 'sent' || n . status === 'expired' )
303- . sort ( ( a , b ) => new Date ( b . triggeredAt || b . notificationTime ) - new Date ( a . triggeredAt || a . notificationTime ) )
304- . slice ( 0 , 10 ) ;
297+ . filter ( n => new Date ( n . scheduledTime ) . getTime ( ) <= now . getTime ( ) )
298+ . sort ( ( a , b ) => new Date ( b . scheduledTime ) - new Date ( a . scheduledTime ) ) // Sort by when it was supposed to trigger
299+ . slice ( 0 , 20 ) ; // Show a few more past notifications
305300
306301 UIElements . noPastNotificationsMessage . classList . toggle ( 'hidden' , pastNotifications . length > 0 ) ;
307302
308303 pastNotificationsListEl . innerHTML = pastNotifications . map ( notif => {
309304 const programStartTime = new Date ( notif . programStart ) ;
310- const notificationTriggerTime = new Date ( notif . triggeredAt || notif . notificationTime ) ;
305+ // Use the scheduledTime for display consistency, fallback to triggeredAt if needed.
306+ const notificationTriggerTime = new Date ( notif . triggeredAt || notif . scheduledTime ) ;
311307
312308 if ( isNaN ( programStartTime . getTime ( ) ) || isNaN ( notificationTriggerTime . getTime ( ) ) ) {
313309 return '' ;
@@ -317,10 +313,14 @@ export const renderPastNotifications = () => {
317313 const formattedTriggerTime = notificationTriggerTime . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' , hour12 : false } ) ;
318314
319315 let statusText = '' ;
316+ // The status from the server is now reliable for display.
320317 if ( notif . status === 'sent' ) {
321318 statusText = `Notified at ${ formattedTriggerTime } ` ;
322319 } else if ( notif . status === 'expired' ) {
323320 statusText = `Expired at ${ formattedTriggerTime } ` ;
321+ } else {
322+ // This case handles notifications that were due but might not have a final status yet.
323+ statusText = `Should have been notified at ${ formattedTriggerTime } ` ;
324324 }
325325
326326 return `
@@ -362,7 +362,6 @@ const setupNotificationListEventListeners = () => {
362362 async ( ) => {
363363 const success = await deleteProgramNotification ( notificationId ) ;
364364 if ( success ) {
365- // Notify other tabs via BroadcastChannel after a successful local action
366365 notificationChannel . postMessage ( { type : 'refresh-notifications' } ) ;
367366 guideState . userNotifications = guideState . userNotifications . filter ( n => n . id != notificationId ) ;
368367 renderNotifications ( ) ;
@@ -399,11 +398,9 @@ export const navigateToProgramInGuide = async (channelId, programStart, programI
399398 const stableChannelIdSuffix = channelId . includes ( '_' ) ? '_' + channelId . split ( '_' ) . pop ( ) : channelId ;
400399 console . log ( `[NOTIF_NAV] Using stable channel ID suffix for matching: "${ stableChannelIdSuffix } "` ) ;
401400
402- // 1. Navigate to the guide page
403401 navigate ( '/tvguide' ) ;
404402 await new Promise ( resolve => setTimeout ( resolve , 50 ) ) ;
405403
406- // 2. Adjust date if necessary
407404 const targetProgramStart = new Date ( programStart ) ;
408405 const currentGuideDate = new Date ( guideState . currentDate ) ;
409406 currentGuideDate . setHours ( 0 , 0 , 0 , 0 ) ;
@@ -414,7 +411,6 @@ export const navigateToProgramInGuide = async (channelId, programStart, programI
414411 await handleSearchAndFilter ( true ) ;
415412 }
416413
417- // 3. Scroll vertically and wait for the channel row to be rendered
418414 console . log ( '[NOTIF_DEBUG] Awaiting scrollToChannel to confirm vertical scroll and render.' ) ;
419415 const channelScrolledAndRendered = await scrollToChannel ( stableChannelIdSuffix ) ;
420416
@@ -424,17 +420,14 @@ export const navigateToProgramInGuide = async (channelId, programStart, programI
424420 }
425421 console . log ( '[NOTIF_DEBUG] scrollToChannel confirmed channel row is rendered.' ) ;
426422
427- // 4. Now that the row is rendered, find the program element directly with a FRESH query.
428- // This is crucial to avoid stale DOM references after potential re-renders from virtualization.
429423 const currentChannelElement = UIElements . guideGrid . querySelector ( `.channel-info[data-id$="${ stableChannelIdSuffix } "]` ) ;
430424 if ( ! currentChannelElement ) {
431425 console . error ( `[NOTIF_DEBUG] CRITICAL: Channel element with suffix ${ stableChannelIdSuffix } not found after scrollToChannel resolved true.` ) ;
432426 showNotification ( "An unexpected error occurred while locating the channel." , true ) ;
433427 return ;
434428 }
435- const currentDynamicChannelId = currentChannelElement . dataset . id ; // Get the full dynamic ID
429+ const currentDynamicChannelId = currentChannelElement . dataset . id ;
436430
437- // Use a more specific selector now that we have the actual rendered channel's ID
438431 const programElement = UIElements . guideGrid . querySelector (
439432 `.programme-item[data-prog-start="${ programStart } "][data-channel-id="${ currentDynamicChannelId } "]`
440433 ) ;
@@ -447,14 +440,11 @@ export const navigateToProgramInGuide = async (channelId, programStart, programI
447440
448441 console . log ( '[NOTIF_DEBUG] Program element found. Proceeding with centering and opening details.' ) ;
449442
450- // --- 5. Centering and Opening Logic ---
451443 const guideContainer = UIElements . guideContainer ;
452444
453- // Use getBoundingClientRect for accurate positioning relative to the viewport
454445 const programRect = programElement . getBoundingClientRect ( ) ;
455446 const containerRect = guideContainer . getBoundingClientRect ( ) ;
456447
457- // Calculate the desired scroll position to center the element
458448 const desiredScrollTop = guideContainer . scrollTop + programRect . top - containerRect . top - ( containerRect . height / 2 ) + ( programRect . height / 2 ) ;
459449 const desiredScrollLeft = guideContainer . scrollLeft + programRect . left - containerRect . left - ( containerRect . width / 2 ) + ( programRect . width / 2 ) ;
460450
@@ -464,13 +454,11 @@ export const navigateToProgramInGuide = async (channelId, programStart, programI
464454 behavior : 'smooth'
465455 } ) ;
466456
467- // Wait for the smooth scroll to have an effect before opening details
468457 setTimeout ( ( ) => {
469458 console . log ( '[NOTIF_DEBUG] Calling openProgramDetails directly.' ) ;
470- // Pass the re-queried programElement to ensure it's a live DOM node
471459 openProgramDetails ( programElement ) ;
472460
473461 programElement . classList . add ( 'highlighted-search' ) ;
474462 setTimeout ( ( ) => { programElement . classList . remove ( 'highlighted-search' ) ; } , 2500 ) ;
475- } , 300 ) ; // 300ms should be enough for the scroll animation to start
463+ } , 300 ) ;
476464} ;
0 commit comments