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,102 +31,98 @@ 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 () => {
const types = await networkRegistry.getTypes(); try {
const newNetworksMap = new Map(); // Use a Map to prevent duplicates based on chainId const types = await networkRegistry.getTypes();
const newStatusUnsubs = new Map(); const newNetworksMap = new Map();
const newStatusUnsubs = new Map();
for (const type of types) { for (const type of types) {
const list = await networkRegistry.getNetworksByType(type); const list = await networkRegistry.getNetworksByType(type);
for (const module of list) { for (const module of list) {
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,
type, type,
name, name,
id: module, id: module,
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);
newNetworksMap.set(module, network); // Store network in map to prevent duplicates newStatusUnsubs.set(module, statusUnsub);
newNetworksMap.set(module, initialNetworkStatus);
}
} }
statusUnsubs.current.forEach((unsub) => unsub());
statusUnsubs.current = newStatusUnsubs;
setLume((prevLume) => ({
...prevLume,
networks: Array.from(newNetworksMap.values()),
}));
} catch (error) {
console.error("Error fetching and updating networks:", error);
} }
// Unsubscribe from previous status updates
statusUnsubs.current.forEach((unsub) => unsub());
// Store new unsubscribe functions
statusUnsubs.current = newStatusUnsubs;
setLume((prevLume) => ({
...prevLume,
networks: Array.from(newNetworksMap.values()),
})); // Convert Map values to array
}; };
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>
); );
}; };