This commit is contained in:
legop3
2026-08-07 23:25:56 -04:00
parent 6c69c583c5
commit 6a6dec5540
8 changed files with 54 additions and 15 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -12,7 +12,7 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<!-- site-metadata:inject -->
<!-- analytics:inject -->
<script type="module" crossorigin src="/assets/index-r7XMuw_k.js"></script>
<script type="module" crossorigin src="/assets/index-D8T7esRs.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-7PpZTwSc.css">
</head>
<body>
@@ -84,10 +84,29 @@ function listGrantedRoversForRequester(requesterKey) {
}
function applySocketGrantCache(socket) {
if (!socket) return;
if (!socket) return false;
socket.data = socket.data || {};
const requesterKey = buildRequesterKey(socket);
socket.data.privateClosedAccessRovers = listGrantedRoversForRequester(requesterKey);
const nextRoverIds = listGrantedRoversForRequester(requesterKey).sort();
const previousRoverIds = Array.isArray(socket.data.privateClosedAccessRovers)
? socket.data.privateClosedAccessRovers
: [];
const requesterChanged = socket.data.privateAccessRequesterKey !== requesterKey;
const grantsChanged = previousRoverIds.length !== nextRoverIds.length
|| previousRoverIds.some((roverId, index) => String(roverId) !== String(nextRoverIds[index]));
/*
Remember both inputs that determine the socket's private-access projection.
The requester key matters even when both identities currently have no grants,
because pending requests in the session payload are also keyed by requester.
Returning whether either input changed lets transport hooks avoid announcing
a false state change for ordinary live identity-setting updates.
*/
socket.data.privateAccessRequesterKey = requesterKey;
if (grantsChanged) {
socket.data.privateClosedAccessRovers = nextRoverIds;
}
return requesterChanged || grantsChanged;
}
function refreshAllSocketGrantCaches() {
@@ -71,8 +71,15 @@ function registerPrivateRoverAccessHooks(deps) {
socket.on('privateRover:requestAccess', handleRequest);
socket.on('session:privateRover:requestAccess', handleRequest);
socket.on('session:identify', () => {
applySocketGrantCache(socket);
requestEvents.emit('change', { reason: 'identify', socketId: socket.id });
/*
session:identify also carries unrelated live preferences such as audio
adjustments. Only publish a private-access change when identification
actually moved this socket to a different requester or grant set; a
no-op event must not fan out a full session sync to every connection.
*/
if (applySocketGrantCache(socket)) {
requestEvents.emit('change', { reason: 'identify', socketId: socket.id });
}
});
});
}
+16 -3
View File
@@ -66,7 +66,15 @@ export function useSettings() {
export function useSettingsNamespace(namespace, defaults = {}) {
const { data, status, setNamespace } = useSettings();
const value = data[namespace] ?? defaults ?? {};
/*
Defaults describe the initial shape of a namespace; they are not live state.
Keep the first supplied value in lazy hook state because callers naturally pass object
literals. Returning each newly-created literal while the namespace is absent
would make an unchanged setting appear to change on every parent render and
could retrigger effects that synchronize settings to the server.
*/
const [initialDefaults] = useState(() => defaults ?? {});
const value = data[namespace] ?? initialDefaults;
const save = useCallback(
(update) => {
@@ -89,9 +97,14 @@ export function useSettingsNamespace(namespace, defaults = {}) {
);
const reset = useCallback(() => {
const base = typeof defaults === 'object' ? { ...defaults } : defaults;
/*
Reset uses the same stable initial defaults exposed above. This keeps the
namespace contract consistent even if a caller recreates its defaults
object during later renders.
*/
const base = typeof initialDefaults === 'object' ? { ...initialDefaults } : initialDefaults;
setNamespace(namespace, () => base ?? {});
}, [defaults, namespace, setNamespace]);
}, [initialDefaults, namespace, setNamespace]);
return { value, status, save, replace, reset };
}