Improve app initialisation for timeouts (#1412)
Fixes: #1510 Co-authored-by: Arpit Mohan <mohanarpit@users.noreply.github.com>
This commit is contained in:
parent
8ae77f7764
commit
20ef86f118
|
|
@ -1,5 +1,4 @@
|
||||||
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxAction, ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import { EditorModes } from "../components/editorComponents/CodeEditor/EditorConfig";
|
|
||||||
import { APP_MODE } from "../reducers/entityReducers/appReducer";
|
import { APP_MODE } from "../reducers/entityReducers/appReducer";
|
||||||
import { UpdateApplicationPayload } from "api/ApplicationApi";
|
import { UpdateApplicationPayload } from "api/ApplicationApi";
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import {
|
||||||
ReduxActionTypes,
|
ReduxActionTypes,
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
InitializeEditorPayload,
|
InitializeEditorPayload,
|
||||||
|
ReduxActionErrorTypes,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
|
|
||||||
export const initEditor = (
|
export const initEditor = (
|
||||||
|
|
@ -14,3 +15,17 @@ export const initEditor = (
|
||||||
pageId,
|
pageId,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const initEditorError = (): ReduxAction<{ show: false }> => ({
|
||||||
|
type: ReduxActionErrorTypes.INITIALIZE_EDITOR_ERROR,
|
||||||
|
payload: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const initViewerError = (): ReduxAction<{ show: false }> => ({
|
||||||
|
type: ReduxActionErrorTypes.INITIALIZE_PAGE_VIEWER_ERROR,
|
||||||
|
payload: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,10 @@ import {
|
||||||
API_REQUEST_HEADERS,
|
API_REQUEST_HEADERS,
|
||||||
} from "constants/ApiConstants";
|
} from "constants/ApiConstants";
|
||||||
import { ActionApiResponse } from "./ActionAPI";
|
import { ActionApiResponse } from "./ActionAPI";
|
||||||
import {
|
import { AUTH_LOGIN_URL, PAGE_NOT_FOUND_URL } from "constants/routes";
|
||||||
AUTH_LOGIN_URL,
|
|
||||||
PAGE_NOT_FOUND_URL,
|
|
||||||
SERVER_ERROR_URL,
|
|
||||||
} from "constants/routes";
|
|
||||||
import history from "utils/history";
|
import history from "utils/history";
|
||||||
import { convertObjectToQueryParams } from "utils/AppsmithUtils";
|
import { convertObjectToQueryParams } from "utils/AppsmithUtils";
|
||||||
|
import { SERVER_API_TIMEOUT_ERROR } from "../constants/messages";
|
||||||
|
|
||||||
//TODO(abhinav): Refactor this to make more composable.
|
//TODO(abhinav): Refactor this to make more composable.
|
||||||
export const apiRequestConfig = {
|
export const apiRequestConfig = {
|
||||||
|
|
@ -22,8 +19,10 @@ export const apiRequestConfig = {
|
||||||
|
|
||||||
const axiosInstance: AxiosInstance = axios.create();
|
const axiosInstance: AxiosInstance = axios.create();
|
||||||
|
|
||||||
|
export const axiosConnectionAbortedCode = "ECONNABORTED";
|
||||||
const executeActionRegex = /actions\/execute/;
|
const executeActionRegex = /actions\/execute/;
|
||||||
const currentUserRegex = /\/me$/;
|
const timeoutErrorRegex = /timeout of (\d+)ms exceeded/;
|
||||||
|
|
||||||
axiosInstance.interceptors.request.use((config: any) => {
|
axiosInstance.interceptors.request.use((config: any) => {
|
||||||
return { ...config, timer: performance.now() };
|
return { ...config, timer: performance.now() };
|
||||||
});
|
});
|
||||||
|
|
@ -50,20 +49,25 @@ axiosInstance.interceptors.response.use(
|
||||||
return response.data;
|
return response.data;
|
||||||
},
|
},
|
||||||
function(error: any) {
|
function(error: any) {
|
||||||
|
// Return if the call was cancelled via cancel token
|
||||||
if (axios.isCancel(error)) {
|
if (axios.isCancel(error)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (error.code === "ECONNABORTED") {
|
// Return modified response if action execution failed
|
||||||
if (error.config && error.config.url.match(currentUserRegex)) {
|
|
||||||
history.replace({ pathname: SERVER_ERROR_URL });
|
|
||||||
}
|
|
||||||
return Promise.reject({
|
|
||||||
message: "Please check your internet connection",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (error.config && error.config.url.match(executeActionRegex)) {
|
if (error.config && error.config.url.match(executeActionRegex)) {
|
||||||
return makeExecuteActionResponse(error.response);
|
return makeExecuteActionResponse(error.response);
|
||||||
}
|
}
|
||||||
|
// Return error if any timeout happened in other api calls
|
||||||
|
if (
|
||||||
|
error.code === axiosConnectionAbortedCode &&
|
||||||
|
error.message &&
|
||||||
|
error.message.match(timeoutErrorRegex)
|
||||||
|
) {
|
||||||
|
return Promise.reject({
|
||||||
|
...error,
|
||||||
|
message: SERVER_API_TIMEOUT_ERROR,
|
||||||
|
});
|
||||||
|
}
|
||||||
if (error.response) {
|
if (error.response) {
|
||||||
// The request was made and the server responded with a status code
|
// The request was made and the server responded with a status code
|
||||||
// that falls out of the range of 2xx
|
// that falls out of the range of 2xx
|
||||||
|
|
|
||||||
BIN
app/client/src/assets/images/timeout-image.png
Normal file
BIN
app/client/src/assets/images/timeout-image.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
|
|
@ -8,3 +8,20 @@ const APP_STORE_NAMESPACE = "APPSMITH_LOCAL_STORE";
|
||||||
|
|
||||||
export const getAppStoreName = (appId: string) =>
|
export const getAppStoreName = (appId: string) =>
|
||||||
`${APP_STORE_NAMESPACE}-${appId}`;
|
`${APP_STORE_NAMESPACE}-${appId}`;
|
||||||
|
|
||||||
|
export const getAppStore = (appId: string) => {
|
||||||
|
const appStoreName = getAppStoreName(appId);
|
||||||
|
let storeString = "{}";
|
||||||
|
// Check if localStorage exists
|
||||||
|
if (localStorage) {
|
||||||
|
const appStore = localStorage.getItem(appStoreName);
|
||||||
|
if (appStore) storeString = appStore;
|
||||||
|
}
|
||||||
|
let store;
|
||||||
|
try {
|
||||||
|
store = JSON.parse(storeString);
|
||||||
|
} catch (e) {
|
||||||
|
store = {};
|
||||||
|
}
|
||||||
|
return store;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -296,6 +296,7 @@ export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTy
|
||||||
|
|
||||||
export const ReduxActionErrorTypes: { [key: string]: string } = {
|
export const ReduxActionErrorTypes: { [key: string]: string } = {
|
||||||
INITIALIZE_EDITOR_ERROR: "INITIALIZE_EDITOR_ERROR",
|
INITIALIZE_EDITOR_ERROR: "INITIALIZE_EDITOR_ERROR",
|
||||||
|
INITIALIZE_PAGE_VIEWER_ERROR: "INITIALIZE_PAGE_VIEWER_ERROR",
|
||||||
API_ERROR: "API_ERROR",
|
API_ERROR: "API_ERROR",
|
||||||
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
|
WIDGET_DELETE_ERROR: "WIDGET_DELETE_ERROR",
|
||||||
UPDATE_APPLICATION_ERROR: "UPDATE_APPLICATION_ERROR",
|
UPDATE_APPLICATION_ERROR: "UPDATE_APPLICATION_ERROR",
|
||||||
|
|
|
||||||
|
|
@ -168,3 +168,6 @@ export const GOOGLE_RECAPTCHA_KEY_ERROR =
|
||||||
"Google Re-Captcha Token Generation failed! Please check the Re-captcha Site Key.";
|
"Google Re-Captcha Token Generation failed! Please check the Re-captcha Site Key.";
|
||||||
export const GOOGLE_RECAPTCHA_DOMAIN_ERROR =
|
export const GOOGLE_RECAPTCHA_DOMAIN_ERROR =
|
||||||
"Google Re-Captcha Token Generation failed! Please check the allowed domains.";
|
"Google Re-Captcha Token Generation failed! Please check the allowed domains.";
|
||||||
|
|
||||||
|
export const SERVER_API_TIMEOUT_ERROR =
|
||||||
|
"Appsmith server is taking too long to respond. Please try again after some time";
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,10 @@ import {
|
||||||
getApplicationViewerPageURL,
|
getApplicationViewerPageURL,
|
||||||
} from "constants/routes";
|
} from "constants/routes";
|
||||||
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
import { ReduxActionTypes } from "constants/ReduxActionConstants";
|
||||||
import { getIsInitialized } from "selectors/appViewSelectors";
|
import {
|
||||||
|
getIsInitialized,
|
||||||
|
getIsInitializeError,
|
||||||
|
} from "selectors/appViewSelectors";
|
||||||
import { executeAction } from "actions/widgetActions";
|
import { executeAction } from "actions/widgetActions";
|
||||||
import { ExecuteActionPayload } from "constants/ActionConstants";
|
import { ExecuteActionPayload } from "constants/ActionConstants";
|
||||||
import { updateWidgetPropertyRequest } from "actions/controlActions";
|
import { updateWidgetPropertyRequest } from "actions/controlActions";
|
||||||
|
|
@ -23,6 +26,8 @@ import {
|
||||||
} from "actions/metaActions";
|
} from "actions/metaActions";
|
||||||
import { editorInitializer } from "utils/EditorUtils";
|
import { editorInitializer } from "utils/EditorUtils";
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
|
import log from "loglevel";
|
||||||
|
import ServerTimeout from "../common/ServerTimeout";
|
||||||
const SentryRoute = Sentry.withSentryRouting(Route);
|
const SentryRoute = Sentry.withSentryRouting(Route);
|
||||||
|
|
||||||
const AppViewerBody = styled.section`
|
const AppViewerBody = styled.section`
|
||||||
|
|
@ -36,6 +41,7 @@ const AppViewerBody = styled.section`
|
||||||
export type AppViewerProps = {
|
export type AppViewerProps = {
|
||||||
initializeAppViewer: (applicationId: string, pageId?: string) => void;
|
initializeAppViewer: (applicationId: string, pageId?: string) => void;
|
||||||
isInitialized: boolean;
|
isInitialized: boolean;
|
||||||
|
isInitializeError: boolean;
|
||||||
executeAction: (actionPayload: ExecuteActionPayload) => void;
|
executeAction: (actionPayload: ExecuteActionPayload) => void;
|
||||||
updateWidgetProperty: (
|
updateWidgetProperty: (
|
||||||
widgetId: string,
|
widgetId: string,
|
||||||
|
|
@ -62,7 +68,7 @@ class AppViewer extends Component<
|
||||||
this.setState({ registered: true });
|
this.setState({ registered: true });
|
||||||
});
|
});
|
||||||
const { applicationId, pageId } = this.props.match.params;
|
const { applicationId, pageId } = this.props.match.params;
|
||||||
console.log({ applicationId, pageId });
|
log.debug({ applicationId, pageId });
|
||||||
if (applicationId) {
|
if (applicationId) {
|
||||||
this.props.initializeAppViewer(applicationId, pageId);
|
this.props.initializeAppViewer(applicationId, pageId);
|
||||||
}
|
}
|
||||||
|
|
@ -73,7 +79,10 @@ class AppViewer extends Component<
|
||||||
};
|
};
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { isInitialized } = this.props;
|
const { isInitialized, isInitializeError } = this.props;
|
||||||
|
if (isInitializeError) {
|
||||||
|
return <ServerTimeout />;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<EditorContext.Provider
|
<EditorContext.Provider
|
||||||
value={{
|
value={{
|
||||||
|
|
@ -100,6 +109,7 @@ class AppViewer extends Component<
|
||||||
|
|
||||||
const mapStateToProps = (state: AppState) => ({
|
const mapStateToProps = (state: AppState) => ({
|
||||||
isInitialized: getIsInitialized(state),
|
isInitialized: getIsInitialized(state),
|
||||||
|
isInitializeError: getIsInitializeError(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = (dispatch: any) => ({
|
const mapDispatchToProps = (dispatch: any) => ({
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import { Helmet } from "react-helmet";
|
import { Helmet } from "react-helmet";
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { withRouter, RouteComponentProps } from "react-router-dom";
|
import { RouteComponentProps, withRouter } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
BuilderRouteParams,
|
BuilderRouteParams,
|
||||||
getApplicationViewerPageURL,
|
getApplicationViewerPageURL,
|
||||||
|
|
@ -13,15 +13,16 @@ import TouchBackend from "react-dnd-touch-backend";
|
||||||
import {
|
import {
|
||||||
getCurrentApplicationId,
|
getCurrentApplicationId,
|
||||||
getCurrentPageId,
|
getCurrentPageId,
|
||||||
getPublishingError,
|
|
||||||
getIsEditorLoading,
|
|
||||||
getIsEditorInitialized,
|
getIsEditorInitialized,
|
||||||
|
getIsEditorInitializeError,
|
||||||
|
getIsEditorLoading,
|
||||||
getIsPublishingApplication,
|
getIsPublishingApplication,
|
||||||
|
getPublishingError,
|
||||||
} from "selectors/editorSelectors";
|
} from "selectors/editorSelectors";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
|
||||||
Classes,
|
|
||||||
AnchorButton,
|
AnchorButton,
|
||||||
|
Classes,
|
||||||
|
Dialog,
|
||||||
Hotkey,
|
Hotkey,
|
||||||
Hotkeys,
|
Hotkeys,
|
||||||
Spinner,
|
Spinner,
|
||||||
|
|
@ -40,11 +41,12 @@ import ConfirmRunModal from "pages/Editor/ConfirmRunModal";
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import {
|
import {
|
||||||
copyWidget,
|
copyWidget,
|
||||||
pasteWidget,
|
|
||||||
deleteSelectedWidget,
|
|
||||||
cutWidget,
|
cutWidget,
|
||||||
|
deleteSelectedWidget,
|
||||||
|
pasteWidget,
|
||||||
} from "actions/widgetActions";
|
} from "actions/widgetActions";
|
||||||
import { isMac } from "utils/helpers";
|
import { isMac } from "utils/helpers";
|
||||||
|
import ServerTimeout from "../common/ServerTimeout";
|
||||||
|
|
||||||
type EditorProps = {
|
type EditorProps = {
|
||||||
currentApplicationId?: string;
|
currentApplicationId?: string;
|
||||||
|
|
@ -53,6 +55,7 @@ type EditorProps = {
|
||||||
isPublishing: boolean;
|
isPublishing: boolean;
|
||||||
isEditorLoading: boolean;
|
isEditorLoading: boolean;
|
||||||
isEditorInitialized: boolean;
|
isEditorInitialized: boolean;
|
||||||
|
isEditorInitializeError: boolean;
|
||||||
errorPublishing: boolean;
|
errorPublishing: boolean;
|
||||||
copySelectedWidget: () => void;
|
copySelectedWidget: () => void;
|
||||||
pasteCopiedWidget: () => void;
|
pasteCopiedWidget: () => void;
|
||||||
|
|
@ -189,6 +192,8 @@ class Editor extends Component<Props> {
|
||||||
nextProps.isPublishing !== this.props.isPublishing ||
|
nextProps.isPublishing !== this.props.isPublishing ||
|
||||||
nextProps.isEditorLoading !== this.props.isEditorLoading ||
|
nextProps.isEditorLoading !== this.props.isEditorLoading ||
|
||||||
nextProps.errorPublishing !== this.props.errorPublishing ||
|
nextProps.errorPublishing !== this.props.errorPublishing ||
|
||||||
|
nextProps.isEditorInitializeError !==
|
||||||
|
this.props.isEditorInitializeError ||
|
||||||
nextState.isDialogOpen !== this.state.isDialogOpen ||
|
nextState.isDialogOpen !== this.state.isDialogOpen ||
|
||||||
nextState.registered !== this.state.registered
|
nextState.registered !== this.state.registered
|
||||||
);
|
);
|
||||||
|
|
@ -200,6 +205,9 @@ class Editor extends Component<Props> {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
public render() {
|
public render() {
|
||||||
|
if (this.props.isEditorInitializeError) {
|
||||||
|
return <ServerTimeout />;
|
||||||
|
}
|
||||||
if (!this.props.isEditorInitialized || !this.state.registered) {
|
if (!this.props.isEditorInitialized || !this.state.registered) {
|
||||||
return (
|
return (
|
||||||
<CenteredWrapper style={{ height: "calc(100vh - 48px)" }}>
|
<CenteredWrapper style={{ height: "calc(100vh - 48px)" }}>
|
||||||
|
|
@ -260,6 +268,7 @@ const mapStateToProps = (state: AppState) => ({
|
||||||
isPublishing: getIsPublishingApplication(state),
|
isPublishing: getIsPublishingApplication(state),
|
||||||
isEditorLoading: getIsEditorLoading(state),
|
isEditorLoading: getIsEditorLoading(state),
|
||||||
isEditorInitialized: getIsEditorInitialized(state),
|
isEditorInitialized: getIsEditorInitialized(state),
|
||||||
|
isEditorInitializeError: getIsEditorInitializeError(state),
|
||||||
user: getCurrentUser(state),
|
user: getCurrentUser(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { RouterProps } from "react-router";
|
import { RouteComponentProps, withRouter } from "react-router";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import Button from "components/editorComponents/Button";
|
import Button from "components/editorComponents/Button";
|
||||||
import PageUnavailableImage from "assets/images/404-image.png";
|
import PageUnavailableImage from "assets/images/404-image.png";
|
||||||
|
|
@ -20,7 +20,7 @@ const Wrapper = styled.div`
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
class PageNotFound extends React.PureComponent<RouterProps> {
|
class PageNotFound extends React.PureComponent<RouteComponentProps> {
|
||||||
public render() {
|
public render() {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -54,4 +54,4 @@ class PageNotFound extends React.PureComponent<RouterProps> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PageNotFound;
|
export default withRouter(PageNotFound);
|
||||||
|
|
|
||||||
56
app/client/src/pages/common/ServerTimeout.tsx
Normal file
56
app/client/src/pages/common/ServerTimeout.tsx
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import React from "react";
|
||||||
|
import styled from "styled-components";
|
||||||
|
import AppTimeoutImage from "assets/images/timeout-image.png";
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
|
height: calc(100vh - ${props => props.theme.headerHeight});
|
||||||
|
.bold-text {
|
||||||
|
font-weight: ${props => props.theme.fontWeights[3]};
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
.page-unavailable-img {
|
||||||
|
width: 35%;
|
||||||
|
}
|
||||||
|
.button-position {
|
||||||
|
margin: auto;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const RetryButton = styled.button`
|
||||||
|
background-color: #f3672a;
|
||||||
|
color: white;
|
||||||
|
height: 40px;
|
||||||
|
width: 300px;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 17px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ServerTimeout = () => {
|
||||||
|
return (
|
||||||
|
<Wrapper>
|
||||||
|
<img
|
||||||
|
src={AppTimeoutImage}
|
||||||
|
alt="Page Unavailable"
|
||||||
|
className="page-unavailable-img"
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<p className="bold-text">
|
||||||
|
Appsmith server is taking too long to respond
|
||||||
|
</p>
|
||||||
|
<p>Please retry after some time</p>
|
||||||
|
<RetryButton onClick={() => window.location.reload()}>
|
||||||
|
{"Retry"}
|
||||||
|
</RetryButton>
|
||||||
|
</div>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ServerTimeout;
|
||||||
|
|
@ -5,12 +5,14 @@ import {
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
ReduxActionTypes,
|
ReduxActionTypes,
|
||||||
PageListPayload,
|
PageListPayload,
|
||||||
|
ReduxActionErrorTypes,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
import { FetchPublishedPageSuccessPayload } from "actions/pageActions";
|
import { FetchPublishedPageSuccessPayload } from "actions/pageActions";
|
||||||
|
|
||||||
const initialState: AppViewReduxState = {
|
const initialState: AppViewReduxState = {
|
||||||
isFetchingPage: false,
|
isFetchingPage: false,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
|
initializeError: false,
|
||||||
pages: [],
|
pages: [],
|
||||||
pageWidgetId: "0",
|
pageWidgetId: "0",
|
||||||
};
|
};
|
||||||
|
|
@ -24,6 +26,11 @@ const appViewReducer = createReducer(initialState, {
|
||||||
) => {
|
) => {
|
||||||
return { ...state, initialized: true };
|
return { ...state, initialized: true };
|
||||||
},
|
},
|
||||||
|
[ReduxActionErrorTypes.INITIALIZE_PAGE_VIEWER_ERROR]: (
|
||||||
|
state: AppViewReduxState,
|
||||||
|
) => {
|
||||||
|
return { ...state, initializeError: true };
|
||||||
|
},
|
||||||
[ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT]: (state: AppViewReduxState) => {
|
[ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT]: (state: AppViewReduxState) => {
|
||||||
return { ...state, dsl: undefined, isFetchingPage: true };
|
return { ...state, dsl: undefined, isFetchingPage: true };
|
||||||
},
|
},
|
||||||
|
|
@ -45,6 +52,7 @@ const appViewReducer = createReducer(initialState, {
|
||||||
|
|
||||||
export interface AppViewReduxState {
|
export interface AppViewReduxState {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
|
initializeError: boolean;
|
||||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
dsl?: ContainerWidgetProps<WidgetProps>;
|
||||||
isFetchingPage: boolean;
|
isFetchingPage: boolean;
|
||||||
currentLayoutId?: string;
|
currentLayoutId?: string;
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,11 @@ import {
|
||||||
ReduxActionTypes,
|
ReduxActionTypes,
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
} from "constants/ReduxActionConstants";
|
} from "constants/ReduxActionConstants";
|
||||||
import { WidgetProps } from "widgets/BaseWidget";
|
|
||||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
|
||||||
import moment from "moment";
|
import moment from "moment";
|
||||||
|
|
||||||
const initialState: EditorReduxState = {
|
const initialState: EditorReduxState = {
|
||||||
initialized: false,
|
initialized: false,
|
||||||
|
initializationError: false,
|
||||||
loadingStates: {
|
loadingStates: {
|
||||||
publishing: false,
|
publishing: false,
|
||||||
publishingError: false,
|
publishingError: false,
|
||||||
|
|
@ -68,11 +67,8 @@ const editorReducer = createReducer(initialState, {
|
||||||
) => {
|
) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
loadingStates: {
|
initializationError: true,
|
||||||
...state.loadingStates,
|
initialized: false,
|
||||||
loading: false,
|
|
||||||
loadingError: true,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
[ReduxActionTypes.PUBLISH_APPLICATION_INIT]: (state: EditorReduxState) => {
|
[ReduxActionTypes.PUBLISH_APPLICATION_INIT]: (state: EditorReduxState) => {
|
||||||
|
|
@ -178,7 +174,7 @@ const editorReducer = createReducer(initialState, {
|
||||||
|
|
||||||
export interface EditorReduxState {
|
export interface EditorReduxState {
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
dsl?: ContainerWidgetProps<WidgetProps>;
|
initializationError: boolean;
|
||||||
pageWidgetId?: string;
|
pageWidgetId?: string;
|
||||||
currentLayoutId?: string;
|
currentLayoutId?: string;
|
||||||
currentPageName?: string;
|
currentPageName?: string;
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { put, takeLatest, call } from "redux-saga/effects";
|
||||||
import { ERROR_401, ERROR_500, ERROR_0 } from "constants/messages";
|
import { ERROR_401, ERROR_500, ERROR_0 } from "constants/messages";
|
||||||
import { ToastType } from "react-toastify";
|
import { ToastType } from "react-toastify";
|
||||||
import log from "loglevel";
|
import log from "loglevel";
|
||||||
|
import { axiosConnectionAbortedCode } from "../api/Api";
|
||||||
|
|
||||||
export function* callAPI(apiCall: any, requestPayload: any) {
|
export function* callAPI(apiCall: any, requestPayload: any) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -60,7 +61,7 @@ export function getResponseErrorMessage(response: ApiResponse) {
|
||||||
: undefined;
|
: undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrorPayloadType = { code?: number; message?: string };
|
type ErrorPayloadType = { code?: number | string; message?: string };
|
||||||
let ActionErrorDisplayMap: {
|
let ActionErrorDisplayMap: {
|
||||||
[key: string]: (error: ErrorPayloadType) => string;
|
[key: string]: (error: ErrorPayloadType) => string;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
@ -92,6 +93,7 @@ export function* errorSaga(
|
||||||
type,
|
type,
|
||||||
payload: { error, show = true },
|
payload: { error, show = true },
|
||||||
} = errorAction;
|
} = errorAction;
|
||||||
|
|
||||||
const message =
|
const message =
|
||||||
error && error.message ? error.message : ActionErrorDisplayMap[type](error);
|
error && error.message ? error.message : ActionErrorDisplayMap[type](error);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
import { all, call, put, select, take, takeLatest } from "redux-saga/effects";
|
import {
|
||||||
|
all,
|
||||||
|
call,
|
||||||
|
put,
|
||||||
|
select,
|
||||||
|
take,
|
||||||
|
takeLatest,
|
||||||
|
race,
|
||||||
|
} from "redux-saga/effects";
|
||||||
import {
|
import {
|
||||||
InitializeEditorPayload,
|
InitializeEditorPayload,
|
||||||
Page,
|
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
ReduxActionTypes,
|
ReduxActionTypes,
|
||||||
|
|
@ -21,31 +28,17 @@ import { fetchActions, fetchActionsForView } from "actions/actionActions";
|
||||||
import { fetchApplication } from "actions/applicationActions";
|
import { fetchApplication } from "actions/applicationActions";
|
||||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||||
import { getCurrentApplication } from "selectors/applicationSelectors";
|
import { getCurrentApplication } from "selectors/applicationSelectors";
|
||||||
import { AppState } from "reducers";
|
|
||||||
import PageApi, { FetchPageResponse } from "api/PageApi";
|
|
||||||
import { validateResponse } from "./ErrorSagas";
|
|
||||||
import { extractCurrentDSL } from "utils/WidgetPropsUtils";
|
|
||||||
import { APP_MODE } from "reducers/entityReducers/appReducer";
|
import { APP_MODE } from "reducers/entityReducers/appReducer";
|
||||||
import { getAppStoreName } from "constants/AppConstants";
|
import { getAppStore } from "constants/AppConstants";
|
||||||
import { getDefaultPageId } from "./selectors";
|
import { getDefaultPageId } from "./selectors";
|
||||||
|
import { populatePageDSLsSaga } from "./PageSagas";
|
||||||
const getAppStore = (appId: string) => {
|
import { initEditorError, initViewerError } from "../actions/initActions";
|
||||||
const appStoreName = getAppStoreName(appId);
|
|
||||||
const storeString = localStorage.getItem(appStoreName) || "{}";
|
|
||||||
let store;
|
|
||||||
try {
|
|
||||||
store = JSON.parse(storeString);
|
|
||||||
} catch (e) {
|
|
||||||
store = {};
|
|
||||||
}
|
|
||||||
return store;
|
|
||||||
};
|
|
||||||
|
|
||||||
function* initializeEditorSaga(
|
function* initializeEditorSaga(
|
||||||
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
|
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
|
||||||
) {
|
) {
|
||||||
const { applicationId, pageId } = initializeEditorAction.payload;
|
const { applicationId, pageId } = initializeEditorAction.payload;
|
||||||
// Step 1: Set App Mode. Start getting all the data needed
|
try {
|
||||||
yield put(setAppMode(APP_MODE.EDIT));
|
yield put(setAppMode(APP_MODE.EDIT));
|
||||||
yield put({ type: ReduxActionTypes.START_EVALUATION });
|
yield put({ type: ReduxActionTypes.START_EVALUATION });
|
||||||
yield all([
|
yield all([
|
||||||
|
|
@ -55,24 +48,45 @@ function* initializeEditorSaga(
|
||||||
put(fetchPage(pageId)),
|
put(fetchPage(pageId)),
|
||||||
put(fetchApplication(applicationId, APP_MODE.EDIT)),
|
put(fetchApplication(applicationId, APP_MODE.EDIT)),
|
||||||
]);
|
]);
|
||||||
// Step 2: Wait for all data to be in the state
|
|
||||||
yield all([
|
const resultOfPrimaryCalls = yield race({
|
||||||
|
success: all([
|
||||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||||
take(ReduxActionTypes.FETCH_PAGE_SUCCESS),
|
take(ReduxActionTypes.FETCH_PAGE_SUCCESS),
|
||||||
take(ReduxActionTypes.SWITCH_CURRENT_PAGE_ID),
|
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
|
||||||
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
|
take(ReduxActionTypes.FETCH_ACTIONS_SUCCESS),
|
||||||
]);
|
]),
|
||||||
|
failure: take([
|
||||||
|
ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
|
||||||
|
ReduxActionErrorTypes.FETCH_PAGE_ERROR,
|
||||||
|
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
|
||||||
|
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resultOfPrimaryCalls.failure) {
|
||||||
|
yield put(initEditorError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Step 3: Call all the APIs which needs Organization Id from PageList API response.
|
|
||||||
yield all([put(fetchPlugins()), put(fetchDatasources())]);
|
yield all([put(fetchPlugins()), put(fetchDatasources())]);
|
||||||
|
|
||||||
// Step 4: Wait for all data to be in the state
|
const resultOfSecondaryCalls = yield race({
|
||||||
yield all([
|
success: all([
|
||||||
take(ReduxActionTypes.FETCH_PLUGINS_SUCCESS),
|
take(ReduxActionTypes.FETCH_PLUGINS_SUCCESS),
|
||||||
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
|
take(ReduxActionTypes.FETCH_DATASOURCES_SUCCESS),
|
||||||
]);
|
]),
|
||||||
|
failure: take([
|
||||||
|
ReduxActionErrorTypes.FETCH_PLUGINS_ERROR,
|
||||||
|
ReduxActionErrorTypes.FETCH_DATASOURCES_ERROR,
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resultOfSecondaryCalls.failure) {
|
||||||
|
yield put(initEditorError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Step 5: Set app store
|
|
||||||
yield put(updateAppStore(getAppStore(applicationId)));
|
yield put(updateAppStore(getAppStore(applicationId)));
|
||||||
|
|
||||||
const currentApplication = yield select(getCurrentApplication);
|
const currentApplication = yield select(getCurrentApplication);
|
||||||
|
|
@ -85,64 +99,17 @@ function* initializeEditorSaga(
|
||||||
appName: appName,
|
appName: appName,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Step 6: Notify UI that the editor is ready to go
|
|
||||||
yield put({
|
yield put({
|
||||||
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
|
||||||
});
|
});
|
||||||
|
} catch (e) {
|
||||||
|
yield put(initEditorError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
yield call(populatePageDSLsSaga);
|
yield call(populatePageDSLsSaga);
|
||||||
}
|
}
|
||||||
|
|
||||||
function* fetchPageDSLSaga(pageId: string) {
|
|
||||||
try {
|
|
||||||
const fetchPageResponse: FetchPageResponse = yield call(PageApi.fetchPage, {
|
|
||||||
id: pageId,
|
|
||||||
});
|
|
||||||
const isValidResponse = yield validateResponse(fetchPageResponse);
|
|
||||||
if (isValidResponse) {
|
|
||||||
return {
|
|
||||||
pageId: pageId,
|
|
||||||
dsl: extractCurrentDSL(fetchPageResponse),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
yield put({
|
|
||||||
type: ReduxActionTypes.FETCH_PAGE_DSL_ERROR,
|
|
||||||
payload: {
|
|
||||||
pageId: pageId,
|
|
||||||
error,
|
|
||||||
show: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function* populatePageDSLsSaga() {
|
|
||||||
try {
|
|
||||||
yield put({
|
|
||||||
type: ReduxActionTypes.POPULATE_PAGEDSLS_INIT,
|
|
||||||
});
|
|
||||||
const pageIds: string[] = yield select((state: AppState) =>
|
|
||||||
state.entities.pageList.pages.map((page: Page) => page.pageId),
|
|
||||||
);
|
|
||||||
const pageDSLs = yield all(
|
|
||||||
pageIds.map((pageId: string) => {
|
|
||||||
return call(fetchPageDSLSaga, pageId);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
yield put({
|
|
||||||
type: ReduxActionTypes.FETCH_PAGE_DSLS_SUCCESS,
|
|
||||||
payload: pageDSLs,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
yield put({
|
|
||||||
type: ReduxActionErrorTypes.POPULATE_PAGEDSLS_ERROR,
|
|
||||||
payload: {
|
|
||||||
error,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function* initializeAppViewerSaga(
|
export function* initializeAppViewerSaga(
|
||||||
action: ReduxAction<{ applicationId: string; pageId: string }>,
|
action: ReduxAction<{ applicationId: string; pageId: string }>,
|
||||||
) {
|
) {
|
||||||
|
|
@ -156,11 +123,23 @@ export function* initializeAppViewerSaga(
|
||||||
put(fetchApplication(applicationId, APP_MODE.PUBLISHED)),
|
put(fetchApplication(applicationId, APP_MODE.PUBLISHED)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
yield all([
|
const resultOfPrimaryCalls = yield race({
|
||||||
|
success: all([
|
||||||
take(ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS),
|
take(ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS),
|
||||||
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
take(ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS),
|
||||||
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
|
take(ReduxActionTypes.FETCH_APPLICATION_SUCCESS),
|
||||||
]);
|
]),
|
||||||
|
failure: take([
|
||||||
|
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
|
||||||
|
ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
|
||||||
|
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
|
||||||
|
]),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (resultOfPrimaryCalls.failure) {
|
||||||
|
yield put(initViewerError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
yield put(updateAppStore(getAppStore(applicationId)));
|
yield put(updateAppStore(getAppStore(applicationId)));
|
||||||
const defaultPageId = yield select(getDefaultPageId);
|
const defaultPageId = yield select(getDefaultPageId);
|
||||||
|
|
@ -168,7 +147,15 @@ export function* initializeAppViewerSaga(
|
||||||
|
|
||||||
if (toLoadPageId) {
|
if (toLoadPageId) {
|
||||||
yield put(fetchPublishedPage(toLoadPageId, true));
|
yield put(fetchPublishedPage(toLoadPageId, true));
|
||||||
yield take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS);
|
|
||||||
|
const resultOfFetchPage = yield race({
|
||||||
|
success: take(ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS),
|
||||||
|
failure: take(ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR),
|
||||||
|
});
|
||||||
|
if (resultOfFetchPage.failure) {
|
||||||
|
yield put(initViewerError());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
yield put(setAppMode(APP_MODE.PUBLISHED));
|
yield put(setAppMode(APP_MODE.PUBLISHED));
|
||||||
yield put(updateAppStore(getAppStore(applicationId)));
|
yield put(updateAppStore(getAppStore(applicationId)));
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
|
import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
|
||||||
import { AppState } from "reducers";
|
import { AppState } from "reducers";
|
||||||
import {
|
import {
|
||||||
|
Page,
|
||||||
PageListPayload,
|
PageListPayload,
|
||||||
ReduxAction,
|
ReduxAction,
|
||||||
ReduxActionErrorTypes,
|
ReduxActionErrorTypes,
|
||||||
|
|
@ -43,7 +44,6 @@ import {
|
||||||
select,
|
select,
|
||||||
takeLatest,
|
takeLatest,
|
||||||
takeLeading,
|
takeLeading,
|
||||||
take,
|
|
||||||
} from "redux-saga/effects";
|
} from "redux-saga/effects";
|
||||||
import history from "utils/history";
|
import history from "utils/history";
|
||||||
import { BUILDER_PAGE_URL } from "constants/routes";
|
import { BUILDER_PAGE_URL } from "constants/routes";
|
||||||
|
|
@ -117,6 +117,16 @@ export function* fetchPageListSaga(
|
||||||
PerformanceTracker.stopAsyncTracking(
|
PerformanceTracker.stopAsyncTracking(
|
||||||
PerformanceTransactionName.FETCH_PAGE_LIST_API,
|
PerformanceTransactionName.FETCH_PAGE_LIST_API,
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
PerformanceTracker.stopAsyncTracking(
|
||||||
|
PerformanceTransactionName.FETCH_PAGE_LIST_API,
|
||||||
|
);
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
|
||||||
|
payload: {
|
||||||
|
error: response.responseMeta.error,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
PerformanceTracker.stopAsyncTracking(
|
PerformanceTracker.stopAsyncTracking(
|
||||||
|
|
@ -608,6 +618,57 @@ export function* setDataUrl() {
|
||||||
yield put(setUrlData(urlData));
|
yield put(setUrlData(urlData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function* fetchPageDSLSaga(pageId: string) {
|
||||||
|
try {
|
||||||
|
const fetchPageResponse: FetchPageResponse = yield call(PageApi.fetchPage, {
|
||||||
|
id: pageId,
|
||||||
|
});
|
||||||
|
const isValidResponse = yield validateResponse(fetchPageResponse);
|
||||||
|
if (isValidResponse) {
|
||||||
|
return {
|
||||||
|
pageId: pageId,
|
||||||
|
dsl: extractCurrentDSL(fetchPageResponse),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.FETCH_PAGE_DSL_ERROR,
|
||||||
|
payload: {
|
||||||
|
pageId: pageId,
|
||||||
|
error,
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function* populatePageDSLsSaga() {
|
||||||
|
try {
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.POPULATE_PAGEDSLS_INIT,
|
||||||
|
});
|
||||||
|
const pageIds: string[] = yield select((state: AppState) =>
|
||||||
|
state.entities.pageList.pages.map((page: Page) => page.pageId),
|
||||||
|
);
|
||||||
|
const pageDSLs = yield all(
|
||||||
|
pageIds.map((pageId: string) => {
|
||||||
|
return call(fetchPageDSLSaga, pageId);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionTypes.FETCH_PAGE_DSLS_SUCCESS,
|
||||||
|
payload: pageDSLs,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
yield put({
|
||||||
|
type: ReduxActionErrorTypes.POPULATE_PAGEDSLS_ERROR,
|
||||||
|
payload: {
|
||||||
|
error,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default function* pageSagas() {
|
export default function* pageSagas() {
|
||||||
yield all([
|
yield all([
|
||||||
takeLatest(ReduxActionTypes.FETCH_PAGE_INIT, fetchPageSaga),
|
takeLatest(ReduxActionTypes.FETCH_PAGE_INIT, fetchPageSaga),
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,11 @@ export const getIsInitialized = createSelector(
|
||||||
(view: AppViewReduxState) => view.initialized,
|
(view: AppViewReduxState) => view.initialized,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const getIsInitializeError = createSelector(
|
||||||
|
getAppViewState,
|
||||||
|
(view: AppViewReduxState) => view.initializeError,
|
||||||
|
);
|
||||||
|
|
||||||
export const getCurrentDSLPageId = createSelector(
|
export const getCurrentDSLPageId = createSelector(
|
||||||
getPageListState,
|
getPageListState,
|
||||||
(pageList: PageListReduxState) => pageList.currentPageId,
|
(pageList: PageListReduxState) => pageList.currentPageId,
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,12 @@ import { getDataTree } from "selectors/dataTreeSelectors";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
import { ContainerWidgetProps } from "widgets/ContainerWidget";
|
||||||
import { DataTreeWidget, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
import { DataTreeWidget, ENTITY_TYPE } from "entities/DataTree/dataTreeFactory";
|
||||||
import { getActions, getWidgetsMeta } from "sagas/selectors";
|
import { getActions } from "sagas/selectors";
|
||||||
|
|
||||||
import * as log from "loglevel";
|
|
||||||
import PerformanceTracker, {
|
import PerformanceTracker, {
|
||||||
PerformanceTransactionName,
|
PerformanceTransactionName,
|
||||||
} from "utils/PerformanceTracker";
|
} from "utils/PerformanceTracker";
|
||||||
import { getCanvasWidgets } from "./entitiesSelector";
|
import { getCanvasWidgets } from "./entitiesSelector";
|
||||||
import { MetaState } from "../reducers/entityReducers/metaReducer";
|
|
||||||
import { WidgetTypes } from "../constants/WidgetConstants";
|
import { WidgetTypes } from "../constants/WidgetConstants";
|
||||||
|
|
||||||
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
|
const getWidgetConfigs = (state: AppState) => state.entities.widgetConfig;
|
||||||
|
|
@ -45,6 +43,9 @@ const getWidgets = (state: AppState): CanvasWidgetsReduxState =>
|
||||||
export const getIsEditorInitialized = (state: AppState) =>
|
export const getIsEditorInitialized = (state: AppState) =>
|
||||||
state.ui.editor.initialized;
|
state.ui.editor.initialized;
|
||||||
|
|
||||||
|
export const getIsEditorInitializeError = (state: AppState): boolean =>
|
||||||
|
state.ui.editor.initializationError;
|
||||||
|
|
||||||
export const getIsEditorLoading = (state: AppState) =>
|
export const getIsEditorLoading = (state: AppState) =>
|
||||||
state.ui.editor.loadingStates.loading;
|
state.ui.editor.loadingStates.loading;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user