132 lines
4.1 KiB
TypeScript
132 lines
4.1 KiB
TypeScript
const OPENCLAW_3D_VIEWER_SESSION_PREFIX = "openclaw-3d-viewer:v1:";
|
|
const OPENCLAW_3D_VIEWER_SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
|
|
export type OpenClaw3DViewerSession = {
|
|
messageId: string;
|
|
viewMode: "product" | "packaging";
|
|
explodeAmount: number;
|
|
packagingExplodeAmount: number;
|
|
selectedNodeName: string | null;
|
|
selectedPackagingNodeName: string | null;
|
|
updatedAt: number;
|
|
};
|
|
|
|
type ViewerSessionStorage = Pick<Storage, "getItem" | "setItem" | "removeItem">;
|
|
|
|
function getViewerSessionKey(chatId: string) {
|
|
return `${OPENCLAW_3D_VIEWER_SESSION_PREFIX}${chatId}`;
|
|
}
|
|
|
|
function removeViewerSession(storage: ViewerSessionStorage, key: string) {
|
|
try {
|
|
storage.removeItem(key);
|
|
} catch {
|
|
// Storage can be unavailable under strict browser privacy policies.
|
|
}
|
|
}
|
|
|
|
function normalizeExplodeAmount(value: unknown) {
|
|
const amount = typeof value === "number" ? value : Number(value);
|
|
if (!Number.isFinite(amount)) return 0;
|
|
return Math.max(0, Math.min(1, amount));
|
|
}
|
|
|
|
export function readOpenClaw3DViewerSession(params: {
|
|
storage: ViewerSessionStorage;
|
|
chatId: string;
|
|
now?: number;
|
|
}): OpenClaw3DViewerSession | null {
|
|
const key = getViewerSessionKey(params.chatId);
|
|
try {
|
|
const raw = params.storage.getItem(key);
|
|
if (!raw) return null;
|
|
const value = JSON.parse(raw) as Record<string, unknown>;
|
|
const messageId =
|
|
typeof value.messageId === "string" ? value.messageId.trim() : "";
|
|
const updatedAt = Number(value.updatedAt);
|
|
const now = params.now ?? Date.now();
|
|
if (
|
|
!messageId ||
|
|
!Number.isFinite(updatedAt) ||
|
|
updatedAt <= 0 ||
|
|
now - updatedAt > OPENCLAW_3D_VIEWER_SESSION_MAX_AGE_MS
|
|
) {
|
|
removeViewerSession(params.storage, key);
|
|
return null;
|
|
}
|
|
return {
|
|
messageId,
|
|
viewMode: value.viewMode === "packaging" ? "packaging" : "product",
|
|
explodeAmount: normalizeExplodeAmount(
|
|
value.productExplodeAmount ?? value.explodeAmount
|
|
),
|
|
packagingExplodeAmount:
|
|
value.packagingExplodeAmount === undefined
|
|
? 1
|
|
: normalizeExplodeAmount(value.packagingExplodeAmount),
|
|
selectedNodeName:
|
|
typeof value.selectedNodeName === "string" &&
|
|
value.selectedNodeName.trim()
|
|
? value.selectedNodeName.trim()
|
|
: null,
|
|
selectedPackagingNodeName:
|
|
typeof value.selectedPackagingNodeName === "string" &&
|
|
value.selectedPackagingNodeName.trim()
|
|
? value.selectedPackagingNodeName.trim()
|
|
: null,
|
|
updatedAt,
|
|
};
|
|
} catch {
|
|
removeViewerSession(params.storage, key);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function writeOpenClaw3DViewerSession(params: {
|
|
storage: ViewerSessionStorage;
|
|
chatId: string;
|
|
messageId: string;
|
|
explodeAmount: number;
|
|
viewMode?: "product" | "packaging";
|
|
packagingExplodeAmount?: number;
|
|
selectedNodeName?: string | null;
|
|
selectedPackagingNodeName?: string | null;
|
|
now?: number;
|
|
}) {
|
|
const messageId = params.messageId.trim();
|
|
if (!messageId) return;
|
|
try {
|
|
params.storage.setItem(
|
|
getViewerSessionKey(params.chatId),
|
|
JSON.stringify({
|
|
messageId,
|
|
viewMode: params.viewMode || "product",
|
|
explodeAmount: normalizeExplodeAmount(params.explodeAmount),
|
|
packagingExplodeAmount:
|
|
params.packagingExplodeAmount === undefined
|
|
? 1
|
|
: normalizeExplodeAmount(params.packagingExplodeAmount),
|
|
selectedNodeName: params.selectedNodeName?.trim() || null,
|
|
selectedPackagingNodeName:
|
|
params.selectedPackagingNodeName?.trim() || null,
|
|
updatedAt: params.now ?? Date.now(),
|
|
} satisfies OpenClaw3DViewerSession)
|
|
);
|
|
} catch {
|
|
// Private browsing and storage policies may disable sessionStorage.
|
|
}
|
|
}
|
|
|
|
export function clearOpenClaw3DViewerSession(params: {
|
|
storage: ViewerSessionStorage;
|
|
chatId: string;
|
|
messageId: string;
|
|
}) {
|
|
const current = readOpenClaw3DViewerSession({
|
|
storage: params.storage,
|
|
chatId: params.chatId,
|
|
});
|
|
if (current?.messageId !== params.messageId) return;
|
|
removeViewerSession(params.storage, getViewerSessionKey(params.chatId));
|
|
}
|