Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | 4845x 72x 79x 4x 4x 4x 330x 7914x 7914x 7914x 7392x 3528x 2224x 1260x 44x 522x 76x 8244x 7100x 1143x 1x 1x 1x | import type { FlowsheetAccess, FlowsheetRead } from "@/api/apiStore.gen";
export type FlowsheetWithAccess = Omit<FlowsheetRead, "access"> & {
access?: FlowsheetAccess;
};
export type SharedUserAccess = {
email: string;
read_only: boolean;
};
export function getFlowsheetAccess(
project?: FlowsheetRead,
): FlowsheetAccess | undefined {
return (project as FlowsheetWithAccess | undefined)?.access;
}
export function normalizeSharedUsers(users: unknown): SharedUserAccess[] {
if (!Array.isArray(users)) {
return [];
}
return users.flatMap((user) => {
if (!user || typeof user !== "object") {
return [];
}
const email = "email" in user ? user.email : undefined;
const readOnly = "read_only" in user ? user.read_only : undefined;
if (typeof email !== "string") {
return [];
}
return [{ email, read_only: Boolean(readOnly) }];
});
}
export function getCachedFlowsheetAccess(
state: Record<string, any> | undefined,
reducerPath: string,
flowsheetId: number | null,
): FlowsheetAccess | undefined {
if (!state || !reducerPath || !flowsheetId) {
return undefined;
}
const queries = state[reducerPath]?.queries;
if (!queries || typeof queries !== "object") {
return undefined;
}
const directQueryKey = `coreFlowsheetsRetrieve({"id":"${flowsheetId}"})`;
const directData = queries[directQueryKey]?.data as
| FlowsheetWithAccess
| undefined;
if (directData?.access) {
return directData.access;
}
// Some screens only have the flowsheet cached via list-style queries, so fall
// back to scanning the RTK Query cache instead of requiring a dedicated detail
// fetch before the UI can decide whether to disable mutation controls.
for (const value of Object.values(queries)) {
const data = (value as { data?: unknown } | undefined)?.data;
if (!data || typeof data !== "object") {
continue;
}
if ((data as { id?: unknown }).id !== flowsheetId) {
continue;
}
const access = (data as FlowsheetWithAccess).access;
if (access) {
return access;
}
}
return undefined;
}
const READ_ONLY_ALLOWED_MUTATION_PATH_SEGMENTS = [
"/api/flowsheet/copy",
"/download",
"/download_",
"/export",
];
export function isReadOnlyMutationBlocked({
method,
pathname,
access,
}: {
method: string;
pathname: string;
access?: FlowsheetAccess;
}) {
const normalizedMethod = method.toUpperCase();
if (
normalizedMethod === "GET" ||
normalizedMethod === "HEAD" ||
normalizedMethod === "OPTIONS"
) {
return false;
}
if (!access?.read_only) {
return false;
}
const normalizedPath = pathname.toLowerCase();
// Copy / download / export are intentionally still allowed for read-only
// shares, because they do not mutate the shared source flowsheet itself.
return !READ_ONLY_ALLOWED_MUTATION_PATH_SEGMENTS.some((segment) =>
normalizedPath.includes(segment),
);
}
|