postiz/apps/frontend/src/components/launches/calendar.context.tsx

205 lines
5.2 KiB
TypeScript

'use client';
import 'reflect-metadata';
import {
createContext,
FC,
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import dayjs from 'dayjs';
import useSWR from 'swr';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { Post, Integration } from '@prisma/client';
import { useSearchParams } from 'next/navigation';
import isoWeek from 'dayjs/plugin/isoWeek';
import weekOfYear from 'dayjs/plugin/weekOfYear';
import { extend } from 'dayjs';
extend(isoWeek);
extend(weekOfYear);
export const CalendarContext = createContext({
currentDay: dayjs().day() as 0 | 1 | 2 | 3 | 4 | 5 | 6,
currentWeek: dayjs().week(),
currentYear: dayjs().year(),
currentMonth: dayjs().month(),
comments: [] as Array<{ date: string; total: number }>,
integrations: [] as (Integrations & {refreshNeeded?: boolean})[],
trendings: [] as string[],
posts: [] as Array<Post & { integration: Integration }>,
reloadCalendarView: () => {
/** empty **/
},
display: 'week',
setFilters: (filters: {
currentWeek: number;
currentYear: number;
currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6;
currentMonth: number;
display: 'week' | 'month' | 'day';
}) => {
/** empty **/
},
changeDate: (id: string, date: dayjs.Dayjs) => {
/** empty **/
},
});
export interface Integrations {
name: string;
id: string;
disabled?: boolean;
inBetweenSteps: boolean;
display: string;
identifier: string;
type: string;
picture: string;
changeProfilePicture: boolean;
changeNickName: boolean;
time: { time: number }[];
customer?: {
name?: string;
id?: string;
}
}
function getWeekNumber(date: Date) {
// Copy date so don't modify original
const targetDate = new Date(
Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())
);
// Set to nearest Thursday: current date + 4 - current day number
// Make Sunday's day number 7
targetDate.setUTCDate(
targetDate.getUTCDate() + 4 - (targetDate.getUTCDay() || 7)
);
// Get first day of year
const yearStart = new Date(Date.UTC(targetDate.getUTCFullYear(), 0, 1));
// Calculate full weeks to nearest Thursday
return Math.ceil(
((targetDate.getTime() - yearStart.getTime()) / 86400000 + 1) / 7
);
}
export const CalendarWeekProvider: FC<{
children: ReactNode;
integrations: Integrations[];
}> = ({ children, integrations }) => {
const fetch = useFetch();
const [internalData, setInternalData] = useState([] as any[]);
const [trendings] = useState<string[]>([]);
const searchParams = useSearchParams();
const display = searchParams.get('display') || 'week';
const [filters, setFilters] = useState({
currentDay: +(searchParams.get('day') || dayjs().day()) as
| 0
| 1
| 2
| 3
| 4
| 5
| 6,
currentWeek: +(searchParams.get('week') || getWeekNumber(new Date())),
currentMonth: +(searchParams.get('month') || dayjs().month()),
currentYear: +(searchParams.get('year') || dayjs().year()),
display,
});
const params = useMemo(() => {
return new URLSearchParams({
display: filters.display,
day: filters.currentDay.toString(),
week: filters.currentWeek.toString(),
month: (filters.currentMonth + 1).toString(),
year: filters.currentYear.toString(),
}).toString();
}, [filters, display]);
const loadData = useCallback(async () => {
const data = (await fetch(`/posts?${params}`)).json();
return data;
}, [filters, params]);
const swr = useSWR(`/posts-${params}`, loadData, {
refreshInterval: 3600000,
refreshWhenOffline: false,
refreshWhenHidden: false,
revalidateOnFocus: false,
});
const setFiltersWrapper = useCallback(
(filters: {
currentDay: 0 | 1 | 2 | 3 | 4 | 5 | 6;
currentWeek: number;
currentYear: number;
currentMonth: number;
display: 'week' | 'month' | 'day';
}) => {
setFilters(filters);
setInternalData([]);
const path = [
`day=${filters.currentDay}`,
`week=${filters.currentWeek}`,
`month=${filters.currentMonth}`,
`year=${filters.currentYear}`,
`display=${filters.display}`,
].filter((f) => f);
window.history.replaceState(null, '', `/launches?${path.join('&')}`);
},
[filters, swr.mutate]
);
const { isLoading } = swr;
const { posts, comments } = swr?.data || { posts: [], comments: [] };
const changeDate = useCallback(
(id: string, date: dayjs.Dayjs) => {
setInternalData((d) =>
d.map((post: Post) => {
if (post.id === id) {
return {
...post,
publishDate: date.utc().format('YYYY-MM-DDTHH:mm:ss'),
};
}
return post;
})
);
},
[posts, internalData]
);
useEffect(() => {
if (posts) {
setInternalData(posts);
}
}, [posts]);
return (
<CalendarContext.Provider
value={{
trendings,
reloadCalendarView: swr.mutate,
...filters,
posts: isLoading ? [] : internalData,
integrations,
setFilters: setFiltersWrapper,
changeDate,
comments,
}}
>
{children}
</CalendarContext.Provider>
);
};
export const useCalendar = () => useContext(CalendarContext);