feat(message): remove the unfinished scheduled messages feature (#2233)

This commit is contained in:
Hampus authored and GitHub committed 2026-08-31 13:32:55 +02:00
1 parent eeea391b63
commit 3c8466d714
125 files changed
+17 -3730

No files matched your search

@@ -1,46 +0,0 @@
/* SPDX-License-Identifier: AGPL-3.0-or-later */
.text {
display: flex;
flex-direction: column;
gap: 0.125rem;
overflow: hidden;
}
.label {
display: flex;
align-items: center;
gap: 0.375rem;
font-size: 0.875rem;
color: var(--text-primary);
}
.timestamp {
font-size: 0.75rem;
color: var(--text-primary-muted);
}
.controls {
display: flex;
align-items: center;
}
.button {
cursor: pointer;
flex-shrink: 0;
border: none;
background-color: transparent;
padding: 0.5rem 0 0.5rem 1rem;
color: var(--text-primary-muted);
line-height: 0;
transition: color 200ms;
}
.button:hover {
color: var(--text-primary);
}
.icon {
height: 1.25rem;
width: 1.25rem;
}
@@ -1,80 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import styles from '@app/features/channel/components/ScheduledMessageEditBar.module.css';
import wrapperStyles from '@app/features/channel/components/textarea/InputWrapper.module.css';
import FocusRing from '@app/features/ui/focus_ring/FocusRing';
import {getCurrentLocale} from '@app/features/user/utils/LocaleUtils';
import {getFormattedDateTimeInZone} from '@fluxer/date_utils/src/DateFormatting';
import {msg} from '@lingui/core/macro';
import {useLingui} from '@lingui/react/macro';
import {ClockIcon, XCircleIcon} from '@phosphor-icons/react';
import {observer} from 'mobx-react-lite';
import {useCallback, useMemo} from 'react';
const EDITING_SCHEDULED_MESSAGE_DESCRIPTOR = msg({
message: 'Editing scheduled message',
comment: 'Button or menu action label in the channel and chat scheduled message edit bar. Keep it concise.',
});
const CANCEL_EDITING_SCHEDULED_MESSAGE_DESCRIPTOR = msg({
message: 'Cancel editing scheduled message',
comment: 'Button or menu action label in the channel and chat scheduled message edit bar. Keep it concise.',
});
interface ScheduledMessageEditBarProps {
scheduledLocalAt: string;
timezone: string;
onCancel: () => void;
}
const formatScheduleLabel = (local: string, timezone: string): string => {
const locale = getCurrentLocale();
const formatted = getFormattedDateTimeInZone(local, timezone, locale);
return `${formatted} (${timezone})`;
};
export const ScheduledMessageEditBar = observer(
({scheduledLocalAt, timezone, onCancel}: ScheduledMessageEditBarProps) => {
const {i18n} = useLingui();
const scheduleLabel = useMemo(() => formatScheduleLabel(scheduledLocalAt, timezone), [scheduledLocalAt, timezone]);
const handleStopEditing = useCallback(() => {
onCancel();
}, [onCancel]);
return (
<div
className={`${wrapperStyles.box} ${wrapperStyles.wrapperSides} ${wrapperStyles.roundedTop} ${wrapperStyles.noBottomBorder}`}
data-flx="channel.scheduled-message-edit-bar.div"
>
<div
className={wrapperStyles.barInner}
style={{gridTemplateColumns: '1fr auto'}}
data-flx="channel.scheduled-message-edit-bar.div--2"
>
<div className={styles.text} data-flx="channel.scheduled-message-edit-bar.text">
<div className={styles.label} data-flx="channel.scheduled-message-edit-bar.label">
<ClockIcon className={styles.icon} weight="fill" data-flx="channel.scheduled-message-edit-bar.icon" />
<span data-flx="channel.scheduled-message-edit-bar.span">
{i18n._(EDITING_SCHEDULED_MESSAGE_DESCRIPTOR)}
</span>
</div>
<div className={styles.timestamp} data-flx="channel.scheduled-message-edit-bar.timestamp">
{scheduleLabel}
</div>
</div>
<div className={styles.controls} data-flx="channel.scheduled-message-edit-bar.controls">
<FocusRing offset={-2} data-flx="channel.scheduled-message-edit-bar.focus-ring">
<button
type="button"
className={styles.button}
onClick={handleStopEditing}
aria-label={i18n._(CANCEL_EDITING_SCHEDULED_MESSAGE_DESCRIPTOR)}
data-flx="channel.scheduled-message-edit-bar.button.stop-editing"
>
<XCircleIcon className={styles.icon} data-flx="channel.scheduled-message-edit-bar.icon--2" />
</button>
</FocusRing>
</div>
</div>
<div className={wrapperStyles.separator} data-flx="channel.scheduled-message-edit-bar.div--3" />
</div>
);
},
);
@@ -17,7 +17,6 @@ function signals(overrides: Partial<MobileTextareaButtonSignals> = {}): MobileTe
hasContent: false,
hasAttachments: false,
hasPendingSticker: false,
isEditingScheduledMessage: false,
...overrides,
};
}
@@ -93,15 +92,6 @@ describe('MobileTextareaButtonStateMachine', () => {
});
});
it('does not expose voice recording while editing a scheduled message', () => {
expect(state({isEditingScheduledMessage: true})).toMatchObject({
mode: 'sendBlocked',
visibleButton: 'send',
sendButton: {disabled: true},
voiceButton: {disabled: true},
});
});
it('falls back to a disabled send button when voice recording is unavailable and the composer is empty', () => {
expect(state({canRecordVoice: false})).toMatchObject({
mode: 'sendBlocked',
@@ -16,7 +16,6 @@ export interface MobileTextareaButtonSignals {
hasContent: boolean;
hasAttachments: boolean;
hasPendingSticker: boolean;
isEditingScheduledMessage: boolean;
}
export interface MobileTextareaButtonModel {
@@ -51,16 +50,13 @@ export function selectMobileTextareaButtonModel(signals: MobileTextareaButtonSig
const typedText = hasTypedText(signals.value);
const hasTextContent = signals.hasContent || typedText;
const hasSubmissionContent = hasTextContent || signals.hasAttachments || signals.hasPendingSticker;
const shouldShowVoice =
signals.canRecordVoice && !signals.isEditingMessage && !signals.isEditingScheduledMessage && !hasSubmissionContent;
const shouldShowVoice = signals.canRecordVoice && !signals.isEditingMessage && !hasSubmissionContent;
const sendDisabled =
signals.disabled ||
signals.isOverCharacterLimit ||
signals.isEditingScheduledMessage ||
(signals.isSlowmodeActive && !signals.isEditingMessage) ||
(!hasSubmissionContent && !signals.isEditingMessage);
const voiceDisabled =
signals.disabled || signals.isSlowmodeActive || signals.isOverCharacterLimit || signals.isEditingScheduledMessage;
const voiceDisabled = signals.disabled || signals.isSlowmodeActive || signals.isOverCharacterLimit;
const visibleButton = shouldShowVoice ? 'voice' : 'send';
const mode = visibleButton === 'voice' ? 'voice' : sendDisabled ? 'sendBlocked' : 'sendReady';
return {
@@ -84,7 +80,6 @@ const DEFAULT_SIGNALS: MobileTextareaButtonSignals = {
hasContent: false,
hasAttachments: false,
hasPendingSticker: false,
isEditingScheduledMessage: false,
};
const DEFAULT_MODEL = selectMobileTextareaButtonModel(DEFAULT_SIGNALS);
@@ -106,7 +106,6 @@ export const TextareaButtons = React.forwardRef<HTMLDivElement, TextareaButtonsP
hasContent,
hasAttachments,
hasPendingSticker,
isEditingScheduledMessage: false,
}),
[
canRecordVoice,