diff --git a/apps/frontend/src/components/analytics/analytics.component.tsx b/apps/frontend/src/components/analytics/analytics.component.tsx index acebdf7d..210c09f9 100644 --- a/apps/frontend/src/components/analytics/analytics.component.tsx +++ b/apps/frontend/src/components/analytics/analytics.component.tsx @@ -6,22 +6,28 @@ import { StarsTableComponent } from '@gitroom/frontend/components/analytics/star import useSWR from 'swr'; import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { LoadingComponent } from '@gitroom/frontend/components/layout/loading'; +import clsx from 'clsx'; +import { useStateCallback } from '@gitroom/react/helpers/use.state.callback'; export const AnalyticsComponent: FC = () => { const fetch = useFetch(); + const [page, setPage] = useStateCallback(1); const load = useCallback(async (path: string) => { return await (await fetch(path)).json(); }, []); - const starsCallback = useCallback(async (path: string) => { - return await ( - await fetch(path, { - body: JSON.stringify({ page: 1 }), - method: 'POST', - }) - ).json(); - }, []); + const starsCallback = useCallback( + async (path: string) => { + return await ( + await fetch(path, { + body: JSON.stringify({ page }), + method: 'POST', + }) + ).json(); + }, + [page] + ); const { isLoading: isLoadingAnalytics, data: analytics } = useSWR( '/analytics', @@ -31,9 +37,17 @@ export const AnalyticsComponent: FC = () => { '/analytics/trending', load ); - const { isLoading: isLoadingStars, data: stars } = useSWR( - '/analytics/stars', - starsCallback + const { + isLoading: isLoadingStars, + data: stars, + mutate, + } = useSWR('/analytics/stars', starsCallback); + + const changePage = useCallback( + (type: 'increase' | 'decrease') => () => { + setPage(type === 'increase' ? page + 1 : page - 1, () => mutate()); + }, + [page, mutate] ); if (isLoadingAnalytics || isLoadingTrending || isLoadingStars) { @@ -43,11 +57,61 @@ export const AnalyticsComponent: FC = () => { return (
- +
-

Stars per day

+
+
+ + + +
+

Stars per day

+
+ + + +
+
- {!!stars?.stars?.length ? :
Load your GitHub repository from settings to see analytics
} + {stars?.stars?.length ? ( + + ) : ( +
+ Load your GitHub repository from settings to see analytics +
+ )}
diff --git a/libraries/react-shared-libraries/src/helpers/use.state.callback.ts b/libraries/react-shared-libraries/src/helpers/use.state.callback.ts new file mode 100644 index 00000000..50345a8c --- /dev/null +++ b/libraries/react-shared-libraries/src/helpers/use.state.callback.ts @@ -0,0 +1,24 @@ +import { useCallback, useEffect, useRef, useState } from 'react'; + +export function useStateCallback( + initialState: T +): [T, (state: T, cb?: (state: T) => void) => void] { + const [state, setState] = useState(initialState); + const cbRef = useRef<((state: T) => void) | undefined>(undefined); // init mutable ref container for callbacks + + const setStateCallback = useCallback((state: T, cb?: (state: T) => void) => { + cbRef.current = cb; // store current, passed callback in ref + setState(state); + }, []); // keep object reference stable, exactly like `useState` + + useEffect(() => { + // cb.current is `undefined` on initial render, + // so we only invoke callback on state *updates* + if (cbRef.current) { + cbRef.current(state); + cbRef.current = undefined; // reset callback after execution + } + }, [state]); + + return [state, setStateCallback]; +}