diff --git a/frontend/src/pages/Guide.jsx b/frontend/src/pages/Guide.jsx
index 11b17e5b..9c90bb1d 100644
--- a/frontend/src/pages/Guide.jsx
+++ b/frontend/src/pages/Guide.jsx
@@ -1,5 +1,11 @@
// frontend/src/pages/Guide.js
-import React, { useMemo, useState, useEffect, useRef, useCallback } from 'react';
+import React, {
+ useMemo,
+ useState,
+ useEffect,
+ useRef,
+ useCallback,
+} from 'react';
import dayjs from 'dayjs';
import API from '../api';
import useChannelsStore from '../store/channels';
@@ -204,26 +210,28 @@ const GuideRow = React.memo(({ index, style, data }) => {
channelPrograms.map((program) => renderProgram(program))
) : (
<>
- {Array.from({ length: Math.ceil(24 / 2) }).map((_, placeholderIndex) => (
-
- No program data
-
- ))}
+ {Array.from({ length: Math.ceil(24 / 2) }).map(
+ (_, placeholderIndex) => (
+
+ No program data
+
+ )
+ )}
>
)}
@@ -435,29 +443,33 @@ export default function TVChannelGuide({ startDate, endDate }) {
const [timeFormatSetting] = useLocalStorage('time-format', '12h');
const [dateFormatSetting] = useLocalStorage('date-format', 'mdy');
+ // Use user preference for time format
const timeFormat = timeFormatSetting === '12h' ? 'h:mm A' : 'HH:mm';
const dateFormat = dateFormatSetting === 'mdy' ? 'MMMM D' : 'D MMMM';
// Format day label using relative terms when possible (Today, Tomorrow, etc)
- const formatDayLabel = useCallback((time) => {
- const today = dayjs().startOf('day');
- const tomorrow = today.add(1, 'day');
- const weekLater = today.add(7, 'day');
+ const formatDayLabel = useCallback(
+ (time) => {
+ const today = dayjs().startOf('day');
+ const tomorrow = today.add(1, 'day');
+ const weekLater = today.add(7, 'day');
- const day = time.startOf('day');
+ const day = time.startOf('day');
- if (day.isSame(today, 'day')) {
- return 'Today';
- } else if (day.isSame(tomorrow, 'day')) {
- return 'Tomorrow';
- } else if (day.isBefore(weekLater)) {
- // Within a week, show day name
- return time.format('dddd');
- } else {
- // Beyond a week, show month and day
- return time.format(dateFormat);
- }
- }, [dateFormat]);
+ if (day.isSame(today, 'day')) {
+ return 'Today';
+ } else if (day.isSame(tomorrow, 'day')) {
+ return 'Tomorrow';
+ } else if (day.isBefore(weekLater)) {
+ // Within a week, show day name
+ return time.format('dddd');
+ } else {
+ // Beyond a week, show month and day
+ return time.format(dateFormat);
+ }
+ },
+ [dateFormat]
+ );
// Hourly marks with day labels
const hourTimeline = useMemo(() => {
@@ -503,7 +515,10 @@ export default function TVChannelGuide({ startDate, endDate }) {
return;
}
- if (timelineRef.current && timelineRef.current.scrollLeft !== scrollLeft) {
+ if (
+ timelineRef.current &&
+ timelineRef.current.scrollLeft !== scrollLeft
+ ) {
isSyncingScroll.current = true;
timelineRef.current.scrollLeft = scrollLeft;
requestAnimationFrame(() => {
@@ -533,38 +548,35 @@ export default function TVChannelGuide({ startDate, endDate }) {
return (minutesSinceStart / MINUTE_INCREMENT) * MINUTE_BLOCK_WIDTH;
}, [now, start, end]);
- const syncScrollLeft = useCallback(
- (nextLeft, behavior = 'auto') => {
- const guideNode = guideRef.current;
- const timelineNode = timelineRef.current;
+ const syncScrollLeft = useCallback((nextLeft, behavior = 'auto') => {
+ const guideNode = guideRef.current;
+ const timelineNode = timelineRef.current;
- isSyncingScroll.current = true;
+ isSyncingScroll.current = true;
- if (guideNode) {
- if (typeof guideNode.scrollTo === 'function') {
- guideNode.scrollTo({ left: nextLeft, behavior });
- } else {
- guideNode.scrollLeft = nextLeft;
- }
+ if (guideNode) {
+ if (typeof guideNode.scrollTo === 'function') {
+ guideNode.scrollTo({ left: nextLeft, behavior });
+ } else {
+ guideNode.scrollLeft = nextLeft;
}
+ }
- if (timelineNode) {
- if (typeof timelineNode.scrollTo === 'function') {
- timelineNode.scrollTo({ left: nextLeft, behavior });
- } else {
- timelineNode.scrollLeft = nextLeft;
- }
+ if (timelineNode) {
+ if (typeof timelineNode.scrollTo === 'function') {
+ timelineNode.scrollTo({ left: nextLeft, behavior });
+ } else {
+ timelineNode.scrollLeft = nextLeft;
}
+ }
- guideScrollLeftRef.current = nextLeft;
- setGuideScrollLeft(nextLeft);
+ guideScrollLeftRef.current = nextLeft;
+ setGuideScrollLeft(nextLeft);
- requestAnimationFrame(() => {
- isSyncingScroll.current = false;
- });
- },
- []
- );
+ requestAnimationFrame(() => {
+ isSyncingScroll.current = false;
+ });
+ }, []);
// Scroll to the nearest half-hour mark ONLY on initial load
useEffect(() => {
@@ -651,7 +663,10 @@ export default function TVChannelGuide({ startDate, endDate }) {
try {
await useChannelsStore.getState().fetchRecordings();
} catch (error) {
- console.warn('Failed to refresh recordings after saving series rule', error);
+ console.warn(
+ 'Failed to refresh recordings after saving series rule',
+ error
+ );
}
notifications.show({
title: mode === 'new' ? 'Record new episodes' : 'Record all episodes',
@@ -706,7 +721,8 @@ export default function TVChannelGuide({ startDate, endDate }) {
(program, event) => {
event.stopPropagation();
- const programStartMs = program.startMs ?? dayjs(program.start_time).valueOf();
+ const programStartMs =
+ program.startMs ?? dayjs(program.start_time).valueOf();
const startOffsetMinutes = (programStartMs - start.valueOf()) / 60000;
const leftPx =
(startOffsetMinutes / MINUTE_INCREMENT) * MINUTE_BLOCK_WIDTH;
@@ -753,8 +769,7 @@ export default function TVChannelGuide({ startDate, endDate }) {
: now.startOf('hour').add(30, 'minute');
const nowOffset = roundedNow.diff(start, 'minute');
const scrollPosition =
- (nowOffset / MINUTE_INCREMENT) * MINUTE_BLOCK_WIDTH -
- MINUTE_BLOCK_WIDTH;
+ (nowOffset / MINUTE_INCREMENT) * MINUTE_BLOCK_WIDTH - MINUTE_BLOCK_WIDTH;
const scrollPos = Math.max(scrollPosition, 0);
syncScrollLeft(scrollPos, 'smooth');
@@ -844,8 +859,7 @@ export default function TVChannelGuide({ startDate, endDate }) {
const gapSize = 2;
const widthPx =
- (durationMinutes / MINUTE_INCREMENT) * MINUTE_BLOCK_WIDTH -
- gapSize * 2;
+ (durationMinutes / MINUTE_INCREMENT) * MINUTE_BLOCK_WIDTH - gapSize * 2;
const recording = recordingsByProgramId.get(program.id);
@@ -955,7 +969,8 @@ export default function TVChannelGuide({ startDate, endDate }) {
overflow: 'hidden',
}}
>
- {programStart.format(timeFormat)} - {programEnd.format(timeFormat)}
+ {programStart.format(timeFormat)} -{' '}
+ {programEnd.format(timeFormat)}
@@ -1039,10 +1054,7 @@ export default function TVChannelGuide({ startDate, endDate }) {
[hourTimeline]
);
- const virtualizedHeight = useMemo(
- () => guideHeight || 600,
- [guideHeight]
- );
+ const virtualizedHeight = useMemo(() => guideHeight || 600, [guideHeight]);
const virtualizedWidth = useMemo(() => {
if (guideWidth) {
@@ -1507,9 +1519,32 @@ export default function TVChannelGuide({ startDate, endDate }) {
}}
>
-
-
-
+
+
+
{recordingForProgram && (
<>
)}
{existingRuleMode && (
-
+
)}
@@ -1586,56 +1640,78 @@ export default function TVChannelGuide({ startDate, endDate }) {
>
{(!rules || rules.length === 0) && (
- No series rules configured
+
+ No series rules configured
+
)}
- {rules && rules.map((r) => (
-
- {r.title || r.tvg_id} — {r.mode === 'new' ? 'New episodes' : 'Every episode'}
-
-
-
-
-
- ))}
+ {rules &&
+ rules.map((r) => (
+
+
+ {r.title || r.tvg_id} —{' '}
+ {r.mode === 'new' ? 'New episodes' : 'Every episode'}
+
+
+
+
+
+
+ ))}
)}