more specific about single vs multiple subscriptions
This commit is contained in:
parent
2ef1af3eff
commit
2a79af8a0e
@ -81,9 +81,7 @@ const AddGuildOverlay: FC<AddGuildOverlayProps> = (props: AddGuildOverlayProps)
|
||||
const [ avatarInputValid, setAvatarInputValid ] = useState<boolean>(false);
|
||||
|
||||
const [ exampleAvatarBuff, exampleAvatarBuffError ] = ReactHelper.useAsyncActionSubscription(
|
||||
async () => {
|
||||
return await fs.readFile(exampleAvatarPath);
|
||||
},
|
||||
async () => await fs.readFile(exampleAvatarPath),
|
||||
null,
|
||||
[ exampleAvatarPath ]
|
||||
);
|
||||
|
@ -22,9 +22,9 @@ export type SubscriptionEvents = {
|
||||
interface EffectParams<T> {
|
||||
guild: CombinedGuild;
|
||||
onFetch: (value: T | null) => void;
|
||||
onUpdate: (value: T) => void;
|
||||
onConflict: (value: T) => void;
|
||||
onFetchError: (e: unknown) => void;
|
||||
bindEventsFunc: () => void;
|
||||
unbindEventsFunc: () => void;
|
||||
}
|
||||
|
||||
type Arguments<T> = T extends (...args: infer A) => unknown ? A : never;
|
||||
@ -34,67 +34,57 @@ interface EventMappingParams<T, UE extends keyof Connectable, CE extends keyof C
|
||||
updateEventArgsMap: (...args: Arguments<Connectable[UE]>) => T; // should be same as the params list from Connectable
|
||||
conflictEventName: CE;
|
||||
conflictEventArgsMap: (...args: Arguments<Conflictable[CE]>) => T; // should be the same as the params list from Conflictable
|
||||
fetchDeps: DependencyList;
|
||||
}
|
||||
|
||||
export default class GuildSubscriptions {
|
||||
private static useGuildSubscriptionEffect<T, UE extends keyof Connectable, CE extends keyof Conflictable>(
|
||||
subscriptionParams: EffectParams<T>, eventMappingParams: EventMappingParams<T, UE, CE>, fetchFunc: (() => Promise<T>) | (() => Promise<T | null>)
|
||||
private static useGuildSubscriptionEffect<T>(
|
||||
isMountedRef: React.MutableRefObject<boolean>,
|
||||
subscriptionParams: EffectParams<T>,
|
||||
fetchFunc: (() => Promise<T>) | (() => Promise<T | null>),
|
||||
fetchDeps: DependencyList
|
||||
) {
|
||||
const { guild, onFetch, onUpdate, onConflict, onFetchError } = subscriptionParams;
|
||||
const { updateEventName, updateEventArgsMap, conflictEventName, conflictEventArgsMap, fetchDeps } = eventMappingParams;
|
||||
|
||||
const isMounted = useRef(false);
|
||||
const { guild, onFetch, onFetchError, bindEventsFunc, unbindEventsFunc } = subscriptionParams;
|
||||
|
||||
const fetchManagerFunc = useCallback(async () => {
|
||||
if (!isMounted.current) return;
|
||||
if (!isMountedRef.current) return;
|
||||
try {
|
||||
const value = await fetchFunc();
|
||||
if (!isMounted.current) return;
|
||||
if (!isMountedRef.current) return;
|
||||
onFetch(value);
|
||||
} catch (e: unknown) {
|
||||
LOG.error('error fetching for subscription', e);
|
||||
if (!isMounted.current) return;
|
||||
if (!isMountedRef.current) return;
|
||||
onFetchError(e);
|
||||
}
|
||||
}, [ ...fetchDeps, fetchFunc ]);
|
||||
|
||||
const boundUpdateFunc = useCallback((...args: Arguments<Connectable[UE]>): void => {
|
||||
if (!isMounted.current) return;
|
||||
const value = updateEventArgsMap(...args);
|
||||
onUpdate(value);
|
||||
}, []) as (Connectable & Conflictable)[UE]; // I think the typed EventEmitter class isn't ready for this level of type safety
|
||||
const boundConflictFunc = useCallback((...args: Arguments<Conflictable[CE]>): void => {
|
||||
if (!isMounted.current) return;
|
||||
const value = conflictEventArgsMap(...args); // otherwise, I may have done this wrong. Using never to force it to work
|
||||
onConflict(value);
|
||||
}, []) as (Connectable & Conflictable)[CE];
|
||||
|
||||
useEffect(() => {
|
||||
isMounted.current = true;
|
||||
isMountedRef.current = true;
|
||||
|
||||
// Bind guild events to make sure we have the most up to date information
|
||||
guild.on('connect', fetchManagerFunc);
|
||||
guild.on(updateEventName, boundUpdateFunc);
|
||||
guild.on(conflictEventName, boundConflictFunc);
|
||||
bindEventsFunc();
|
||||
|
||||
// Fetch the data once
|
||||
fetchManagerFunc();
|
||||
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
isMountedRef.current = false;
|
||||
|
||||
// Unbind the events so that we don't have any memory leaks
|
||||
guild.off('connect', fetchManagerFunc);
|
||||
guild.off(updateEventName, boundUpdateFunc);
|
||||
guild.off(conflictEventName, boundConflictFunc);
|
||||
unbindEventsFunc();
|
||||
}
|
||||
}, [ fetchManagerFunc ]);
|
||||
}
|
||||
|
||||
private static useGuildSubscription<T, UE extends keyof Connectable, CE extends keyof Conflictable>(
|
||||
guild: CombinedGuild, eventMappingParams: EventMappingParams<T, UE, CE>, fetchFunc: (() => Promise<T>) | (() => Promise<T | null>)
|
||||
private static useSingleGuildSubscription<T, UE extends keyof Connectable, CE extends keyof Conflictable>(
|
||||
guild: CombinedGuild, eventMappingParams: EventMappingParams<T, UE, CE>, fetchFunc: (() => Promise<T>) | (() => Promise<T | null>), fetchDeps: DependencyList
|
||||
): [value: T | null, fetchError: unknown | null, events: EventEmitter<SubscriptionEvents>] {
|
||||
const { updateEventName, updateEventArgsMap, conflictEventName, conflictEventArgsMap } = eventMappingParams;
|
||||
|
||||
const isMountedRef = useRef<boolean>(false);
|
||||
|
||||
const [ fetchError, setFetchError ] = useState<unknown | null>(null);
|
||||
const [ value, setValue ] = useState<T | null>(null);
|
||||
|
||||
@ -122,37 +112,57 @@ export default class GuildSubscriptions {
|
||||
events.emit('conflict');
|
||||
}, []);
|
||||
|
||||
GuildSubscriptions.useGuildSubscriptionEffect({
|
||||
// I think the typed EventEmitter class isn't ready for this level of insane type safety
|
||||
// otherwise, I may have done this wrong. Forcing it to work with these calls
|
||||
const boundUpdateFunc = useCallback((...args: Arguments<Connectable[UE]>): void => {
|
||||
if (!isMountedRef.current) return;
|
||||
const value = updateEventArgsMap(...args);
|
||||
onUpdate(value);
|
||||
}, []) as (Connectable & Conflictable)[UE];
|
||||
const boundConflictFunc = useCallback((...args: Arguments<Conflictable[CE]>): void => {
|
||||
if (!isMountedRef.current) return;
|
||||
const value = conflictEventArgsMap(...args);
|
||||
onConflict(value);
|
||||
}, []) as (Connectable & Conflictable)[CE];
|
||||
|
||||
const bindEventsFunc = useCallback(() => {
|
||||
guild.on(updateEventName, boundUpdateFunc);
|
||||
guild.on(conflictEventName, boundConflictFunc);
|
||||
}, []);
|
||||
const unbindEventsFunc = useCallback(() => {
|
||||
guild.off(updateEventName, boundUpdateFunc);
|
||||
guild.off(conflictEventName, boundConflictFunc);
|
||||
}, []);
|
||||
|
||||
GuildSubscriptions.useGuildSubscriptionEffect(isMountedRef, {
|
||||
guild,
|
||||
onFetch,
|
||||
onUpdate,
|
||||
onConflict,
|
||||
onFetchError
|
||||
}, eventMappingParams, fetchFunc);
|
||||
onFetchError,
|
||||
bindEventsFunc,
|
||||
unbindEventsFunc
|
||||
}, fetchFunc, fetchDeps);
|
||||
|
||||
return [ value, fetchError, events ];
|
||||
}
|
||||
|
||||
static useGuildMetadataSubscription(guild: CombinedGuild) {
|
||||
return GuildSubscriptions.useGuildSubscription<GuildMetadata, 'update-metadata', 'conflict-metadata'>(guild, {
|
||||
return GuildSubscriptions.useSingleGuildSubscription<GuildMetadata, 'update-metadata', 'conflict-metadata'>(guild, {
|
||||
updateEventName: 'update-metadata',
|
||||
updateEventArgsMap: (guildMeta: GuildMetadata) => guildMeta,
|
||||
conflictEventName: 'conflict-metadata',
|
||||
conflictEventArgsMap: (changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => newGuildMeta,
|
||||
fetchDeps: [ guild ]
|
||||
}, async () => await guild.fetchMetadata());
|
||||
conflictEventArgsMap: (changesType: AutoVerifierChangesType, oldGuildMeta: GuildMetadata, newGuildMeta: GuildMetadata) => newGuildMeta
|
||||
}, async () => await guild.fetchMetadata(), [ guild ]);
|
||||
}
|
||||
|
||||
static useResourceSubscription(guild: CombinedGuild, resourceId: string | null) {
|
||||
return GuildSubscriptions.useGuildSubscription<Resource, 'update-resource', 'conflict-resource'>(guild, {
|
||||
return GuildSubscriptions.useSingleGuildSubscription<Resource, 'update-resource', 'conflict-resource'>(guild, {
|
||||
updateEventName: 'update-resource',
|
||||
updateEventArgsMap: (resource: Resource) => resource,
|
||||
conflictEventName: 'conflict-resource',
|
||||
conflictEventArgsMap: (query: IDQuery, changesType: AutoVerifierChangesType, oldResource: Resource, newResource: Resource) => newResource,
|
||||
fetchDeps: [ guild, resourceId ]
|
||||
conflictEventArgsMap: (query: IDQuery, changesType: AutoVerifierChangesType, oldResource: Resource, newResource: Resource) => newResource
|
||||
}, async () => {
|
||||
if (resourceId === null) return null;
|
||||
return await guild.fetchResource(resourceId);
|
||||
});
|
||||
}, [ guild, resourceId ]);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user