Skip to content

Commit

Permalink
refactor(providers): Use classes instead of fns, new PlayerProviders
Browse files Browse the repository at this point in the history
  • Loading branch information
busybox11 committed Nov 17, 2024
1 parent c8f7cee commit ffab780
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 39 deletions.
42 changes: 42 additions & 0 deletions app/components/contexts/PlayerProviders.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { createContext, useContext, useCallback, useMemo } from "react";
import type { ReactNode } from "@tanstack/react-router";

import providers from "@/providers";
import { IProviderClient } from "@/types/providers/client";

const PlayerProvidersContext = createContext<{
[key: string]: IProviderClient;
}>({});

export function PlayerProvidersProvider({
children,
}: Readonly<{
children: ReactNode;
}>) {
const handleAuth = useCallback((provider: string) => {
console.log("auth", provider);
}, []);

const value = useMemo(() => {
return Object.fromEntries(
providers.map(([id, provider]) => {
return [
id,
new provider({
onAuth: () => handleAuth(id),
}),
];
})
);
}, []);

return (
<PlayerProvidersContext.Provider value={value}>
{children}
</PlayerProvidersContext.Provider>
);
}

export function usePlayerProviders() {
return useContext(PlayerProvidersContext);
}
28 changes: 21 additions & 7 deletions app/providers/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
import { ProviderMeta } from "@/types/providers/meta";
import type {
IProviderClient,
IProviderClientConstructor,
} from "@/types/providers/client";

const providersGlob = import.meta.glob<ProviderMeta>("./*/index.ts", {
eager: true,
import: "default",
});
type NewProviderClient = new (
args: IProviderClientConstructor
) => IProviderClient;

const providers = Object.fromEntries(
Object.values(providersGlob).map((value) => [value.id, value])
const providersClientGlob = import.meta.glob<NewProviderClient>(
"./*/client.ts",
{
eager: true,
import: "default",
}
);

const providers: [string, NewProviderClient][] = Object.entries(
providersClientGlob
).map(([importPath, client]) => {
const [, id] = importPath.split("/");

return [id, client];
});

export default providers;
49 changes: 35 additions & 14 deletions app/providers/spotify/client.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,50 @@
import { SPOTIFY_OAUTH_SCOPES } from "./constants";
import {
AccessToken,
SpotifyApi,
AuthorizationCodeWithPKCEStrategy,
} from "@spotify/web-api-ts-sdk";

import { providerConfig } from "./config";
import {
IProviderClient,
IProviderClientConstructor,
} from "@/types/providers/client";
import spotifyProviderMeta from "@/providers/spotify";

const { VITE_SPOTIFY_CLIENT_ID, VITE_SPOTIFY_REDIRECT_URI } = providerConfig;

export async function authenticate(callback: (token: AccessToken) => void) {
const auth = new AuthorizationCodeWithPKCEStrategy(
VITE_SPOTIFY_CLIENT_ID,
VITE_SPOTIFY_REDIRECT_URI,
[...SPOTIFY_OAUTH_SCOPES]
);
const client = new SpotifyApi(auth);
export default class SpotifyProvider implements IProviderClient {
readonly meta = spotifyProviderMeta;
isAuthenticated = false;

// API event handlers
private onAuth: () => void;

constructor({ onAuth }: IProviderClientConstructor) {
this.onAuth = onAuth;
}

try {
const { authenticated } = await client.authenticate();
async authenticate() {
const auth = new AuthorizationCodeWithPKCEStrategy(
VITE_SPOTIFY_CLIENT_ID,
VITE_SPOTIFY_REDIRECT_URI,
[...SPOTIFY_OAUTH_SCOPES]
);
const client = new SpotifyApi(auth);

if (authenticated) {
console.log("Authenticated");
try {
const { authenticated } = await client.authenticate();

if (authenticated) {
this.isAuthenticated = true;
this.onAuth();
}
} catch (e) {
console.error(e);
}
} catch (e) {
console.error(e);
}

async callback() {
await this.authenticate();
}
}
8 changes: 4 additions & 4 deletions app/providers/spotify/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import type { ProviderMeta } from "@/types/providers/meta";

export default {
const spotifyProviderMeta: ProviderMeta = {
name: "Spotify",
id: "spotify",
auth: (await import("./client")).authenticate,
callback: (await import("./client")).authenticate,
} as ProviderMeta;
};

export default spotifyProviderMeta;
5 changes: 4 additions & 1 deletion app/routes/__root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { Body, Head, Html, Meta, Scripts } from "@tanstack/start";
import type { ReactNode } from "react";

import appCss from "../styles/app.css?url";
import { PlayerProvidersProvider } from "@/components/contexts/PlayerProviders";

export const Route = createRootRoute({
meta: () => [
Expand Down Expand Up @@ -34,7 +35,9 @@ export const Route = createRootRoute({
function RootComponent() {
return (
<RootDocument>
<Outlet />
<PlayerProvidersProvider>
<Outlet />
</PlayerProvidersProvider>
</RootDocument>
);
}
Expand Down
3 changes: 3 additions & 0 deletions app/routes/auth/callback.$provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router";

import providers from "@/providers";
import { useEffect } from "react";
import { usePlayerProviders } from "@/components/contexts/PlayerProviders";

export const Route = createFileRoute("/auth/callback/$provider")({
component: RouteComponent,
Expand All @@ -11,6 +12,8 @@ function RouteComponent() {
const { provider } = Route.useParams();
const navigate = useNavigate();

const providers = usePlayerProviders();

if (!providers[provider]) {
return <h1>Provider not found</h1>;
}
Expand Down
25 changes: 14 additions & 11 deletions app/routes/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { createFileRoute } from "@tanstack/react-router";
import { MiscLinks } from "@/components/MiscLinks";

import providers from "@/providers";
import { usePlayerProviders } from "@/components/contexts/PlayerProviders";

export const Route = createFileRoute("/")({
component: Home,
});

function Home() {
const providers = usePlayerProviders();

return (
<main className="flex flex-col items-center justify-center h-screen gap-12">
<section className="flex flex-row gap-6 items-center">
Expand All @@ -25,15 +26,17 @@ function Home() {
</section>

<div className="flex flex-col gap-4">
{Object.values(providers).map((provider) => (
<button
key={provider.id}
onClick={() => provider.auth()}
className="border-b-2 text-white/70 hover:text-white border-white/50 hover:border-white/70 text-lg tracking-wide active:scale-95 transition mx-auto"
>
{provider.name}
</button>
))}
{Object.entries(providers).map(([id, provider]) => {
return (
<button
key={id}
onClick={() => provider.authenticate()}
className="border-b-2 text-white/70 hover:text-white border-white/50 hover:border-white/70 text-lg tracking-wide active:scale-95 transition mx-auto"
>
{provider.meta.name}
</button>
);
})}
</div>
</main>
);
Expand Down
14 changes: 14 additions & 0 deletions app/types/providers/client.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { ProviderMeta } from "@/types/providers/meta";

export interface IProviderClientConstructor {
onAuth: () => void;
}

export interface IProviderClient {
readonly meta: ProviderMeta;

isAuthenticated: boolean;

authenticate(): Promise<void>;
callback(): Promise<void>;
}
2 changes: 0 additions & 2 deletions app/types/providers/meta.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
export interface ProviderMeta {
name: string;
id: string;
auth: (callback?: (token: any) => void) => any;
callback: (callback?: (token: any) => void) => any;
}

0 comments on commit ffab780

Please sign in to comment.