fix: persist status message and timezone across reconnects
Status message: Synapse clears status_msg when a user goes offline/reconnects.
Fix by caching to localStorage and re-sending on setOnline(). The sync
effect no longer overwrites the local value with an empty server event.
Timezone: PUT /profile/{userId}/m.tz is MSC1769 (unstable) and not
supported by standard Synapse — save/load silently fails. Fix by using
Matrix account data (im.lotus.timezone) instead, which is fully
supported. Own profile falls back to account data; other users still
try the m.tz profile endpoint (for federated servers that support it).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ export const useExtendedProfile = (userId: string): ExtendedProfile => {
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
const myUserId = mx.getUserId();
|
||||
|
||||
const fetchField = async <T extends Record<string, string>>(
|
||||
field: string,
|
||||
@@ -28,14 +29,32 @@ export const useExtendedProfile = (userId: string): ExtendedProfile => {
|
||||
}
|
||||
};
|
||||
|
||||
Promise.all([
|
||||
fetchField<{ 'm.pronouns': string }>('m.pronouns'),
|
||||
fetchField<{ 'm.tz': string }>('m.tz'),
|
||||
]).then(([pronouns, timezone]) => {
|
||||
const run = async () => {
|
||||
const [pronouns, tzFromProfile] = await Promise.all([
|
||||
fetchField<{ 'm.pronouns': string }>('m.pronouns'),
|
||||
fetchField<{ 'm.tz': string }>('m.tz'),
|
||||
]);
|
||||
|
||||
let timezone = tzFromProfile;
|
||||
// Standard Synapse doesn't support m.tz — fall back to account data for own profile
|
||||
if (!timezone && userId === myUserId) {
|
||||
try {
|
||||
const res = await mx.http.authedRequest<{ timezone: string }>(
|
||||
Method.Get,
|
||||
`/user/${encodeURIComponent(userId)}/account_data/im.lotus.timezone`,
|
||||
);
|
||||
timezone = res.timezone || undefined;
|
||||
} catch {
|
||||
// not set yet
|
||||
}
|
||||
}
|
||||
|
||||
if (!cancelled) {
|
||||
setExtProfile({ pronouns: pronouns || undefined, timezone: timezone || undefined });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
run();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
||||
@@ -16,10 +16,23 @@ export function usePresenceUpdater() {
|
||||
const lastActivityRef = useRef(0);
|
||||
|
||||
useEffect(() => {
|
||||
const userId = mx.getUserId();
|
||||
const storedStatus = userId ? localStorage.getItem(`lotus-status-msg-${userId}`) ?? '' : '';
|
||||
|
||||
const setOnline = () =>
|
||||
mx.setPresence({ presence: 'online' }).catch(() => undefined);
|
||||
mx.setPresence({
|
||||
presence: 'online',
|
||||
...(storedStatus ? { status_msg: storedStatus } : {}),
|
||||
}).catch(() => undefined);
|
||||
const setUnavailable = (statusMsg?: string) =>
|
||||
mx.setPresence({ presence: 'unavailable', ...(statusMsg ? { status_msg: statusMsg } : {}) }).catch(() => undefined);
|
||||
mx.setPresence({
|
||||
presence: 'unavailable',
|
||||
...(statusMsg
|
||||
? { status_msg: statusMsg }
|
||||
: storedStatus
|
||||
? { status_msg: storedStatus }
|
||||
: {}),
|
||||
}).catch(() => undefined);
|
||||
const setOffline = () =>
|
||||
mx.setPresence({ presence: 'offline', status_msg: '' }).catch(() => undefined);
|
||||
|
||||
@@ -44,7 +57,7 @@ export function usePresenceUpdater() {
|
||||
// presenceStatus === 'auto' — original activity-tracking behavior.
|
||||
const startIdleTimer = () => {
|
||||
clearTimeout(idleTimerRef.current);
|
||||
idleTimerRef.current = window.setTimeout(() => {
|
||||
idleTimerRef.current = setTimeout(() => {
|
||||
isIdleRef.current = true;
|
||||
setUnavailable();
|
||||
}, IDLE_TIMEOUT_MS);
|
||||
|
||||
Reference in New Issue
Block a user