UNPKG

repond-events

Version:
387 lines (329 loc) 13.8 kB
import { forEach } from "chootils/dist/loops"; import { ItemType, StatePath, getItemIds, onNextTick, whenSettingStates } from "repond"; import { UnsetEmojiKeysType } from "./declarations"; import { _addEvent, _addEvents, _makeLiveIdFromEventBlock, _setStatesToRunEventsInMode, eventBlockBaseToEventBlock, getChainIdFromLiveEventId, getLiveEventsIdsUpToLiveEventId, getLiveIdsForGroup, makeNewChainId, } from "./internal"; import { repondEventsMeta } from "./meta"; import { ChainId, EmojiKeys, EventBlock, EventBlockOptions, EventGroupName, EventName, EventParams, EventTypeDefinition, RunMode, RunModeExtraOptions, TypeOrUndefinedIfAllOptional, ValueBlock, ValueBlockOptions, ValueEmojiKeys, ValueGroupName, ValueName, ValueParams, ValueTypeDefinition, } from "./types"; // --------------------------------------------- // Utils type OptionalIfUndefinableSpreadType<T extends any> = T extends undefined ? [T?, EventBlockOptions?] : [T, EventBlockOptions?]; type ExcludedOriginalKeys = { [K in keyof EmojiKeys]: EmojiKeys[K]; }[keyof EmojiKeys]; type AcceptableKeys = Exclude<EventGroupName, ExcludedOriginalKeys> | keyof EmojiKeys; type ResolveGroupType<T_Key extends AcceptableKeys> = EmojiKeys extends UnsetEmojiKeysType ? T_Key : T_Key extends keyof EmojiKeys ? EmojiKeys[T_Key] : T_Key; // Returns an event block tuple, useful to get a typed event with auto-completion // meant to use with runEvents like runEvents([run("group", "name", params), run("group", "name", params)]) export function todo< T_Key extends AcceptableKeys, // T_Group extends T_Key extends keyof EmojiKeys ? EmojiKeys[T_Key] : T_Key, T_Group extends ResolveGroupType<T_Key>, T_Name extends EventName<T_Group>, T_GenericParamA >( groupOrEmoji: T_Key, name: T_Name, ...args: OptionalIfUndefinableSpreadType<TypeOrUndefinedIfAllOptional<EventParams<T_Group, T_Name, T_GenericParamA>>> ) { const group = groupOrEmoji in repondEventsMeta.emojiKeys ? repondEventsMeta.emojiKeys[groupOrEmoji as keyof EmojiKeys] : groupOrEmoji; // ...params: OptionalIfUndefinableSpreadType< // TypeOrUndefinedIfAllOptional<EventParams<T_Group, T_Name, T_GenericParamA>> // >, // options?: EventBlockOptions // Destructure args to get params and options let params = args[0]; // This would be your number or undefined let options = args[1]; // This would be your EventBlockOptions or undefined // const eventTuple: EventBlockTuple = [group, name, params ?? {}, options] as EventBlockTuple; const eventBlock: EventBlock = { group, name, params: params ?? {}, options } as EventBlock; return eventBlock; } // --------------------------------------------- // Events and chains // NOTE most of the helper functions // - Run inside set state - to read the latest state if setStates were run before this // - Run inside onNextTick - to run after the current setStates and effects, to run at the start of the next frame export function runEvent< T_Key extends AcceptableKeys, // T_Group extends T_Key extends keyof EmojiKeys ? EmojiKeys[T_Key] : T_Key, T_Group extends ResolveGroupType<T_Key>, T_Name extends EventName<T_Group>, T_GenericParamA >( groupOrEmoji: T_Key, name: T_Name, ...args: OptionalIfUndefinableSpreadType<TypeOrUndefinedIfAllOptional<EventParams<T_Group, T_Name, T_GenericParamA>>> ) { const group = groupOrEmoji in repondEventsMeta.emojiKeys ? repondEventsMeta.emojiKeys[groupOrEmoji as keyof EmojiKeys] : groupOrEmoji; // Destructure args to get params and options let params = args[0]; // This would be your number or undefined let options = args[1]; // This would be your EventBlockOptions or undefined const eventBlock = eventBlockBaseToEventBlock({ group, name, params: params ?? {} }, options); // generate the same chainId that would be generated if the event was added, but it's needed to get the liveId // NOTE in _addEvents, a liveId given means its the parent liveId for subEvents, meaning the chainId will be the same as the parent liveId, // but since this function only adds one event, the chainId will be different from the liveId const chainId = options?.chainId ?? repondEventsMeta.defaultChainId ?? makeNewChainId(); eventBlock.options.chainId = chainId; const liveId = options?.liveId ?? _makeLiveIdFromEventBlock(eventBlock); const realLiveId = _addEvent({ group, name, params: params ?? {} }, { ...options, liveId, chainId }); return realLiveId; } export function runPriorityEvent<T_Group extends EventGroupName, T_Name extends EventName<T_Group>, T_GenericParamA>( group: T_Group, name: T_Name, params: EventParams<T_Group, T_Name, T_GenericParamA>, options: EventBlockOptions ) { // NOTE _addEvent runs _addEvents which has onNextTick inside const chainId = _addEvent({ group, name, params: params ?? {} }, { hasPriority: true, ...options }); return chainId; } export function runEvents<T_Events extends EventBlock[]>(eventsToRun: T_Events, options?: EventBlockOptions) { // NOTE _addEvents has onNextTick inside // NOTE adding a liveId will make the chain a subChain of the liveEvent // const chainId = _addEvents(eventTuplesToEventBlocks(eventsToRun), { ...options }); const chainId = _addEvents(eventsToRun, { ...options }); return chainId; } export function addSubEvents<T_Events extends EventBlock[]>( liveId: string, eventsToRun: T_Events, options?: EventBlockOptions ) { runEvents(eventsToRun, { liveId, ...options }); } export function runPriorityEvents<T_Events extends EventBlock[]>(eventsToRun: T_Events, options?: EventBlockOptions) { // NOTE runEvents runs _addEvents which has onNextTick inside return runEvents(eventsToRun, { hasPriority: true, ...options }); } export function eventDo(runMode: RunMode, liveId: string, runOptions?: RunModeExtraOptions) { onNextTick(() => whenSettingStates(() => _setStatesToRunEventsInMode({ runMode, targetLiveIds: [liveId], runOptions })) ); } export function chainDo(runMode: RunMode, chainId: ChainId, runOptions?: RunModeExtraOptions) { onNextTick(() => whenSettingStates(() => _setStatesToRunEventsInMode({ runMode, chainId, runOptions }))); } export function chainWithEventDo(runMode: RunMode, liveId: string, runOptions?: RunModeExtraOptions) { whenSettingStates(() => { const chainId = getChainIdFromLiveEventId(liveId); // `no chain found for ${liveId}` if (!chainId) return; _setStatesToRunEventsInMode({ runMode, chainId, runOptions }); }); } export function allGroupEventsDo(groupName: string, runMode: RunMode, runOptions?: RunModeExtraOptions) { onNextTick(() => { whenSettingStates(() => { const targetLiveIds = getLiveIdsForGroup(groupName); _setStatesToRunEventsInMode({ runMode, targetLiveIds, runOptions }); }); }); } export function doForAllBeforeEvent(runMode: RunMode, liveId: string, runOptions?: RunModeExtraOptions) { onNextTick(() => { whenSettingStates(() => { const targetLiveIds = getLiveEventsIdsUpToLiveEventId(liveId); _setStatesToRunEventsInMode({ runMode, targetLiveIds, runOptions }); }); }); } export function skipToEvent(liveId: string, runOptions?: RunModeExtraOptions) { doForAllBeforeEvent("skip", liveId, runOptions); } export function cancelUpToEvent(liveId: string, runOptions?: RunModeExtraOptions) { doForAllBeforeEvent("cancel", liveId, runOptions); } export function allEventsDo(runMode: RunMode, runOptions?: RunModeExtraOptions) { whenSettingStates(() => { const targetLiveIds = getItemIds("liveEvents"); _setStatesToRunEventsInMode({ runMode, targetLiveIds, runOptions }); }); } // --------------------------------------------- export function cancelFastChain(chainId: ChainId) { // set isCancelled to true, // and while it can’t find another child in meta, // keep setting children to isCanceled const fastChainMeta = repondEventsMeta.fastChain.nowFastChainsInfoMap[chainId]; if (!fastChainMeta) return; fastChainMeta.isCanceled = true; let nextChildChainId = fastChainMeta.nowChildFastChainId; while (nextChildChainId) { const nextChildMeta = repondEventsMeta.fastChain.nowFastChainsInfoMap[nextChildChainId]; if (!nextChildMeta) break; nextChildMeta.isCanceled = true; nextChildChainId = nextChildMeta.nowChildFastChainId; } } // --------------------------------------------- // Make Events function makeEventType<T_Params extends Record<any, any>>(event: EventTypeDefinition<T_Params>) { return event; } export type MakeEventType = <T_Params extends Record<any, any>>( eventTypeDefition: EventTypeDefinition<T_Params> ) => EventTypeDefinition<T_Params>; export function makeEventTypes< K_EventName extends string, T_EventsMap extends Record<K_EventName, EventTypeDefinition<any>> >(eventsToAdd: (arg0: { event: MakeEventType }) => T_EventsMap) { return eventsToAdd({ event: makeEventType }); } export function initEventTypeGroups< T extends Record<string, ReturnType<typeof makeEventTypes>>, T_TimePathItemType extends ItemType >( groups: T, options?: { defaultElapsedTimePath?: StatePath<T_TimePathItemType>; defaultChainId?: string; // leave undefined to generate a random name for each chain emojiKeys?: Record<string, string>; } ): T { repondEventsMeta.defaultElapsedTimePath = options?.defaultElapsedTimePath ?? null; repondEventsMeta.defaultChainId = options?.defaultChainId ?? null; repondEventsMeta.emojiKeys = options?.emojiKeys ?? {}; const transformedGroups: Record<string, ReturnType<typeof makeEventTypes>> = {}; Object.entries(groups).forEach(([key, value]) => { // Remove "Events" from the end of the key, if present const newKey = key.replace(/Events$/, ""); transformedGroups[newKey] = value; }); const groupNames = Object.keys(transformedGroups); // loop through the groups and rename the effects forEach(groupNames, (groupName) => { const theGroup = transformedGroups[groupName]!; const eventTypeNames = Object.keys(theGroup); forEach(eventTypeNames, (eventTypeName) => { const theEventType = theGroup[eventTypeName]!; theEventType.id = `${groupName}_${eventTypeName}`; }); }); // Store the transformed groups repondEventsMeta.allEventTypeGroups = transformedGroups; return groups; } // --------------------------------------------- // Make Values function makeValueType<T_Params extends Record<any, any>>(value: ValueTypeDefinition<T_Params>) { return value; } export type MakeValueType = <T_Params extends Record<any, any>>( valueTypeDefition: ValueTypeDefinition<T_Params> ) => ValueTypeDefinition<T_Params>; export function makeValueTypes< K_ValueName extends string, T_ValuesMap extends Record<K_ValueName, ValueTypeDefinition<any>> >(valuesToAdd: (arg0: { value: MakeValueType }) => T_ValuesMap) { return valuesToAdd({ value: makeValueType }); } export function initValueTypeGroups<T extends Record<string, ReturnType<typeof makeValueTypes>>>( groups: T, options?: { // Can add some defaults for values here if wanted emojiKeys?: Record<string, string>; } ): T { repondEventsMeta.valueEmojiKeys = options?.emojiKeys ?? {}; const transformedGroups: Record<string, ReturnType<typeof makeValueTypes>> = {}; Object.entries(groups).forEach(([key, value]) => { // Remove "Values" from the end of the key, if present const newKey = key.replace(/Values$/, ""); transformedGroups[newKey] = value; }); const groupNames = Object.keys(transformedGroups); // loop through the groups and rename the effects forEach(groupNames, (groupName) => { const theGroup = transformedGroups[groupName]!; const valueTypeNames = Object.keys(theGroup); forEach(valueTypeNames, (valueTypeName) => { const theValueType = theGroup[valueTypeName]!; theValueType.id = `${groupName}_${valueTypeName}`; }); }); // Store the transformed groups repondEventsMeta.allValueTypeGroups = transformedGroups; return groups; } type OptionalIfUndefinableSpreadType_Value<T extends any> = T extends undefined ? [T?, ValueBlockOptions?] : [T, ValueBlockOptions?]; type ExcludedOriginalValueKeys = { [K in keyof EmojiKeys]: EmojiKeys[K]; }[keyof EmojiKeys]; type AcceptableValueKeys = Exclude<ValueGroupName, ExcludedOriginalValueKeys> | keyof ValueEmojiKeys; type ResolveValueType<T_Key extends AcceptableValueKeys> = ValueEmojiKeys extends UnsetEmojiKeysType ? T_Key : T_Key extends keyof ValueEmojiKeys ? ValueEmojiKeys[T_Key] : T_Key; // Returns an event block tuple, useful to get a typed event with auto-completion // meant to use with runEvents like runEvents([run("group", "name", params), run("group", "name", params)]) export function makeValue< T_Key extends AcceptableValueKeys, // T_Group extends T_Key extends keyof EmojiKeys ? EmojiKeys[T_Key] : T_Key, T_Group extends ResolveValueType<T_Key>, T_Name extends ValueName<T_Group>, T_GenericParamA >( groupOrEmoji: T_Key, name: T_Name, ...args: OptionalIfUndefinableSpreadType_Value< TypeOrUndefinedIfAllOptional<ValueParams<T_Group, T_Name, T_GenericParamA>> > ) { const group = groupOrEmoji in repondEventsMeta.valueEmojiKeys ? repondEventsMeta.emojiKeys[groupOrEmoji as keyof EmojiKeys] : groupOrEmoji; // ...params: OptionalIfUndefinableSpreadType< // TypeOrUndefinedIfAllOptional<EventParams<T_Group, T_Name, T_GenericParamA>> // >, // options?: EventBlockOptions // Destructure args to get params and options let params = args[0]; let options = args[1]; // This would be your EventBlockOptions or undefined const valueBlock: ValueBlock = { group, name, params: params || {}, options: options ?? {}, type: "value" }; return valueBlock; }