Skip to content

Commit

Permalink
refactor: don't allow null for context
Browse files Browse the repository at this point in the history
throw if the provider is missing and the context doesn't have a value

give it that kasper je ne sais quoi
  • Loading branch information
6lr61 committed Sep 13, 2024
1 parent e9033f8 commit aa8abad
Show file tree
Hide file tree
Showing 16 changed files with 66 additions and 85 deletions.
14 changes: 4 additions & 10 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useContext, useMemo } from "react";
import { AuthStateContext } from "./contexts/auth-state/AuthStateContext";
import { AuthContext } from "./contexts/auth-state/AuthContext";
import LoginButton from "./components/LoginButton";
import Chat from "./components/Chat";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

export default function App() {
const authContext = useContext(AuthStateContext);
const { authState } = useContext(AuthContext);
const queryClient = useMemo(
() =>
new QueryClient({
Expand All @@ -14,19 +14,13 @@ export default function App() {
[]
);

if (!authContext) {
return <p>Missing AuthStateContext provider?</p>;
}

return (
<>
<QueryClientProvider client={queryClient}>
<LoginButton />
{authContext.authState && (
{authState && (
<section>
<p className="bg-pink-50">
Hello: {authContext.authState.user.login}
</p>
<p className="bg-pink-50">Hello: {authState.user.login}</p>
<article>
<h2>Chat Messages:</h2>
<Chat />
Expand Down
2 changes: 1 addition & 1 deletion src/components/BadgeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export default function BadgeList({
}: Props): React.ReactElement | undefined {
const twitchBadges = useContext(TwitchBadgeConext);

if (!twitchBadges || badges.length === 0) {
if (badges.length === 0) {
return;
}

Expand Down
14 changes: 5 additions & 9 deletions src/components/LoginButton.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,18 @@
import { useContext } from "react";
import { AuthStateContext } from "../contexts/auth-state/AuthStateContext";
import { AuthContext } from "../contexts/auth-state/AuthContext";

export default function LoginButton(): React.ReactElement {
const context = useContext(AuthStateContext);
const { authState, login, signOut } = useContext(AuthContext);

if (!context) {
throw new Error("LoginButton: Missing AuthStateContext");
}

if (context.authState) {
if (authState) {
return (
<button type="button" onClick={context.signOut}>
<button type="button" onClick={signOut}>
Sign out
</button>
);
} else {
return (
<button type="button" onClick={context.login}>
<button type="button" onClick={login}>
Login
</button>
);
Expand Down
6 changes: 3 additions & 3 deletions src/components/MentionSegment.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useContext } from "react";
import { AuthStateContext } from "../contexts/auth-state/AuthStateContext";
import { AuthContext } from "../contexts/auth-state/AuthContext";

interface MentionProps {
text: string;
Expand All @@ -9,12 +9,12 @@ export default function MentionSegment({
text,
}: MentionProps): React.ReactElement {
const mentioned = text.replace("@", "").toLocaleLowerCase();
const authStateContext = useContext(AuthStateContext);
const { authState } = useContext(AuthContext);

return (
<span
className={
mentioned === authStateContext?.authState?.user.login
mentioned === authState?.user.login
? "font-bold p-1 rounded-sm bg-slate-600"
: "p-1 rounded-sm bg-slate-700"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,10 @@ interface Value {
signOut: () => void;
}

export const AuthStateContext = createContext<Value | null>(null);
export const AuthContext = createContext<Value>(
new Proxy({} as Value, {
get() {
throw new Error("AuthContext must be provided");
},
})
);
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { useLocalStorage } from "../../hooks/useLocalStorage";
import { validateToken } from "../../utils/validateToken";
import type { OAuthMessage } from "../../callback";
import { revokeToken } from "../../utils/revokeToken";
import { type AuthState, AuthStateContext } from "./AuthStateContext";
import { type AuthState, AuthContext } from "./AuthContext";

const OAUTH2_URL = "https://id.twitch.tv/oauth2/authorize";

export default function AuthStateProvider({
export default function AuthProvider({
children,
}: {
children: React.ReactNode;
Expand Down Expand Up @@ -101,9 +101,5 @@ export default function AuthStateProvider({

const value = { login, signOut, authState };

return (
<AuthStateContext.Provider value={value}>
{children}
</AuthStateContext.Provider>
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
11 changes: 6 additions & 5 deletions src/contexts/badges/TwitchBadgeContext.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { createContext } from "react";
import type { Badge, BadgeSet } from "../../utils/api/getBadges";

export type TwitchBadges = Map<
`${BadgeSet["set_id"]}/${Badge["id"]}`,
Omit<Badge, "id">
>;
export type BadgeKey = `${BadgeSet["set_id"]}/${Badge["id"]}`;
export type BadgeValue = Omit<Badge, "id">;
export type TwitchBadges = Map<BadgeKey, BadgeValue>;

export const TwitchBadgeConext = createContext<TwitchBadges | null>(null);
export const TwitchBadgeConext = createContext<TwitchBadges>(
new Map<BadgeKey, BadgeValue>()
);
29 changes: 16 additions & 13 deletions src/contexts/badges/TwitchBadgeProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { useContext, useEffect, useState } from "react";
import { TwitchBadgeConext, type TwitchBadges } from "./TwitchBadgeContext";
import { AuthStateContext } from "../auth-state/AuthStateContext";
import {
TwitchBadgeConext,
type BadgeKey,
type BadgeValue,
type TwitchBadges,
} from "./TwitchBadgeContext";
import { AuthContext } from "../auth-state/AuthContext";
import { getBadges } from "../../utils/api/getBadges";

interface Props {
Expand All @@ -10,21 +15,19 @@ interface Props {
export default function TwitchBadgeProvider({
children,
}: Props): React.ReactElement {
const [badges, setBadges] = useState<TwitchBadges | null>(null);
const authStateContext = useContext(AuthStateContext);
const [badges, setBadges] = useState<TwitchBadges>(
() => new Map<BadgeKey, BadgeValue>()
);
const { authState } = useContext(AuthContext);

useEffect(() => {
if (!authStateContext?.authState) {
let keep = true;

if (!authState) {
return;
}

let keep = true;

getBadges(
authStateContext.authState.token.value,
authStateContext.authState.client.id,
authStateContext.authState.user.id
)
getBadges(authState.token.value, authState.client.id, authState.user.id)
.then((sets) => {
const tmp = sets.flatMap(({ set_id, versions }) =>
versions.map(({ id, ...rest }) => [`${set_id}/${id}`, rest] as const)
Expand All @@ -41,7 +44,7 @@ export default function TwitchBadgeProvider({
return () => {
keep = false;
};
}, [authStateContext]);
}, [authState]);

return (
<TwitchBadgeConext.Provider value={badges}>
Expand Down
8 changes: 7 additions & 1 deletion src/contexts/event-sub/EventSubContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,10 @@ interface Value {
subscribe: typeof EventSub.instance.subscribe;
}

export const EventSubContext = createContext<Value | null>(null);
export const EventSubContext = createContext<Value>(
new Proxy({} as Value, {
get() {
throw new Error("EventSubContext must be provided");
},
})
);
16 changes: 3 additions & 13 deletions src/hooks/useEventSub.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import { useCallback, useContext, useEffect, useState } from "react";
import { EventSubContext } from "../contexts/event-sub/EventSubContext";
import { AuthStateContext } from "../contexts/auth-state/AuthStateContext";
import { AuthContext } from "../contexts/auth-state/AuthContext";

export function useEventSub(
type: string | string[],
condition: Record<string, unknown>,
bufferSize = 50
) {
const authStateContext = useContext(AuthStateContext);
const { authState } = useContext(AuthContext);
const eventSubContext = useContext(EventSubContext);
const [messages, setMessages] = useState<Record<string, unknown>[]>([]);
const [lastMessage, setLastMessage] = useState<Record<string, unknown>>();

if (!authStateContext) {
throw new Error("useEventSub: Needs an AuthStateContext Provider!");
}

if (!eventSubContext) {
throw new Error("useEventSub: Needs a EventSubContext Provider!");
}

const { subscribe } = eventSubContext;

const handleMessage = useCallback(
Expand All @@ -31,8 +23,6 @@ export function useEventSub(
);

useEffect(() => {
const { authState } = authStateContext;

if (!authState) {
return;
}
Expand All @@ -46,7 +36,7 @@ export function useEventSub(
}

return subscribe(authState, type, condition, handleMessage);
}, [authStateContext, condition, handleMessage, subscribe, type]);
}, [authState, condition, handleMessage, subscribe, type]);

return { lastMessage, messages };
}
5 changes: 2 additions & 3 deletions src/hooks/useGetProfile.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { useContext } from "react";
import { AuthStateContext } from "../contexts/auth-state/AuthStateContext";
import { AuthContext } from "../contexts/auth-state/AuthContext";
import { getUser } from "../utils/api/getUser";
import { useQuery } from "@tanstack/react-query";

export function useGetProfile(login: string) {
const authStateContext = useContext(AuthStateContext);
const authState = authStateContext?.authState;
const { authState } = useContext(AuthContext);

return useQuery({
enabled: !!authState,
Expand Down
16 changes: 3 additions & 13 deletions src/hooks/useTwitchChat.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useCallback, useContext, useEffect, useState } from "react";
import { AuthStateContext } from "../contexts/auth-state/AuthStateContext";
import { AuthContext } from "../contexts/auth-state/AuthContext";
import type { ChatMessageEvent } from "../utils/event-sub/events/chat/message";
import { EventSubContext } from "../contexts/event-sub/EventSubContext";
import type { ChatEventCommon } from "../utils/event-sub/events/chat/_common";
Expand Down Expand Up @@ -31,18 +31,10 @@ const subscriptions = [
];

export function useTwitchChat(bufferSize = 50, channelId?: string) {
const authStateContext = useContext(AuthStateContext);
const { authState } = useContext(AuthContext);
const eventSubContext = useContext(EventSubContext);
const [messages, setMessages] = useState<ChatMessage[]>([]);

if (!authStateContext) {
throw new Error("useTwitchChat: Needs an AuthStateContext Provider!");
}

if (!eventSubContext) {
throw new Error("useTwitchChat: Needs a EventSubContext Provider!");
}

const { subscribe } = eventSubContext;

const handleMessage = useCallback(
Expand Down Expand Up @@ -82,8 +74,6 @@ export function useTwitchChat(bufferSize = 50, channelId?: string) {
);

useEffect(() => {
const { authState } = authStateContext;

if (!authState) {
return;
}
Expand All @@ -94,7 +84,7 @@ export function useTwitchChat(bufferSize = 50, channelId?: string) {
};

return subscribe(authState, subscriptions, condition, handleMessage);
}, [authStateContext, channelId, handleMessage, subscribe]);
}, [authState, channelId, handleMessage, subscribe]);

return messages;
}
6 changes: 3 additions & 3 deletions src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import AuthStateProvider from "./contexts/auth-state/AuthStateProvider.tsx";
import AuthProvider from "./contexts/auth-state/AuthProvider.tsx";
import EventSubProvider from "./contexts/event-sub/EventSubProvider.tsx";

const rootElement = document.querySelector("#root");
Expand All @@ -13,10 +13,10 @@ if (!(rootElement instanceof HTMLDivElement)) {

createRoot(rootElement).render(
<StrictMode>
<AuthStateProvider>
<AuthProvider>
<EventSubProvider>
<App />
</EventSubProvider>
</AuthStateProvider>
</AuthProvider>
</StrictMode>
);
2 changes: 1 addition & 1 deletion src/utils/api/event-sub/subscribe.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AuthState } from "../../../contexts/auth-state/AuthStateContext";
import type { AuthState } from "../../../contexts/auth-state/AuthContext";

interface SubscriptionsResponse {
data: [
Expand Down
2 changes: 1 addition & 1 deletion src/utils/api/event-sub/unsubscribe.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AuthState } from "../../../contexts/auth-state/AuthStateContext";
import type { AuthState } from "../../../contexts/auth-state/AuthContext";

export async function unsubscribe(
authState: AuthState,
Expand Down
2 changes: 1 addition & 1 deletion src/utils/event-sub/EventSub.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AuthState } from "../../contexts/auth-state/AuthStateContext";
import type { AuthState } from "../../contexts/auth-state/AuthContext";
import { subscribe } from "../api/event-sub/subscribe";
import type {
EventSubMessage,
Expand Down

0 comments on commit aa8abad

Please sign in to comment.