From 16281062eb3a67d7bbd91ee167f419e7bd7e64bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc=20Gu=CC=88ell=20Segarra?= Date: Wed, 15 Jan 2025 13:26:31 +0100 Subject: [PATCH] fix: improvements in context/domain raw handling in opening shared urls https://github.com/gisce/webclient/issues/1612 --- src/context/ContentRootContext.tsx | 15 ++++- src/context/TabManagerContext.tsx | 30 +-------- src/helpers/shareUrlHelper.ts | 66 +++++++++++++++++++- src/types/index.ts | 9 +++ src/views/RootView.tsx | 97 +++++++++++++++++++++++++++--- 5 files changed, 177 insertions(+), 40 deletions(-) diff --git a/src/context/ContentRootContext.tsx b/src/context/ContentRootContext.tsx index 985059f45..7ca2ce888 100644 --- a/src/context/ContentRootContext.tsx +++ b/src/context/ContentRootContext.tsx @@ -1,4 +1,9 @@ -import { FormView, GenerateReportOptions, ViewType } from "@/types"; +import { + FormView, + GenerateReportOptions, + ViewType, + ActionRawData, +} from "@/types"; import React, { useContext, useRef, @@ -217,6 +222,7 @@ const ContentRootProvider = ( )[0]; } + const rawContext = actionData.context; const responseContext = typeof actionData?.context === "string" ? parseContext({ @@ -233,6 +239,7 @@ const ContentRootProvider = ( let parsedDomain = []; + const rawDomain = actionData.domain; if (actionData.domain) { parsedDomain = await ConnectionProvider.getHandler().evalDomain({ domain: actionData.domain, @@ -283,6 +290,12 @@ const ContentRootProvider = ( initialView, action_id: actionData.id, action_type: actionData.type, + actionRawData: { + context: rawContext, + domain: rawDomain, + fields, + values, + }, }); return { closeParent: true }; diff --git a/src/context/TabManagerContext.tsx b/src/context/TabManagerContext.tsx index d0286b200..8cd0065b8 100644 --- a/src/context/TabManagerContext.tsx +++ b/src/context/TabManagerContext.tsx @@ -1,35 +1,9 @@ -import { InitialViewData, Tab, View, ViewType } from "@/types"; +import { ActionInfo, InitialViewData, Tab, View, ViewType } from "@/types"; import { ShortcutApi } from "@/ui/FavouriteButton"; import React, { useState, useContext, useMemo } from "react"; export type TabManagerContextType = { - openAction: ({ - domain, - context, - model, - views, - title, - target, - initialView, - action_id, - action_type, - res_id, - values, - forced_values, - }: { - domain: any; - context: any; - model: string; - views: any[]; - title: string; - target: string; - initialView: InitialViewData; - action_id: number; - action_type: string; - res_id?: number | boolean; - values?: any; - forced_values?: any; - }) => void; + openAction: (action: ActionInfo) => void; openRelate: ({ relateData, fields, diff --git a/src/helpers/shareUrlHelper.ts b/src/helpers/shareUrlHelper.ts index d8d8b2a15..79ace7156 100644 --- a/src/helpers/shareUrlHelper.ts +++ b/src/helpers/shareUrlHelper.ts @@ -1,14 +1,20 @@ -import { ActionInfo } from "@/types"; +import { ActionInfo, ActionRawData } from "@/types"; export const createShareOpenUrl = (action: ActionInfo) => { const url = new URL(window.location.href); url.pathname += url.pathname.endsWith("/") ? "open" : "/open"; // Parameters to exclude from the URL - const ignoredParams = ["target"]; + const ignoredParams = ["target", "context", "domain"]; + + const finalAction = { + ...action, + actionRawData: + action?.actionRawData && filterActionRawData(action.actionRawData), + }; // Add all non-null properties from action to URL - Object.entries(action).forEach(([key, value]) => { + Object.entries(finalAction).forEach(([key, value]) => { if ( !ignoredParams.includes(key) && value && @@ -27,3 +33,57 @@ const convertToString = (value: any): string => { } return value.toString(); }; + +const filterActionRawData = (actionRawData: ActionRawData) => { + const { context, domain, values, fields } = actionRawData; + + const filteredData: Partial = {}; + + // Handle context + if (context) { + if (typeof context === "string") { + // Don't include if it's a string containing empty object + if (context !== "{}") { + filteredData.context = context; + } + } else { + // Include if it's an object with properties + if (typeof context === "object" && Object.keys(context).length > 0) { + filteredData.context = context; + } + } + } + + // Handle domain + if (domain) { + if (Array.isArray(domain)) { + if (domain.length > 0) { + filteredData.domain = domain; + } + } else if (domain !== "false") { + filteredData.domain = domain; + } + } + + // Include values and fields only if they are non-empty objects + if ( + (filteredData.domain || filteredData.context) && + values && + typeof values === "object" && + Object.keys(values).length > 0 + ) { + const { arch, ...restValues } = values; // ignore arch if exists + filteredData.values = restValues; + } + if ( + (filteredData.domain || filteredData.context) && + fields && + typeof fields === "object" && + Object.keys(fields).length > 0 + ) { + filteredData.fields = fields; + } + + // Return undefined if no properties were added to filteredData + return Object.keys(filteredData).length > 0 ? filteredData : undefined; +}; diff --git a/src/types/index.ts b/src/types/index.ts index c6ecba0ff..a50c95dd5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -403,6 +403,7 @@ type ActionInfo = { forced_values?: any; treeExpandable?: boolean; limit?: number; + actionRawData?: ActionRawData; }; type Tab = { @@ -413,6 +414,13 @@ type Tab = { action: ActionInfo | null; }; +type ActionRawData = { + domain?: any; + context?: any; + values?: any; + fields?: any; +}; + export type { Strings, SearchFields, @@ -450,4 +458,5 @@ export type { ViewTuple, ActionInfo, Tab, + ActionRawData, }; diff --git a/src/views/RootView.tsx b/src/views/RootView.tsx index 719496cd0..a342f1ddf 100644 --- a/src/views/RootView.tsx +++ b/src/views/RootView.tsx @@ -43,7 +43,7 @@ function RootView(props: RootViewProps, ref: any) { useImperativeHandle(ref, () => ({ retrieveAndOpenAction, - openAction, + handleOpenUrl, openShortcut, })); @@ -73,6 +73,62 @@ function RootView(props: RootViewProps, ref: any) { tabViewsCloseFunctions.current.set(tabKey, canWeClose); } + async function handleOpenUrl(action: ActionInfo) { + const { actionRawData } = action; + + let parsedContext; + if ( + actionRawData?.context && + typeof actionRawData.context === "object" && + actionRawData.context !== null + ) { + parsedContext = actionRawData; + } else { + parsedContext = + actionRawData && + parseContext({ + context: actionRawData.context, + fields: actionRawData.fields || {}, + values: { ...globalValues, ...(actionRawData.values || {}) }, + }); + } + + const parsedDomain = await (async () => { + try { + if ( + actionRawData?.domain && + Array.isArray(actionRawData.domain) && + actionRawData.domain.length > 0 + ) { + return actionRawData.domain; + } else if (actionRawData && !Array.isArray(actionRawData.domain)) { + return await ConnectionProvider.getHandler().evalDomain({ + domain: actionRawData.domain, + values: actionRawData.fields + ? transformPlainMany2Ones({ + fields: actionRawData.fields, + values: { ...(actionRawData.values || {}), ...globalValues }, + }) + : {}, + context: { ...rootContext, ...parsedContext }, + fields: actionRawData.fields, + }); + } + return []; + } catch (err) { + console.error(err); + return []; + } + })(); + + openAction({ + ...action, + context: { ...rootContext, ...parsedContext }, + domain: parsedDomain, + actionRawData, + }); + } + async function retrieveAndOpenAction({ action, values, @@ -101,17 +157,19 @@ function RootView(props: RootViewProps, ref: any) { const [action_type, action_id_string] = action.split(","); const action_id = parseInt(action_id_string); + const rawContext = dataForAction.context; const parsedContext = parseContext({ - context: dataForAction.context, + context: rawContext, values: globalValues, - fields: {}, }); + const rawDomain = dataForAction.domain; + const parsedDomain = await (async () => { try { - if (dataForAction.domain) { + if (rawDomain) { return await ConnectionProvider.getHandler().evalDomain({ - domain: dataForAction.domain, + domain: rawDomain, values: globalValues, context: { ...rootContext, ...parsedContext }, }); @@ -183,6 +241,10 @@ function RootView(props: RootViewProps, ref: any) { res_id, treeExpandable, limit, + actionRawData: { + context: rawContext, + domain: rawDomain, + }, }); } @@ -242,13 +304,15 @@ function RootView(props: RootViewProps, ref: any) { const [id, type] = views[0]; const initialView = { id, type }; + const rawContext = context; const parsedContext = parseContext({ - context, + context: rawContext, values: { ...values, ...globalValues }, - fields, }); + const rawDomain = domain; + const parsedDomain = domain ? await ConnectionProvider.getHandler().evalDomain({ domain, @@ -272,6 +336,12 @@ function RootView(props: RootViewProps, ref: any) { action_id, action_type, limit, + actionRawData: { + context: rawContext, + domain: rawDomain, + fields, + values, + }, }); } @@ -291,14 +361,20 @@ function RootView(props: RootViewProps, ref: any) { action, context: rootContext, }); + + const rawContext = dataForAction.context; + const parsedContext = parseContext({ - context: dataForAction.context, + context: rawContext, values: { ...globalValues, ...values }, fields: {}, }); let parsedDomain = []; + const rawDomain = + domain && domain.length > 0 ? domain : dataForAction.domain; + if (domain?.length > 0) { parsedDomain = domain; } else if (dataForAction.domain) { @@ -375,6 +451,11 @@ function RootView(props: RootViewProps, ref: any) { overrideUnsettedLimit && (limit === 0 || limit === false) ? DEFAULT_SEARCH_LIMIT : limit, + actionRawData: { + context: rawContext, + domain: rawDomain, + values, + }, }); }