Skip to content

feat(ScheduledMessages): manage scheduled messages from the list (REACT-1389) - #3371

Draft
NikolaGoranNaumovski wants to merge 12 commits into
feat/react-1388-scheduled-messages-listfrom
feat/react-1389-scheduled-messages-manage
Draft

NikolaGoranNaumovski wants to merge 12 commits into
feat/react-1388-scheduled-messages-listfrom
feat/react-1389-scheduled-messages-manage

Conversation

@NikolaGoranNaumovski

@NikolaGoranNaumovski NikolaGoranNaumovski commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

🎯 Goal

Let users manage their pending scheduled messages from the scheduled message list: edit, reschedule, send now, delete, and copy the text of a message whose channel was deleted. This follows the web designs for the row menu, the edit preview and the delete confirmation (Figma: Scheduled List and Edit, Reschedule and Delete sections).

Part of the scheduled messages feature (REACT-1389). Stacked on #3366 (list) and depends on GetStream/stream-chat-js#1915 (state layer) and GetStream/stream-chat-js#1956 (composer edit mode).

🛠 Implementation details

Row menu (ScheduledMessageActions)

  • Each list item gets an actions button that opens a ContextMenu. Which actions appear depends on the status:
    • scheduled: Edit, Reschedule, Send now, Delete
    • failed: Delete
    • failed because the channel was deleted (channel_deleted): Copy, Delete
    • sending: the button is disabled
  • Edit appears only when the host passes an onEdit handler. Both hosts do: the banner's list dialog and the Channel Detail section.
  • Action rules live in a pure util, getScheduledMessageActions.

Actions

  • Reschedule: opens RescheduleMessageDialog, pre-filled with the current time, and saves through client.scheduledMessages.updateScheduledMessage(id, { scheduled_at }).
  • Send now: calls sendScheduledMessageNow and confirms with a "Message sent" toast. The item leaves the list on scheduled_message.sent.
  • Delete: asks for confirmation (DeleteScheduledMessageAlert), then calls deleteScheduledMessage.
  • Copy: copies the message text to the clipboard.
  • Failures: reported as typed notifications. The new translateScheduledMessageActionFailed translates them by reason:
    • a 404 shows "Already sent", and the store has already dropped the item;
    • sending, conflict, not-editable and too-soon / too-far each get their own copy;
    • anything else falls back to a generic message for that action.
  • Conflicts: a 409 conflict reloads the store.

Shared date/time dialog

  • The form inside ScheduleMessageDialog is extracted into ScheduleTimeDialog. It takes the channel, preview, title, error text and submit function as props, so scheduling and rescheduling use the same dialog.
  • useScheduleTimeForm now takes { channel, initialScheduledAt? }.
  • The new useScheduleTimeSubmit runs any submit function, and useScheduleMessageSubmit wraps it.
  • ScheduleMessageDialog renders the same as before.

