fix(reminders): make cancel optimistic, no contradictory error
The shared account-data store removes optimistically with no rollback, so the reminder row vanished the instant Cancel was clicked. Showing a "could not cancel" error beside the already-gone row (and it reappearing on next sync) was self-contradictory. Match the removeBookmark convention: fire-and-forget optimistic removal, no inline error. Drops the now-moot cancelling busy-guard and uses a collision-safe React key for same-minute custom reminders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -62,7 +62,6 @@ export function RemindMeDialog({ roomId, eventId, previewText, onClose }: Remind
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [customOpen, setCustomOpen] = useState(false);
|
||||
const [cancelling, setCancelling] = useState<Set<number>>(new Set());
|
||||
const def = useMemo(() => defaultCustomDate(), []);
|
||||
const [dateValue, setDateValue] = useState(() => toLocalDate(def));
|
||||
const [timeValue, setTimeValue] = useState(() => toLocalTime(def));
|
||||
@@ -83,21 +82,13 @@ export function RemindMeDialog({ roomId, eventId, previewText, onClose }: Remind
|
||||
if (customOpen) dateInputRef.current?.focus();
|
||||
}, [customOpen]);
|
||||
|
||||
const handleCancelExisting = async (timestamp: number) => {
|
||||
if (cancelling.has(timestamp)) return;
|
||||
setCancelling((prev) => new Set(prev).add(timestamp));
|
||||
setError(null);
|
||||
try {
|
||||
await removeReminder(eventId, timestamp);
|
||||
} catch {
|
||||
setError('Could not cancel reminder. Try again.');
|
||||
} finally {
|
||||
setCancelling((prev) => {
|
||||
const next = new Set(prev);
|
||||
next.delete(timestamp);
|
||||
return next;
|
||||
});
|
||||
}
|
||||
const handleCancelExisting = (timestamp: number) => {
|
||||
// Optimistic, matching removeBookmark: the shared account-data store drops
|
||||
// the reminder locally at once (no rollback) and re-syncs from the server.
|
||||
// We deliberately show no inline error — the store has no rollback path, so a
|
||||
// failed write simply reappears on the next sync rather than leaving a stale
|
||||
// "couldn't cancel" message beside an already-vanished row.
|
||||
removeReminder(eventId, timestamp).catch(() => undefined);
|
||||
};
|
||||
|
||||
const commit = async (timestamp: number) => {
|
||||
@@ -191,8 +182,10 @@ export function RemindMeDialog({ roomId, eventId, previewText, onClose }: Remind
|
||||
<Text size="L400" priority="300" style={{ paddingLeft: config.space.S200 }}>
|
||||
{existing.length === 1 ? 'Reminder set' : 'Reminders set'}
|
||||
</Text>
|
||||
{existing.map((r) => (
|
||||
<Box key={r.timestamp} alignItems="Center" gap="200">
|
||||
{existing.map((r, idx) => (
|
||||
// Composite key: two custom reminders on one message can share
|
||||
// a minute-precision timestamp; index keeps React keys unique.
|
||||
<Box key={`${r.timestamp}-${idx}`} alignItems="Center" gap="200">
|
||||
<Icon src={Icons.Clock} size="100" style={{ flexShrink: 0 }} />
|
||||
<Text size="T200" style={{ flexGrow: 1, minWidth: 0 }} truncate>
|
||||
{formatFriendlyDateTime(r.timestamp)}
|
||||
@@ -202,7 +195,6 @@ export function RemindMeDialog({ roomId, eventId, previewText, onClose }: Remind
|
||||
radii="300"
|
||||
variant="SurfaceVariant"
|
||||
fill="None"
|
||||
disabled={cancelling.has(r.timestamp)}
|
||||
onClick={() => handleCancelExisting(r.timestamp)}
|
||||
aria-label={`Cancel reminder for ${formatFriendlyDateTime(r.timestamp)}`}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user