Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,6 @@ import {
getCommunicationThreadIdFromTaskPayload,
} from '@roomote/types';

/** Fixed Slack reaction for closed (not merged) PRs on the originating message. */
export const SLACK_PR_CLOSED_REACTION_EMOJI = 'heavy_multiplication_x';

const LINEAR_MCP_URL = 'https://mcp.linear.app/mcp';

interface NotifyPullRequestTerminalStatusParams {
Expand Down Expand Up @@ -206,7 +203,8 @@ async function deliverSlackTerminalStatus({

const notifiedThreads = new Set<string>();
const notifier = new SlackNotifier(slackInstallation.botAccessToken);
const { ackEmoji, completionEmoji } = await resolveSlackReactionNames();
const { ackEmoji, completionEmoji, prClosedEmoji } =
await resolveSlackReactionNames();
const statusNotification = buildPullRequestStatusNotificationText({
prTitle,
prUrl,
Expand All @@ -216,7 +214,7 @@ async function deliverSlackTerminalStatus({
formatStatus: (value) => `*${value}*`,
});
const terminalReaction =
status === 'closed' ? SLACK_PR_CLOSED_REACTION_EMOJI : completionEmoji;
status === 'closed' ? prClosedEmoji : completionEmoji;

for (const target of slackTargets) {
if (notifiedThreads.has(target.slackThreadTs)) {
Expand Down
4 changes: 3 additions & 1 deletion apps/docs/vibes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ in team-facing messages.
| ------------------------- | --------------------------------------------------------------------------- | ------------------------ |
| **Acknowledgement emoji** | The reaction Roomote uses to show it saw a Slack message | `eyes` |
| **Completion emoji** | The reaction Roomote can use when supported work finishes | `white_check_mark` |
| **Closed PR emoji** | The reaction Roomote adds when a linked pull request is closed without merge | `x` |
| **Summon emoji** | The reaction teammates can add to an existing Slack message to start a task | `shipit` |
| **Style guidance** | Optional guidance for how Roomote should sound in messages | "Direct, concise, calm." |

Expand Down Expand Up @@ -44,7 +45,8 @@ channels.

The acknowledgement reaction is the quick "I saw this" signal. The completion
reaction is for supported completion moments where a Slack reaction is the
right confirmation.
right confirmation. The closed PR reaction is applied to the originating Slack
message when a linked pull request is closed without merging.

If teammates are missing Roomote's status updates, choose reactions that stand
out in your workspace. If channels feel too busy, keep the defaults or use
Expand Down
56 changes: 55 additions & 1 deletion apps/web/src/components/settings/VibesSettings.client.test.tsx

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

56 changes: 54 additions & 2 deletions apps/web/src/components/settings/VibesSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ const VIBES_IDEA_DOWNLOADS = [
type VibesFieldName =
| 'slackSummonEmoji'
| 'slackAckEmoji'
| 'slackCompletionEmoji';
| 'slackCompletionEmoji'
| 'slackPrClosedEmoji';

type VibesDraft = Record<VibesFieldName, string>;

Expand All @@ -77,11 +78,13 @@ function toDraft(settings: {
slackSummonEmoji: string | null;
slackAckEmoji: string;
slackCompletionEmoji: string;
slackPrClosedEmoji: string;
}): VibesDraft {
return {
slackSummonEmoji: settings.slackSummonEmoji ?? '',
slackAckEmoji: settings.slackAckEmoji,
slackCompletionEmoji: settings.slackCompletionEmoji,
slackPrClosedEmoji: settings.slackPrClosedEmoji,
};
}

Expand All @@ -93,13 +96,15 @@ const FIELD_TO_TOAST_TYPE: Record<VibesFieldName, string> = {
slackSummonEmoji: 'summon',
slackAckEmoji: 'acknowledgement',
slackCompletionEmoji: 'completion',
slackPrClosedEmoji: 'closed PR',
};

function hasDirtyField(draft: VibesDraft, saved: VibesDraft) {
return (
draft.slackSummonEmoji !== saved.slackSummonEmoji ||
draft.slackAckEmoji !== saved.slackAckEmoji ||
draft.slackCompletionEmoji !== saved.slackCompletionEmoji
draft.slackCompletionEmoji !== saved.slackCompletionEmoji ||
draft.slackPrClosedEmoji !== saved.slackPrClosedEmoji
);
}

Expand Down Expand Up @@ -140,11 +145,13 @@ export function VibesSettings() {
slackSummonEmoji: '',
slackAckEmoji: '',
slackCompletionEmoji: '',
slackPrClosedEmoji: '',
});
const [savedDraft, setSavedDraft] = useState<VibesDraft>({
slackSummonEmoji: '',
slackAckEmoji: '',
slackCompletionEmoji: '',
slackPrClosedEmoji: '',
});
const [fieldErrors, setFieldErrors] = useState<VibesFieldErrors>({});
const [hasLoadedInitialState, setHasLoadedInitialState] = useState(false);
Expand Down Expand Up @@ -386,6 +393,8 @@ export function VibesSettings() {
const ackDefault = settingsQuery.data?.defaults.slackAckEmoji ?? 'eyes';
const completionDefault =
settingsQuery.data?.defaults.slackCompletionEmoji ?? 'white_check_mark';
const prClosedDefault =
settingsQuery.data?.defaults.slackPrClosedEmoji ?? 'x';
const isStyleGuidanceDirty = styleGuidance !== savedStyleGuidance;
const showStyleGuidanceCounter =
styleGuidance.length >= ROOMOTE_STYLE_GUIDANCE_MAX_LENGTH * 0.9;
Expand Down Expand Up @@ -532,6 +541,49 @@ export function VibesSettings() {
</p>
) : null}
</div>

<div className="space-y-2">
<div className="flex items-center justify-between gap-3">
<div>
<Label htmlFor="slack-pr-closed-emoji">Closed PR</Label>
<p className="text-xs text-muted-foreground mt-1">
Used when a linked pull request is closed without merging.
Default ❌
</p>
</div>
<Button
variant="ghost"
size="icon"
type="button"
onClick={() =>
updateField({
field: 'slackPrClosedEmoji',
value: prClosedDefault,
saveImmediately: true,
})
}
aria-label="Reset closed PR emoji"
>
<RotateCcw />
</Button>
</div>
<Input
id="slack-pr-closed-emoji"
value={draft.slackPrClosedEmoji}
onChange={(event) =>
updateField({
field: 'slackPrClosedEmoji',
value: event.target.value,
})
}
onBlur={() => handleBlur('slackPrClosedEmoji')}
/>
{fieldErrors.slackPrClosedEmoji ? (
<p className="text-xs text-destructive">
{fieldErrors.slackPrClosedEmoji}
</p>
) : null}
</div>
</div>
</Section>

Expand Down
Loading
Loading