diff --git a/src/app/features/settings/account/Profile.tsx b/src/app/features/settings/account/Profile.tsx index 5a287395a..0fa983ce9 100644 --- a/src/app/features/settings/account/Profile.tsx +++ b/src/app/features/settings/account/Profile.tsx @@ -57,7 +57,7 @@ import { createUploadAtom, UploadSuccess } from '../../../state/upload'; import { CompactUploadCardRenderer } from '../../../components/upload-card'; import { useCapabilities } from '../../../hooks/useCapabilities'; import { Presence, useUserPresence } from '../../../hooks/useUserPresence'; -import { noteLocalStatusWrite } from '../../../utils/ownStatusSync'; +import { DND_STATUS_SENTINEL, noteLocalStatusWrite } from '../../../utils/ownStatusSync'; import { ProfileDecoration } from './ProfileDecoration'; import { EmojiBoard } from '../../../components/emoji-board'; import { useStatusPresets } from '../../../hooks/useStatusPresets'; @@ -396,7 +396,8 @@ function ProfileStatus() { const [presenceStatus] = useSetting(settingsAtom, 'presenceStatus'); const [hidePresence] = useSetting(settingsAtom, 'hidePresence'); - const initialStatus = presence?.status ?? localStorage.getItem(STATUS_MSG_KEY(userId)) ?? ''; + const serverStatus = presence?.status === DND_STATUS_SENTINEL ? undefined : presence?.status; + const initialStatus = serverStatus ?? localStorage.getItem(STATUS_MSG_KEY(userId)) ?? ''; const [statusMsg, setStatusMsg] = useState(initialStatus); // True while the user has unsaved local edits — prevents a server presence // echo from overwriting what the user is currently typing/inserting. @@ -442,6 +443,9 @@ function ProfileStatus() { pendingAppliedRef.current = null; // window elapsed — accept external changes } } + // [Gitea #283] "dnd" is the Do Not Disturb sentinel, not something the user + // typed: don't show it in the status field or keep it as their status. + if (remoteStatus === DND_STATUS_SENTINEL) return; if (remoteStatus) { setStatusMsg(remoteStatus); localStorage.setItem(STATUS_MSG_KEY(userId), remoteStatus); diff --git a/src/app/utils/ownStatusSync.test.ts b/src/app/utils/ownStatusSync.test.ts index 6c256a2b6..e17601622 100644 --- a/src/app/utils/ownStatusSync.test.ts +++ b/src/app/utils/ownStatusSync.test.ts @@ -48,3 +48,34 @@ test("a fresh local save wins over a server read that hasn't caught up", () => { 'previous', ); }); + +test('pickHeartbeatStatus: the "dnd" sentinel is never adopted as the user status (#283)', () => { + const base = { lastLocalWriteAt: 0, now: 100_000 }; + // leaving DND: the server still says "dnd" → restore the real local status + assert.equal( + pickHeartbeatStatus({ + ...base, + local: '🎮 Gaming', + server: { presence: 'unavailable', status_msg: 'dnd' }, + }), + '🎮 Gaming', + ); + // a local copy that had absorbed the sentinel becomes empty, not "dnd" + assert.equal( + pickHeartbeatStatus({ + ...base, + local: 'dnd', + server: { presence: 'online', status_msg: 'dnd' }, + }), + '', + ); + // ordinary statuses are unaffected + assert.equal( + pickHeartbeatStatus({ + ...base, + local: 'old', + server: { presence: 'online', status_msg: 'new' }, + }), + 'new', + ); +}); diff --git a/src/app/utils/ownStatusSync.ts b/src/app/utils/ownStatusSync.ts index 7d80c5502..d56324602 100644 --- a/src/app/utils/ownStatusSync.ts +++ b/src/app/utils/ownStatusSync.ts @@ -9,6 +9,9 @@ * overridden by a server read that hasn't caught up yet. */ +/** The status_msg Lotus sends for Do Not Disturb (see usePresenceUpdater). */ +export const DND_STATUS_SENTINEL = 'dnd'; + /** Trust this device's own copy for this long after it saved or cleared it. */ export const LOCAL_WRITE_GRACE_MS = 15_000; @@ -37,7 +40,12 @@ export const pickHeartbeatStatus = ({ lastLocalWriteAt: number; now: number; }): string => { - if (!server || server.presence === 'offline') return local; - if (now - writtenAt < LOCAL_WRITE_GRACE_MS) return local; + // [Gitea #283] Lotus encodes Do Not Disturb as status_msg "dnd", so a device + // leaving DND must not adopt that sentinel as the user's status — keep (and + // so restore) the real one it holds locally. + const mine = local === DND_STATUS_SENTINEL ? '' : local; + if (!server || server.presence === 'offline') return mine; + if (now - writtenAt < LOCAL_WRITE_GRACE_MS) return mine; + if (server.status_msg === DND_STATUS_SENTINEL) return mine; return server.status_msg ?? ''; };