refactor: split Lume Provider into LumeStatusProvider, NetworksProvider, and AuthProvider

This commit is contained in:
Derrick Hammer 2023-10-16 14:55:01 -04:00
parent 40f6699064
commit dc3fe6adb6
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
9 changed files with 137 additions and 69 deletions

View File

@ -0,0 +1,38 @@
import { createContext, useState, useContext, ReactNode } from "react";
// AuthContextType
export interface AuthContextType {
isLoggedIn: boolean;
setIsLoggedIn: (value: boolean) => void;
}
// AuthContext
const AuthContext = createContext<AuthContextType | undefined>(undefined);
// AuthProvider
interface AuthProviderProps {
children: ReactNode;
}
export const AuthProvider: React.FC<AuthProviderProps> = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
return (
<AuthContext.Provider value={{ isLoggedIn, setIsLoggedIn }}>
{children}
</AuthContext.Provider>
);
};
// useAuth hook
export function useAuth() {
const context = useContext(AuthContext);
if (!context) {
throw new Error("useAuth must be used within an AuthProvider");
}
return context;
}
export default AuthProvider;

View File

@ -0,0 +1,45 @@
import { createContext, useState, useContext, ReactNode } from "react";
// LumeStatusContextType
interface LumeStatusContextType {
inited: boolean;
setInited: (value: boolean) => void;
ready: boolean;
setReady: (value: boolean) => void;
}
// LumeStatusContext
export const LumeStatusContext = createContext<
LumeStatusContextType | undefined
>(undefined);
// LumeStatusProvider
interface LumeStatusProviderProps {
children: ReactNode;
}
export const LumeStatusProvider: React.FC<LumeStatusProviderProps> = ({
children,
}) => {
const [inited, setInited] = useState(false);
const [ready, setReady] = useState(false);
return (
<LumeStatusContext.Provider value={{ inited, setInited, ready, setReady }}>
{children}
</LumeStatusContext.Provider>
);
};
// useLumeStatus hook
export function useLumeStatus() {
const context = useContext(LumeStatusContext);
if (!context) {
throw new Error("useLumeStatus must be used within a LumeStatusProvider");
}
return context;
}
export default LumeStatusProvider;

View File

