172 lines
5.7 KiB
TypeScript
172 lines
5.7 KiB
TypeScript
import type {
|
|
ActiveObjectReference,
|
|
AuthInfo,
|
|
EffectiveViewProjection,
|
|
QuickAccessLaunchContext,
|
|
QuickAccessResult
|
|
} from "../types";
|
|
import type { TemporalDataSelection } from "./temporal";
|
|
|
|
|
|
export const QUICK_ACCESS_LAUNCH_CONTEXT_VERSION = "2" as const;
|
|
export const QUICK_ACCESS_REFERENCE_CONTRACT_VERSION = "1" as const;
|
|
export const QUICK_ACCESS_RESULT_CONTRACT_VERSION = "1" as const;
|
|
export const QUICK_ACCESS_LAUNCH_STATE_KEY = "govoplanQuickAccessLaunch";
|
|
export const QUICK_ACCESS_RESULT_EVENT = "govoplan:quick-access-result";
|
|
|
|
export type QuickAccessLaunchContextInput = {
|
|
pathname: string;
|
|
search?: string;
|
|
hash?: string;
|
|
historyIndex?: number | null;
|
|
auth: AuthInfo;
|
|
activeObject?: ActiveObjectReference | null;
|
|
temporalContext: TemporalDataSelection;
|
|
viewContext?: EffectiveViewProjection | null;
|
|
};
|
|
|
|
export function createQuickAccessLaunchContext({
|
|
pathname,
|
|
search = "",
|
|
hash = "",
|
|
historyIndex = null,
|
|
auth,
|
|
activeObject = null,
|
|
temporalContext,
|
|
viewContext = null
|
|
}: QuickAccessLaunchContextInput): QuickAccessLaunchContext {
|
|
const tenant = auth.active_tenant ?? auth.tenant;
|
|
const principal = auth.principal;
|
|
return {
|
|
contractVersion: QUICK_ACCESS_LAUNCH_CONTEXT_VERSION,
|
|
referenceContractVersion: QUICK_ACCESS_REFERENCE_CONTRACT_VERSION,
|
|
origin: {
|
|
pathname: normalizePathname(pathname),
|
|
search: normalizeSearch(search),
|
|
hash: normalizeHash(hash),
|
|
historyIndex: Number.isInteger(historyIndex) ? historyIndex ?? null : null
|
|
},
|
|
tenantId: tenant.id,
|
|
accountId: auth.user.account_id,
|
|
activeObject: activeObject?.tenantId === tenant.id ? activeObject : null,
|
|
actingContext: principal?.acting_assignment_id || principal?.acting_for_account_id
|
|
? {
|
|
assignmentId: principal.acting_assignment_id ?? null,
|
|
actingForAccountId: principal.acting_for_account_id ?? null
|
|
}
|
|
: null,
|
|
temporalContext: {
|
|
validityMode: temporalContext.validityMode,
|
|
validAt: temporalContext.validAt ?? null,
|
|
recordedAt: temporalContext.recordedAt ?? null
|
|
},
|
|
viewContext: viewContext?.activeViewId
|
|
? {
|
|
viewId: viewContext.activeViewId,
|
|
revisionId: viewContext.activeRevisionId,
|
|
name: viewContext.activeViewName,
|
|
recommendedToolIds: viewContext.presentation?.quickAccessRecommendedToolIds ?? [],
|
|
focusedToolIds: viewContext.presentation?.quickAccessFocusedToolIds ?? []
|
|
}
|
|
: null
|
|
};
|
|
}
|
|
|
|
export function quickAccessLaunchState(
|
|
context: QuickAccessLaunchContext
|
|
): Record<string, QuickAccessLaunchContext> {
|
|
return { [QUICK_ACCESS_LAUNCH_STATE_KEY]: context };
|
|
}
|
|
|
|
export function quickAccessLaunchContextFromState(
|
|
value: unknown
|
|
): QuickAccessLaunchContext | null {
|
|
if (!isRecord(value)) return null;
|
|
const candidate = value[QUICK_ACCESS_LAUNCH_STATE_KEY];
|
|
if (
|
|
!isRecord(candidate)
|
|
|| candidate.contractVersion !== QUICK_ACCESS_LAUNCH_CONTEXT_VERSION
|
|
|| candidate.referenceContractVersion !== QUICK_ACCESS_REFERENCE_CONTRACT_VERSION
|
|
) {
|
|
return null;
|
|
}
|
|
if (!isRecord(candidate.origin) || typeof candidate.origin.pathname !== "string") {
|
|
return null;
|
|
}
|
|
if (
|
|
typeof candidate.tenantId !== "string"
|
|
|| typeof candidate.accountId !== "string"
|
|
|| !candidate.origin.pathname.startsWith("/")
|
|
) {
|
|
return null;
|
|
}
|
|
return candidate as QuickAccessLaunchContext;
|
|
}
|
|
|
|
export function quickAccessReturnPath(context: QuickAccessLaunchContext): string {
|
|
return `${context.origin.pathname}${context.origin.search}${context.origin.hash}`;
|
|
}
|
|
|
|
export function dispatchQuickAccessResult(
|
|
toolId: string,
|
|
launchContext: QuickAccessLaunchContext,
|
|
result: QuickAccessResult,
|
|
returnedReferenceKinds?: readonly string[]
|
|
): boolean {
|
|
if (
|
|
typeof window === "undefined"
|
|
|| !isQuickAccessResultAllowed(result, launchContext, returnedReferenceKinds)
|
|
) return false;
|
|
window.dispatchEvent(new CustomEvent(QUICK_ACCESS_RESULT_EVENT, {
|
|
detail: { toolId, launchContext, result }
|
|
}));
|
|
return true;
|
|
}
|
|
|
|
export function isQuickAccessResultAllowed(
|
|
result: QuickAccessResult,
|
|
launchContext: QuickAccessLaunchContext,
|
|
returnedReferenceKinds?: readonly string[]
|
|
): boolean {
|
|
if (result.contractVersion !== QUICK_ACCESS_RESULT_CONTRACT_VERSION) return false;
|
|
if (result.outcome === "cancelled") {
|
|
return result.action === undefined
|
|
&& result.reference === undefined
|
|
&& (result.reason === undefined
|
|
|| (["user", "dismissed", "unavailable", "failed"] as const).includes(result.reason));
|
|
}
|
|
if (result.outcome !== "completed") return false;
|
|
if (!(["created", "selected", "updated", "completed"] as const).includes(result.action)) return false;
|
|
if (!result.reference) return true;
|
|
if (
|
|
result.reference.tenantId !== launchContext.tenantId
|
|
|| !result.reference.ownerModule
|
|
|| !result.reference.kind
|
|
|| !result.reference.objectId
|
|
|| (result.reference.path !== undefined
|
|
&& result.reference.path !== null
|
|
&& (!result.reference.path.startsWith("/") || result.reference.path.startsWith("//")))
|
|
) return false;
|
|
if (returnedReferenceKinds) {
|
|
const returnedKind = `${result.reference.ownerModule}.${result.reference.kind}`;
|
|
if (!returnedReferenceKinds.includes(returnedKind)) return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
function normalizePathname(value: string): string {
|
|
return value.startsWith("/") ? value : "/";
|
|
}
|
|
|
|
function normalizeSearch(value: string): string {
|
|
return !value ? "" : value.startsWith("?") ? value : "";
|
|
}
|
|
|
|
function normalizeHash(value: string): string {
|
|
return !value ? "" : value.startsWith("#") ? value : "";
|
|
}
|
|
|
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
}
|