feat(chat): add support action cards and live entitlements
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
"use client";
|
||||
|
||||
import type {
|
||||
ChatAction,
|
||||
ChatActionEventType,
|
||||
} from "@/data/schemas/chat";
|
||||
import { chatApi } from "@/data/services/api";
|
||||
|
||||
const EVENT_PREFIX = "cozsweet:chat-action-event:v1";
|
||||
const ARRIVAL_KEY = "cozsweet:chat-action-arrival:v1";
|
||||
|
||||
interface PendingChatActionArrival {
|
||||
actionId: string;
|
||||
characterId: string;
|
||||
expectedPath: string;
|
||||
}
|
||||
|
||||
export async function recordChatActionEvent(
|
||||
action: ChatAction,
|
||||
characterId: string,
|
||||
eventType: ChatActionEventType,
|
||||
): Promise<void> {
|
||||
await recordChatActionEventById(action.actionId, characterId, eventType);
|
||||
}
|
||||
|
||||
export async function recordChatActionEventById(
|
||||
actionId: string,
|
||||
characterId: string,
|
||||
eventType: ChatActionEventType,
|
||||
): Promise<void> {
|
||||
await chatApi.recordActionEvent({
|
||||
eventId: getOrCreateEventId(actionId, eventType),
|
||||
actionId,
|
||||
characterId,
|
||||
eventType,
|
||||
});
|
||||
}
|
||||
|
||||
export function rememberChatActionArrival(
|
||||
action: ChatAction,
|
||||
characterId: string,
|
||||
targetUrl: string,
|
||||
): void {
|
||||
if (typeof window === "undefined") return;
|
||||
const target = new URL(targetUrl, window.location.origin);
|
||||
if (target.origin !== window.location.origin) return;
|
||||
const pending: PendingChatActionArrival = {
|
||||
actionId: action.actionId,
|
||||
characterId,
|
||||
expectedPath: target.pathname,
|
||||
};
|
||||
window.sessionStorage.setItem(ARRIVAL_KEY, JSON.stringify(pending));
|
||||
}
|
||||
|
||||
export function readPendingChatActionArrival(): PendingChatActionArrival | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
try {
|
||||
const raw = window.sessionStorage.getItem(ARRIVAL_KEY);
|
||||
if (!raw) return null;
|
||||
const parsed = JSON.parse(raw) as Partial<PendingChatActionArrival>;
|
||||
if (!parsed.actionId || !parsed.characterId || !parsed.expectedPath) {
|
||||
return null;
|
||||
}
|
||||
return parsed as PendingChatActionArrival;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export function clearPendingChatActionArrival(): void {
|
||||
if (typeof window === "undefined") return;
|
||||
window.sessionStorage.removeItem(ARRIVAL_KEY);
|
||||
}
|
||||
|
||||
function getOrCreateEventId(
|
||||
actionId: string,
|
||||
eventType: ChatActionEventType,
|
||||
): string {
|
||||
const key = `${EVENT_PREFIX}:${actionId}:${eventType}`;
|
||||
if (typeof window !== "undefined") {
|
||||
const existing = window.sessionStorage.getItem(key);
|
||||
if (existing) return existing;
|
||||
}
|
||||
const eventId = createUuid();
|
||||
if (typeof window !== "undefined") {
|
||||
window.sessionStorage.setItem(key, eventId);
|
||||
}
|
||||
return eventId;
|
||||
}
|
||||
|
||||
function createUuid(): string {
|
||||
if (typeof globalThis.crypto?.randomUUID === "function") {
|
||||
return globalThis.crypto.randomUUID();
|
||||
}
|
||||
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (value) => {
|
||||
const random = Math.floor(Math.random() * 16);
|
||||
const next = value === "x" ? random : (random & 0x3) | 0x8;
|
||||
return next.toString(16);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user