@mica/sdk (SDK contract v1)
    Preparing search index...

    Interface ReactionStore

    Reaction summaries keyed by target id, plus the two verbs that change them.

    A Readable, not a Writable: everything that writes is on this interface, so there is no path by which a component can set a count the server has not agreed to.

    interface ReactionStore {
        load: (targetIds: number[]) => Promise<void>;
        toggle: (targetId: number, emoji: string) => Promise<void>;
    }

    Hierarchy

    • Readable<Record<number, ReactionSummary>>
      • ReactionStore
    Index
    load: (targetIds: number[]) => Promise<void>

    Read these targets and merge the answer into the map. A no-op for an empty list.

    Every id asked about is present when this resolves, as NO_REACTIONS if the reply did not mention it — the transport may omit an empty summary, this may not. Ids nobody asked about are left alone, which is what makes a second page merge rather than replace.

    The invariant is load-bearing, not tidiness. Without it undefined means both "never asked" and "asked, and it has nothing", a target whose last reaction was removed keeps its chips forever, and toggle cannot roll back — merging a reply that omits the target leaves the entry optimism just painted exactly where it was.

    toggle: (targetId: number, emoji: string) => Promise<void>

    Add the caller's reaction, or take it back if it is already theirs. Optimistic, then reconciled: a failed write refetches the one target and rethrows, so the caller can toast. The optimistic entry does not outlive a refused write, whatever the refetch answers.