feat: presence fix, voice ringing fix, user private notes + doc updates
- usePresenceUpdater: replace stale closure with readStatus() called at invocation time so changing custom status in Profile Settings is never silently overwritten by subsequent activity events - CallEmbedProvider: fix m.space.parent state-key lookup by switching getStateEvent → getStateEvents (plural); space channel voice rooms no longer trigger the incoming-call ring/animation - Add useUserNotes hook (io.lotus.user_notes account data, reactive via useAccountDataCallback, 500-char limit, cross-device sync) - UserRoomProfile: add UserPrivateNotes textarea with 800ms debounced auto-save, saving indicator, char counter when <100 chars remain; shown only when viewing another user's profile - LOTUS_FEATURES.md: add Private Notes section, Status Revert fix note, animation improvements subsection, Seasonal Themes section - LOTUS_BUGS.md: mark presence revert + voice ringing bugs as resolved - README.md + landing/index.html: document all new June 2026 features Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -17,26 +17,34 @@ export function usePresenceUpdater() {
|
||||
|
||||
useEffect(() => {
|
||||
const userId = mx.getUserId();
|
||||
const storedStatus = userId ? (localStorage.getItem(`lotus-status-msg-${userId}`) ?? '') : '';
|
||||
|
||||
const setOnline = () =>
|
||||
mx
|
||||
// Read status from localStorage at call time so manual updates from the
|
||||
// Profile settings are never overwritten by a stale closure value.
|
||||
const readStatus = () =>
|
||||
userId ? (localStorage.getItem(`lotus-status-msg-${userId}`) ?? '') : '';
|
||||
|
||||
const setOnline = () => {
|
||||
const status = readStatus();
|
||||
return mx
|
||||
.setPresence({
|
||||
presence: 'online',
|
||||
...(storedStatus ? { status_msg: storedStatus } : {}),
|
||||
...(status ? { status_msg: status } : {}),
|
||||
})
|
||||
.catch(() => undefined);
|
||||
const setUnavailable = (statusMsg?: string) =>
|
||||
mx
|
||||
};
|
||||
const setUnavailable = (statusMsg?: string) => {
|
||||
const status = readStatus();
|
||||
return mx
|
||||
.setPresence({
|
||||
presence: 'unavailable',
|
||||
...(statusMsg
|
||||
? { status_msg: statusMsg }
|
||||
: storedStatus
|
||||
? { status_msg: storedStatus }
|
||||
: status
|
||||
? { status_msg: status }
|
||||
: {}),
|
||||
})
|
||||
.catch(() => undefined);
|
||||
};
|
||||
const setOffline = () =>
|
||||
mx.setPresence({ presence: 'offline', status_msg: '' }).catch(() => undefined);
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { MatrixClient } from 'matrix-js-sdk';
|
||||
import { useMatrixClient } from './useMatrixClient';
|
||||
import { useAccountDataCallback } from './useAccountDataCallback';
|
||||
|
||||
const NOTES_KEY = 'io.lotus.user_notes';
|
||||
export const USER_NOTE_MAX_LENGTH = 500;
|
||||
|
||||
type UserNotesContent = Record<string, string>;
|
||||
|
||||
function readNotes(mx: MatrixClient): UserNotesContent {
|
||||
return (mx.getAccountData(NOTES_KEY as any)?.getContent() as UserNotesContent | undefined) ?? {};
|
||||
}
|
||||
|
||||
export function useUserNotes(): {
|
||||
getNote: (userId: string) => string;
|
||||
setNote: (userId: string, note: string) => Promise<void>;
|
||||
} {
|
||||
const mx = useMatrixClient();
|
||||
const [notes, setNotes] = useState<UserNotesContent>(() => readNotes(mx));
|
||||
|
||||
useAccountDataCallback(
|
||||
mx,
|
||||
useCallback(
|
||||
(evt) => {
|
||||
if (evt.getType() === NOTES_KEY) {
|
||||
setNotes(evt.getContent<UserNotesContent>() ?? {});
|
||||
}
|
||||
},
|
||||
[],
|
||||
),
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setNotes(readNotes(mx));
|
||||
}, [mx]);
|
||||
|
||||
const getNote = useCallback((userId: string) => notes[userId] ?? '', [notes]);
|
||||
|
||||
const setNote = useCallback(
|
||||
async (userId: string, note: string) => {
|
||||
const current = readNotes(mx);
|
||||
const updated = { ...current };
|
||||
const trimmed = note.trim().slice(0, USER_NOTE_MAX_LENGTH);
|
||||
if (trimmed) {
|
||||
updated[userId] = trimmed;
|
||||
} else {
|
||||
delete updated[userId];
|
||||
}
|
||||
await (mx as any).setAccountData(NOTES_KEY, updated);
|
||||
},
|
||||
[mx],
|
||||
);
|
||||
|
||||
return { getNote, setNote };
|
||||
}
|
||||
Reference in New Issue
Block a user