PR comments

- Account loading now dedupes in-flight requests per network instead of sharing one global promise across all networks.
- Regression tests cover same-network reuse and cross-network isolation.
This commit is contained in:
Tommy Verrall
2026-06-08 19:10:36 +02:00
parent 13d48b4bb6
commit 6f5e831127
3 changed files with 65 additions and 14 deletions
+5 -14
View File
@@ -21,6 +21,7 @@ import { Console } from '../utils/console';
import { createSignInWindow, getReactState, setReactState } from '../requests/app';
import { fetchNymPriceDeduped, getNetworkOverviewEndpoints, clearNymPriceCache } from '../api/networkOverview';
import { signInAndNavigateToBalance } from '../utils/signInAndNavigateToBalance';
import { dedupeInflightByKey } from '../utils/dedupeInflightByKey';
import { toDisplay } from '../utils';
export const urls = (networkName?: Network) =>
@@ -103,7 +104,7 @@ export const AppProvider: FCWithChildren = ({ children }) => {
const [loginType, setLoginType] = useState<'mnemonic' | 'password'>();
const [isLoading, setIsLoadingInternal] = useState(false);
const hadClientDetailsRef = useRef(false);
const accountLoadInflightRef = useRef<Promise<Account | undefined> | null>(null);
const accountLoadInflightRef = useRef<Map<Network, Promise<Account | undefined>>>(new Map());
const [loadingPresentation, setLoadingPresentation] = useState<AppLoadingPresentation>('auth-splash');
const [loadingOverlayTitle, setLoadingOverlayTitle] = useState('');
const [loadingOverlaySubtitle, setLoadingOverlaySubtitle] = useState<string | undefined>();
@@ -161,12 +162,8 @@ export const AppProvider: FCWithChildren = ({ children }) => {
setMixnodeDetails(null);
};
const loadAccount = async (n: Network): Promise<Account | undefined> => {
if (accountLoadInflightRef.current) {
return accountLoadInflightRef.current;
}
const pending = (async () => {
const loadAccount = async (n: Network): Promise<Account | undefined> =>
dedupeInflightByKey(accountLoadInflightRef.current, n, async () => {
try {
const client = await selectNetwork(n);
setClientDetails(client);
@@ -175,14 +172,8 @@ export const AppProvider: FCWithChildren = ({ children }) => {
enqueueSnackbar('Error loading account', { variant: 'error' });
Console.error(e as string);
return undefined;
} finally {
accountLoadInflightRef.current = null;
}
})();
accountLoadInflightRef.current = pending;
return pending;
};
});
const loadStoredAccounts = async () => {
const accounts = await listAccounts();
@@ -0,0 +1,43 @@
import { dedupeInflightByKey } from './dedupeInflightByKey';
describe('dedupeInflightByKey', () => {
it('reuses the in-flight promise for the same key', async () => {
const inflight = new Map<string, Promise<string>>();
let calls = 0;
const load = () => {
calls += 1;
return new Promise<string>((resolve) => {
setTimeout(() => resolve('done'), 20);
});
};
const first = dedupeInflightByKey(inflight, 'MAINNET', load);
const second = dedupeInflightByKey(inflight, 'MAINNET', load);
expect(first).toBe(second);
await first;
expect(calls).toBe(1);
});
it('does not reuse promises across different keys', async () => {
const inflight = new Map<string, Promise<string>>();
let calls = 0;
const load = (value: string) => () => {
calls += 1;
return Promise.resolve(value);
};
const mainnet = dedupeInflightByKey(inflight, 'MAINNET', load('mainnet'));
const sandbox = dedupeInflightByKey(inflight, 'SANDBOX', load('sandbox'));
await expect(mainnet).resolves.toBe('mainnet');
await expect(sandbox).resolves.toBe('sandbox');
expect(calls).toBe(2);
});
it('clears the key after the promise settles', async () => {
const inflight = new Map<string, Promise<string>>();
await dedupeInflightByKey(inflight, 'MAINNET', async () => 'done');
expect(inflight.has('MAINNET')).toBe(false);
});
});
@@ -0,0 +1,17 @@
export function dedupeInflightByKey<K, T>(
inflight: Map<K, Promise<T>>,
key: K,
load: () => Promise<T>,
): Promise<T> {
const existing = inflight.get(key);
if (existing) {
return existing;
}
const pending = load().finally(() => {
inflight.delete(key);
});
inflight.set(key, pending);
return pending;
}