import { ResourceUri } from "@atcute/lexicons"; interface StateObj { like?: ResourceUri | false; repost?: ResourceUri | false; bookmark?: boolean; } export type InteractionType = keyof StateObj; export class InteractionCache { #state = new Map(); #optimisticState = new Map(); get(uri: ResourceUri) { return this.#state.get(uri); } addOptimistic(type: InteractionType, uri: ResourceUri, interaction: ResourceUri | boolean) { this.#getAndSet(this.#optimisticState, uri, (val) => { val ??= {}; val[type] = interaction as any; return val; }); } override(type: InteractionType, uri: ResourceUri, interaction: ResourceUri | boolean) { this.#getAndSet(this.#state, uri, (val) => { val ??= {}; val[type] = interaction as any; return val; }); this.discard(type, uri); } commit(type: InteractionType, uri: ResourceUri) { if (this.#optimisticState.has(uri)) { this.override(type, uri, this.#optimisticState.get(uri)![type]!); } } discard(type: InteractionType, uri: ResourceUri) { const newVal = this.#getAndSet(this.#optimisticState, uri, (val) => { val ??= {}; val[type] = undefined; return val; }); if (!Object.values(newVal).reduce((a, b) => a || b)) { this.#optimisticState.delete(uri); } } #getAndSet(map: Map, key: T, fun: (val: U | undefined) => U): U { const newVal = fun(map.get(key)); map.set(key, newVal); return newVal; } }