85 lines · 3.3 KB · TypeScript
RawDownload
import { useAtomValue } from "@effect/atom-react";import type { EnvironmentShellStatus } from "@t3tools/client-runtime/state/shell";import type { EnvironmentId, MessageId } from "@t3tools/contracts";import { Atom } from "effect/unstable/reactivity"; import { appAtomRegistry } from "./atom-registry";import { environmentShell } from "./shell";import { threadOutboxManager } from "./thread-outbox"; const threadOutboxShellStatusesAtom = Atom.make(  (get): ReadonlyMap<EnvironmentId, EnvironmentShellStatus> => {    const statuses = new Map<EnvironmentId, EnvironmentShellStatus>();    for (const queue of Object.values(get(threadOutboxManager.queuedMessagesByThreadKeyAtom))) {      const environmentId = queue[0]?.environmentId;      if (environmentId !== undefined && !statuses.has(environmentId)) {        statuses.set(environmentId, get(environmentShell.stateValueAtom(environmentId)).status);      }    }    return statuses;  },).pipe(Atom.withLabel("mobile:thread-outbox:shell-statuses")); /** * Queued pending tasks the outbox drain must not deliver right now: the one * open in the new-task editor, plus any whose latest edits could not be saved * back yet (delivering those would send stale content). Editing sessions hold * their message id here and release it once the queued payload is current. */export const editingQueuedMessageIdsAtom = Atom.make<Readonly<Record<MessageId, true>>>({}).pipe(  Atom.keepAlive,  Atom.withLabel("mobile:thread-outbox:editing-message-ids"),); export const dispatchingQueuedMessageIdAtom = Atom.make<MessageId | null>(null).pipe(  Atom.keepAlive,  Atom.withLabel("mobile:thread-outbox:dispatching-message-id"),); export function holdEditingQueuedMessage(messageId: MessageId): void {  const current = appAtomRegistry.get(editingQueuedMessageIdsAtom);  if (current[messageId]) {    return;  }  appAtomRegistry.set(editingQueuedMessageIdsAtom, { ...current, [messageId]: true });} export function releaseEditingQueuedMessage(messageId: MessageId): void {  const current = appAtomRegistry.get(editingQueuedMessageIdsAtom);  if (!current[messageId]) {    return;  }  const next = { ...current };  delete next[messageId];  appAtomRegistry.set(editingQueuedMessageIdsAtom, next);} export function useThreadOutboxMessages() {  return useAtomValue(threadOutboxManager.queuedMessagesByThreadKeyAtom);} /** * Thread keys (`environmentId:threadId`) of existing threads with a message * waiting in the outbox. Creations are excluded: they have no thread row yet * and surface as pending tasks instead. Derived once so list builders and * reorder planners agree on which settled threads are pulled back to active. */export const queuedThreadKeysAtom = Atom.make((get): ReadonlySet<string> => {  const keys = new Set<string>();  for (const [threadKey, queue] of Object.entries(    get(threadOutboxManager.queuedMessagesByThreadKeyAtom),  )) {    if (queue.some((message) => message.creation === undefined)) {      keys.add(threadKey);    }  }  return keys;}).pipe(Atom.withLabel("mobile:thread-outbox:queued-thread-keys")); export function useQueuedThreadKeys(): ReadonlySet<string> {  return useAtomValue(queuedThreadKeysAtom);} export function useThreadOutboxShellStatuses() {  return useAtomValue(threadOutboxShellStatusesAtom);}