Skip to content

Commit

Permalink
feat(GIST-56): add refresh token mechanism
Browse files Browse the repository at this point in the history
  • Loading branch information
Courtcircuits authored and dorian-grst committed Oct 20, 2024
1 parent 5131611 commit adb8a0e
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 104 deletions.
66 changes: 34 additions & 32 deletions src/app/(gistLayout)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,65 @@
'use client'

import { ReactNode, useCallback } from 'react'
import GistLayout from './layout-ui'
import { useMe } from '@/lib/queries/user.queries'
import { useToast } from '@/components/shadcn/use-toast'
import { useCreateGist } from '@/lib/queries/gists.queries'
import { useCreateOrg } from '@/lib/queries/orgs.queries'

export default function GistLayoutFeature({ children }: { children: ReactNode }) {
const { data } = useMe()
const { toast } = useToast()
"use client";

import { ReactNode, useCallback } from "react";
import GistLayout from "./layout-ui";
import { useMe } from "@/lib/queries/user.queries";
import { useToast } from "@/components/shadcn/use-toast";
import { useCreateGist } from "@/lib/queries/gists.queries";
import { useCreateOrg } from "@/lib/queries/orgs.queries";

export default function GistLayoutFeature({
children,
}: {
children: ReactNode;
}) {
const { data, error } = useMe();
const { toast } = useToast();
const { mutate: createGist } = useCreateGist({
onSuccess: () => {
toast({
title: 'Gist Created',
description: 'Your gist has been created successfully',
})
title: "Gist Created",
description: "Your gist has been created successfully",
});
},
})
});

const { mutate: createTeam } = useCreateOrg({
onSuccess: () => {
toast({
title: 'Team Created',
description: 'Your team has been created successfully',
})
title: "Team Created",
description: "Your team has been created successfully",
});
},
})
});

const onMyGistsClick = () => {
}
const onMyGistsClick = () => {};

const onCreateTeamClick = useCallback(
(name: string) => {
createTeam(name)
createTeam(name);
},
[toast, createTeam]
)
[toast, createTeam],
);

const onLogoutClick = () => {
}
const onLogoutClick = () => {};

const onCreateGistClick = (name: string, content: string) => {
createGist({
content,
name,
})
}
});
};

return (
<GistLayout
onLogoutClick={onLogoutClick}
username={data?.name ?? ''}
avatar={data?.picture ?? ''}
username={data?.name ?? ""}
avatar={data?.picture ?? ""}
onCreateTeamClick={onCreateTeamClick}
onMyGistsClick={onMyGistsClick}
onCreateGistClick={onCreateGistClick}
>
{children}
</GistLayout>
)
);
}
6 changes: 4 additions & 2 deletions src/components/api/api-provider.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"use client";

import getQueryClient from "@/lib/queries/queries";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { useState } from "react";

Expand All @@ -8,8 +9,9 @@ export default function QueryProvider({
}: {
children: React.ReactNode;
}) {
const [queryClient] = useState<QueryClient>(() => new QueryClient());
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
<QueryClientProvider client={getQueryClient()}>
{children}
</QueryClientProvider>
);
}
133 changes: 66 additions & 67 deletions src/components/feature/gist-details-wrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
'use client'
"use client";

import { useState, useEffect, useCallback } from 'react'
import { Gist } from '@/types'
import GistLanding from '@/components/ui/gist-landing'
import { toast } from '../shadcn/use-toast'
import { useRouter } from 'next/navigation'
import { useKeyPress } from '@/lib/hook/use-key-press'
import { useState, useEffect, useCallback } from "react";
import { Gist } from "@/types";
import GistLanding from "@/components/ui/gist-landing";
import { toast } from "../shadcn/use-toast";
import { useRouter } from "next/navigation";
import { useKeyPress } from "@/lib/hook/use-key-press";

console.log(`
_______ ________ ______ _________ ______ ________ ______ ______
Expand All @@ -17,101 +17,100 @@ console.log(`
\\_____\\/ \\________\\/ \\_____\\/ \\__\\/ \\_____\\/ \\:_\\/ \\__\\/\\__\\/ \\_\\/ \\_\\/
\n
Your snippet vault
`)
`);

export default function GistDetailsWrapper() {
const router = useRouter()
const router = useRouter();
const [gist, setGist] = useState<Gist>({
id: 'example',
name: 'Gist example',
code: '',
})
const [isShareDialogOpen, setIsShareDialogOpen] = useState(false)
id: "example",
name: "Gist example",
code: "",
});
const [isShareDialogOpen, setIsShareDialogOpen] = useState(false);

useEffect(() => {
const storedGistName = localStorage.getItem('gistName') || 'Gist example'
const storedGistCode = localStorage.getItem('gistCode') || ''
const storedGistName = localStorage.getItem("gistName") || "Gist example";
const storedGistCode = localStorage.getItem("gistCode") || "";
setGist((prevGist) => ({
...prevGist,
name: storedGistName,
code: storedGistCode,
}))
}, [])
}));
}, []);

