taipyReducers.ts 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  1. /*
  2. * Copyright 2021-2024 Avaiga Private Limited
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
  5. * the License. You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
  10. * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. * specific language governing permissions and limitations under the License.
  12. */
  13. import { Dispatch } from "react";
  14. import { PaletteMode } from "@mui/material";
  15. import { createTheme, Theme } from "@mui/material/styles";
  16. import { io, Socket } from "socket.io-client";
  17. import merge from "lodash/merge";
  18. import { TAIPY_CLIENT_ID, WsMessage, sendWsMessage } from "./wsUtils";
  19. import { getBaseURL, TIMEZONE_CLIENT } from "../utils";
  20. import { parseData } from "../utils/dataFormat";
  21. import { MenuProps } from "../utils/lov";
  22. import { FilterDesc } from "../components/Taipy/TableFilter";
  23. import { stylekitModeThemes, stylekitTheme } from "../themes/stylekit";
  24. import { getLocalStorageValue, storeClientId, IdMessage } from "./utils";
  25. enum Types {
  26. SocketConnected = "SOCKET_CONNECTED",
  27. Update = "UPDATE",
  28. MultipleUpdate = "MULTIPLE_UPDATE",
  29. SendUpdate = "SEND_UPDATE_ACTION",
  30. Action = "SEND_ACTION_ACTION",
  31. RequestDataUpdate = "REQUEST_DATA_UPDATE",
  32. RequestUpdate = "REQUEST_UPDATE",
  33. SetLocations = "SET_LOCATIONS",
  34. SetTheme = "SET_THEME",
  35. SetTimeZone = "SET_TIMEZONE",
  36. SetAlert = "SET_ALERT",
  37. DeleteAlert = "DELETE_ALERT",
  38. SetBlock = "SET_BLOCK",
  39. Navigate = "NAVIGATE",
  40. ClientId = "CLIENT_ID",
  41. MultipleMessages = "MULTIPLE_MESSAGES",
  42. SetMenu = "SET_MENU",
  43. DownloadFile = "DOWNLOAD_FILE",
  44. Partial = "PARTIAL",
  45. Acknowledgement = "ACKNOWLEDGEMENT",
  46. }
  47. /**
  48. * The state of the underlying Taipy application.
  49. */
  50. export interface TaipyState {
  51. socket?: Socket;
  52. isSocketConnected?: boolean;
  53. data: Record<string, unknown>;
  54. theme: Theme;
  55. locations: Record<string, string>;
  56. timeZone?: string;
  57. dateFormat?: string;
  58. dateTimeFormat?: string;
  59. numberFormat?: string;
  60. alerts: AlertMessage[];
  61. block?: BlockMessage;
  62. navigateTo?: string;
  63. navigateParams?: Record<string, string>;
  64. navigateTab?: string;
  65. navigateForce?: boolean;
  66. id: string;
  67. menu: MenuProps;
  68. download?: FileDownloadProps;
  69. ackList: string[];
  70. }
  71. /**
  72. * Application actions as used by the application reducer.
  73. */
  74. export interface TaipyBaseAction {
  75. type: Types;
  76. }
  77. interface NamePayload {
  78. name: string;
  79. payload: Record<string, unknown>;
  80. }
  81. export interface AlertMessage {
  82. atype: string;
  83. message: string;
  84. system: boolean;
  85. duration: number;
  86. }
  87. interface TaipyAction extends NamePayload, TaipyBaseAction {
  88. propagate?: boolean;
  89. context?: string;
  90. }
  91. interface TaipyMultipleAction extends TaipyBaseAction {
  92. payload: NamePayload[];
  93. }
  94. interface TaipyMultipleMessageAction extends TaipyBaseAction {
  95. actions: TaipyBaseAction[];
  96. }
  97. interface TaipyAlertAction extends TaipyBaseAction, AlertMessage {}
  98. export const BLOCK_CLOSE = { action: "", message: "", close: true, noCancel: false } as BlockMessage;
  99. export interface BlockMessage {
  100. action: string;
  101. noCancel: boolean;
  102. close: boolean;
  103. message: string;
  104. }
  105. interface TaipyBlockAction extends TaipyBaseAction, BlockMessage {}
  106. interface NavigateMessage {
  107. to?: string;
  108. params?: Record<string, string>;
  109. tab?: string;
  110. force?: boolean;
  111. }
  112. interface TaipyNavigateAction extends TaipyBaseAction, NavigateMessage {}
  113. export interface FileDownloadProps {
  114. content?: string;
  115. name?: string;
  116. onAction?: string;
  117. }
  118. interface TaipyIdAction extends TaipyBaseAction, IdMessage {}
  119. interface TaipyAckAction extends TaipyBaseAction, IdMessage {}
  120. interface TaipyDownloadAction extends TaipyBaseAction, FileDownloadProps {}
  121. interface TaipySetMenuAction extends TaipyBaseAction {
  122. menu: MenuProps;
  123. }
  124. interface TaipyPartialAction extends TaipyBaseAction {
  125. name: string;
  126. create: boolean;
  127. }
  128. export interface FormatConfig {
  129. timeZone: string;
  130. forceTZ: boolean;
  131. date: string;
  132. dateTime: string;
  133. number: string;
  134. }
  135. const getUserTheme = (mode: PaletteMode) => {
  136. const tkTheme = (window.taipyConfig?.stylekit && stylekitTheme) || {};
  137. const tkModeTheme = (window.taipyConfig?.stylekit && stylekitModeThemes[mode]) || {};
  138. const userTheme = window.taipyConfig?.themes?.base || {};
  139. const modeTheme = (window.taipyConfig?.themes && window.taipyConfig.themes[mode]) || {};
  140. return createTheme(
  141. merge(tkTheme, tkModeTheme, userTheme, modeTheme, {
  142. palette: {
  143. mode: mode,
  144. },
  145. components: {
  146. MuiUseMediaQuery: {
  147. defaultProps: {
  148. noSsr: true,
  149. },
  150. },
  151. },
  152. })
  153. );
  154. };
  155. const themes = {
  156. light: getUserTheme("light"),
  157. dark: getUserTheme("dark"),
  158. };
  159. export const INITIAL_STATE: TaipyState = {
  160. data: {},
  161. theme: window.taipyConfig?.darkMode ? themes.dark : themes.light,
  162. locations: {},
  163. timeZone: window.taipyConfig?.timeZone
  164. ? window.taipyConfig.timeZone === "client"
  165. ? TIMEZONE_CLIENT
  166. : window.taipyConfig.timeZone
  167. : undefined,
  168. id: getLocalStorageValue(TAIPY_CLIENT_ID, ""),
  169. menu: {},
  170. ackList: [],
  171. alerts: [],
  172. };
  173. export const taipyInitialize = (initialState: TaipyState): TaipyState => ({
  174. ...initialState,
  175. isSocketConnected: false,
  176. socket: io("/", { autoConnect: false, path: `${getBaseURL()}socket.io` }),
  177. });
  178. const messageToAction = (message: WsMessage) => {
  179. if (message.type) {
  180. if (message.type === "MU" && Array.isArray(message.payload)) {
  181. return createMultipleUpdateAction(message.payload as NamePayload[]);
  182. } else if (message.type === "U") {
  183. return createUpdateAction(message as unknown as NamePayload);
  184. } else if (message.type === "AL") {
  185. return createAlertAction(message as unknown as AlertMessage);
  186. } else if (message.type === "BL") {
  187. return createBlockAction(message as unknown as BlockMessage);
  188. } else if (message.type === "NA") {
  189. return createNavigateAction(
  190. (message as unknown as NavigateMessage).to,
  191. (message as unknown as NavigateMessage).params,
  192. (message as unknown as NavigateMessage).tab,
  193. (message as unknown as NavigateMessage).force
  194. );
  195. } else if (message.type === "ID") {
  196. return createIdAction((message as unknown as IdMessage).id);
  197. } else if (message.type === "DF") {
  198. return createDownloadAction(message as unknown as FileDownloadProps);
  199. } else if (message.type === "PR") {
  200. return createPartialAction((message as unknown as Record<string, string>).name, true);
  201. } else if (message.type === "ACK") {
  202. return createAckAction((message as unknown as IdMessage).id);
  203. }
  204. }
  205. return {} as TaipyBaseAction;
  206. };
  207. const getWsMessageListener = (dispatch: Dispatch<TaipyBaseAction>) => {
  208. const dispatchWsMessage = (message: WsMessage) => {
  209. if (message.type === "MU" && Array.isArray(message.payload)) {
  210. const payloads = message.payload as NamePayload[];
  211. Promise.all(payloads.map((pl) => parseData(pl.payload.value as Record<string, unknown>)))
  212. .then((vals) => {
  213. vals.forEach((val, idx) => (payloads[idx].payload.value = val));
  214. dispatch(messageToAction(message));
  215. })
  216. .catch(console.warn);
  217. return;
  218. } else if (message.type === "MS" && Array.isArray(message.payload)) {
  219. (message.payload as WsMessage[]).forEach((msg) => dispatchWsMessage(msg));
  220. return;
  221. }
  222. dispatch(messageToAction(message));
  223. };
  224. return dispatchWsMessage;
  225. };
  226. export const initializeWebSocket = (socket: Socket | undefined, dispatch: Dispatch<TaipyBaseAction>): void => {
  227. if (socket) {
  228. // Websocket confirm successful initialization
  229. socket.on("connect", () => {
  230. const id = getLocalStorageValue(TAIPY_CLIENT_ID, "");
  231. sendWsMessage(socket, "ID", TAIPY_CLIENT_ID, id, id, undefined, false, () => {
  232. dispatch({ type: Types.SocketConnected });
  233. });
  234. });
  235. // try to reconnect on connect_error
  236. socket.on("connect_error", () => {
  237. setTimeout(() => {
  238. socket.connect();
  239. }, 500);
  240. });
  241. // try to reconnect on server disconnection
  242. socket.on("disconnect", (reason) => {
  243. if (reason === "io server disconnect") {
  244. socket.connect();
  245. }
  246. });
  247. // handle message data from backend
  248. socket.on("message", getWsMessageListener(dispatch));
  249. // only now does the socket tries to open/connect
  250. socket.connect();
  251. }
  252. };
  253. const addRows = (previousRows: Record<string, unknown>[], newRows: Record<string, unknown>[], start: number) =>
  254. newRows.reduce((arr, row) => {
  255. arr[start++] = row;
  256. return arr;
  257. }, previousRows.concat([]));
  258. const storeBlockUi = (block?: BlockMessage) => () => {
  259. if (localStorage) {
  260. if (block) {
  261. document.visibilityState !== "visible" && localStorage.setItem("TaipyBlockUi", JSON.stringify(block));
  262. } else {
  263. localStorage.removeItem("TaipyBlockUi");
  264. }
  265. }
  266. };
  267. export const retreiveBlockUi = (): BlockMessage => {
  268. if (localStorage) {
  269. const val = localStorage.getItem("TaipyBlockUi");
  270. if (val) {
  271. try {
  272. return JSON.parse(val);
  273. } catch {
  274. // too bad
  275. }
  276. }
  277. }
  278. return {} as BlockMessage;
  279. };
  280. export const taipyReducer = (state: TaipyState, baseAction: TaipyBaseAction): TaipyState => {
  281. const action = baseAction as TaipyAction;
  282. let ackId = "";
  283. switch (action.type) {
  284. case Types.SocketConnected:
  285. return !!state.isSocketConnected ? state : { ...state, isSocketConnected: true };
  286. case Types.Update:
  287. const newValue = action.payload.value as Record<string, unknown>;
  288. const oldValue = (state.data[action.name] as Record<string, unknown>) || {};
  289. if (typeof action.payload.infinite === "boolean" && action.payload.infinite) {
  290. const start = newValue.start;
  291. if (typeof start === "number") {
  292. const rows = ((oldValue[action.payload.pagekey as string] &&
  293. (oldValue[action.payload.pagekey as string] as Record<string, unknown>).data) ||
  294. []) as Record<string, unknown>[];
  295. newValue.data = addRows(rows, newValue.data as Record<string, unknown>[], start);
  296. }
  297. }
  298. return {
  299. ...state,
  300. data: {
  301. ...state.data,
  302. [action.name]: action.payload.pagekey
  303. ? { ...oldValue, [action.payload.pagekey as string]: newValue }
  304. : newValue,
  305. },
  306. };
  307. case Types.SetLocations:
  308. return { ...state, locations: action.payload.value as Record<string, string> };
  309. case Types.SetAlert:
  310. const alertAction = action as unknown as TaipyAlertAction;
  311. return {
  312. ...state,
  313. alerts: [
  314. ...state.alerts,
  315. {
  316. atype: alertAction.atype,
  317. message: alertAction.message,
  318. system: alertAction.system,
  319. duration: alertAction.duration,
  320. },
  321. ],
  322. };
  323. case Types.DeleteAlert:
  324. if (state.alerts.length) {
  325. return { ...state, alerts: state.alerts.filter((_, i) => i) };
  326. }
  327. return state;
  328. case Types.SetBlock:
  329. const blockAction = action as unknown as TaipyBlockAction;
  330. if (blockAction.close) {
  331. storeBlockUi()();
  332. delete state.block;
  333. return { ...state };
  334. } else {
  335. document.onvisibilitychange = storeBlockUi(blockAction as BlockMessage);
  336. return {
  337. ...state,
  338. block: {
  339. noCancel: blockAction.noCancel,
  340. action: blockAction.action,
  341. close: false,
  342. message: blockAction.message,
  343. },
  344. };
  345. }
  346. case Types.Navigate:
  347. return {
  348. ...state,
  349. navigateTo: (action as unknown as TaipyNavigateAction).to,
  350. navigateParams: (action as unknown as TaipyNavigateAction).params,
  351. navigateTab: (action as unknown as TaipyNavigateAction).tab,
  352. navigateForce: (action as unknown as TaipyNavigateAction).force,
  353. };
  354. case Types.ClientId:
  355. const id = (action as unknown as TaipyIdAction).id;
  356. storeClientId(id);
  357. return { ...state, id: id };
  358. case Types.Acknowledgement:
  359. const ackList = state.ackList.filter((v) => v !== (action as unknown as TaipyAckAction).id);
  360. return ackList.length < state.ackList.length ? { ...state, ackList } : state;
  361. case Types.SetTheme: {
  362. let mode = action.payload.value as PaletteMode;
  363. if (action.payload.fromBackend) {
  364. mode = getLocalStorageValue("theme", mode, ["light", "dark"]);
  365. }
  366. localStorage && localStorage.setItem("theme", mode);
  367. if (mode !== state.theme.palette.mode) {
  368. return {
  369. ...state,
  370. theme: themes[mode],
  371. };
  372. }
  373. return state;
  374. }
  375. case Types.SetTimeZone: {
  376. let timeZone = (action.payload.timeZone as string) || "client";
  377. if (!action.payload.fromBackend) {
  378. timeZone = getLocalStorageValue("timeZone", timeZone);
  379. }
  380. if (!timeZone || timeZone === "client") {
  381. timeZone = TIMEZONE_CLIENT;
  382. }
  383. localStorage && localStorage.setItem("timeZone", timeZone);
  384. if (timeZone !== state.timeZone) {
  385. return {
  386. ...state,
  387. timeZone: timeZone,
  388. };
  389. }
  390. return state;
  391. }
  392. case Types.SetMenu: {
  393. const mAction = baseAction as TaipySetMenuAction;
  394. return { ...state, menu: mAction.menu };
  395. }
  396. case Types.DownloadFile: {
  397. const dAction = baseAction as TaipyDownloadAction;
  398. if (dAction.content === undefined) {
  399. delete state.download;
  400. return { ...state };
  401. }
  402. return { ...state, download: { content: dAction.content, name: dAction.name, onAction: dAction.onAction } };
  403. }
  404. case Types.Partial: {
  405. const pAction = baseAction as TaipyPartialAction;
  406. const data = { ...state.data };
  407. if (pAction.create) {
  408. data[pAction.name] = true;
  409. } else {
  410. data[pAction.name] !== undefined && delete data[pAction.name];
  411. }
  412. return { ...state, data: data };
  413. }
  414. case Types.MultipleUpdate:
  415. const mAction = baseAction as TaipyMultipleAction;
  416. return mAction.payload.reduce((nState, pl) => taipyReducer(nState, { ...pl, type: Types.Update }), state);
  417. case Types.MultipleMessages:
  418. const msgAction = baseAction as TaipyMultipleMessageAction;
  419. return msgAction.actions.reduce((pState, act) => taipyReducer(pState, act), state);
  420. case Types.SendUpdate:
  421. ackId = sendWsMessage(
  422. state.socket,
  423. "U",
  424. action.name,
  425. action.payload,
  426. state.id,
  427. action.context,
  428. action.propagate
  429. );
  430. break;
  431. case Types.Action:
  432. ackId = sendWsMessage(state.socket, "A", action.name, action.payload, state.id, action.context);
  433. break;
  434. case Types.RequestDataUpdate:
  435. ackId = sendWsMessage(state.socket, "DU", action.name, action.payload, state.id, action.context);
  436. break;
  437. case Types.RequestUpdate:
  438. ackId = sendWsMessage(state.socket, "RU", action.name, action.payload, state.id, action.context);
  439. break;
  440. }
  441. if (ackId) return { ...state, ackList: [...state.ackList, ackId] };
  442. return state;
  443. };
  444. const createUpdateAction = (payload: NamePayload): TaipyAction => ({
  445. ...payload,
  446. type: Types.Update,
  447. });
  448. const createMultipleUpdateAction = (payload: NamePayload[]): TaipyMultipleAction => ({
  449. type: Types.MultipleUpdate,
  450. payload: payload,
  451. });
  452. /**
  453. * Create a *send update* `Action` that will be used to update `Context`.
  454. *
  455. * This action will update the variable *name* (if *propagate* is true) and trigger the
  456. * invocation of the `on_change` Python function on the backend.
  457. * @param name - The name of the variable holding the requested data
  458. * as received as a property.
  459. * @param value - The new value for the variable named *name*.
  460. * @param context - The execution context.
  461. * @param onChange - The name of the `on_change` Python function to
  462. * invoke on the backend (default is "on_change").
  463. * @param propagate - A flag indicating that the variable should be
  464. * automatically updated on the backend.
  465. * @param relName - The name of the related variable (for
  466. * example the lov when a lov value is updated).
  467. * @returns The action fed to the reducer.
  468. */
  469. export const createSendUpdateAction = (
  470. name = "",
  471. value: unknown,
  472. context: string | undefined,
  473. onChange?: string,
  474. propagate = true,
  475. relName?: string
  476. ): TaipyAction => ({
  477. type: Types.SendUpdate,
  478. name: name,
  479. context: context,
  480. propagate: propagate,
  481. payload: getPayload(value, onChange, relName),
  482. });
  483. const getPayload = (value: unknown, onChange?: string, relName?: string) => {
  484. const ret: Record<string, unknown> = { value: value };
  485. if (relName) {
  486. ret.relvar = relName;
  487. }
  488. if (onChange) {
  489. ret.on_change = onChange;
  490. }
  491. return ret;
  492. };
  493. /**
  494. * Create an *action* `Action` that will be used to update `Context`.
  495. *
  496. * This action will trigger the invocation of the `on_action` Python function on the backend,
  497. * providing all the parameters as a payload.
  498. * @param name - The name of the action function on the backend.
  499. * @param context - The execution context.
  500. * @param value - The value associated with the action. This can be an object or
  501. * any type of value.
  502. * @param args - Additional information associated to the action.
  503. * @returns The action fed to the reducer.
  504. */
  505. export const createSendActionNameAction = (
  506. name: string | undefined,
  507. context: string | undefined,
  508. value: unknown,
  509. ...args: unknown[]
  510. ): TaipyAction => ({
  511. type: Types.Action,
  512. name: name || "",
  513. context: context,
  514. payload:
  515. typeof value === "object" && !Array.isArray(value) && value !== null
  516. ? { ...(value as object), args: args }
  517. : { action: value, args: args },
  518. });
  519. export const createRequestChartUpdateAction = (
  520. name: string | undefined,
  521. id: string | undefined,
  522. context: string | undefined,
  523. columns: string[],
  524. pageKey: string,
  525. decimatorPayload: unknown | undefined
  526. ): TaipyAction =>
  527. createRequestDataUpdateAction(
  528. name,
  529. id,
  530. context,
  531. columns,
  532. pageKey,
  533. {
  534. decimatorPayload: decimatorPayload,
  535. },
  536. true
  537. );
  538. const ligtenPayload = (payload: Record<string, unknown>) => {
  539. return Object.keys(payload || {}).reduce((pv, key) => {
  540. if (payload[key] !== undefined) {
  541. pv[key] = payload[key];
  542. }
  543. return pv;
  544. }, {} as typeof payload)
  545. }
  546. export const createRequestTableUpdateAction = (
  547. name: string | undefined,
  548. id: string | undefined,
  549. context: string | undefined,
  550. columns: string[],
  551. pageKey: string,
  552. start?: number,
  553. end?: number,
  554. orderBy?: string,
  555. sort?: string,
  556. aggregates?: string[],
  557. applies?: Record<string, unknown>,
  558. styles?: Record<string, string>,
  559. tooltips?: Record<string, string>,
  560. handleNan?: boolean,
  561. filters?: Array<FilterDesc>,
  562. compare?: string,
  563. compareDatas?: string,
  564. stateContext?: Record<string, unknown>
  565. ): TaipyAction =>
  566. createRequestDataUpdateAction(name, id, context, columns, pageKey, ligtenPayload({
  567. start: start,
  568. end: end,
  569. orderby: orderBy,
  570. sort: sort,
  571. aggregates: aggregates,
  572. applies: applies,
  573. styles: styles,
  574. tooltips: tooltips,
  575. handlenan: handleNan,
  576. filters: filters,
  577. compare: compare,
  578. compare_datas: compareDatas,
  579. state_context: stateContext,
  580. }));
  581. export const createRequestInfiniteTableUpdateAction = (
  582. name: string | undefined,
  583. id: string | undefined,
  584. context: string | undefined,
  585. columns: string[],
  586. pageKey: string,
  587. start?: number,
  588. end?: number,
  589. orderBy?: string,
  590. sort?: string,
  591. aggregates?: string[],
  592. applies?: Record<string, unknown>,
  593. styles?: Record<string, string>,
  594. tooltips?: Record<string, string>,
  595. handleNan?: boolean,
  596. filters?: Array<FilterDesc>,
  597. compare?: string,
  598. compareDatas?: string,
  599. stateContext?: Record<string, unknown>
  600. ): TaipyAction =>
  601. createRequestDataUpdateAction(name, id, context, columns, pageKey, ligtenPayload({
  602. infinite: true,
  603. start: start,
  604. end: end,
  605. orderby: orderBy,
  606. sort: sort,
  607. aggregates: aggregates,
  608. applies: applies,
  609. styles: styles,
  610. tooltips: tooltips,
  611. handlenan: handleNan,
  612. filters: filters,
  613. compare: compare,
  614. compare_datas: compareDatas,
  615. state_context: stateContext,
  616. }));
  617. /**
  618. * Create a *request data update* `Action` that will be used to update the `Context`.
  619. *
  620. * This action will provoke the invocation of the `get_data()` method of the backend
  621. * library. That invocation generates an update of the elements holding the data named
  622. * *name* on the front-end.
  623. * @param name - The name of the variable holding the requested data as received as
  624. * a property.
  625. * @param id - The identifier of the visual element.
  626. * @param context - The execution context.
  627. * @param columns - The list of the columns needed by the element that emitted this
  628. * action.
  629. * @param pageKey - The unique identifier of the data that will be received from
  630. * this action.
  631. * @param payload - The payload (specific to the type of component
  632. * ie table, chart...).
  633. * @param allData - The flag indicating if all the data is requested.
  634. * @param library - The name of the {@link extension} library.
  635. * @returns The action fed to the reducer.
  636. */
  637. export const createRequestDataUpdateAction = (
  638. name: string | undefined,
  639. id: string | undefined,
  640. context: string | undefined,
  641. columns: string[],
  642. pageKey: string,
  643. payload: Record<string, unknown>,
  644. allData = false,
  645. library?: string
  646. ): TaipyAction => {
  647. payload = payload || {};
  648. if (id !== undefined) {
  649. payload.id = id;
  650. }
  651. payload.columns = columns;
  652. payload.pagekey = pageKey;
  653. if (library !== undefined) {
  654. payload.library = library;
  655. }
  656. if (allData) {
  657. payload.alldata = true;
  658. }
  659. return {
  660. type: Types.RequestDataUpdate,
  661. name: name || "",
  662. context: context,
  663. payload: payload,
  664. };
  665. };
  666. /**
  667. * Create a *request update* `Action` that will be used to update the `Context`.
  668. *
  669. * This action will generate an update of the elements holding the variables named
  670. * *names* on the front-end.
  671. * @param id - The identifier of the visual element.
  672. * @param context - The execution context.
  673. * @param names - The names of the requested variables as received in updateVarName and/or updateVars properties.
  674. * @param forceRefresh - Should Taipy re-evaluate the variables or use the current values
  675. * @returns The action fed to the reducer.
  676. */
  677. export const createRequestUpdateAction = (
  678. id: string | undefined,
  679. context: string | undefined,
  680. names: string[],
  681. forceRefresh = false,
  682. stateContext?: Record<string, unknown>
  683. ): TaipyAction => ({
  684. type: Types.RequestUpdate,
  685. name: "",
  686. context: context,
  687. payload: ligtenPayload({
  688. id: id,
  689. names: names,
  690. refresh: forceRefresh,
  691. state_context: stateContext,
  692. }),
  693. });
  694. export const createSetLocationsAction = (locations: Record<string, string>): TaipyAction => ({
  695. type: Types.SetLocations,
  696. name: "locations",
  697. payload: { value: locations },
  698. });
  699. export const createThemeAction = (dark: boolean, fromBackend = false): TaipyAction => ({
  700. type: Types.SetTheme,
  701. name: "theme",
  702. payload: { value: dark ? "dark" : "light", fromBackend: fromBackend },
  703. });
  704. export const createTimeZoneAction = (timeZone: string, fromBackend = false): TaipyAction => ({
  705. type: Types.SetTimeZone,
  706. name: "timeZone",
  707. payload: { timeZone: timeZone, fromBackend: fromBackend },
  708. });
  709. const getAlertType = (aType: string) => {
  710. aType = aType.trim();
  711. if (aType) {
  712. aType = aType.charAt(0).toLowerCase();
  713. switch (aType) {
  714. case "e":
  715. return "error";
  716. case "w":
  717. return "warning";
  718. case "s":
  719. return "success";
  720. default:
  721. return "info";
  722. }
  723. }
  724. return aType;
  725. };
  726. export const createAlertAction = (alert: AlertMessage): TaipyAlertAction => ({
  727. type: Types.SetAlert,
  728. atype: getAlertType(alert.atype),
  729. message: alert.message,
  730. system: alert.system,
  731. duration: alert.duration,
  732. });
  733. export const createDeleteAlertAction = (): TaipyBaseAction => ({
  734. type: Types.DeleteAlert,
  735. });
  736. export const createBlockAction = (block: BlockMessage): TaipyBlockAction => ({
  737. type: Types.SetBlock,
  738. noCancel: block.noCancel,
  739. action: block.action || "",
  740. close: !!block.close,
  741. message: block.message,
  742. });
  743. export const createNavigateAction = (
  744. to?: string,
  745. params?: Record<string, string>,
  746. tab?: string,
  747. force?: boolean
  748. ): TaipyNavigateAction => ({
  749. type: Types.Navigate,
  750. to,
  751. params,
  752. tab,
  753. force,
  754. });
  755. export const createIdAction = (id: string): TaipyIdAction => ({
  756. type: Types.ClientId,
  757. id,
  758. });
  759. export const createAckAction = (id: string): TaipyAckAction => ({
  760. type: Types.Acknowledgement,
  761. id,
  762. });
  763. export const createDownloadAction = (dMessage?: FileDownloadProps): TaipyDownloadAction => ({
  764. type: Types.DownloadFile,
  765. content: dMessage?.content,
  766. name: dMessage?.name,
  767. onAction: dMessage?.onAction,
  768. });
  769. export const createSetMenuAction = (menu: MenuProps): TaipySetMenuAction => ({
  770. type: Types.SetMenu,
  771. menu,
  772. });
  773. export const createPartialAction = (name: string, create: boolean): TaipyPartialAction => ({
  774. type: Types.Partial,
  775. name,
  776. create,
  777. });