open-slack-channel-from-task.ts2.7 KBView on GitHub 'use client';
/**
* Opening a Slack task's channel — the one definition, shared by every surface that opens a task.
*
* A Slack task's output is a message in a channel, so it opens THE CHANNEL: the same
* `ChannelThreadView` the unibox opens, reached through the same display-artifact mechanism an
* email task reaches its thread with. Three things have to happen together, which is why this is
* one function rather than a closure copied into each caller:
*
* 1. the drafted message is handed to the channel's composer,
* 2. the channel's identity is left where the view can find it (`slackChannelHint`), and
* 3. the artifact is set, which is what actually opens it.
*
* ── Which composer ──
*
* `seedChannelDraft`, NOT `openSlackDraftInChannel`. There are two Slack composers and they are
* fed by different mechanisms: the per-deal timeline composer takes a ProseMirror doc through
* `seedComposerDoc`, and the unibox's `ChannelThreadView` takes raw mrkdwn through
* `pending-channel-draft`. This path opens the unibox one, so seeding the deal's composer put
* the draft somewhere the user was never sent — and left the channel that DID open with an empty
* box. See `documents/table/open-output-in-channel.ts`, which addresses a channel the same way.
*
* ── A sent message is not re-seeded ──
*
* `sendSlackDraftFromTask` posts the message and completes the task, but leaves the text on
* `taskActionData`. Re-opening a done Slack task would therefore drop an already-posted message
* back into the composer, one Enter from a duplicate. Completion is the sent signal (there is no
* `sentAt` on the output), so a done task opens its channel with the box empty.
*/
import { seedChannelDraft } from '@/modules/inbox/store/pending-channel-draft';
import { useCedarStore } from '@/modules/store';
export interface SlackChannelTarget {
conversationId: string | null;
channelId: string;
channelName?: string;
/** The channel cannot be READ without it — the messages query is keyed by workspace + channel. */
workspaceId?: string;
message?: string;
/** The task is finished, so its message is already on Slack — see above. */
alreadySent?: boolean;
}
export function openSlackChannelFromTask({
conversationId,
channelId,
channelName,
workspaceId,
message,
alreadySent,
}: SlackChannelTarget): void {
const store = useCedarStore.getState();
if (message && !alreadySent) seedChannelDraft(channelId, message);
// How to render a channel the feed has not loaded. The real feed row still wins when it lands.
store.setSlackChannelHint({
channelId,
workspaceId: workspaceId ?? '',
channelName: channelName ?? null,
conversationId,
});
store.setSelectedArtifact({ kind: 'slack_thread', id: channelId });
}