fix(status): stop presence heartbeats from clobbering status edits

Selecting an emoji (or any unsaved edit) in the profile Status Message
field could vanish because the presence-sync effect re-applied the remote
status_msg on every presence heartbeat (which fire every few seconds).
The dirty-edit guard alone left a window where a heartbeat carrying the
previous status overwrote the just-inserted emoji, so there was no way to
add emoji to a status.

Track the last remote status we synced and only react when the remote
value actually changes, instead of on every heartbeat. Repeated
heartbeats with an unchanged status are now ignored, so an unsaved local
edit is preserved regardless of the dirty flag's timing. Cross-device
status changes (a genuinely new remote value) and clears still sync, and
the pending-applied stale-echo guard is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-10 00:13:44 -04:00
co-authored by Claude Opus 4.8
parent 53ce2e40a4
commit a739c25f10
+12 -3
View File
@@ -363,12 +363,17 @@ function ProfileStatus() {
const [presenceStatus] = useSetting(settingsAtom, 'presenceStatus');
const [hidePresence] = useSetting(settingsAtom, 'hidePresence');
const [statusMsg, setStatusMsg] = useState<string>(
presence?.status ?? localStorage.getItem(STATUS_MSG_KEY(userId)) ?? '',
);
const initialStatus = presence?.status ?? localStorage.getItem(STATUS_MSG_KEY(userId)) ?? '';
const [statusMsg, setStatusMsg] = useState<string>(initialStatus);
// True while the user has unsaved local edits — prevents a server presence
// echo from overwriting what the user is currently typing/inserting.
const statusDirtyRef = useRef(false);
// The last remote status we synced into the input. Presence heartbeats fire
// every few seconds carrying the SAME status; the sync effect must only react
// when the remote value actually changes, otherwise a repeated heartbeat can
// overwrite an unsaved local edit (e.g. an emoji just inserted) the instant
// the dirty flag is out of sync. Seeded with the value the input started on.
const lastSyncedRemoteRef = useRef<string>(initialStatus);
// The value we most recently applied (with the apply time). A presence
// heartbeat can echo the PREVIOUS status just after we save the new one; that
// stale echo would otherwise revert the input. We ignore non-matching echoes
@@ -390,6 +395,10 @@ function ProfileStatus() {
// wipe the saved status on every invisible toggle.
if (presence.presence === Presence.Offline) return;
const remoteStatus = presence.status ?? '';
// Only act on an actual remote change. Repeated heartbeats carrying the same
// status are ignored so they can never clobber an unsaved local edit.
if (remoteStatus === lastSyncedRemoteRef.current) return;
lastSyncedRemoteRef.current = remoteStatus;
const pending = pendingAppliedRef.current;
if (pending) {
if (remoteStatus === pending.value) {