refactor: simplify GroupListViewer cache usage

- Remove resolvedMetadataMap state and replace with simple cacheVersion counter
- Combine two separate effects into one fire-and-forget effect
- Extract metadata inline in groupsWithRecency observable via updateFromEvent/get
- Reduced from 71 to 32 lines for cache handling

https://claude.ai/code/session_01CCxAcUsRBkWSL6as1wtFoA
This commit is contained in:
Claude
2026-01-30 15:48:33 +00:00
parent 343058efbd
commit 49f52ed864

View File

@@ -340,80 +340,29 @@ export function GroupListViewer({ identifier }: GroupListViewerProps) {
);
}, [groups]);
// Track resolved metadata for UI updates
const [resolvedMetadataMap, setResolvedMetadataMap] = useState<
Map<string, GroupMetadata>
>(new Map());
// Track when cache has been populated (triggers re-render after async resolves)
const [cacheVersion, setCacheVersion] = useState(0);
// Update shared cache when kind 39000 events arrive, then sync to local state
useEffect(() => {
if (!groupMetadataMap || groupMetadataMap.size === 0) return;
// Update cache from received events
for (const [groupId, event] of groupMetadataMap) {
const group = groups.find((g) => g.groupId === groupId);
if (group) {
groupMetadataCache.updateFromEvent(group.relayUrl, event);
}
}
// Sync cache to local state for rendering
setResolvedMetadataMap((prev) => {
const updated = new Map(prev);
for (const group of groups) {
if (group.groupId === "_") continue;
const cached = groupMetadataCache.get(group.relayUrl, group.groupId);
if (cached) {
updated.set(
groupMetadataCache.getKey(group.relayUrl, group.groupId),
cached,
);
}
}
return updated;
});
}, [groups, groupMetadataMap]);
// Resolve metadata for groups not yet in cache (async, triggers on mount)
// Background resolve for all groups - fire-and-forget, triggers re-render when complete
useEffect(() => {
if (groups.length === 0) return;
const resolveUncached = async () => {
const resolved: Array<{
key: string;
metadata: GroupMetadata;
}> = [];
let mounted = true;
await Promise.all(
groups.map(async (group) => {
if (group.groupId === "_") return;
const key = groupMetadataCache.getKey(group.relayUrl, group.groupId);
// Skip if already in shared cache (avoids re-fetching)
if (groupMetadataCache.get(group.relayUrl, group.groupId)) return;
// Resolve using shared cache (will fetch if needed)
const metadata = await groupMetadataCache.resolve(
group.relayUrl,
group.groupId,
);
resolved.push({ key, metadata });
}),
);
if (resolved.length > 0) {
setResolvedMetadataMap((prev) => {
const updated = new Map(prev);
for (const { key, metadata } of resolved) {
updated.set(key, metadata);
}
return updated;
});
Promise.all(
groups.map(async (g) => {
if (g.groupId === "_") return;
await groupMetadataCache.resolve(g.relayUrl, g.groupId);
}),
).then(() => {
if (mounted) {
setCacheVersion((v) => v + 1);
}
};
});
resolveUncached();
return () => {
mounted = false;
};
}, [groups]);
// Subscribe to latest messages (kind 9) for all groups to get recency
@@ -470,15 +419,26 @@ export function GroupListViewer({ identifier }: GroupListViewerProps) {
}
}
// Merge with groups
// Merge with groups - get metadata inline from cache
const groupsWithInfo: GroupInfo[] = groups.map((g) => {
const groupKey = `${g.relayUrl}'${g.groupId}`;
const metadataEvent = groupMetadataMap?.get(g.groupId);
// Update cache from event if available, otherwise get from cache
const resolvedMetadata =
g.groupId === "_"
? undefined
: metadataEvent
? groupMetadataCache.updateFromEvent(
g.relayUrl,
metadataEvent,
)
: groupMetadataCache.get(g.relayUrl, g.groupId);
return {
groupId: g.groupId,
relayUrl: g.relayUrl,
metadata: groupMetadataMap?.get(g.groupId),
metadata: metadataEvent,
lastMessage: messageMap.get(g.groupId),
resolvedMetadata: resolvedMetadataMap.get(groupKey),
resolvedMetadata,
};
});
@@ -492,7 +452,8 @@ export function GroupListViewer({ identifier }: GroupListViewerProps) {
return groupsWithInfo;
}),
);
}, [groups, groupMetadataMap, resolvedMetadataMap]);
// cacheVersion triggers re-render when async resolves complete
}, [groups, groupMetadataMap, cacheVersion]);
// Only require sign-in if no identifier is provided (viewing own groups)
if (!targetPubkey) {