Skip to content

Commit

Permalink
Merge branch 'next' of https://github.com/HolodexNet/Holodex into next
Browse files Browse the repository at this point in the history
  • Loading branch information
sphinxrave committed Oct 10, 2023
2 parents 27c1473 + df3ae11 commit 4f20a77
Show file tree
Hide file tree
Showing 17 changed files with 1,382 additions and 20 deletions.
712 changes: 704 additions & 8 deletions packages/react/package-lock.json

Large diffs are not rendered by default.

8 changes: 7 additions & 1 deletion packages/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,16 @@
"dependencies": {
"@radix-ui/colors": "^3.0.0",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-context-menu": "^2.1.5",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-scroll-area": "^1.0.5",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
"@radix-ui/themes": "^1.1.2",
"@react-oauth/google": "^0.11.1",
"@tanstack/react-query": "^4.36.1",
"@tanstack/react-query-devtools": "^4.36.1",
"axios": "^1.5.1",
"class-variance-authority": "^0.7.0",
"classnames": "^2.3.2",
"clsx": "^2.0.0",
Expand All @@ -28,6 +33,7 @@
"i18next-http-backend": "^2.2.2",
"jotai": "^2.4.3",
"lucide-react": "^0.284.0",
"oauth-open": "^1.0.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-grid-layout": "^1.4.2",
Expand Down Expand Up @@ -70,4 +76,4 @@
"unocss-preset-autoprefixer": "^0.0.6",
"vite": "^4.4.9"
}
}
}
26 changes: 22 additions & 4 deletions packages/react/src/components/header/header.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,32 @@
import { userAtom } from "@/store/auth"
import { useAtomValue } from "jotai"

interface HeaderProps {
onClick: () => void,
id: string
}

export function Header({ onClick, id }: HeaderProps){
const user = useAtomValue(userAtom);

return (
<header id={id} className="border border-green flex">
<button className='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded' onClick={onClick}><div className='i-heroicons:bars-3 rounded-md px-3 py-3 ' /></button>
<div className='justify-start py-1 pl-3 text-xl'>HoloDex</div>
<div className='i-heroicons:chevron-down py-5'/>
<button
className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
onClick={onClick}
>
<div className="i-heroicons:bars-3 rounded-md px-3 py-3 " />
</button>
<div className="justify-start py-1 pl-3 text-xl">HoloDex</div>
<div className="i-heroicons:chevron-down py-5" />
<div className="flex flex-grow" />
{user ? (
<img
src={`https://avatars.dicebear.com/api/jdenticon/${user.id}.svg`}
/>
) : (
<button>Login</button>
)}
</header>
)
);
}
2 changes: 2 additions & 0 deletions packages/react/src/components/layout/Frame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { isFloatingAtom, isMobileAtom, onResizeAtom, sidebarOpenAtom, sidebarSho
import { useAtom } from 'jotai/react'
import { darkAtom } from '@/hooks/useTheme'
import { Header } from "@/components/header/header"
import { Toaster } from '@/shadcn/ui/toaster'

export function Frame() {

Expand Down Expand Up @@ -47,6 +48,7 @@ export function Frame() {
<RouterProvider router={router}></RouterProvider>
</main>
{isMobile && <footer className="">Footer</footer>}
<Toaster />
</div>
)
}
153 changes: 153 additions & 0 deletions packages/react/src/hooks/useAuth.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { useToast } from "@/shadcn/ui/use-toast";
import { tokenAtom, userAtom } from "@/store/auth";
import { CredentialResponse } from "@react-oauth/google";
import { useMutation } from "@tanstack/react-query";
import { useSetAtom } from "jotai";
import { useCallback } from "react";
import { useNavigate } from "react-router-dom";

interface LoginResponse {
jwt: string;
user: User;
}

type LoginArgs =
| {
platform: "google";
error?: Error;
credential: CredentialResponse;
access_token?: never;
jwt?: never;
}
| {
platform: "discord";
error?: Error;
credential?: never;
access_token: string;
jwt?: never;
}
| {
platform: "twitter";
error?: Error;
credential?: never;
access_token?: never;
jwt: string;
};

export function useAuth() {
const { toast } = useToast();
const navigate = useNavigate();
const setUser = useSetAtom(userAtom);
const setToken = useSetAtom(tokenAtom);

const login = useMutation(
async ({
platform,
error,
credential,
access_token,
jwt: tempJWT,
}: LoginArgs) => {
switch (platform) {
case "google":
if (error) return onFailure(error);
if (!credential.credential)
return onFailure(new Error("Credential not provided"));
try {
const { jwt, user } = await getToken({
authToken: credential.credential,
service: "google",
});
setToken(jwt);
setUser(user);
navigate("/settings");
} catch (e) {
onFailure(e as Error);
}
break;

case "discord":
if (error) return onFailure(error);
try {
const { jwt, user } = await getToken({
authToken: access_token,
service: "discord",
});
setToken(jwt);
setUser(user);
navigate("/settings");
} catch (e) {
onFailure(e as Error);
}

break;

case "twitter":
if (error) return onFailure(error);
try {
const { jwt, user } = await getToken({
authToken: tempJWT,
service: "twitter",
});
setToken(jwt);
setUser(user);
navigate("/settings");
} catch (e) {
onFailure(e as Error);
}
break;

default:
onFailure(Error("No login method specified"));

break;
}
},
);

const logout = useCallback(() => {
setToken(null);
setUser(null);
}, [setToken, setUser]);

const onFailure = useCallback(
(err: Error) => {
console.log("[Auth] Login error", err);
toast({
variant: "destructive",
title: "Error while logging in",
});
setUser(null);
setToken(null);
},
[setToken, setUser, toast],
);

return { login, logout };
}

export async function getToken({
authToken,
service,
jwt,
}: {
authToken: string;
service: string;
jwt?: string;
}): Promise<LoginResponse> {
const headers = {
"content-type": "application/json",
...(jwt && { Authorization: `Bearer ${jwt}` }),
};
const body = {
token: authToken,
service,
};
const res = await fetch(`/api/v2/user/login`, {
method: "POST",
headers,
body: JSON.stringify(body),
});
if (!res.ok) throw Error("Failed to acquire token");
return res.json();
}
30 changes: 30 additions & 0 deletions packages/react/src/hooks/useClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { tokenAtom } from "@/store/auth";
import axios, { AxiosRequestConfig, AxiosResponse } from "axios";
import { useAtomValue } from "jotai";
import { useCallback } from "react";

const axiosInstance = axios.create()

export function useClient() {
const token = useAtomValue(tokenAtom);

const AxiosInstance = useCallback(
function <T>(
url: string,
config?: AxiosRequestConfig,
): Promise<AxiosResponse<T>> {
const configWithUser: AxiosRequestConfig = {
baseURL: `${window.location.protocol}//${window.location.host}/api/v2`,
...config,
headers: {
...config?.headers,
Authorization: `Bearer ${token}`
},
};
return axiosInstance(url, configWithUser);
},
[token],
);

return AxiosInstance
}
18 changes: 15 additions & 3 deletions packages/react/src/main.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import React from "react";
import ReactDOM from "react-dom/client";
import { GoogleOAuthProvider } from "@react-oauth/google";
import "./index.css";
import "uno.css";
import { Frame } from "./components/layout/Frame.tsx";
import { darkAtom, useThemeInit } from "./hooks/useTheme.ts";
import { useAtom } from "jotai";
import { useThemeInit } from "./hooks/useTheme.ts";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";

const GOOGLE_CLIENT_ID =
"275540829388-87s7f9v2ht3ih51ah0tjkqng8pd8bqo2.apps.googleusercontent.com";

const queryClient = new QueryClient();

function App() {
useThemeInit();
Expand All @@ -13,6 +20,11 @@ function App() {

ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<App />
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools />
<GoogleOAuthProvider clientId={GOOGLE_CLIENT_ID}>
<App />
</GoogleOAuthProvider>
</QueryClientProvider>
</React.StrictMode>,
);
12 changes: 12 additions & 0 deletions packages/react/src/routes/home.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useLive } from "@/services/live.service"


export function Home () {
const { data } = useLive();

return (
<div className="w-full h-full">
{JSON.stringify(data)}
</div>
)
}
44 changes: 44 additions & 0 deletions packages/react/src/routes/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useAuth } from "@/hooks/useAuth";
import { Button } from "@/shadcn/ui/button";
import { GoogleLogin } from "@react-oauth/google";
import open from "oauth-open";

