repond-events
Version:
events for repond
387 lines (329 loc) • 13.8 kB
text/typescript
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;
}