Page MenuHomePhabricator

D6423.id21492.diff
No OneTemporary

D6423.id21492.diff

diff --git a/lib/hooks/ens-cache.js b/lib/hooks/ens-cache.js
--- a/lib/hooks/ens-cache.js
+++ b/lib/hooks/ens-cache.js
@@ -1,65 +1,123 @@
// @flow
+import invariant from 'invariant';
import * as React from 'react';
import { ENSCacheContext } from '../components/ens-cache-provider.react';
import { userIdentifiedByETHAddress } from '../shared/account-utils';
import { stringForUser } from '../shared/user-utils';
-function useStringForUser(
- user: ?{ +username?: ?string, +isViewer?: ?boolean, ... },
-): ?string {
- const ethAddress = React.useMemo(() => {
- if (
- !user ||
- user.isViewer ||
- !user.username ||
- !userIdentifiedByETHAddress(user)
- ) {
- return null;
- }
- return user.username;
- }, [user]);
-
+type BaseUserInfo = { +username?: ?string, ... };
+function useENSNames<T: ?BaseUserInfo>(users: $ReadOnlyArray<T>): Array<T> {
const cacheContext = React.useContext(ENSCacheContext);
const { ensCache } = cacheContext;
- const cachedResult =
- ethAddress && ensCache
- ? ensCache.getCachedNameForAddress(ethAddress)
- : null;
- const [ensName, setENSName] = React.useState<?string>(null);
+ const cachedInfo = React.useMemo(
+ () =>
+ users.map(user => {
+ if (!user) {
+ return user;
+ }
+ const { username } = user;
+ const ethAddress =
+ username && userIdentifiedByETHAddress(user) ? username : null;
+ return {
+ input: user,
+ ethAddress,
+ cachedResult:
+ ethAddress && ensCache
+ ? ensCache.getCachedNameForAddress(ethAddress)
+ : null,
+ };
+ }),
+ [users, ensCache],
+ );
- React.useEffect(() => {
- // Whenever the ETH address changes, clear out ENS name before requery below
- setENSName(null);
- }, [ethAddress]);
+ const [fetchedAddresses, setFetchedAddresses] = React.useState<
+ $ReadOnlySet<string>,
+ >(new Set());
+ const [ensNames, setENSNames] = React.useState<$ReadOnlyMap<string, string>>(
+ new Map(),
+ );
React.useEffect(() => {
- if (cachedResult || !ethAddress || !ensCache) {
+ if (!ensCache) {
+ return;
+ }
+ const needFetch = cachedInfo
+ .map(user => {
+ if (!user) {
+ return null;
+ }
+ const { ethAddress, cachedResult } = user;
+ if (!ethAddress || cachedResult || fetchedAddresses.has(ethAddress)) {
+ return null;
+ }
+ return ethAddress;
+ })
+ .filter(Boolean);
+ if (needFetch.length === 0) {
return;
}
- let cancelled = false;
- (async () => {
- const result = await ensCache.getNameForAddress(ethAddress);
- if (result && !cancelled) {
- setENSName(result);
+ setFetchedAddresses(oldFetchedAddresses => {
+ const newFetchedAddresses = new Set(oldFetchedAddresses);
+ for (const ethAddress of needFetch) {
+ newFetchedAddresses.add(ethAddress);
}
- })();
- return () => {
- cancelled = true;
- };
- }, [cachedResult, ethAddress, ensCache]);
+ return newFetchedAddresses;
+ });
+ for (const ethAddress of needFetch) {
+ (async () => {
+ const result = await ensCache.getNameForAddress(ethAddress);
+ if (!result) {
+ return;
+ }
+ setENSNames(oldENSNames => {
+ const newENSNames = new Map(oldENSNames);
+ newENSNames.set(ethAddress, result);
+ return newENSNames;
+ });
+ })();
+ }
+ }, [cachedInfo, fetchedAddresses, ensCache]);
- if (ensName) {
- return ensName;
- } else if (cachedResult) {
- return cachedResult;
- } else if (user) {
+ return cachedInfo.map(user => {
+ if (!user) {
+ return user;
+ }
+ const { input, ethAddress, cachedResult } = user;
+ if (cachedResult) {
+ return { ...input, username: cachedResult };
+ } else if (!ethAddress) {
+ return input;
+ }
+ const ensName = ensNames.get(ethAddress);
+ if (ensName) {
+ return { ...input, username: ensName };
+ }
+ return input;
+ });
+}
+
+function useStringForUser(
+ user: ?{ +username?: ?string, +isViewer?: ?boolean, ... },
+): ?string {
+ const toFetch = user?.isViewer ? null : user;
+ // stringForUser ignores username is isViewer, so we skip the ENS fetch
+ const [result] = useENSNames([toFetch]);
+ if (user?.isViewer) {
return stringForUser(user);
+ } else if (result) {
+ return stringForUser(result);
} else {
- return null;
+ invariant(
+ !user,
+ 'the only way result can be falsey is if useENSNames is passed a ' +
+ 'falsey input, and that can only happen if useStringForUser input is ' +
+ 'falsey or isViewer is set',
+ );
+ return user;
}
}
-export { useStringForUser };
+export { useENSNames, useStringForUser };

File Metadata

Mime Type
text/plain
Expires
Wed, Oct 2, 7:30 PM (22 h, 4 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2219332
Default Alt Text
D6423.id21492.diff (4 KB)

Event Timeline