const handleDownload = useCallback((name: string, code: string) => {
const element = document.createElement('a')
const file = new Blob([code], { type: 'text/plain' })
element.href = URL.createObjectURL(file)
element.download = name
document.body.appendChild(element)
element.click()
document.body.removeChild(element)
const element = document.createElement("a");
const file = new Blob([code], { type: "text/plain" });
element.href = URL.createObjectURL(file);
element.download = name;
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
toast({
title: 'Gist Downloaded',
description: 'Your gist has been downloaded successfully',
})
}, [])
title: "Gist Downloaded",
description: "Your gist has been downloaded successfully",
});
}, []);

const handleShare = useCallback(() => {
}, [])
const handleShare = useCallback(() => {}, []);

const handleLogin = useCallback(() => {
router.push('/login')
}, [router])
router.push("/login");
}, [router]);

const handleShareDialog = useCallback(() => {
setIsShareDialogOpen(true)
}, [])
setIsShareDialogOpen(true);
}, []);

const handleGistNameChange = useCallback((newName: string) => {
setGist((prevGist) => ({ ...prevGist, name: newName }))
localStorage.setItem('gistName', newName)
}, [])
setGist((prevGist) => ({ ...prevGist, name: newName }));
localStorage.setItem("gistName", newName);
}, []);

const handleGistCodeChange = useCallback((newCode: string) => {
setGist((prevGist) => ({ ...prevGist, code: newCode }))
localStorage.setItem('gistCode', newCode)
}, [])
setGist((prevGist) => ({ ...prevGist, code: newCode }));
localStorage.setItem("gistCode", newCode);
}, []);

const handleOpenFile = useCallback(() => {
const fileInput = document.createElement('input')
fileInput.type = 'file'
const fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.onchange = (event: Event) => {
const target = event.target as HTMLInputElement
const file = target.files?.[0]
const target = event.target as HTMLInputElement;
const file = target.files?.[0];
if (file) {
handleGistNameChange(file.name)
const reader = new FileReader()
handleGistNameChange(file.name);
const reader = new FileReader();
reader.onload = (e) => {
const fileContent = e.target?.result as string
handleGistCodeChange(fileContent)
}
reader.readAsText(file)
const fileContent = e.target?.result as string;
handleGistCodeChange(fileContent);
};
reader.readAsText(file);
}
}
fileInput.click()
}, [handleGistNameChange, handleGistCodeChange])
};
fileInput.click();
}, [handleGistNameChange, handleGistCodeChange]);

const handleKeyPressDownload = useCallback(
(e: KeyboardEvent) => {
e.preventDefault()
handleDownload(gist.name, gist.code)
e.preventDefault();
handleDownload(gist.name, gist.code);
},
[gist.name, gist.code, handleDownload]
)
[gist.name, gist.code, handleDownload],
);

const handleKeyPressOpenFile = useCallback(
(e: KeyboardEvent) => {
e.preventDefault()
handleOpenFile()
e.preventDefault();
handleOpenFile();
},
[handleOpenFile]
)
[handleOpenFile],
);

useKeyPress('d', handleKeyPressDownload, ['ctrlKey'])
useKeyPress('l', handleLogin, ['ctrlKey'])
useKeyPress('o', handleKeyPressOpenFile, ['ctrlKey'])
useKeyPress('s', handleShareDialog, ['ctrlKey', 'shiftKey'])
useKeyPress("d", handleKeyPressDownload, ["ctrlKey"]);
useKeyPress("l", handleLogin, ["ctrlKey"]);
useKeyPress("o", handleKeyPressOpenFile, ["ctrlKey"]);
useKeyPress("s", handleShareDialog, ["ctrlKey", "shiftKey"]);

return (
<GistLanding
Expand All @@ -126,5 +125,5 @@ export default function GistDetailsWrapper() {
isShareDialogOpen={isShareDialogOpen}
setIsShareDialogOpen={setIsShareDialogOpen}
/>
)
);
}
9 changes: 9 additions & 0 deletions src/lib/queries/auth.queries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ const fetchLocalAuthVerify = async ({
return json;
};

export const renewToken = async () => {
const json = await ky
.post(`${getBackendURL()}/auth/identity/renew`, {
credentials: "include",
})
.json();
return json;
};

/**
* Start the local authentication process by sending an email to the user
* @param email, the user email
Expand Down
4 changes: 4 additions & 0 deletions src/lib/queries/orgs.queries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ const fetchOrgs = async () => {
})
.json<AllApiOrg[]>();

if (!json) {
return [];
}

for (let org of json) {
let team: Team = {
id: org.id,
Expand Down
24 changes: 21 additions & 3 deletions src/lib/queries/queries.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
"use client";
import { QueryClient } from "@tanstack/react-query";
import { cache } from "react";
import { toast } from "@/components/shadcn/use-toast";
import { QueryCache, QueryClient } from "@tanstack/react-query";
import { renewToken } from "./auth.queries";

export const getQueryClient = () => {
return new QueryClient();
return new QueryClient({
queryCache: new QueryCache({
onError: async (error, query) => {
if (error.message.includes("401")) {
try {
await renewToken();
query.fetch(); //try to refetch
} catch {
// need to logout !!!!
toast({
title: "Error",
description: "Please try to log in again",
});
}
}
},
}),
});
};
export default getQueryClient;

0 comments on commit adb8a0e

Please sign in to comment.