Format & fix chapter & localStorage hook

This commit is contained in:
Théo 2023-04-21 23:03:44 +02:00
parent 065f517d54
commit d09b1d5bc0
9 changed files with 90 additions and 27 deletions

View file

@ -0,0 +1,35 @@
import { useEffect, useState } from 'react';
export default function useLocalStorage<T>({
key,
initialValue,
serialize = JSON.stringify,
deserialize = JSON.parse
}: {
key: string;
initialValue: T;
serialize?: (value: T) => string;
deserialize?: (value: string) => T;
}) {
const [stored, setStored] = useState(() => {
try {
const localStorageValue = window.localStorage.getItem(key);
if (localStorageValue !== null) {
return deserialize(localStorageValue);
} else {
return initialValue;
}
} catch {
return initialValue;
}
});
useEffect(() => {
try {
const serializedState = serialize(stored);
window.localStorage.setItem(key, serializedState);
} catch {
// Ignore
}
}, [key, serialize, stored]);
return [stored, setStored];
}

View file

@ -2,8 +2,7 @@ export const getChapters = async ({ token }: { token: string }): Promise<Chapter
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chapters`, { const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/chapters`, {
headers: { headers: {
Authorization: `Bearer ${token}` Authorization: `Bearer ${token}`
}, }
cache: 'force-cache'
}); });
const chapters = (await res.json()) as Chapter[]; const chapters = (await res.json()) as Chapter[];

View file

@ -3,7 +3,8 @@ const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
swcMinify: true, swcMinify: true,
experimental: { experimental: {
appDir: true appDir: true,
scrollRestoration: true
} }
}; };

View file

