mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
refactor(ui): consolidate presence payload parsing (#129029)
* refactor(ui): consolidate presence payload parsing * chore(ui): tighten presence assertion safety baselines
This commit is contained in:
committed by
GitHub
parent
2d0ac795b6
commit
fbf8460da3
@@ -4062,7 +4062,6 @@ ui/src/components/terminal/terminal-panel-upload.ts 2
|
||||
ui/src/components/terminal/terminal-panel.ts 2
|
||||
ui/src/components/terminal/terminal-pending-actions.ts 1
|
||||
ui/src/components/theme-mode-toggle.ts 1
|
||||
ui/src/components/viewer-facepile.ts 2
|
||||
ui/src/components/web-awesome.ts 2
|
||||
ui/src/components/wizard-step-controls.ts 4
|
||||
ui/src/i18n/lib/registry.ts 2
|
||||
@@ -4228,7 +4227,7 @@ ui/src/pages/custodian/custodian-session-store.ts 1
|
||||
ui/src/pages/custodian/custodian-surface.ts 2
|
||||
ui/src/pages/custodian/session-lifecycle.ts 1
|
||||
ui/src/pages/debug/view.ts 3
|
||||
ui/src/pages/devices/devices-page.ts 3
|
||||
ui/src/pages/devices/devices-page.ts 1
|
||||
ui/src/pages/devices/view-exec-approvals.ts 7
|
||||
ui/src/pages/devices/view.ts 5
|
||||
ui/src/pages/labs/labs-registry.ts 6
|
||||
@@ -4243,7 +4242,7 @@ ui/src/pages/new-session/composer.ts 1
|
||||
ui/src/pages/new-session/detail-chip.ts 2
|
||||
ui/src/pages/new-session/discovery.ts 3
|
||||
ui/src/pages/new-session/draft-place-browser.ts 4
|
||||
ui/src/pages/new-session/new-session-runtime.ts 3
|
||||
ui/src/pages/new-session/new-session-runtime.ts 1
|
||||
ui/src/pages/new-session/place-browser.ts 1
|
||||
ui/src/pages/new-session/place-facts.ts 1
|
||||
ui/src/pages/new-session/preferences.ts 1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { html, nothing } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
import type { PresenceEntry } from "../api/types.ts";
|
||||
import { readPresenceEntries } from "../app/user-profile.ts";
|
||||
import {
|
||||
presenceViewerLabel,
|
||||
projectPresenceEntries,
|
||||
@@ -20,14 +20,6 @@ import {
|
||||
} from "./person-activity-link.ts";
|
||||
import "./tooltip.ts";
|
||||
|
||||
function readPresenceEntries(value: unknown): PresenceEntry[] {
|
||||
if (!value || typeof value !== "object") {
|
||||
return [];
|
||||
}
|
||||
const presence = (value as { presence?: unknown }).presence;
|
||||
return Array.isArray(presence) ? (presence as PresenceEntry[]) : [];
|
||||
}
|
||||
|
||||
function normalized(value: string | null | undefined): string | undefined {
|
||||
const trimmed = value?.trim();
|
||||
return trimmed ? trimmed : undefined;
|
||||
@@ -92,7 +84,7 @@ class ViewerFacepile extends OpenClawLightDomContentsElement {
|
||||
|
||||
override render() {
|
||||
const projection = projectPresenceEntries(
|
||||
readPresenceEntries(this.presencePayload),
|
||||
readPresenceEntries(this.presencePayload) ?? [],
|
||||
this.selfUserId,
|
||||
this.selfInstanceId,
|
||||
);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { PresenceEntry } from "../api/types.ts";
|
||||
import { readPresenceEntries } from "../app/user-profile.ts";
|
||||
|
||||
export type PresenceViewer = {
|
||||
id: string;
|
||||
@@ -24,16 +25,6 @@ function firstSorted(values: Iterable<string | null | undefined>): string | unde
|
||||
.toSorted()[0];
|
||||
}
|
||||
|
||||
function readPresenceEntries(value: unknown): PresenceEntry[] {
|
||||
if (!value || typeof value !== "object") {
|
||||
return [];
|
||||
}
|
||||
// SAFETY: Gateway snapshots are protocol-validated before reaching UI projections.
|
||||
const presence = (value as { presence?: unknown }).presence;
|
||||
// SAFETY: The validated presence array carries PresenceEntry protocol records.
|
||||
return Array.isArray(presence) ? (presence as PresenceEntry[]) : [];
|
||||
}
|
||||
|
||||
function presenceEntrySortKey(entry: PresenceEntry): string {
|
||||
return [
|
||||
normalized(entry.host) ?? "",
|
||||
@@ -119,7 +110,7 @@ export function projectPresencePayload(
|
||||
cachedAuthenticatedSelfUserId = authenticatedSelfUserId;
|
||||
cachedSelfInstanceId = selfInstanceId;
|
||||
cachedPresenceProjection = projectPresenceViewers(
|
||||
readPresenceEntries(value),
|
||||
readPresenceEntries(value) ?? [],
|
||||
authenticatedSelfUserId,
|
||||
selfInstanceId,
|
||||
);
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
type ApplicationGatewaySnapshot,
|
||||
} from "../../app/context.ts";
|
||||
import { hasOperatorAdminAccess, hasOperatorPairingAccess } from "../../app/operator-access.ts";
|
||||
import { readPresenceEntries } from "../../app/user-profile.ts";
|
||||
import { showConfirmDialog, type ConfirmDialogOptions } from "../../components/confirm-dialog.ts";
|
||||
import { showSecretRevealDialog } from "../../components/secret-reveal-dialog.ts";
|
||||
import { renderDocsLink } from "../../components/settings-ui.ts";
|
||||
@@ -62,12 +63,6 @@ type InventoryRemovalPrompt =
|
||||
| { kind: "entry"; entry: InventoryRemovalRequest }
|
||||
| { kind: "stale"; entries: InventoryRemovalRequest[] };
|
||||
|
||||
function readPresence(value: unknown): PresenceEntry[] | null {
|
||||
const presence =
|
||||
value && typeof value === "object" ? (value as { presence?: unknown }).presence : null;
|
||||
return Array.isArray(presence) ? (presence as PresenceEntry[]) : null;
|
||||
}
|
||||
|
||||
function presenceConnectivitySignature(entries: PresenceEntry[]): string {
|
||||
const states = new Map<string, "connected" | "offline">();
|
||||
for (const entry of entries) {
|
||||
@@ -154,7 +149,7 @@ class DevicesPage extends OpenClawLightDomElement {
|
||||
if (this.gateway.gateway !== gateway || this.context.gateway !== gateway) {
|
||||
return;
|
||||
}
|
||||
const presence = event.event === "presence" ? readPresence(event.payload) : null;
|
||||
const presence = event.event === "presence" ? readPresenceEntries(event.payload) : null;
|
||||
if (presence) {
|
||||
const connectivityChanged =
|
||||
presenceConnectivitySignature(presence) !==
|
||||
@@ -226,7 +221,7 @@ class DevicesPage extends OpenClawLightDomElement {
|
||||
snapshot.client &&
|
||||
(change.identityChanged || change.connectionChanged)
|
||||
) {
|
||||
const initialPresence = readPresence(snapshot.hello?.snapshot);
|
||||
const initialPresence = readPresenceEntries(snapshot.hello?.snapshot);
|
||||
this.presence = initialPresence ?? [];
|
||||
void this.loadPresence();
|
||||
}
|
||||
@@ -250,7 +245,7 @@ class DevicesPage extends OpenClawLightDomElement {
|
||||
const snapshot = this.context.gateway.snapshot;
|
||||
if (!this.gateway.isRouteDataCurrent(data)) {
|
||||
this.resetServerState(snapshot);
|
||||
this.presence = readPresence(snapshot.hello?.snapshot) ?? [];
|
||||
this.presence = readPresenceEntries(snapshot.hello?.snapshot) ?? [];
|
||||
void this.loadPresence();
|
||||
this.ensureInitialData();
|
||||
return;
|
||||
@@ -261,7 +256,7 @@ class DevicesPage extends OpenClawLightDomElement {
|
||||
connected: snapshot.phase === "connected",
|
||||
requestGeneration: this.gateway.epoch,
|
||||
};
|
||||
const initialPresence = readPresence(snapshot.hello?.snapshot);
|
||||
const initialPresence = readPresenceEntries(snapshot.hello?.snapshot);
|
||||
if (initialPresence) {
|
||||
this.presence = initialPresence;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { selectApplicationSession } from "../../app/agent-selection.ts";
|
||||
import { applicationContext, type ApplicationContext } from "../../app/context.ts";
|
||||
import { beginNativeWindowDragFromTopInset } from "../../app/native-window-drag.ts";
|
||||
import { loadSettings } from "../../app/settings.ts";
|
||||
import { readPresenceEntries } from "../../app/user-profile.ts";
|
||||
import type { ImageLightboxItem } from "../../components/image-lightbox.ts";
|
||||
import { t } from "../../i18n/index.ts";
|
||||
import "../../components/web-awesome-popover.ts";
|
||||
@@ -39,7 +40,6 @@ import {
|
||||
handleSessionPickerEvent,
|
||||
isPlaceTopologyEvent,
|
||||
presenceStateSignature,
|
||||
readPresenceEntries,
|
||||
} from "./new-session-runtime.ts";
|
||||
import { renderProjectChip, resolveProjectChip } from "./project-chip.ts";
|
||||
import type { SubmissionOutcomeReason } from "./session-placement-recovery-state.ts";
|
||||
|
||||
@@ -16,12 +16,6 @@ export function isPlaceTopologyEvent(event: string): boolean {
|
||||
return PLACE_TOPOLOGY_EVENTS.has(event);
|
||||
}
|
||||
|
||||
export function readPresenceEntries(value: unknown): PresenceEntry[] | null {
|
||||
const presence =
|
||||
value && typeof value === "object" ? (value as { presence?: unknown }).presence : null;
|
||||
return Array.isArray(presence) ? (presence as PresenceEntry[]) : null;
|
||||
}
|
||||
|
||||
export function presenceStateSignature(entries: PresenceEntry[]): string {
|
||||
const states = new Map<string, "connected" | "offline">();
|
||||
for (const entry of entries) {
|
||||
|
||||
Reference in New Issue
Block a user