fix(presence): Do Not Disturb is real "busy" presence and keeps your status (#283)
CI / Build & Quality Checks (pull_request) Successful in 1m52s
CI / Trigger Desktop Build (pull_request) Skipped
CI / Docker image build & smoke test (pull_request) Skipped
CI / Secret scan (gitleaks) (pull_request) Successful in 8s
CI / Playwright smoke (e2e) (pull_request) Successful in 10m59s

DND was sent as presence unavailable with the literal status text
"dnd": it replaced your status, and other clients showed "dnd". Where
the server supports MSC3026 (busy_presence), DND now sends presence
org.matrix.msc3026.busy and leaves the status alone (Synapse keeps
busy across /sync set_presence=online). Busy renders as red "Do Not
Disturb" with the real status line; the legacy unavailable + "dnd"
encoding is still recognised and still used on servers without it.
Status saves / clears no longer downgrade busy. The SDK rejects
non-spec presences, so busy goes through a direct authed PUT.

Fixes #283

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017PCb59uY3UKqDqt7B6hzd5
This commit is contained in:
Lotus CI
2026-10-19 01:04:27 -04:00
co-authored by Claude Opus 5.5
parent 04605f2680
commit 00f962a463
8 changed files with 105 additions and 14 deletions
+6 -2
View File
@@ -13,11 +13,13 @@ import {
import React, { ReactNode } from 'react';
import * as css from './styles.css';
import { Presence, usePresenceLabel } from '../../hooks/useUserPresence';
import { isDndPresence } from '../../utils/busyPresence';
const PresenceToColor: Record<Presence, MainColor> = {
[Presence.Online]: 'Success',
[Presence.Unavailable]: 'Warning',
[Presence.Offline]: 'Secondary',
[Presence.Busy]: 'Critical',
};
type PresenceBadgeProps = {
@@ -31,9 +33,11 @@ export function PresenceBadge({ presence, status, size }: PresenceBadgeProps) {
// Disturb" to match PresenceRingAvatar and the settings picker (which both
// special-case 'dnd' → Critical) — the badge was the lone outlier showing a
// yellow "Idle". The 'dnd' sentinel isn't surfaced as a status line.
const isDnd = presence === Presence.Unavailable && status === 'dnd';
// [Gitea #283] or MSC3026 busy, which keeps a real status line.
const isDnd = isDndPresence(presence, status);
const legacyDnd = isDnd && status === 'dnd';
const displayLabel = isDnd ? 'Do Not Disturb' : label[presence];
const displayStatus = isDnd ? undefined : status;
const displayStatus = legacyDnd ? undefined : status;
const ariaLabel = displayStatus ? `${displayLabel} — ${displayStatus}` : displayLabel;
return (