@ -10,7 +10,7 @@ const SCORE_COLORS = ['text-yellow-400', 'text-gray-400', 'text-orange-400'];
export default function Leaderboard() { export default function Leaderboard() {
// TODO CHANGER CECI // TODO CHANGER CECI
const CHAPITRE_EVENT = 3; const CHAPITRE_EVENT = 1;
const subscription: SWRSubscription<string, ScoreEvent, Error> = (key, { next }) => { const subscription: SWRSubscription<string, ScoreEvent, Error> = (key, { next }) => {
const socket = new WebSocket(key); const socket = new WebSocket(key);

View file

@ -1,17 +1,17 @@
'use client'; 'use client';
import cookies from 'js-cookie'; import cookies from 'js-cookie';
import { notFound } from 'next/navigation'; import { notFound, useRouter } from 'next/navigation';
import { useState } from 'react'; import { useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useSWRConfig } from 'swr'; import { useSWRConfig } from 'swr';
import { usePuzzle } from '@/lib/hooks/use-puzzles'; import { usePuzzle } from '@/lib/hooks/use-puzzles';
import AppLink from './AppLink';
import Button from './Button'; import Button from './Button';
import Input from './Input'; import Input from './Input';
import ToHTML from './ToHTML'; import ToHTML from './ToHTML';
import { getURL } from '@/lib/utils';
type PuzzleData = { type PuzzleData = {
answer: string; answer: string;
@ -29,6 +29,7 @@ export default function Puzzle({ token, id }: { token: string; id: number }) {
const { data: puzzle, isLoading } = usePuzzle({ token, id }); const { data: puzzle, isLoading } = usePuzzle({ token, id });
const { mutate } = useSWRConfig(); const { mutate } = useSWRConfig();
const router = useRouter();
const { register, handleSubmit } = useForm<PuzzleData>({ const { register, handleSubmit } = useForm<PuzzleData>({
defaultValues: { defaultValues: {
@ -118,19 +119,21 @@ export default function Puzzle({ token, id }: { token: string; id: number }) {
</form> </form>
) : ( ) : (
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className=" items-center gap-x-2"> <div className="items-center gap-x-2">
<p> <p>
Tentatives : <span className="text-brand-accent">{puzzle.tries}</span> Tentative(s) : <span className="text-brand-accent">{puzzle.tries}</span>
</p> </p>
<p> <p>
Score : <span className="text-brand-accent">{puzzle.score}</span> Score : <span className="text-brand-accent">{puzzle.score}</span>
</p> </p>
</div> </div>
<AppLink href="/dashboard/puzzles"> <Button
<Button kind="brand" type="button"> kind="brand"
Retour aux puzzles type="button"
</Button> onClick={() => router.push(getURL(`/dashboard/puzzles`))}
</AppLink> >
Retour aux puzzles
</Button>
</div> </div>
)} )}
</div> </div>

View file

@ -1,7 +1,7 @@
'use client'; 'use client';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { useContext, useMemo, useState } from 'react'; import { type ChangeEvent, useContext, useEffect, useMemo, useState } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { useSWRConfig } from 'swr'; import { useSWRConfig } from 'swr';
@ -17,6 +17,7 @@ import { useGroups } from '@/lib/hooks/use-groups';
import { usePuzzles } from '@/lib/hooks/use-puzzles'; import { usePuzzles } from '@/lib/hooks/use-puzzles';
import type { Chapter, Puzzle } from '@/lib/puzzles'; import type { Chapter, Puzzle } from '@/lib/puzzles';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import useLocalStorage from '@/lib/hooks/use-local-storage';
export default function Puzzles({ token }: { token: string }) { export default function Puzzles({ token }: { token: string }) {
const { data: me } = useContext(UserContext); const { data: me } = useContext(UserContext);
@ -62,6 +63,16 @@ export default function Puzzles({ token }: { token: string }) {
return data?.find((chapter) => chapter.id === filterChapter)?.puzzles; return data?.find((chapter) => chapter.id === filterChapter)?.puzzles;
}, [data, filter, filterChapter]); }, [data, filter, filterChapter]);
useEffect(() => {
const { hash } = window.location;
if (hash) {
const element = document.querySelector(hash);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
}
}
}, []);
return ( return (
<> <>
{(!isLoading && {(!isLoading &&
@ -283,6 +294,13 @@ function FilterChapter({
setFilter: (filter: string) => void; setFilter: (filter: string) => void;
setFilterChapter: (chapter: number) => void; setFilterChapter: (chapter: number) => void;
}) { }) {
const [stored, setStored] = useLocalStorage({
key: 'puzzles-filter',
initialValue: ''
});
console.log(stored);
let options = [] as { title: string; value: string }[]; let options = [] as { title: string; value: string }[];
options = chapters options = chapters
@ -294,21 +312,28 @@ function FilterChapter({
return { title: t!.name, value: t!.name }; return { title: t!.name, value: t!.name };
}) as { title: string; value: string }[]; }) as { title: string; value: string }[];
options?.unshift({ title: 'Pas encore terminé', value: 'no-completed' });
options?.unshift({ title: 'Pas encore terminé', value: 'no-completed' });
options?.unshift({ title: 'Terminés', value: 'completed' }); options?.unshift({ title: 'Terminés', value: 'completed' });
options?.unshift({ title: 'Tout les puzzles', value: '' }); options?.unshift({ title: 'Tout les puzzles', value: '' });
setFilterChapter(chapter.id); setFilterChapter(chapter.id);
return ( function handleChange(event: ChangeEvent<HTMLSelectElement>) {
<Select setFilter(event.target.value);
className="w-44" // TODO OPTI
options={options || []} // @ts-ignore
value={filter} setStored(event.target.value);
onChange={(event) => setFilter(event.target.value)} }
/>
); useEffect(() => {
if (stored) {
// TODO OPTI
// @ts-ignore
setFilter(stored);
}
}, [stored]);
return <Select className="w-44" options={options} value={filter} onChange={handleChange} />;
} }
type GroupData = { type GroupData = {

View file

@ -22,7 +22,7 @@ const Select = forwardRef<
ref={ref} ref={ref}
> >
{options.map((option) => ( {options.map((option) => (
<option key={option.value} value={option.value}> <option key={option.value} value={option.value} selected={option.value === props.value}>
{option.title} {option.title}
</option> </option>
))} ))}

View file

@ -17,7 +17,7 @@ export default function Tips({ className, kind = 'info', text }: TipsProps) {
className className
})} })}
> >
<p className="py-2 px-4">{text}</p> <p className="px-4 py-2">{text}</p>
</div> </div>
); );
} }