refactor: improve LumeProvider and track login and kernel loaded state

This commit is contained in:
Derrick Hammer 2023-10-11 04:05:06 -04:00
parent 8d54ad3a59
commit 1642fd44e4
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
1 changed files with 65 additions and 69 deletions

View File

@ -6,9 +6,9 @@ import {
useRef, useRef,
useState, useState,
} from "react"; } from "react";
import type { ReactNode } 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 { kernelLoaded, loginComplete } from "@lumeweb/libkernel/kernel";
type SyncState = "done" | "syncing" | "error"; type SyncState = "done" | "syncing" | "error";
@ -31,36 +31,35 @@ type LumeObject = {
}; };
type LumeContextType = { type LumeContextType = {
isLoggedIn: boolean, isLoggedIn: boolean;
setIsLoggedIn: (value: boolean) => void, setIsLoggedIn: (value: boolean) => void;
lume: LumeObject; lume: LumeObject;
ready: boolean;
}; };
const networkRegistry = createNetworkRegistryClient(); const networkRegistry = createNetworkRegistryClient();
const LumeContext = createContext<LumeContextType | undefined>(undefined); const LumeContext = createContext<LumeContextType | undefined>(undefined);
const LumeProvider = ({ children }: { children: ReactNode }) => { const LumeProvider = ({ children }) => {
const [isLoggedIn, setIsLoggedIn] = useState(false);
const [ready, setReady] = useState(false);
const [lume, setLume] = useState<LumeObject>({ networks: [] }); const [lume, setLume] = useState<LumeObject>({ networks: [] });
// Map to store unsubscribe functions for client.status subscriptions
const statusUnsubs = useRef(new Map()); const statusUnsubs = useRef(new Map());
const handleStatusUpdate = useCallback( const handleStatusUpdate = useCallback((id, newNetwork) => {
(id: string, newNetwork: NetworkStatus & { syncState: SyncState }) => {
setLume((prevLume) => { setLume((prevLume) => {
const updatedNetworks = prevLume.networks.map((network) => const updatedNetworks = prevLume.networks.map((network) =>
network.id === id ? { ...network, ...newNetwork } : network, network.id === id ? { ...network, ...newNetwork } : network,
); );
return { ...prevLume, networks: updatedNetworks }; return { ...prevLume, networks: updatedNetworks };
}); });
}, }, []);
[],
);
const update = async () => { const fetchAndUpdateNetworks = async () => {
try {
const types = await networkRegistry.getTypes(); const types = await networkRegistry.getTypes();
const newNetworksMap = new Map(); // Use a Map to prevent duplicates based on chainId const newNetworksMap = new Map();
const newStatusUnsubs = new Map(); const newStatusUnsubs = new Map();
for (const type of types) { for (const type of types) {
@ -70,7 +69,7 @@ const LumeProvider = ({ children }: { children: ReactNode }) => {
const client = createNetworkClient(module); const client = createNetworkClient(module);
const name = await client.name(); const name = await client.name();
const network: Network = { const initialNetworkStatus = {
peers: 0, peers: 0,
ready: false, ready: false,
sync: 0, sync: 0,
@ -80,53 +79,50 @@ const LumeProvider = ({ children }: { children: ReactNode }) => {
syncState: "syncing", syncState: "syncing",
}; };
// Subscribe to status updates const statusUnsub = client.status((newStatus) => {
const statusUnsub = client.status((newStatus: NetworkStatus) => { const syncState = newStatus.ready
let syncState: SyncState = "syncing"; ? "done"
: newStatus.error
if (newStatus.ready) { ? "error"
syncState = "done"; : "syncing";
} else if (newStatus.error) { handleStatusUpdate(module, { ...newStatus, syncState });
syncState = "error";
}
handleStatusUpdate(module, {
...newStatus,
syncState,
});
}); });
newStatusUnsubs.set(module, statusUnsub); newStatusUnsubs.set(module, statusUnsub);
newNetworksMap.set(module, initialNetworkStatus);
newNetworksMap.set(module, network); // Store network in map to prevent duplicates
} }
} }
// Unsubscribe from previous status updates
statusUnsubs.current.forEach((unsub) => unsub()); statusUnsubs.current.forEach((unsub) => unsub());
// Store new unsubscribe functions
statusUnsubs.current = newStatusUnsubs; statusUnsubs.current = newStatusUnsubs;
setLume((prevLume) => ({ setLume((prevLume) => ({
...prevLume, ...prevLume,
networks: Array.from(newNetworksMap.values()), networks: Array.from(newNetworksMap.values()),
})); // Convert Map values to array }));
} catch (error) {
console.error("Error fetching and updating networks:", error);
}
}; };
const subDone = networkRegistry.subscribeToUpdates(update);
useEffect(() => { useEffect(() => {
update(); // Initial update on component mount fetchAndUpdateNetworks();
loginComplete().then(() => setIsLoggedIn(true));
kernelLoaded().then(() => setReady(true));
const subDone = networkRegistry.subscribeToUpdates(fetchAndUpdateNetworks);
return () => { return () => {
subDone(); // Unsubscribe from network registry updates on component unmount subDone();
// Unsubscribe from all client.status updates
statusUnsubs.current.forEach((unsub) => unsub()); statusUnsubs.current.forEach((unsub) => unsub());
}; };
}, []); }, [fetchAndUpdateNetworks]);
const [isLoggedIn, setIsLoggedIn] = useState(false);
return ( return (
<LumeContext.Provider value={{ lume, isLoggedIn, setIsLoggedIn }}>{children}</LumeContext.Provider> <LumeContext.Provider value={{ lume, ready, isLoggedIn, setIsLoggedIn }}>
{children}
</LumeContext.Provider>
); );
}; };