Skip to content

Commit

Permalink
feat: support for toggling between 12/24-hour time in UI (#1026)
Browse files Browse the repository at this point in the history
This is implemented as baseline for region and timezone selection.
  • Loading branch information
chrisbenincasa authored Dec 18, 2024
1 parent 0150be4 commit 891ed29
Show file tree
Hide file tree
Showing 19 changed files with 629 additions and 502 deletions.
4 changes: 4 additions & 0 deletions server/src/services/TvGuideService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,10 @@ export class TVGuideService {
lastUpdate: mapValues(this.lastUpdateTime, (time) =>
dayjs(time).format(),
),
guideTimes: mapValues(this.cachedGuide, ({ channel, programs }) => ({
start: dayjs(first(programs)?.startTimeMs).format(),
end: dayjs(this.lastEndTime[channel.uuid]).format(),
})),
channelIds: keys(this.cachedGuide),
};
}
Expand Down
34 changes: 34 additions & 0 deletions web/src/Tunarr.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { DayjsProvider } from '@/providers/DayjsProvider.tsx';
import useStore from '@/store/index.ts';
import { LocalizationProvider } from '@mui/x-date-pickers';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import { QueryClientProvider } from '@tanstack/react-query';
import { RouterProvider } from '@tanstack/react-router';
import { SnackbarProvider } from 'notistack';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { TunarrApiProvider } from './components/TunarrApiContext.tsx';
import { ServerEventsProvider } from './components/server_events/ServerEventsProvider.tsx';
import { router } from './main.tsx';
import { queryClient } from './queryClient.ts';

