From fa10c8a7a98aa2e87a8b508a573603afc52e8b55 Mon Sep 17 00:00:00 2001 From: Michael Peters Date: Thu, 13 Jan 2022 23:17:15 -0600 Subject: [PATCH] added the guild that the fetch value was fetched from alongside fetch values in guild subscriptions --- .../displays/display-guild-invites.tsx | 4 +- .../displays/display-guild-overview.tsx | 5 +- .../lists/components/guild-list-element.tsx | 2 +- .../lists/components/message-element.tsx | 2 +- .../webapp/elements/lists/message-list.tsx | 1 + .../elements/overlays/overlay-image.tsx | 2 +- .../elements/overlays/overlay-personalize.tsx | 2 +- .../elements/require/guild-subscriptions.ts | 161 ++++++++++++------ src/client/webapp/elements/sections/guild.tsx | 8 +- 9 files changed, 127 insertions(+), 60 deletions(-) diff --git a/src/client/webapp/elements/displays/display-guild-invites.tsx b/src/client/webapp/elements/displays/display-guild-invites.tsx index cd1e246..5694058 100644 --- a/src/client/webapp/elements/displays/display-guild-invites.tsx +++ b/src/client/webapp/elements/displays/display-guild-invites.tsx @@ -25,9 +25,9 @@ const GuildInvitesDisplay: FC = (props: GuildInvitesDi const url = 'https://localhost:3030'; // TODO: this will likely be a dropdown list at some point - const [ fetchRetryCallable, tokens, tokensError ] = useTokensSubscription(guild); + const [ fetchRetryCallable, tokens, tokensGuild, tokensError ] = useTokensSubscription(guild); - const [ guildMeta, guildMetaError ] = useGuildMetadataSubscription(guild); + const [ guildMeta, guildMetaGuild, guildMetaError ] = useGuildMetadataSubscription(guild); const [ expiresFromNow, setExpiresFromNow ] = useState(moment.duration(1, 'day')); const [ expiresFromNowText, setExpiresFromNowText ] = useState('1 day'); diff --git a/src/client/webapp/elements/displays/display-guild-overview.tsx b/src/client/webapp/elements/displays/display-guild-overview.tsx index 55d6a2b..1749a3a 100644 --- a/src/client/webapp/elements/displays/display-guild-overview.tsx +++ b/src/client/webapp/elements/displays/display-guild-overview.tsx @@ -18,8 +18,9 @@ export interface GuildOverviewDisplayProps { const GuildOverviewDisplay: FC = (props: GuildOverviewDisplayProps) => { const { guild } = props; - const [ guildMeta, guildMetaError ] = useGuildMetadataSubscription(guild); - const [ iconResource, iconResourceError ] = useResourceSubscription(guild, guildMeta?.iconResourceId ?? null); + // TODO: Use the one from guild.tsx (for both of these?) + const [ guildMeta, guildMetaGuild, guildMetaError ] = useGuildMetadataSubscription(guild); + const [ iconResource, iconResourceGuild, iconResourceError ] = useResourceSubscription(guild, guildMeta?.iconResourceId ?? null); const [ savedName, setSavedName ] = useState(null); const [ savedIconBuff, setSavedIconBuff ] = useState(null); diff --git a/src/client/webapp/elements/lists/components/guild-list-element.tsx b/src/client/webapp/elements/lists/components/guild-list-element.tsx index 842e682..ca3ef5d 100644 --- a/src/client/webapp/elements/lists/components/guild-list-element.tsx +++ b/src/client/webapp/elements/lists/components/guild-list-element.tsx @@ -22,7 +22,7 @@ const GuildListElement: FC = (props: GuildListElementProp // TODO: state higher up // TODO: handle metadata error - const [ guildMeta, guildMetaError ] = useGuildMetadataSubscription(guild); + const [ guildMeta, guildMetaGuild, guildMetaError ] = useGuildMetadataSubscription(guild); const [ selfMember ] = useSelfMemberSubscription(guild); const [ iconSrc ] = useSoftImageSrcResourceSubscription(guild, guildMeta?.iconResourceId ?? null); diff --git a/src/client/webapp/elements/lists/components/message-element.tsx b/src/client/webapp/elements/lists/components/message-element.tsx index 60d6595..f6eec1f 100644 --- a/src/client/webapp/elements/lists/components/message-element.tsx +++ b/src/client/webapp/elements/lists/components/message-element.tsx @@ -53,7 +53,7 @@ const PreviewImageElement: FC = (props: PreviewImageEl const { guild, previewWidth, previewHeight, resourcePreviewId, resourceId, resourceName, setOverlay } = props; // TODO: Handle resourceError - const [ previewImgSrc, previewResource, previewResourceError ] = useSoftImageSrcResourceSubscription(guild, resourcePreviewId); + const [ previewImgSrc, previewResource, previewResourceGuild, previewResourceError ] = useSoftImageSrcResourceSubscription(guild, resourcePreviewId); const [ contextMenu, onContextMenu ] = useContextClickContextMenu((alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => { if (!previewResource) return null; diff --git a/src/client/webapp/elements/lists/message-list.tsx b/src/client/webapp/elements/lists/message-list.tsx index 46db3b9..0e52831 100644 --- a/src/client/webapp/elements/lists/message-list.tsx +++ b/src/client/webapp/elements/lists/message-list.tsx @@ -26,6 +26,7 @@ const MessageList: FC = (props: MessageListProps) => { setScrollRatio, fetchResult, messages, + messagesGuild, messagesFetchError, messagesFetchAboveError, messagesFetchBelowError diff --git a/src/client/webapp/elements/overlays/overlay-image.tsx b/src/client/webapp/elements/overlays/overlay-image.tsx index 1fadb39..de17a7a 100644 --- a/src/client/webapp/elements/overlays/overlay-image.tsx +++ b/src/client/webapp/elements/overlays/overlay-image.tsx @@ -24,7 +24,7 @@ const ImageOverlay: FC = (props: ImageOverlayProps) => { const rootRef = useRef(null); - const [ imgSrc, resource, resourceError ] = useSoftImageSrcResourceSubscription(guild, resourceId); + const [ imgSrc, resource, resourceGuild, resourceError ] = useSoftImageSrcResourceSubscription(guild, resourceId); const [ contextMenu, onContextMenu ] = useContextClickContextMenu((alignment: IAlignment, relativeToPos: { x: number, y: number }, close: () => void) => { if (!resource) return null; diff --git a/src/client/webapp/elements/overlays/overlay-personalize.tsx b/src/client/webapp/elements/overlays/overlay-personalize.tsx index 73e82b4..d8c6624 100644 --- a/src/client/webapp/elements/overlays/overlay-personalize.tsx +++ b/src/client/webapp/elements/overlays/overlay-personalize.tsx @@ -25,7 +25,7 @@ const PersonalizeOverlay: FC = (props: PersonalizeOverl const rootRef = useRef(null); - const [ avatarResource, avatarResourceError ] = useResourceSubscription(guild, selfMember.avatarResourceId) + const [ avatarResource, avatarResourceGuild, avatarResourceError ] = useResourceSubscription(guild, selfMember.avatarResourceId) const displayNameInputRef = createRef(); diff --git a/src/client/webapp/elements/require/guild-subscriptions.ts b/src/client/webapp/elements/require/guild-subscriptions.ts index c232a2f..eb97c54 100644 --- a/src/client/webapp/elements/require/guild-subscriptions.ts +++ b/src/client/webapp/elements/require/guild-subscriptions.ts @@ -34,7 +34,7 @@ export type MultipleSubscriptionEvents = { interface EffectParams { guild: CombinedGuild; - onFetch: (value: T | null) => void; + onFetch: (value: T | null, valueGuild: CombinedGuild) => void; onFetchError: (e: unknown) => void; bindEventsFunc: () => void; unbindEventsFunc: () => void; @@ -77,6 +77,7 @@ function useGuildSubscriptionEffect( const isMounted = useIsMountedRef(); const guildRef = useRef(guild); + guildRef.current = guild; const fetchManagerFunc = useCallback(async () => { if (!isMounted.current) return; @@ -84,8 +85,8 @@ function useGuildSubscriptionEffect( try { const value = await fetchFunc(); if (!isMounted.current) return; - if (guildRef.current !== guild) return; // Don't call onFetch if we changed guilds - onFetch(value); + if (guildRef.current !== guild) return; // Don't call onFetch if we changed guilds. TODO: Test this + onFetch(value, guild); } catch (e: unknown) { LOG.error('error fetching for subscription', e); if (!isMounted.current) return; @@ -118,18 +119,22 @@ function useSingleGuildSubscription, fetchFunc: (() => Promise) | (() => Promise) -): [value: T | null, fetchError: unknown | null, events: EventEmitter] { +): [value: T | null, valueGuild: CombinedGuild | null, fetchError: unknown | null, events: EventEmitter] { const { updatedEventName, updatedEventArgsMap, conflictEventName, conflictEventArgsMap } = eventMappingParams; const isMounted = useIsMountedRef(); + const guildRef = useRef(guild); + guildRef.current = guild; const [ fetchError, setFetchError ] = useState(null); const [ value, setValue ] = useState(null); + const [ valueGuild, setValueGuild ] = useState(null); const events = useMemo(() => new EventEmitter(), []); - const onFetch = useCallback((fetchValue: T | null) => { + const onFetch = useCallback((fetchValue: T | null, fetchValueGuild: CombinedGuild) => { setValue(fetchValue); + setValueGuild(fetchValueGuild); setFetchError(null); events.emit('fetch'); }, []); @@ -137,16 +142,23 @@ function useSingleGuildSubscription { setFetchError(e); setValue(null); + setValueGuild(null); events.emit('fetch-error'); }, []); - const onUpdated = useCallback((updateValue: T) => { + const onUpdated = useCallback((updateValue: T, updateValueGuild: CombinedGuild) => { setValue(updateValue); + if (updateValueGuild !== guildRef.current) { + LOG.warn(`update guild (${updateValueGuild.id}) != current guild (${guildRef.current})`); + } events.emit('updated'); }, []); - const onConflict = useCallback((conflictValue: T) => { + const onConflict = useCallback((conflictValue: T, conflictValueGuild: CombinedGuild) => { setValue(conflictValue); + if (conflictValueGuild !== guildRef.current) { + LOG.warn(`conflict guild (${conflictValueGuild.id}) != current guild (${guildRef.current})`); + } events.emit('conflict'); }, []); @@ -154,23 +166,25 @@ function useSingleGuildSubscription): void => { if (!isMounted.current) return; + if (guildRef.current !== guild) return; const value = updatedEventArgsMap(...args); - onUpdated(value); - }, []) as (Connectable & Conflictable)[UE]; + onUpdated(value, guild); + }, [ guild ]) as (Connectable & Conflictable)[UE]; const boundConflictFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; + if (guildRef.current !== guild) return; const value = conflictEventArgsMap(...args); - onConflict(value); - }, []) as (Connectable & Conflictable)[CE]; + onConflict(value, guild); + }, [ guild ]) as (Connectable & Conflictable)[CE]; const bindEventsFunc = useCallback(() => { guild.on(updatedEventName, boundUpdateFunc); guild.on(conflictEventName, boundConflictFunc); - }, []); + }, [ boundUpdateFunc, boundConflictFunc ]); const unbindEventsFunc = useCallback(() => { guild.off(updatedEventName, boundUpdateFunc); guild.off(conflictEventName, boundConflictFunc); - }, []); + }, [ boundUpdateFunc, boundConflictFunc ]); useGuildSubscriptionEffect({ guild, @@ -180,7 +194,7 @@ function useSingleGuildSubscription Promise, value: T[] | null, + valueGuild: CombinedGuild | null, fetchError: unknown | null, events: EventEmitter> ] { @@ -208,15 +223,19 @@ function useMultipleGuildSubscription< } = eventMappingParams; const isMounted = useIsMountedRef(); + const guildRef = useRef(guild); + guildRef.current = guild; const [ fetchError, setFetchError ] = useState(null); const [ value, setValue ] = useState(null); + const [ valueGuild, setValueGuild ] = useState(null); const events = useMemo(() => new EventEmitter>(), []); - const onFetch = useCallback((fetchValue: T[] | null) => { + const onFetch = useCallback((fetchValue: T[] | null, fetchValueGuild: CombinedGuild) => { if (fetchValue) fetchValue.sort(sortFunc); setValue(fetchValue); + setValueGuild(fetchValueGuild); setFetchError(null); events.emit('fetch'); }, [ sortFunc ]); @@ -227,30 +246,39 @@ function useMultipleGuildSubscription< events.emit('fetch-error'); }, []); - const onNew = useCallback((newElements: T[]) => { + const onNew = useCallback((newElements: T[], newElementsGuild: CombinedGuild) => { setValue(currentValue => { - if (currentValue === null) return null; + if (currentValue === null) return Array.from(newElements).sort(sortFunc); return currentValue.concat(newElements).sort(sortFunc); - }) + }); + if (newElementsGuild !== guildRef.current) { + LOG.warn(`new elements guild (${newElementsGuild.id}) != current guild (${guildRef.current})`); + } events.emit('new', newElements); }, [ sortFunc ]); - const onUpdated = useCallback((updatedElements: T[]) => { + const onUpdated = useCallback((updatedElements: T[], updatedElementsGuild: CombinedGuild) => { setValue(currentValue => { if (currentValue === null) return null; return currentValue.map(element => updatedElements.find(updatedElement => updatedElement.id === element.id) ?? element).sort(sortFunc); }); + if (updatedElementsGuild !== guildRef.current) { + LOG.warn(`updated elements guild (${updatedElementsGuild.id}) != current guild (${guildRef.current})`); + } events.emit('updated', updatedElements); }, [ sortFunc ]); - const onRemoved = useCallback((removedElements: T[]) => { + const onRemoved = useCallback((removedElements: T[], removedElementsGuild: CombinedGuild) => { setValue(currentValue => { if (currentValue === null) return null; const deletedIds = new Set(removedElements.map(deletedElement => deletedElement.id)); return currentValue.filter(element => !deletedIds.has(element.id)).sort(sortFunc); }); + if (removedElementsGuild !== guildRef.current) { + LOG.warn(`removed elements guild (${removedElementsGuild.id}) != current guild (${guildRef.current})`); + } events.emit('removed', removedElements); }, [ sortFunc ]); - const onConflict = useCallback((changes: Changes) => { + const onConflict = useCallback((changes: Changes, changesGuild: CombinedGuild) => { setValue(currentValue => { if (currentValue === null) return null; const deletedIds = new Set(changes.deleted.map(deletedElement => deletedElement.id)); @@ -260,6 +288,10 @@ function useMultipleGuildSubscription< .filter(element => !deletedIds.has(element.id)) .sort(sortFunc); }); + if (changesGuild !== guildRef.current) { + LOG.warn(`conflict changes guild (${changesGuild.id}) != current guild (${guildRef.current})`); + } + setValueGuild(changesGuild); events.emit('conflict', changes); }, [ sortFunc ]); @@ -267,20 +299,24 @@ function useMultipleGuildSubscription< // otherwise, I may have done this wrong. Forcing it to work with these calls const boundNewFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onNew(newEventArgsMap(...args)); - }, [ onNew, newEventArgsMap ]) as (Connectable & Conflictable)[NE]; + if (guildRef.current !== guild) return; // prevent changes from a different guild + onNew(newEventArgsMap(...args), guild); + }, [ guild, onNew, newEventArgsMap ]) as (Connectable & Conflictable)[NE]; const boundUpdateFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onUpdated(updatedEventArgsMap(...args)); - }, [ onUpdated, updatedEventArgsMap ]) as (Connectable & Conflictable)[UE]; + if (guildRef.current !== guild) return; + onUpdated(updatedEventArgsMap(...args), guild); + }, [ guild, onUpdated, updatedEventArgsMap ]) as (Connectable & Conflictable)[UE]; const boundRemovedFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onRemoved(removedEventArgsMap(...args)); - }, [ onRemoved, removedEventArgsMap ]) as (Connectable & Conflictable)[RE]; + if (guildRef.current !== guild) return; + onRemoved(removedEventArgsMap(...args), guild); + }, [ guild, onRemoved, removedEventArgsMap ]) as (Connectable & Conflictable)[RE]; const boundConflictFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onConflict(conflictEventArgsMap(...args)); - }, [ onConflict, conflictEventArgsMap ]) as (Connectable & Conflictable)[CE]; + if (guildRef.current !== guild) return; + onConflict(conflictEventArgsMap(...args), guild); + }, [ guild, onConflict, conflictEventArgsMap ]) as (Connectable & Conflictable)[CE]; const bindEventsFunc = useCallback(() => { guild.on(newEventName, boundNewFunc); @@ -303,7 +339,7 @@ function useMultipleGuildSubscription< unbindEventsFunc }, fetchFunc); - return [ fetchRetryCallable, value, fetchError, events ]; + return [ fetchRetryCallable, value, valueGuild, fetchError, events ]; } function useMultipleGuildSubscriptionScrolling< @@ -327,6 +363,7 @@ function useMultipleGuildSubscriptionScrolling< setScrollRatio: Dispatch>, fetchResult: { hasMoreAbove: boolean, hasMoreBelow: boolean } | null, value: T[] | null, + valueGuild: CombinedGuild | null, fetchError: unknown | null, fetchAboveError: unknown | null, fetchBelowError: unknown | null, @@ -341,8 +378,11 @@ function useMultipleGuildSubscriptionScrolling< } = eventMappingParams; const isMounted = useIsMountedRef(); + const guildRef = useRef(guild); + guildRef.current = guild; const [ value, setValue ] = useState(null); + const [ valueGuild, setValueGuild ] = useState(null); const [ fetchError, setFetchError ] = useState(null); const [ fetchAboveError, setFetchAboveError ] = useState(null); @@ -376,11 +416,13 @@ function useMultipleGuildSubscriptionScrolling< const fetchAboveCallable = useCallback(async (): Promise<{ hasMoreAbove: boolean, removedFromBottom: boolean }> => { if (!isMounted.current) return { hasMoreAbove: false, removedFromBottom: false }; + if (guildRef.current !== guild) return { hasMoreAbove: false, removedFromBottom: false }; if (!value || value.length === 0) return { hasMoreAbove: false, removedFromBottom: false }; try { const reference = value[0] as T; const aboveElements = await fetchAboveFunc(reference); if (!isMounted.current) return { hasMoreAbove: false, removedFromBottom: false }; + if (guildRef.current !== guild) return { hasMoreAbove: false, removedFromBottom: false }; setFetchAboveError(null); if (aboveElements) { const hasMoreAbove = aboveElements.length >= maxFetchElements; @@ -400,18 +442,21 @@ function useMultipleGuildSubscriptionScrolling< } catch (e: unknown) { LOG.error('error fetching above for subscription', e); if (!isMounted.current) return { hasMoreAbove: false, removedFromBottom: false }; + if (guildRef.current !== guild) return { hasMoreAbove: false, removedFromBottom: false }; setFetchAboveError(e); return { hasMoreAbove: true, removedFromBottom: false }; } - }, [ value, fetchAboveFunc, maxFetchElements ]); + }, [ guild, value, fetchAboveFunc, maxFetchElements ]); const fetchBelowCallable = useCallback(async (): Promise<{ hasMoreBelow: boolean, removedFromTop: boolean }> => { if (!isMounted.current) return { hasMoreBelow: false, removedFromTop: false }; + if (guildRef.current !== guild) return { hasMoreBelow: false, removedFromTop: false }; if (!value || value.length === 0) return { hasMoreBelow: false, removedFromTop: false }; try { const reference = value[value.length - 1] as T; const belowElements = await fetchBelowFunc(reference); if (!isMounted.current) return { hasMoreBelow: false, removedFromTop: false }; + if (guildRef.current !== guild) return { hasMoreBelow: false, removedFromTop: false }; setFetchBelowError(null); if (belowElements) { const hasMoreBelow = belowElements.length >= maxFetchElements; @@ -431,12 +476,13 @@ function useMultipleGuildSubscriptionScrolling< } catch (e: unknown) { LOG.error('error fetching below for subscription', e); if (!isMounted.current) return { hasMoreBelow: false, removedFromTop: false }; + if (guildRef.current !== guild) return { hasMoreBelow: false, removedFromTop: false }; setFetchBelowError(e); return { hasMoreBelow: true, removedFromTop: false }; } }, [ value, fetchBelowFunc, maxFetchElements ]); - const onFetch = useCallback((fetchValue: T[] | null) => { + const onFetch = useCallback((fetchValue: T[] | null, fetchValueGuild: CombinedGuild) => { let hasMoreAbove = false; if (fetchValue) { if (fetchValue.length >= maxFetchElements) hasMoreAbove = true; @@ -444,6 +490,7 @@ function useMultipleGuildSubscriptionScrolling< } setFetchResult({ hasMoreAbove, hasMoreBelow: false }); setValue(fetchValue); + setValueGuild(fetchValueGuild); setFetchError(null); events.emit('fetch'); }, [ sortFunc, maxFetchElements, maxElements ]); @@ -451,10 +498,11 @@ function useMultipleGuildSubscriptionScrolling< const onFetchError = useCallback((e: unknown) => { setFetchError(e); setValue(null); + setValueGuild(null); events.emit('fetch-error'); }, []); - const onNew = useCallback((newElements: T[]) => { + const onNew = useCallback((newElements: T[], newElementsGuild: CombinedGuild) => { setValue(currentValue => { if (currentValue === null) return null; let newValue = currentValue.concat(newElements).sort(sortFunc); @@ -462,26 +510,35 @@ function useMultipleGuildSubscriptionScrolling< newValue = removeByCounts(newValue, getRemoveCounts(newValue.length - maxElements)); } return newValue; - }) + }); + if (newElementsGuild !== guildRef.current) { + LOG.warn(`new elements guild (${newElementsGuild.id}) != current guild (${guildRef.current})`); + } events.emit('new', newElements); }, [ sortFunc, getRemoveCounts ]); - const onUpdated = useCallback((updatedElements: T[]) => { + const onUpdated = useCallback((updatedElements: T[], updatedElementsGuild: CombinedGuild) => { setValue(currentValue => { if (currentValue === null) return null; return currentValue.map(element => updatedElements.find(updatedElement => updatedElement.id === element.id) ?? element).sort(sortFunc); }); + if (updatedElementsGuild !== guildRef.current) { + LOG.warn(`updated elements guild (${updatedElementsGuild.id}) != current guild (${guildRef.current})`); + } events.emit('updated', updatedElements); }, [ sortFunc ]); - const onRemoved = useCallback((removedElements: T[]) => { + const onRemoved = useCallback((removedElements: T[], removedElementsGuild: CombinedGuild) => { setValue(currentValue => { if (currentValue === null) return null; const deletedIds = new Set(removedElements.map(deletedElement => deletedElement.id)); return currentValue.filter(element => !deletedIds.has(element.id)).sort(sortFunc); }); + if (removedElementsGuild !== guildRef.current) { + LOG.warn(`updated elements guild (${removedElementsGuild.id}) != current guild (${guildRef.current})`); + } events.emit('removed', removedElements); }, [ sortFunc ]); - const onConflict = useCallback((changes: Changes) => { + const onConflict = useCallback((changes: Changes, changesGuild: CombinedGuild) => { setValue(currentValue => { if (currentValue === null) return null; const deletedIds = new Set(changes.deleted.map(deletedElement => deletedElement.id)); @@ -495,6 +552,9 @@ function useMultipleGuildSubscriptionScrolling< } return newValue; }); + if (changesGuild !== guildRef.current) { + LOG.warn(`conflict changes guild (${changesGuild.id}) != current guild (${guildRef.current})`); + } events.emit('conflict', changes); }, [ sortFunc, getRemoveCounts ]); @@ -502,19 +562,23 @@ function useMultipleGuildSubscriptionScrolling< // otherwise, I may have done this wrong. Forcing it to work with these calls const boundNewFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onNew(newEventArgsMap(...args)); - }, [ onNew, newEventArgsMap ]) as (Connectable & Conflictable)[NE]; + if (guildRef.current !== guild) return; // Cancel calls when the guild changes + onNew(newEventArgsMap(...args), guild); + }, [ guild, onNew, newEventArgsMap ]) as (Connectable & Conflictable)[NE]; const boundUpdateFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onUpdated(updatedEventArgsMap(...args)); + if (guildRef.current !== guild) return; // Cancel calls when the guild changes + onUpdated(updatedEventArgsMap(...args), guild); }, [ onUpdated, updatedEventArgsMap ]) as (Connectable & Conflictable)[UE]; const boundRemovedFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onRemoved(removedEventArgsMap(...args)); + if (guildRef.current !== guild) return; // Cancel calls when the guild changes + onRemoved(removedEventArgsMap(...args), guild); }, [ onRemoved, removedEventArgsMap ]) as (Connectable & Conflictable)[RE]; const boundConflictFunc = useCallback((...args: Arguments): void => { if (!isMounted.current) return; - onConflict(conflictEventArgsMap(...args)); + if (guildRef.current !== guild) return; // Cancel calls when the guild changes + onConflict(conflictEventArgsMap(...args), guild); }, [ onConflict, conflictEventArgsMap ]) as (Connectable & Conflictable)[CE]; const bindEventsFunc = useCallback(() => { @@ -545,6 +609,7 @@ function useMultipleGuildSubscriptionScrolling< setScrollRatio, fetchResult, value, + valueGuild, fetchError, fetchAboveError, fetchBelowError, @@ -579,8 +644,8 @@ export function useResourceSubscription(guild: CombinedGuild, resourceId: string }, fetchResourceFunc); } -export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null): [ imgSrc: string, resource: Resource | null, fetchError: unknown | null ] { - const [ resource, fetchError ] = useResourceSubscription(guild, resourceId); +export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resourceId: string | null): [ imgSrc: string, resource: Resource | null, resourceGuild: CombinedGuild | null, fetchError: unknown | null ] { + const [ resource, resourceGuild, fetchError ] = useResourceSubscription(guild, resourceId); const [ imgSrc ] = useOneTimeAsyncAction( async () => { @@ -592,7 +657,7 @@ export function useSoftImageSrcResourceSubscription(guild: CombinedGuild, resour [ resource, fetchError ] ); - return [ imgSrc, resource, fetchError ]; + return [ imgSrc, resource, resourceGuild, fetchError ]; } export function useChannelsSubscription(guild: CombinedGuild) { @@ -629,8 +694,8 @@ export function useMembersSubscription(guild: CombinedGuild) { }, fetchMembersFunc); } -export function useSelfMemberSubscription(guild: CombinedGuild): [ selfMember: Member | null ] { - const [ fetchRetryCallable, members, fetchError ] = useMembersSubscription(guild); +export function useSelfMemberSubscription(guild: CombinedGuild): [ selfMember: Member | null, selfMemberGuild: CombinedGuild | null ] { + const [ fetchRetryCallable, members, membersGuild, fetchError ] = useMembersSubscription(guild); // TODO: Show an error if we can't fetch and allow retry @@ -646,7 +711,7 @@ export function useSelfMemberSubscription(guild: CombinedGuild): [ selfMember: M return null; }, [ guild.memberId, members ]); - return [ selfMember ]; + return [ selfMember, membersGuild ]; } export function useTokensSubscription(guild: CombinedGuild) { diff --git a/src/client/webapp/elements/sections/guild.tsx b/src/client/webapp/elements/sections/guild.tsx index 8cf45eb..f150af5 100644 --- a/src/client/webapp/elements/sections/guild.tsx +++ b/src/client/webapp/elements/sections/guild.tsx @@ -23,10 +23,10 @@ const GuildElement: FC = (props: GuildElementProps) => { // TODO: React set hasMessagesAbove and hasMessagesBelow when re-verified? // TODO: React jump messages to bottom when the current user sent a message - const [ selfMember ] = useSelfMemberSubscription(guild); - const [ guildMeta, guildMetaFetchError ] = useGuildMetadataSubscription(guild); - const [ membersRetry, members, membersFetchError ] = useMembersSubscription(guild); - const [ channelsRetry, channels, channelsFetchError ] = useChannelsSubscription(guild); + const [ selfMember, selfMemberGuild ] = useSelfMemberSubscription(guild); + const [ guildMeta, guildMetaGuild, guildMetaFetchError ] = useGuildMetadataSubscription(guild); + const [ membersRetry, members, membersGuild, membersFetchError ] = useMembersSubscription(guild); + const [ channelsRetry, channels, channelsGuild, channelsFetchError ] = useChannelsSubscription(guild); const [ activeChannel, setActiveChannel ] = useState(null);