Edit mode in the composer

  • Edit calls composer.editScheduledMessage(scheduledMessage):
    • from the banner's list, on that banner's composer (channel or thread);
    • from Channel Detail, on the channel's composer, after closing the modal.
  • While editing:
    • the composer shows EditedScheduledMessagePreview, with cancel calling cancelEditScheduledMessage();
    • useSubmitHandler sends Enter and Send to updateScheduledMessage() instead of sendMessage / editMessage;
    • the send button shows the checkmark;
    • useCanScheduleMessage hides the schedule button.
  • New ComponentContext slot: EditedScheduledMessagePreview.
  • If the message is sent, deleted or fails elsewhere during the edit, MessageComposer ends the edit (feat: schedule and edit scheduled messages from MessageComposer stream-chat-js#1956). The new translateScheduledMessageCompositionEnded explains why: "This scheduled message was already sent / was deleted / failed to send".

Not in this PR

Translations
21 new strings in all 12 locales.

Commits

  1. refactor: share the date and time dialog
  2. feat: row menu with reschedule, send now, delete and copy
  3. feat: edit scheduled messages in the composer
  4. feat(i18n): translations
  5. feat: explain why a scheduled message edit ended
  6. feat(i18n): translations for it

Commits 1–3 each typecheck and pass the scheduled-message, composer, Channel Detail and i18n tests on their own. The full suite passes on the last commit (3460 tests).

Testing

  • Unit:
    • action rules per status;
    • each action (send now, delete confirm / cancel, reschedule pre-fill and submit, reschedule failure keeps the dialog open, copy);
    • the edit flow (preview, schedule button hidden, Enter / Send save via updateScheduledMessage with no sendMessage, cancel restores the earlier draft);
    • the translator per reason and per action;
    • axe on the open menu.
  • Manual in examples/vite against the scheduled messages test app:
    • reschedule moved the item to the new time;
    • Edit → Enter saved the text through PATCH, and nothing appeared in the channel;
    • Delete with confirmation emptied the list;
    • no console errors.

🎨 UI Changes

Screenshots: row menu (scheduled), row menu (deleted channel), reschedule dialog, delete confirmation, edit preview in the composer.

Extract the date and time form of ScheduleMessageDialog into
ScheduleTimeDialog, which takes the channel, preview, title, error copy and
submit function as props, so rescheduling can reuse it.
useScheduleTimeForm takes the channel and an optional initial time, and
useScheduleTimeSubmit runs any submit function; useScheduleMessageSubmit
now wraps it. ScheduleMessageDialog renders the same as before.

Refs: REACT-1389
Each list item gets an actions button that opens a row menu by status:
scheduled messages offer Edit, Reschedule, Send now and Delete; failed ones
offer Delete, plus Copy when the channel was deleted; the button is
disabled while a message is being sent. Edit appears only when the list is
given an onEdit handler.

- Reschedule opens ScheduleTimeDialog pre-filled with the current time and
  saves through the manager's updateScheduledMessage
- Delete asks for confirmation in DeleteScheduledMessageAlert
- Send now confirms with a toast
- failures are reported as typed notifications, and a conflict reloads the
  store

Refs: REACT-1389
Edit from the scheduled message list puts the channel or thread composer in
scheduled edit mode:
- the composer shows EditedScheduledMessagePreview with a cancel button
  (overridable through ComponentContext)
- Enter and Send save through updateScheduledMessage instead of sending
- the send button shows the checkmark and the schedule button is hidden
  while editing

The banner's list edits in the composer it belongs to, and the Channel
Detail section in the channel's composer, closing its modal first.

Refs: REACT-1389
Translate the row menu, the reschedule and delete dialogs, the edit preview
and the action notifications in all 12 locales. Action failures are
translated by reason: a 404 reads "Already sent", and sending, conflict,
not editable and time bounds get their own copy, with a fallback per
action.

Refs: REACT-1389
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@NikolaGoranNaumovski NikolaGoranNaumovski changed the title feat(ScheduledMessages): manage scheduled messages from the list feat(ScheduledMessages): manage scheduled messages from the list (REACT-1389) Nov 2, 2026
MessageComposer ends a scheduled message edit (or scheduling a failed
message again) when the message is sent, deleted or fails elsewhere, and
reports it as validation:scheduled-message:composition:ended with the
reason. translateScheduledMessageCompositionEnded turns it into "This
scheduled message was already sent / was deleted / failed to send".

Refs: REACT-1389
Translate why a scheduled message edit ended (already sent, deleted,
failed to send, or no longer editable) in all 12 locales.

Refs: REACT-1389
@NikolaGoranNaumovski
NikolaGoranNaumovski force-pushed the feat/react-1388-scheduled-messages-list branch from 831f72f to 9d9b6c1 Compare November 3, 2026 09:07
@NikolaGoranNaumovski
NikolaGoranNaumovski force-pushed the feat/react-1389-scheduled-messages-manage branch from bba632b to 05b03dc Compare November 3, 2026 09:07
…t-1389-scheduled-messages-manage

# Conflicts:
#	src/components/ScheduledMessages/ScheduleMessageDialog.tsx
#	src/components/ScheduledMessages/ScheduledMessageItem.tsx
#	src/components/ScheduledMessages/ScheduledMessageListDialog.tsx
#	src/i18n/de.json
#	src/i18n/en.json
#	src/i18n/es.json
#	src/i18n/fr.json
#	src/i18n/hi.json
#	src/i18n/it.json
#	src/i18n/ja.json
#	src/i18n/ko.json
#	src/i18n/nl.json
#	src/i18n/pt.json
#	src/i18n/ru.json
#	src/i18n/tr.json
…e design

Show the row actions like the channel list item actions: a floating
group at the top end of the row, revealed on hover, focus or while its
menu is open. A scheduled message gets an Edit button next to the menu,
so the menu keeps Reschedule, Send now and Delete. The copy action is
labelled "Copy text", and the separator only appears when more than one
action precedes Delete.
…sign

The delete confirmation asks "Delete scheduled message?", names the
channel and uses a solid destructive button. The reschedule dialog shows
"Scheduled for Fri, Oct 23 at 09:00" above the message and saves with
"Save". Sending now confirms with "Scheduled message sent", and an edit
of a message that was sent elsewhere reports "This message was already
sent".

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant