Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F2860724
D6423.id21492.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
4 KB
Referenced Files
None
Subscribers
None
D6423.id21492.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D6423: [lib] Introduce useENSNames to fetch more than one ENS name at a time
Attached
Detach File
Event Timeline
Log In to Comment