export const Tunarr = () => {
const locale = useStore((store) => store.settings.ui.i18n.locale);
return (
<TunarrApiProvider queryClient={queryClient}>
<DayjsProvider>
<LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale={locale}>
<DndProvider backend={HTML5Backend}>
<ServerEventsProvider>
<QueryClientProvider client={queryClient}>
<SnackbarProvider maxSnack={2} autoHideDuration={5000}>
<RouterProvider basepath="/web" router={router} />
</SnackbarProvider>
</QueryClientProvider>
</ServerEventsProvider>
</DndProvider>
</LocalizationProvider>
</DayjsProvider>
</TunarrApiProvider>
);
};
4 changes: 1 addition & 3 deletions web/src/components/ProgramDetailsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,7 @@ export default function ProgramDetailsDialog({
return (
<Chip
key="time"
label={`${dayjs(start).format('h:mm')} - ${dayjs(stop).format(
'h:mma',
)}`}
label={`${dayjs(start).format('LT')} - ${dayjs(stop).format('LT')}`}
sx={{ mt: 1, mr: 1 }}
color="primary"
/>
Expand Down
34 changes: 10 additions & 24 deletions web/src/components/channel_config/ChannelProgrammingList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import IconButton from '@mui/material/IconButton';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemText from '@mui/material/ListItemText';
import { ChannelProgram } from '@tunarr/types';
import { Channel, ChannelProgram } from '@tunarr/types';
import dayjs, { Dayjs } from 'dayjs';
import { findIndex, isString, isUndefined, map, sumBy } from 'lodash-es';
import React, { CSSProperties, useCallback, useState } from 'react';
Expand All @@ -44,16 +44,6 @@ import ProgramDetailsDialog from '../ProgramDetailsDialog.tsx';
import AddFlexModal from '../programming_controls/AddFlexModal.tsx';
import AddRedirectModal from '../programming_controls/AddRedirectModal.tsx';

const ListItemTimeFormatter = new Intl.DateTimeFormat(undefined, {
dateStyle: 'medium',
timeStyle: 'short',
});

const MobileListItemTimeFormatter = new Intl.DateTimeFormat(undefined, {
dateStyle: 'short',
timeStyle: 'short',
});

type CommonProps = {
moveProgram?: (originalIndex: number, toIndex: number) => void;
deleteProgram?: (index: number) => void;
Expand Down Expand Up @@ -110,6 +100,7 @@ type ListItemProps = {
program: (UIFlexProgram | UIRedirectProgram) & { index: number },
) => void;
titleFormatter: (program: ChannelProgram) => string;
channel: Channel;
};

type ListDragItem = {
Expand All @@ -122,7 +113,6 @@ const ProgramListItem = ({
style,
program,
index,
startTimeDate,
moveProgram,
deleteProgram,
findProgram,
Expand All @@ -132,6 +122,7 @@ const ProgramListItem = ({
enableEdit,
enableDelete,
titleFormatter,
channel,
}: ListItemProps) => {
const [{ isDragging }, drag] = useDrag(
() => ({
Expand Down Expand Up @@ -164,19 +155,17 @@ const ProgramListItem = ({
const theme = useTheme();
const smallViewport = useMediaQuery(theme.breakpoints.down('sm'));

const startTime = startTimeDate
? smallViewport
? MobileListItemTimeFormatter.format(startTimeDate)
: ListItemTimeFormatter.format(startTimeDate)
: null;
const startTimeDate = !isUndefined(program.startTimeOffset)
? dayjs(channel.startTime + program.startTimeOffset)
: undefined;

const startTime = startTimeDate?.format(smallViewport ? 'L LT' : 'lll');

const handleInfoButtonClick = (e: React.MouseEvent) => {
e.stopPropagation();
onInfoClicked(program);
};

// const dayBoundary = startTimes[idx + 1].isAfter(startTimes[idx], 'day');

let title = `${titleFormatter(program)}`;
if (!smallViewport && startTime) {
title += ` - ${startTime}`;
Expand Down Expand Up @@ -384,22 +373,19 @@ export default function ChannelProgrammingList(props: Props) {

const renderProgram = (idx: number, style?: CSSProperties) => {
const program = programList[idx];
const startTimeDate = !isUndefined(program.startTimeOffset)
? dayjs(channel!.startTime + program.startTimeOffset).toDate()
: undefined;
return (
<ProgramListItem
index={idx}
program={program}
style={style}
startTimeDate={startTimeDate}
channel={channel!}
moveProgram={moveProgram}
findProgram={findProgram}
deleteProgram={deleteProgram}
enableDrag={!!enableDnd}
enableDelete={props.enableRowDelete ?? true}
enableEdit={props.enableRowEdit ?? true}
onInfoClicked={() => openDetailsDialog(program, startTimeDate)}
onInfoClicked={() => openDetailsDialog(program)}
onEditClicked={openEditDialog}
titleFormatter={titleFormatter}
/>
Expand Down
2 changes: 1 addition & 1 deletion web/src/components/channel_config/PlexFilterBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export function PlexValueNode({
},
}}
value={dayjs(field.value)}
onChange={(e) => field.onChange(e?.format('YYYY-MM-DD'))}
onChange={(e) => field.onChange(e?.format('L'))}
/>
)}
/>
Expand Down
17 changes: 10 additions & 7 deletions web/src/components/guide/TvGuide.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ export function TvGuide({ channelId, start, end }: Props) {
const [channelMenu, setChannelMenu] = useState<ChannelLineup>();

const [progress, setProgress] = useState(calcProgress(start, end));
const [currentTime, setCurrentTime] = useState(dayjs().format('h:mm'));
const [currentTime, setCurrentTime] = useState(dayjs().format('LT'));

const [modalProgram, setModalProgram] = useState<
TvGuideProgram | undefined
Expand Down Expand Up @@ -191,15 +191,15 @@ export function TvGuide({ channelId, start, end }: Props) {

useEffect(() => {
setProgress(calcProgress(start, end));
setCurrentTime(dayjs().format('h:mm'));
setCurrentTime(dayjs().format('LT'));
if (ref.current) {
setMinHeight(ref.current.offsetHeight);
}
}, [start, end]);

useInterval(() => {
setProgress(calcProgress(start, end));
setCurrentTime(dayjs().format('h:mm'));
setCurrentTime(dayjs().format('LT'));
}, 60000);

useTvGuidesPrefetch(
Expand Down Expand Up @@ -384,9 +384,7 @@ export function TvGuide({ channelId, start, end }: Props) {
{((smallViewport && pct > 20) || (!smallViewport && pct > 8)) && (
<>
<Box sx={{ fontSize: '12px' }}>
{`${programStart.format('h:mm')} - ${programEnd.format(
'h:mma',
)}`}
{`${programStart.format('LT')} - ${programEnd.format('LT')}`}
</Box>
<Box sx={{ fontSize: '12px' }}>
{isPlaying ? ` (${remainingTime}m left)` : null}
Expand Down Expand Up @@ -560,12 +558,17 @@ export function TvGuide({ channelId, start, end }: Props) {
width={100 / intervalArray.length}
sx={{
height: '2rem',
borderLeft: '1px solid white',
textAlign: 'center',
'&:last-child': {
borderRight: '1px solid white',
},
}}
key={slot}
>
{start
.add(slot * increments, 'minutes')
.format(`${smallViewport ? 'h:mm' : 'h:mm A'}`)}
.format(`${smallViewport ? 'h:mm' : 'LT'}`)}
</GridChild>
))}
</GridParent>
Expand Down
Loading

0 comments on commit 891ed29

Please sign in to comment.