export function Login() {
const {
login: { mutate },
} = useAuth();

return (
<div className="w-full h-full">
<div className="flex-col gap-4">
<GoogleLogin
onSuccess={(credential) => mutate({ platform: "google", credential })}
onError={() => console.log("Google login failed")}
/>
<Button
className="w-full"
onClick={() =>
open(
`https://discord.com/api/oauth2/authorize?client_id=793619250115379262&redirect_uri=${encodeURIComponent(
`${window.location.protocol}//${window.location.host}/discord`,
)}&response_type=token&scope=identify`,
(error: Error, { access_token }: { access_token: string }) =>
mutate({ platform: "discord", error, access_token }),
)
}
>
Login with Discord
</Button>
{/* Twitter login is currently unavailable and is on hold */}
{/* <Button
className="w-full"
onClick={() =>
(window.location.href = `${window.origin}/api/v2/user/login/twitter`)
}
>
Login with Twitter
</Button> */}
</div>
</div>
);
}
6 changes: 4 additions & 2 deletions packages/react/src/routes/router.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import Kitchensink from "@/Kitchensink";
import { Outlet, createBrowserRouter, redirect } from "react-router-dom";
import { Home } from "./home";
import { Login } from "./login";

const settings = {} as any; // TODO: replace with your actual settings store
const site = {} as any; // TODO: replace with your actual site store
Expand Down Expand Up @@ -33,7 +35,7 @@ const router = createBrowserRouter([
},
{
path: "/org/:org",
element: <div>Home_Org</div>,
element: <Home />,
},
{
path: "/channels",
Expand Down Expand Up @@ -101,7 +103,7 @@ const router = createBrowserRouter([
},
{
path: "/login",
element: <div>Login</div>,
element: <Login />,
},
{
path: "/tlclient",
Expand Down
Loading

0 comments on commit 4f20a77

Please sign in to comment.