diff --git a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/Error_handling_spec.js b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/Error_handling_spec.js index cc9082479e..7529e65c52 100644 --- a/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/Error_handling_spec.js +++ b/app/client/cypress/e2e/Regression/ClientSide/ActionExecution/Error_handling_spec.js @@ -30,7 +30,7 @@ describe("Test Create Api and Bind to Button widget", function () { cy.updateCodeInput($el, "{{Api1.run()}}"); }); - cy.PublishtheApp(); + _.deployMode.DeployApp(); cy.wait(3000); cy.get("span:contains('Submit')").closest("div").click(); @@ -54,7 +54,7 @@ describe("Test Create Api and Bind to Button widget", function () { cy.updateCodeInput($el, "{{Api1.run(() => {}, () => {})}}"); }); - cy.PublishtheApp(); + _.deployMode.DeployApp(); cy.wait(3000); cy.get("span:contains('Submit')").closest("div").click(); diff --git a/app/client/cypress/e2e/Regression/ClientSide/PublishedApps/PublishedModeToastToggle.ts b/app/client/cypress/e2e/Regression/ClientSide/PublishedApps/PublishedModeToastToggle.ts new file mode 100644 index 0000000000..653744aecf --- /dev/null +++ b/app/client/cypress/e2e/Regression/ClientSide/PublishedApps/PublishedModeToastToggle.ts @@ -0,0 +1,104 @@ +import * as _ from "../../../../support/Objects/ObjectsCore"; + +const SHOW_ALERT_WORKING_BUTTON = "Show alert working"; +const SHOW_ALERT_MSG = "Hello World!"; +const SHOW_ALERT_NOT_WORKING_BUTTON = "Show alert not working"; +const SHOW_ALERT_NOT_WORKING_MSG = "Correct_input2 is not defined"; +const RUN_JS_OBJECT_BUTTON = "RUN JSOBJECT"; +const RUN_JS_OBJECT_MSG = "Incorrect_users failed to execute"; + +const PAGE_LOAD_MSG = `The action "Incorrect_users" has failed.`; + +describe("Published mode toggle toast with debug flag in the url", function () { + before(() => { + cy.fixture("publishedModeToastToggleDsl").then((val) => { + _.agHelper.AddDsl(val); + }); + }); + + it("1. Should not show any application related toasts", function () { + cy.fixture("datasources").then((datasourceFormData) => { + _.apiPage.CreateAndFillApi( + datasourceFormData["mockApiUrl"], + "Correct_users", + ); + _.apiPage.ToggleOnPageLoadRun(true); + _.apiPage.CreateAndFillApi( + datasourceFormData["mockApiUrl"].replace("mock-api", "mock-api2err"), + "Incorrect_users", + ); + _.apiPage.ToggleOnPageLoadRun(true); + _.jsEditor.CreateJSObject( + `export default { + async myFun1 () { + const res = await Correct_users.run(); + showAlert("Hello info", "info"); + showAlert("Hello error", "error"); + showAlert("Hello warning", "warning"); + showAlert("Hello success", "success"); + await Incorrect_users.run(); + return res; + } + }`, + { + paste: true, + completeReplace: true, + toRun: false, + shouldCreateNewJSObj: true, + }, + ); + _.deployMode.DeployApp(undefined, true, false); + + _.agHelper.AssertElementAbsence(_.locators._toastMsg); + + _.agHelper.ClickButton(SHOW_ALERT_WORKING_BUTTON); + _.agHelper.AssertContains(SHOW_ALERT_MSG, "exist", _.locators._toastMsg); + + _.agHelper.ClickButton(SHOW_ALERT_NOT_WORKING_BUTTON); + _.agHelper.AssertContains( + SHOW_ALERT_NOT_WORKING_MSG, + "not.exist", + _.locators._toastMsg, + ); + + _.agHelper.ClickButton(RUN_JS_OBJECT_BUTTON); + _.agHelper.AssertContains("Hello success", "exist", _.locators._toastMsg); + _.agHelper.AssertContains( + RUN_JS_OBJECT_MSG, + "not.exist", + _.locators._toastMsg, + ); + }); + }); + + it("2. Should show all application related toasts with debug flag true in url", function () { + cy.url().then((url) => { + cy.visit({ + url, + qs: { + debug: "true", + }, + }); + _.agHelper.GetNAssertContains(_.locators._toastMsg, PAGE_LOAD_MSG); + + _.agHelper.ClickButton(SHOW_ALERT_WORKING_BUTTON); + _.agHelper.AssertContains(SHOW_ALERT_MSG, "exist", _.locators._toastMsg); + + _.agHelper.Sleep(2000); + _.agHelper.ClickButton(SHOW_ALERT_NOT_WORKING_BUTTON); + _.agHelper.AssertContains( + SHOW_ALERT_NOT_WORKING_MSG, + "exist", + _.locators._toastMsg, + ); + + _.agHelper.ClickButton(RUN_JS_OBJECT_BUTTON); + _.agHelper.AssertContains("Hello success", "exist", _.locators._toastMsg); + _.agHelper.AssertContains( + RUN_JS_OBJECT_MSG, + "exist", + _.locators._toastMsg, + ); + }); + }); +}); diff --git a/app/client/cypress/fixtures/publishedModeToastToggleDsl.json b/app/client/cypress/fixtures/publishedModeToastToggleDsl.json new file mode 100644 index 0000000000..b9f39f934e --- /dev/null +++ b/app/client/cypress/fixtures/publishedModeToastToggleDsl.json @@ -0,0 +1,277 @@ +{ + "id": "64746c35c8cae423b46bf2b0", + "userPermissions": [], + "dsl": { + "widgetName": "MainContainer", + "backgroundColor": "none", + "rightColumn": 4896, + "snapColumns": 64, + "detachFromLayout": true, + "widgetId": "0", + "topRow": 0, + "bottomRow": 380, + "containerStyle": "none", + "snapRows": 124, + "parentRowSpace": 1, + "type": "CANVAS_WIDGET", + "canExtend": true, + "version": 79, + "minHeight": 1292, + "dynamicTriggerPathList": [], + "parentColumnSpace": 1, + "dynamicBindingPathList": [], + "leftColumn": 0, + "children": [ + { + "boxShadow": "none", + "iconSVG": "/static/media/icon.d0ce957b6c4640f8a7418ce846ee200e.svg", + "topRow": 0, + "labelWidth": 5, + "type": "INPUT_WIDGET_V2", + "animateLoading": true, + "resetOnSubmit": true, + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "accentColor" + }, + { + "key": "borderRadius" + } + ], + "labelStyle": "", + "inputType": "TEXT", + "isDisabled": false, + "isRequired": false, + "dynamicHeight": "FIXED", + "accentColor": "{{appsmith.theme.colors.primaryColor}}", + "showStepArrows": false, + "isVisible": true, + "version": 2, + "isLoading": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "mobileBottomRow": 39, + "widgetName": "Correct_input", + "displayName": "Input", + "searchTags": [ + "form", + "text input", + "number", + "textarea" + ], + "bottomRow": 4, + "parentRowSpace": 10, + "autoFocus": false, + "hideCard": false, + "mobileRightColumn": 21, + "parentColumnSpace": 12.578125, + "dynamicTriggerPathList": [], + "labelPosition": "Top", + "key": "zssusa8tjd", + "labelTextSize": "0.875rem", + "isDeprecated": false, + "rightColumn": 37, + "widgetId": "9vv4sm9msa", + "minWidth": 450, + "label": "", + "parentId": "0", + "labelAlignment": "left", + "renderMode": "CANVAS", + "mobileTopRow": 32, + "responsiveBehavior": "fill", + "mobileLeftColumn": 1, + "maxDynamicHeight": 9000, + "iconAlign": "left", + "defaultText": "Hello World!", + "minDynamicHeight": 4 + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "mobileBottomRow": 36, + "widgetName": "Button1", + "onClick": "{{showAlert(Correct_input.text, 'success');}}", + "buttonColor": "{{appsmith.theme.colors.primaryColor}}", + "displayName": "Button", + "iconSVG": "/static/media/icon.7beb9123fb53027d9d6b778cdfe4caed.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 6, + "bottomRow": 10, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "mobileRightColumn": 38, + "animateLoading": true, + "parentColumnSpace": 12.578125, + "dynamicTriggerPathList": [ + { + "key": "onClick" + } + ], + "leftColumn": 0, + "dynamicBindingPathList": [ + { + "key": "buttonColor" + }, + { + "key": "borderRadius" + } + ], + "text": "Show alert working", + "isDisabled": false, + "key": "sco9w67akk", + "isDeprecated": false, + "rightColumn": 16, + "isDefaultClickDisabled": true, + "widgetId": "cqagzdpjej", + "minWidth": 120, + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "mobileTopRow": 32, + "responsiveBehavior": "hug", + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "mobileLeftColumn": 22, + "buttonVariant": "PRIMARY", + "placement": "CENTER" + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "mobileBottomRow": 42, + "widgetName": "Button2", + "onClick": "{{showAlert(Correct_input2.text, '');}}", + "buttonColor": "#b91c1c", + "displayName": "Button", + "iconSVG": "/static/media/icon.7beb9123fb53027d9d6b778cdfe4caed.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 6, + "bottomRow": 10, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "mobileRightColumn": 38, + "animateLoading": true, + "parentColumnSpace": 12.578125, + "dynamicTriggerPathList": [ + { + "key": "onClick" + } + ], + "leftColumn": 21, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + } + ], + "text": "Show alert not working", + "isDisabled": false, + "key": "sco9w67akk", + "isDeprecated": false, + "rightColumn": 37, + "isDefaultClickDisabled": true, + "widgetId": "s7lvatrsrg", + "minWidth": 120, + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "mobileTopRow": 38, + "responsiveBehavior": "hug", + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "mobileLeftColumn": 22, + "buttonVariant": "PRIMARY", + "placement": "CENTER" + }, + { + "resetFormOnClick": false, + "boxShadow": "none", + "mobileBottomRow": 27, + "widgetName": "Button3", + "onClick": "{{JSObject1.myFun1();}}", + "buttonColor": "#52525b", + "displayName": "Button", + "iconSVG": "/static/media/icon.7beb9123fb53027d9d6b778cdfe4caed.svg", + "searchTags": [ + "click", + "submit" + ], + "topRow": 0, + "bottomRow": 4, + "parentRowSpace": 10, + "type": "BUTTON_WIDGET", + "hideCard": false, + "mobileRightColumn": 60, + "animateLoading": true, + "parentColumnSpace": 12.578125, + "dynamicTriggerPathList": [ + { + "key": "onClick" + } + ], + "leftColumn": 44, + "dynamicBindingPathList": [ + { + "key": "borderRadius" + } + ], + "text": "RUN JSOBJECT", + "isDisabled": false, + "key": "sco9w67akk", + "isDeprecated": false, + "rightColumn": 60, + "isDefaultClickDisabled": true, + "widgetId": "fia0l15iti", + "minWidth": 120, + "isVisible": true, + "recaptchaType": "V3", + "version": 1, + "parentId": "0", + "renderMode": "CANVAS", + "isLoading": false, + "mobileTopRow": 23, + "responsiveBehavior": "hug", + "disabledWhenInvalid": false, + "borderRadius": "{{appsmith.theme.borderRadius.appBorderRadius}}", + "mobileLeftColumn": 44, + "buttonVariant": "PRIMARY", + "placement": "CENTER" + } + ] + }, + "layoutOnLoadActions": [ + [ + { + "id": "64746c35c8cae423b46bf2b3", + "name": "Incorrect_users", + "confirmBeforeExecute": false, + "pluginType": "API", + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + }, + { + "id": "64746c35c8cae423b46bf2b2", + "name": "Correct_users", + "confirmBeforeExecute": false, + "pluginType": "API", + "jsonPathKeys": [], + "timeoutInMillisecond": 10000 + } + ] + ], + "layoutOnLoadActionErrors": [], + "new": false +} \ No newline at end of file diff --git a/app/client/cypress/support/Pages/DeployModeHelper.ts b/app/client/cypress/support/Pages/DeployModeHelper.ts index c0f6febf3f..b998f0a22f 100644 --- a/app/client/cypress/support/Pages/DeployModeHelper.ts +++ b/app/client/cypress/support/Pages/DeployModeHelper.ts @@ -28,6 +28,7 @@ export class DeployMode { public DeployApp( eleToCheckInDeployPage: string = this.locator._backToEditor, toCheckFailureToast = true, + addDebugFlag = true, ) { //cy.intercept("POST", "/api/v1/applications/publish/*").as("publishAppli"); // Wait before publish @@ -36,7 +37,12 @@ export class DeployMode { // Stubbing window.open to open in the same tab cy.window().then((window) => { cy.stub(window, "open").callsFake((url) => { - window.location.href = Cypress.config().baseUrl + url.substring(1); + const updatedUrl = `${Cypress.config().baseUrl + url.substring(1)}`; + window.location.href = `${updatedUrl}${ + addDebugFlag + ? (updatedUrl.indexOf("?") > -1 ? "&" : "?") + "debug=true" + : "" + }`; }); }); cy.get(this.locator._publishButton).click(); diff --git a/app/client/src/sagas/ActionExecution/PostMessageSaga.ts b/app/client/src/sagas/ActionExecution/PostMessageSaga.ts index 93ab3ca572..d4adb14be7 100644 --- a/app/client/src/sagas/ActionExecution/PostMessageSaga.ts +++ b/app/client/src/sagas/ActionExecution/PostMessageSaga.ts @@ -1,4 +1,4 @@ -import { spawn } from "redux-saga/effects"; +import { call, spawn } from "redux-saga/effects"; import { logActionExecutionError, TriggerFailureError, @@ -35,6 +35,6 @@ export function* executePostMessage( } } } catch (error) { - logActionExecutionError((error as Error).message, true); + yield call(logActionExecutionError, (error as Error).message, true); } } diff --git a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts index 747de49887..803774fe1c 100644 --- a/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/ShowAlertActionSaga.ts @@ -1,8 +1,10 @@ import AppsmithConsole from "utils/AppsmithConsole"; import { ActionValidationError } from "sagas/ActionExecution/errorUtils"; import { getType, Types } from "utils/TypeHelpers"; -import { toast } from "design-system"; +import type { ToastKind } from "design-system"; import type { TShowAlertDescription } from "workers/Evaluation/fns/showAlert"; +import { call } from "redux-saga/effects"; +import showToast from "sagas/ToastSagas"; export default function* showAlertSaga(action: TShowAlertDescription) { const { payload } = action; @@ -14,24 +16,15 @@ export default function* showAlertSaga(action: TShowAlertDescription) { getType(payload.message), ); } - let kind: "success" | "info" | "warning" | "error" | undefined = undefined; - switch (payload.style) { - case "info": - kind = "info"; - break; - case "success": - kind = "success"; - break; - case "warning": - kind = "warning"; - break; - case "error": - kind = "error"; - break; - } - toast.show(payload.message, { - kind: kind, - }); + // This is the toast that is rendered which is user generated by using `showAlert` platform function. This is forceDisplayed no matter the conditions. + yield call( + showToast, + payload.message, + { + kind: payload.style as ToastKind, + }, + { forceDisplay: true }, + ); AppsmithConsole.info({ text: payload.style ? `showAlert('${payload.message}', '${payload.style}') was triggered` diff --git a/app/client/src/sagas/ActionExecution/errorUtils.ts b/app/client/src/sagas/ActionExecution/errorUtils.ts index ce2e559470..0b242bff8a 100644 --- a/app/client/src/sagas/ActionExecution/errorUtils.ts +++ b/app/client/src/sagas/ActionExecution/errorUtils.ts @@ -8,7 +8,6 @@ import type { Types } from "utils/TypeHelpers"; import type { ActionTriggerKeys } from "@appsmith/workers/Evaluation/fns/index"; import { getActionTriggerFunctionNames } from "@appsmith/workers/Evaluation/fns/index"; import { getAppsmithConfigs } from "@appsmith/configs"; -import { toast } from "design-system"; import { getAppMode } from "@appsmith/selectors/applicationSelectors"; import AnalyticsUtil from "../../utils/AnalyticsUtil"; import { @@ -17,6 +16,8 @@ import { } from "../../actions/debuggerActions"; import { DEBUGGER_TAB_KEYS } from "../../components/editorComponents/Debugger/helpers"; import store from "store"; +import showToast from "sagas/ToastSagas"; +import { call } from "redux-saga/effects"; const APPSMITH_CONFIGS = getAppsmithConfigs(); @@ -62,10 +63,10 @@ export class ActionValidationError extends TriggerFailureError { } } -export const logActionExecutionError = ( +export function* logActionExecutionError( errorMessage: string, isExecuteJSFunc = true, -) => { +) { //Commenting as per decision taken for the error hanlding epic to not show the trigger errors in the debugger. // if (triggerPropertyName) { // AppsmithConsole.addErrors([ @@ -102,8 +103,9 @@ export const logActionExecutionError = ( store.dispatch(setDebuggerSelectedTab(DEBUGGER_TAB_KEYS.ERROR_TAB)); } - isExecuteJSFunc && - toast.show(errorMessage, { + if (isExecuteJSFunc) + // This is the toast that is rendered when any unhandled error occurs in JS object. + yield call(showToast, errorMessage, { kind: "error", action: { text: "debug", @@ -111,7 +113,7 @@ export const logActionExecutionError = ( className: "t--toast-debug-button", }, }); -}; +} /* * Thrown when action execution fails for some reason diff --git a/app/client/src/sagas/ActionExecution/geolocationSaga.test.ts b/app/client/src/sagas/ActionExecution/geolocationSaga.test.ts index 8da0865b97..f63dc47f1a 100644 --- a/app/client/src/sagas/ActionExecution/geolocationSaga.test.ts +++ b/app/client/src/sagas/ActionExecution/geolocationSaga.test.ts @@ -5,6 +5,8 @@ import { getUserLocation, } from "./geolocationSaga"; import { setUserCurrentGeoLocation } from "actions/browserRequestActions"; +import { logActionExecutionError } from "./errorUtils"; + const mockFn = jest.fn(); jest.mock("./errorUtils.ts", () => ({ @@ -79,8 +81,14 @@ describe("getCurrentLocationSaga", () => { }; const iter = getCurrentLocationSaga(trigger); expect(iter.next().value).toEqual(call(getUserLocation, payload.options)); - iter.next(); - expect(mockFn).toBeCalled(); + + expect(iter.next().value).toEqual( + call( + logActionExecutionError, + "Cannot read properties of undefined (reading 'coords')", + true, + ), + ); expect(iter.next().done).toBe(true); }); }); diff --git a/app/client/src/sagas/ActionExecution/geolocationSaga.ts b/app/client/src/sagas/ActionExecution/geolocationSaga.ts index 39e39f219d..a32bcbfd91 100644 --- a/app/client/src/sagas/ActionExecution/geolocationSaga.ts +++ b/app/client/src/sagas/ActionExecution/geolocationSaga.ts @@ -102,7 +102,7 @@ function* errorCallbackHandler(triggerMeta: TriggerMeta, listenerId?: string) { { error: sanitizeGeolocationError(error) }, listenerId, ); - logActionExecutionError(error.message, true); + yield call(logActionExecutionError, error.message, true); } } @@ -117,7 +117,7 @@ export function* getCurrentLocationSaga(action: TGetGeoLocationDescription) { yield put(setUserCurrentGeoLocation(currentLocation)); return currentLocation; } catch (error) { - logActionExecutionError((error as Error).message, true); + yield call(logActionExecutionError, (error as Error).message, true); if (error instanceof GeolocationPositionError) { const sanitizedError = sanitizeGeolocationError(error); throw new GeoLocationError(sanitizedError.message, [sanitizedError]); @@ -135,7 +135,8 @@ export function* watchCurrentLocation( if (watchId) { // When a watch is already active, we will not start a new watch. // at a given point in time, only one watch is active - logActionExecutionError( + yield call( + logActionExecutionError, "A watchLocation is already active. Clear it before before starting a new one", true, ); @@ -165,7 +166,7 @@ export function* watchCurrentLocation( export function* stopWatchCurrentLocation() { if (watchId === undefined) { - logActionExecutionError("No location watch active", true); + yield call(logActionExecutionError, "No location watch active", true); return; } navigator.geolocation.clearWatch(watchId); diff --git a/app/client/src/sagas/ErrorSagas.tsx b/app/client/src/sagas/ErrorSagas.tsx index cd5ad5fac7..fe504bbf5d 100644 --- a/app/client/src/sagas/ErrorSagas.tsx +++ b/app/client/src/sagas/ErrorSagas.tsx @@ -32,7 +32,7 @@ import * as Sentry from "@sentry/react"; import { axiosConnectionAbortedCode } from "@appsmith/api/ApiUtils"; import { getLoginUrl } from "@appsmith/utils/adminSettingsHelpers"; import type { PluginErrorDetails } from "api/ActionAPI"; -import { toast } from "design-system"; +import showToast from "sagas/ToastSagas"; /** * making with error message with action name @@ -224,7 +224,8 @@ export function* errorSaga(errorAction: ReduxAction) { break; } case ErrorEffectTypes.SHOW_ALERT: { - showAlertAboutError(message); + // This is the toast that is rendered when any page load API fails. + yield call(showToast, message, { kind: "error" }); break; } case ErrorEffectTypes.SAFE_CRASH: { @@ -252,10 +253,6 @@ function logErrorSaga(action: ReduxAction<{ error: ErrorPayloadType }>) { if (action.payload) log.error(action.payload.error); } -function showAlertAboutError(message: string) { - toast.show(message, { kind: "error" }); -} - /** * this saga do some logic before actually setting safeCrash to true */ diff --git a/app/client/src/sagas/PostEvaluationSagas.ts b/app/client/src/sagas/PostEvaluationSagas.ts index 355547aea4..6ad3a07e9b 100644 --- a/app/client/src/sagas/PostEvaluationSagas.ts +++ b/app/client/src/sagas/PostEvaluationSagas.ts @@ -23,7 +23,7 @@ import type { EvalError, EvaluationError } from "utils/DynamicBindingUtils"; import { EvalErrorTypes, getEvalErrorPath } from "utils/DynamicBindingUtils"; import { find, get, some } from "lodash"; import LOG_TYPE from "entities/AppsmithConsole/logtype"; -import { put, select } from "redux-saga/effects"; +import { call, put, select } from "redux-saga/effects"; import type { AnyReduxAction } from "@appsmith/constants/ReduxActionConstants"; import AppsmithConsole from "utils/AppsmithConsole"; import * as Sentry from "@sentry/react"; @@ -324,7 +324,7 @@ export function* dynamicTriggerErrorHandler(errors: any[]) { for (const error of errors) { const errorMessage = error.errorMessage.message.message || error.errorMessage.message; - logActionExecutionError(errorMessage, true); + yield call(logActionExecutionError, errorMessage, true); } } } diff --git a/app/client/src/sagas/ToastSagas.ts b/app/client/src/sagas/ToastSagas.ts new file mode 100644 index 0000000000..4f12bc06f8 --- /dev/null +++ b/app/client/src/sagas/ToastSagas.ts @@ -0,0 +1,35 @@ +import type { ToastProps } from "design-system"; +import { toast } from "design-system"; +import { APP_MODE } from "entities/App"; +import { select } from "redux-saga/effects"; +import { getAppMode } from "selectors/entitiesSelector"; +import log from "loglevel"; + +type ExtraOptions = { + // This enables showing of toast no matter the conditions + forceDisplay?: boolean; +}; + +/** + * Shows toast + * @param message + * @param [options] These are toast props that toast from design-sytem(react-toastify) library takes + * @param [extraOtions] These additional options enable the addition of additional requirements, based on which the toast will only then be produced. (for future extensibility as well) + * @returns + */ +export default function* showToast( + message: string, + options?: ToastProps, + extraOtions?: ExtraOptions, +) { + const appMode: APP_MODE | undefined = yield select(getAppMode); + const urlObject = new URL(window?.location?.href); + const debugFlag = urlObject?.searchParams?.get("debug"); + const debug = debugFlag === "true" || debugFlag; + if (appMode === APP_MODE.PUBLISHED && !debug && !extraOtions?.forceDisplay) { + log.error(message); + return; + } + + toast.show(message, options); +} diff --git a/contributions/docs/GlobalFunctions.md b/contributions/docs/GlobalFunctions.md index d89f35f32b..4370b3882f 100644 --- a/contributions/docs/GlobalFunctions.md +++ b/contributions/docs/GlobalFunctions.md @@ -129,9 +129,7 @@ function* executeInIntervals( source: triggerMeta.source, }); } catch (e) { - logActionExecutionError( - e.message, - ); + yield call(logActionExecutionError, e.message); } yield delay(interval); }