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

195 lines
4.9 KiB
TypeScript

'use client';
import 'reflect-metadata';
import {
createContext,
FC,
ReactNode,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import dayjs from 'dayjs';
import useSWR, { useSWRConfig } from 'swr';
import { useFetch } from '@gitroom/helpers/utils/custom.fetch';
import { Post, Integration } from '@prisma/client';
import { useRouter, useSearchParams } from 'next/navigation';
import { isGeneral } from '@gitroom/react/helpers/is.general';
import isoWeek from 'dayjs/plugin/isoWeek';
import weekOfYear from 'dayjs/plugin/weekOfYear';
dayjs.extend(isoWeek);
dayjs.extend(weekOfYear);
const CalendarContext = createContext({
currentWeek: dayjs().week(),
currentYear: dayjs().year(),
currentMonth: dayjs().month(),
comments: [] as Array<{ date: string; total: number }>,
integrations: [] as Integrations[],
trendings: [] as string[],
posts: [] as Array<Post & { integration: Integration }>,
display: 'week',
setFilters: (filters: {
currentWeek: number;
currentYear: number;
currentMonth: number;
display: 'week' | 'month';
}) => {
/** empty **/
},
changeDate: (id: string, date: dayjs.Dayjs) => {
/** empty **/
},
});
export interface Integrations {
name: string;
id: string;
disabled?: boolean;
inBetweenSteps: boolean;
identifier: string;
type: string;
picture: 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('month') ? 'month' : 'week';
const [filters, setFilters] = useState({
currentWeek:
display === 'week'
? +(searchParams.get('week') || getWeekNumber(new Date()))
: 0,
currentMonth:
display === 'week' ? 0 : +(searchParams.get('month') || dayjs().month()),
currentYear: +(searchParams.get('year') || dayjs().year()),
display,
});
const params = useMemo(() => {
return new URLSearchParams(
filters.currentWeek
? {
week: filters.currentWeek.toString(),
year: filters.currentYear.toString(),
}
: {
year: filters.currentYear.toString(),
month: (filters.currentMonth + 1).toString(),
}
).toString();
}, [filters]);
const loadData = useCallback(
async (url: string) => {
const data = (await fetch(`/posts${url}?${params}`)).json();
return data;
},
[filters, params]
);
const swr = useSWR(`/posts-${params}`, loadData, {
refreshInterval: 3600000,
refreshWhenOffline: false,
refreshWhenHidden: false,
revalidateOnFocus: false,
});
const setFiltersWrapper = useCallback(
(filters: {
currentWeek: number;
currentYear: number;
currentMonth: number;
display: 'week' | 'month';
}) => {
setFilters(filters);
setInternalData([]);
window.history.replaceState(
null,
'',
`/launches?${
filters.currentWeek
? `week=${filters.currentWeek}`
: `month=${filters.currentMonth}`
}&year=${filters.currentYear}`
);
setTimeout(() => {
swr.mutate();
}, 10);
},
[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,
...filters,
posts: isLoading ? [] : internalData,
integrations,
setFilters: setFiltersWrapper,
changeDate,
comments,
}}
>
{children}
</CalendarContext.Provider>
);
};
export const useCalendar = () => useContext(CalendarContext);