@ -8,7 +8,6 @@ import {
} from "react"; } from "react";
import { createClient as createNetworkRegistryClient } from "@lumeweb/kernel-network-registry-client"; import { createClient as createNetworkRegistryClient } from "@lumeweb/kernel-network-registry-client";
import { createNetworkClient } from "@lumeweb/libkernel/module"; import { createNetworkClient } from "@lumeweb/libkernel/module";
import { init, loginComplete } from "@lumeweb/libkernel/kernel";
type SyncState = "done" | "syncing" | "error"; type SyncState = "done" | "syncing" | "error";
@ -26,45 +25,35 @@ interface NetworkStatus {
error?: string; error?: string;
} }
type LumeObject = { export interface NetworksContextType {
networks: Network[]; networks: Network[];
}; setNetworks: React.Dispatch<React.SetStateAction<Network[]>>;
}
export type LumeContextType = {
isLoggedIn: boolean;
setIsLoggedIn: (value: boolean) => void;
lume: LumeObject;
inited: boolean;
setInited: React.Dispatch<React.SetStateAction<boolean>>;
ready: boolean;
setReady: React.Dispatch<React.SetStateAction<boolean>>;
};
const networkRegistry = createNetworkRegistryClient(); const networkRegistry = createNetworkRegistryClient();
const LumeContext = createContext<LumeContextType | undefined>(undefined); // Networks Context
const NetworksContext = createContext<NetworksContextType | undefined>(
undefined,
);
const LumeProvider = ({ children }) => { const NetworksProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false); const [networks, setNetworks] = useState<Network[]>([]);
const [ready, setReady] = useState<boolean>(false);
const [inited, setInited] = useState<boolean>(false);
const [lume, setLume] = useState<LumeObject>({ networks: [] });
const statusUnsubs = useRef(new Map()); const statusUnsubs = useRef(new Map());
const isMounted = useRef(true); // Use a ref to track mounting const isMounted = useRef(true); // Use a ref to track mounting
const handleStatusUpdate = useCallback((id, newNetwork) => { const handleStatusUpdate = useCallback((id, newNetwork) => {
setLume((prevLume) => { setNetworks((prevNetworks) => {
const updatedNetworks = prevLume.networks.map((network) => return prevNetworks.map((network) =>
network.id === id ? { ...network, ...newNetwork } : network, network.id === id ? { ...network, ...newNetwork } : network,
); );
return { ...prevLume, networks: updatedNetworks };
}); });
}, []); }, []);
const fetchAndUpdateNetworks = useCallback(async () => { const fetchAndUpdateNetworks = useCallback(async () => {
const unsub = () => { const unsub = () => {
statusUnsubs.current.forEach((unsub) => unsub()); statusUnsubs.current.forEach((unsub) => unsub());
statusUnsubs.current = new Map<any, any>(); statusUnsubs.current.clear();
}; };
try { try {
@ -110,10 +99,7 @@ const LumeProvider = ({ children }) => {
statusUnsubs.current = newStatusUnsubs; statusUnsubs.current = newStatusUnsubs;
if (isMounted.current) { if (isMounted.current) {
setLume((prevLume) => ({ setNetworks(Array.from(newNetworksMap.values()));
...prevLume,
networks: Array.from(newNetworksMap.values()),
}));
} else { } else {
unsub(); unsub();
} }
@ -139,29 +125,18 @@ const LumeProvider = ({ children }) => {
}, [fetchAndUpdateNetworks]); }, [fetchAndUpdateNetworks]);
return ( return (
<LumeContext.Provider <NetworksContext.Provider value={{ networks, setNetworks }}>
value={{
lume,
ready,
setReady,
isLoggedIn,
setIsLoggedIn,
inited,
setInited,
}}>
{children} {children}
</LumeContext.Provider> </NetworksContext.Provider>
); );
}; };
export default LumeProvider; export function useNetworks() {
const context = useContext(NetworksContext);
export function useLume() { if (!context) {
const ctx = useContext(LumeContext); throw new Error("useNetworks must be used within a NetworksProvider");
}
if (!ctx) { return context;
throw new Error("useLume must be used within a LumeProvider");
} }
return ctx; export default NetworksProvider;
}

View File

@ -1,11 +1,11 @@
import * as Dialog from "@radix-ui/react-dialog"; import * as Dialog from "@radix-ui/react-dialog";
import { Network, useLume } from "../LumeProvider";
import Logo from "../../../assets/lume-logo.png"; import Logo from "../../../assets/lume-logo.png";
import { cva } from "class-variance-authority"; import { cva } from "class-variance-authority";
import { cn } from "../../utils"; import { cn } from "../../utils";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import React from "react"; import React from "react";
import camelCase from "camelcase"; import camelCase from "camelcase";
import { useNetworks, type Network } from "../../NetworksProvider";
const SYNCSTATE_TO_TEXT: Record<Network["syncState"], string> = { const SYNCSTATE_TO_TEXT: Record<Network["syncState"], string> = {
done: "Synced", done: "Synced",
@ -18,9 +18,7 @@ LumeDashboardTrigger.displayName = "LumeDashboardTrigger";
const LumeDashboard = (props: any) => { const LumeDashboard = (props: any) => {
const { children }: { children: React.PropsWithChildren } = props; const { children }: { children: React.PropsWithChildren } = props;
const { const { networks } = useNetworks();
lume: { networks },
} = useLume();
const [uniqueNetworkTypes, setUniqueNetworkTypes] = useState<string[]>([]); const [uniqueNetworkTypes, setUniqueNetworkTypes] = useState<string[]>([]);

View File

@ -3,15 +3,15 @@ import {
// loginComplete, // loginComplete,
// logoutComplete, // logoutComplete,
} from "@lumeweb/libkernel/kernel"; } from "@lumeweb/libkernel/kernel";
import { useLume } from "../LumeProvider";
import React, { useContext } from "react"; import React, { useContext } from "react";
import { useAuth } from "../../AuthProvider.js";
export const LumeIdentityContext = React.createContext< export const LumeIdentityContext = React.createContext<
{ open: boolean; setOpen: (open: boolean) => void } | undefined { open: boolean; setOpen: (open: boolean) => void } | undefined
>(undefined); >(undefined);
export function useLumeIndentity() { export function useLumeIdentity() {
const { isLoggedIn, setIsLoggedIn } = useLume(); const { isLoggedIn, setIsLoggedIn } = useAuth();
const ctx = useContext(LumeIdentityContext); const ctx = useContext(LumeIdentityContext);
if (!ctx) { if (!ctx) {

View File

@ -10,7 +10,7 @@ import {
ExclamationTriangleIcon, ExclamationTriangleIcon,
} from "@radix-ui/react-icons"; } from "@radix-ui/react-icons";
import { AnimatePresence, m } from "framer-motion"; import { AnimatePresence, m } from "framer-motion";
import { useLumeIndentity } from "./LumeIdentityContext"; import { useLumeIdentity } from "./LumeIdentityContext";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import * as bip39 from "@scure/bip39"; import * as bip39 from "@scure/bip39";
@ -54,7 +54,7 @@ const SubmitButtonComponent = () => {
}; };
const SeedPhraseInputComponent = () => { const SeedPhraseInputComponent = () => {
const { signIn } = useLumeIndentity(); const { signIn } = useLumeIdentity();
return ( return (
<m.form <m.form
className="flex-col flex gap-y-4" className="flex-col flex gap-y-4"
@ -118,7 +118,7 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
"idle" | "clicked" "idle" | "clicked"
>("idle"); >("idle");
const [step, setStep] = useState<number>(0); const [step, setStep] = useState<number>(0);
const { signIn } = useLumeIndentity(); const { signIn } = useLumeIdentity();
const phrases = useMemo(() => { const phrases = useMemo(() => {
return bip39 return bip39
@ -158,10 +158,12 @@ const SeedPhraseGenerationComponent = ({ phraseLength = 12 }) => {
<div <div
key={`SeedPhrase_${index}`} key={`SeedPhrase_${index}`}
className={`${TW_PREFIX} relative justify-center items-center gap-2 flex h-10 rounded border border-current ring-current text-neutral-700 w-[calc(33%-8px)]`}> className={`${TW_PREFIX} relative justify-center items-center gap-2 flex h-10 rounded border border-current ring-current text-neutral-700 w-[calc(33%-8px)]`}>
<span className={`${TW_PREFIX} text-white text-md font-normal leading-normal w-full h-fit px-2 bg-transparent text-center`}> <span
className={`${TW_PREFIX} text-white text-md font-normal leading-normal w-full h-fit px-2 bg-transparent text-center`}>
{phrase} {phrase}
</span> </span>
<span className={`${TW_PREFIX} left-[6px] top-0 absolute text-current text-xs font-normal leading-normal`}> <span
className={`${TW_PREFIX} left-[6px] top-0 absolute text-current text-xs font-normal leading-normal`}>
{index + 1} {index + 1}
</span> </span>
</div> </div>

View File

@ -1,8 +1,18 @@
export { export {
default as LumeProvider, default as AuthProvider,
useLume, useAuth,
type LumeContextType, type AuthContextType,
} from "./components/lume/LumeProvider"; } from "./components/AuthProvider";
export {
default as NetworksProvider,
useNetworks,
type NetworksContextType,
} from "./components/NetworksProvider";
export {
default as LumeStatusProvider,
useLumeStatus,
type LumeStatusContext,
} from "src/components/LumeStatusProvider.js";
export { export {
default as LumeDashboard, default as LumeDashboard,
LumeDashboardTrigger, LumeDashboardTrigger,

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { StoryFn, Meta } from "@storybook/react"; import { StoryFn, Meta } from "@storybook/react";
import LumeDashboard from "../src/components/lume/LumeDashboard/LumeDashboard.js"; import LumeDashboard from "../src/components/lume/LumeDashboard/LumeDashboard.js";
import LumeProvider from "../src/components/lume/LumeProvider.js"; import NetworksProvider from "src/components/NetworksProvider.js";
export default { export default {
title: "LumeDashboard", title: "LumeDashboard",
@ -9,9 +9,9 @@ export default {
} as Meta<typeof LumeDashboard>; } as Meta<typeof LumeDashboard>;
const Template: StoryFn<typeof LumeDashboard> = (args) => ( const Template: StoryFn<typeof LumeDashboard> = (args) => (
<LumeProvider> <NetworksProvider>
<LumeDashboard {...args} /> <LumeDashboard {...args} />
</LumeProvider> </NetworksProvider>
); );
export const Primary = Template.bind({}); export const Primary = Template.bind({});

View File

@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import { StoryFn, Meta } from "@storybook/react"; import { StoryFn, Meta } from "@storybook/react";
import LumeIdentity from "../src/components/lume/LumeIdentity/LumeIdentity.js"; import LumeIdentity from "../src/components/lume/LumeIdentity/LumeIdentity.js";
import LumeProvider from "../src/components/lume/LumeProvider.js"; import AuthProvider from "src/components/AuthProvider.js";
export default { export default {
title: "LumeIdentity", title: "LumeIdentity",
@ -9,9 +9,9 @@ export default {
} as Meta<typeof LumeIdentity>; } as Meta<typeof LumeIdentity>;
const Template: StoryFn<typeof LumeIdentity> = (args) => ( const Template: StoryFn<typeof LumeIdentity> = (args) => (
<LumeProvider> <AuthProvider>
<LumeIdentity {...args} /> <LumeIdentity {...args} />
</LumeProvider> </AuthProvider>
); );
export const Primary = Template.bind({}); export const Primary = Template.bind({});