fix(matrix): upload retry/backoff, robust MatrixError, typed m.direct, reliable presence on unload
- uploadContent: bounded retry (max 3) reusing rateLimitedActions' capped
exponential backoff; retries only transient failures (network/429/5xx), never 4xx.
- Robust MatrixError construction from UploadResponse / unknown error shapes.
- addRoomIdToMDirect/removeRoomIdFromMDirect: drop `as any`, use typed
EventType.Direct + MDirectContent.
- usePresenceUpdater: keep fetch({keepalive}) for the unload offline-presence
update (sendBeacon can't set the auth header) and log redacted warnings instead
of silently swallowing presence errors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -23,6 +23,13 @@ export function usePresenceUpdater() {
|
||||
const readStatus = () =>
|
||||
userId ? (localStorage.getItem(`lotus-status-msg-${userId}`) ?? '') : '';
|
||||
|
||||
// Log presence failures without leaking PII (user id, token, status message).
|
||||
const warnPresenceFailure = (presence: string, err: unknown) => {
|
||||
const reason =
|
||||
err instanceof Error ? err.message : typeof err === 'string' ? err : 'unknown error';
|
||||
console.warn(`Failed to set presence to "${presence}":`, reason);
|
||||
};
|
||||
|
||||
const setOnline = () => {
|
||||
const status = readStatus();
|
||||
return mx
|
||||
@@ -30,7 +37,7 @@ export function usePresenceUpdater() {
|
||||
presence: 'online',
|
||||
...(status ? { status_msg: status } : {}),
|
||||
})
|
||||
.catch(() => undefined);
|
||||
.catch((err) => warnPresenceFailure('online', err));
|
||||
};
|
||||
const setUnavailable = (statusMsg?: string) => {
|
||||
const status = readStatus();
|
||||
@@ -39,10 +46,12 @@ export function usePresenceUpdater() {
|
||||
presence: 'unavailable',
|
||||
...(statusMsg ? { status_msg: statusMsg } : status ? { status_msg: status } : {}),
|
||||
})
|
||||
.catch(() => undefined);
|
||||
.catch((err) => warnPresenceFailure('unavailable', err));
|
||||
};
|
||||
const setOffline = () =>
|
||||
mx.setPresence({ presence: 'offline', status_msg: '' }).catch(() => undefined);
|
||||
mx
|
||||
.setPresence({ presence: 'offline', status_msg: '' })
|
||||
.catch((err) => warnPresenceFailure('offline', err));
|
||||
|
||||
// Manual presence overrides — no activity tracking needed.
|
||||
if (hidePresence || presenceStatus === 'invisible') {
|
||||
@@ -100,6 +109,11 @@ export function usePresenceUpdater() {
|
||||
const baseUrl = mx.getHomeserverUrl();
|
||||
if (!userId || !token || !baseUrl) return;
|
||||
|
||||
// Reliable delivery during page teardown: navigator.sendBeacon cannot set the
|
||||
// Authorization header required by the authenticated Matrix presence endpoint, so
|
||||
// it isn't usable here. fetch(..., { keepalive: true }) lets the request outlive the
|
||||
// page and is the correct mechanism for an authed endpoint. (keepalive bodies are
|
||||
// capped at 64KB, which this tiny payload is well under.)
|
||||
fetch(`${baseUrl}/_matrix/client/v3/presence/${encodeURIComponent(userId)}/status`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -108,7 +122,7 @@ export function usePresenceUpdater() {
|
||||
},
|
||||
body: JSON.stringify({ presence: 'offline' }),
|
||||
keepalive: true,
|
||||
}).catch(() => undefined);
|
||||
}).catch((err) => warnPresenceFailure('offline (pagehide)', err));
|
||||
};
|
||||
|
||||
setOnline();
|
||||
|
||||
Reference in New Issue
Block a user