fix: AppViewer init and page fetch logic (#14294)

OnPageSwitch we need not `initializeAppViewPage` but instead, only `fetchPublishedPage` this changes fixes that.
This commit is contained in:
Rishabh Rathod 2022-06-10 23:52:59 +05:30 committed by GitHub
parent 43cba21013
commit 4e13fc5125
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 248 additions and 161 deletions

View File

@ -11,14 +11,12 @@ import { QueryActionConfig } from "entities/Action";
export const FIRST_EVAL_REDUX_ACTIONS = [
// Pages
// ReduxActionTypes.FETCH_PAGE_SUCCESS,
// ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
ReduxActionTypes.FETCH_ALL_PAGE_ENTITY_COMPLETION,
];
export const EVALUATE_REDUX_ACTIONS = [
...FIRST_EVAL_REDUX_ACTIONS,
// Actions
ReduxActionTypes.FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_PLUGIN_FORM_CONFIGS_SUCCESS,
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,

View File

@ -1,3 +1,4 @@
import { APP_MODE } from "entities/App";
import {
ReduxActionTypes,
ReduxAction,
@ -7,6 +8,7 @@ export type InitializeEditorPayload = {
applicationId?: string;
pageId?: string;
branch?: string;
mode: APP_MODE;
};
export const initEditor = (
@ -16,6 +18,28 @@ export const initEditor = (
payload,
});
export type InitAppViewerPayload = {
branch: string;
applicationId: string;
pageId: string;
mode: APP_MODE;
};
export const initAppViewer = ({
applicationId,
branch,
mode,
pageId,
}: InitAppViewerPayload) => ({
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER,
payload: {
branch: branch,
applicationId,
pageId,
mode,
},
});
export const resetEditorRequest = () => ({
type: ReduxActionTypes.RESET_EDITOR_REQUEST,
});

View File

@ -714,7 +714,6 @@ export const ReduxActionTypes = {
"GET_SIMILAR_TEMPLATES_SUCCESS" /* This action constants is for identifying the status of the updates of the entities */,
ENTITY_UPDATE_STARTED: "ENTITY_UPDATE_STARTED",
ENTITY_UPDATE_SUCCESS: "ENTITY_UPDATE_SUCCESS",
FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS: "FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS",
SET_APP_VIEWER_HEADER_HEIGHT: "SET_APP_VIEWER_HEADER_HEIGHT",
UPDATE_BETA_CARD_SHOWN: "UPDATE_BETA_CARD_SHOWN",
CLOSE_BETA_CARD_SHOWN: "CLOSE_BETA_CARD_SHOWN",

View File

@ -8,7 +8,6 @@ import {
BuilderRouteParams,
GIT_BRANCH_QUERY_KEY,
} from "constants/routes";
import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants";
import {
getIsInitialized,
getAppViewHeaderHeight,
@ -41,6 +40,11 @@ import {
} from "actions/controlActions";
import { setAppViewHeaderHeight } from "actions/appViewActions";
import { showPostCompletionMessage } from "selectors/onboardingSelectors";
import { fetchPublishedPage } from "actions/pageActions";
import usePrevious from "utils/hooks/usePrevious";
import { getIsBranchUpdated } from "../utils";
import { APP_MODE } from "entities/App";
import { initAppViewer } from "actions/initActions";
import { getShowBrandingBadge } from "@appsmith/selectors/organizationSelectors";
const AppViewerBody = styled.section<{
@ -80,7 +84,7 @@ const DEFAULT_FONT_NAME = "System Default";
function AppViewer(props: Props) {
const dispatch = useDispatch();
const { search } = props.location;
const { pathname, search } = props.location;
const { applicationId, pageId } = props.match.params;
const [registered, setRegistered] = useState(false);
const isInitialized = useSelector(getIsInitialized);
@ -91,24 +95,65 @@ function AppViewer(props: Props) {
);
const showGuidedTourMessage = useSelector(showPostCompletionMessage);
const headerHeight = useSelector(getAppViewHeaderHeight);
const branch = getSearchQuery(search, GIT_BRANCH_QUERY_KEY);
const showBrandingBadge = useSelector(getShowBrandingBadge);
const branch = getSearchQuery(search, GIT_BRANCH_QUERY_KEY);
const prevValues = usePrevious({ branch, location: props.location, pageId });
/**
* initializes the widgets factory and registers all widgets
*/
useEffect(() => {
editorInitializer().then(() => setRegistered(true));
editorInitializer().then(() => {
setRegistered(true);
});
// onMount initPage
if (applicationId || pageId) {
dispatch(
initAppViewer({
applicationId,
branch,
pageId,
mode: APP_MODE.PUBLISHED,
}),
);
}
}, []);
/**
* initialize the app if branch, pageId or application is changed
*/
useEffect(() => {
if (applicationId || pageId) {
initializeAppViewerCallback(branch, applicationId, pageId);
const prevBranch = prevValues?.branch;
const prevLocation = prevValues?.location;
const prevPageId = prevValues?.pageId;
let isBranchUpdated = false;
if (prevBranch && prevLocation) {
isBranchUpdated = getIsBranchUpdated(props.location, prevLocation);
}
}, [branch, pageId, applicationId]);
const isPageIdUpdated = pageId !== prevPageId;
if (prevBranch && isBranchUpdated && (applicationId || pageId)) {
dispatch(
initAppViewer({
applicationId,
branch,
pageId,
mode: APP_MODE.PUBLISHED,
}),
);
} else {
/**
* First time load is handled by init sagas
* If we don't check for `prevPageId`: fetch page is retriggered
* when redirected to the default page
*/
if (prevPageId && pageId && isPageIdUpdated) {
dispatch(fetchPublishedPage(pageId, true));
}
}
}, [branch, pageId, applicationId, pathname]);
useEffect(() => {
const header = document.querySelector(".js-appviewer-header");
@ -141,24 +186,6 @@ function AppViewer(props: Props) {
document.body.style.fontFamily = appFontFamily;
}, [selectedTheme.properties.fontFamily.appFont]);
/**
* callback for initialize app
*/
const initializeAppViewerCallback = (
branch: string,
applicationId: string,
pageId: string,
) => {
dispatch({
type: ReduxActionTypes.INITIALIZE_PAGE_VIEWER,
payload: {
branch: branch,
applicationId,
pageId,
},
});
};
/**
* callback for executing an action
*/

View File

@ -53,6 +53,9 @@ import GuidedTourModal from "./GuidedTour/DeviationModal";
import { getPageLevelSocketRoomId } from "sagas/WebsocketSagas/utils";
import RepoLimitExceededErrorModal from "./gitSync/RepoLimitExceededErrorModal";
import ImportedApplicationSuccessModal from "./gitSync/ImportedAppSuccessModal";
import { getIsBranchUpdated } from "../utils";
import { APP_MODE } from "entities/App";
import { GIT_BRANCH_QUERY_KEY } from "constants/routes";
type EditorProps = {
currentApplicationId?: string;
@ -95,11 +98,16 @@ class Editor extends Component<Props> {
const {
location: { search },
} = this.props;
const branch = getSearchQuery(search, "branch");
const branch = getSearchQuery(search, GIT_BRANCH_QUERY_KEY);
const { applicationId, pageId } = this.props.match.params;
if (applicationId || pageId)
this.props.initEditor({ applicationId, pageId, branch });
if (pageId)
this.props.initEditor({
applicationId,
pageId,
branch,
mode: APP_MODE.EDIT,
});
this.props.handlePathUpdated(window.location);
this.unlisten = history.listen(this.handleHistoryChange);
@ -110,22 +118,11 @@ class Editor extends Component<Props> {
}
}
getIsBranchUpdated(props1: Props, props2: Props) {
const {
location: { search: search1 },
} = props1;
const {
location: { search: search2 },
} = props2;
const branch1 = getSearchQuery(search1, "branch");
const branch2 = getSearchQuery(search2, "branch");
return branch1 !== branch2;
}
shouldComponentUpdate(nextProps: Props, nextState: { registered: boolean }) {
const isBranchUpdated = this.getIsBranchUpdated(this.props, nextProps);
const isBranchUpdated = getIsBranchUpdated(
this.props.location,
nextProps.location,
);
return (
isBranchUpdated ||
@ -148,16 +145,30 @@ class Editor extends Component<Props> {
componentDidUpdate(prevProps: Props) {
const { applicationId, pageId } = this.props.match.params || {};
const { pageId: prevPageId } = prevProps.match.params || {};
const isBranchUpdated = this.getIsBranchUpdated(this.props, prevProps);
const isBranchUpdated = getIsBranchUpdated(
this.props.location,
prevProps.location,
);
const branch = getSearchQuery(this.props.location.search, "branch");
const prevBranch = getSearchQuery(prevProps.location.search, "branch");
const branch = getSearchQuery(
this.props.location.search,
GIT_BRANCH_QUERY_KEY,
);
const prevBranch = getSearchQuery(
prevProps.location.search,
GIT_BRANCH_QUERY_KEY,
);
const isPageIdUpdated = pageId !== prevPageId;
// to prevent re-init during connect
if (prevBranch && isBranchUpdated && (applicationId || pageId)) {
this.props.initEditor({ pageId, branch, applicationId });
if (prevBranch && isBranchUpdated && pageId) {
this.props.initEditor({
applicationId,
pageId,
branch,
mode: APP_MODE.EDIT,
});
} else {
/**
* First time load is handled by init sagas
@ -182,7 +193,7 @@ class Editor extends Component<Props> {
const {
location: { search },
} = this.props;
const branch = getSearchQuery(search, "branch");
const branch = getSearchQuery(search, GIT_BRANCH_QUERY_KEY);
this.props.resetEditorRequest();
if (typeof this.unlisten === "function") this.unlisten();
this.props.collabStopSharingPointerEvent(

View File

@ -0,0 +1,15 @@
import { getSearchQuery } from "utils/helpers";
import { Location } from "history";
export const getIsBranchUpdated = (
prevLocation: Location<unknown>,
currentLocation: Location<unknown>,
) => {
const { search: search1 } = prevLocation;
const { search: search2 } = currentLocation;
const branch1 = getSearchQuery(search1, "branch");
const branch2 = getSearchQuery(search2, "branch");
return branch1 !== branch2;
};

View File

@ -84,6 +84,7 @@ import { Org } from "constants/orgConstants";
import { log } from "loglevel";
import GIT_ERROR_CODES from "constants/GitErrorCodes";
import { builderURL } from "RouteBuilder";
import { APP_MODE } from "../entities/App";
export function* handleRepoLimitReachedError(response?: ApiResponse) {
const { responseMeta } = response || {};
@ -547,6 +548,7 @@ function* gitPullSaga(
initEditor({
pageId: currentPageId,
branch: currentBranch,
mode: APP_MODE.EDIT,
}),
);
}

View File

@ -57,17 +57,14 @@ import {
restoreRecentEntitiesRequest,
} from "actions/globalSearchActions";
import {
InitializeEditorPayload,
resetEditorSuccess,
InitializeEditorPayload,
InitAppViewerPayload,
} from "actions/initActions";
import PerformanceTracker, {
PerformanceTransactionName,
} from "utils/PerformanceTracker";
import {
getCurrentApplicationId,
getIsEditorInitialized,
getPageById,
} from "selectors/editorSelectors";
import { getIsEditorInitialized, getPageById } from "selectors/editorSelectors";
import { getIsInitialized as getIsViewerInitialized } from "selectors/appViewSelectors";
import { fetchCommentThreadsInit } from "actions/commentActions";
import { fetchJSCollectionsForView } from "actions/jsActionActions";
@ -127,11 +124,13 @@ export function* failFastApiCalls(
* @param initializeEditorAction
* @returns
*/
function* bootstrapEditor(payload: InitializeEditorPayload) {
const { branch } = payload;
yield put(resetEditorSuccess());
function* bootstrap(payload: InitializeEditorPayload) {
const { branch, mode } = payload;
if (mode === APP_MODE.EDIT) {
yield put(resetEditorSuccess());
}
yield put(updateBranchLocally(branch || ""));
yield put(setAppMode(APP_MODE.EDIT));
yield put(setAppMode(mode));
yield put({ type: ReduxActionTypes.START_EVALUATION });
}
@ -200,19 +199,18 @@ function* initiateURLUpdate(
}
}
function* initiateEditorApplicationAndPages(payload: InitializeEditorPayload) {
const pageId = payload.pageId;
const applicationId = payload.applicationId;
function* initiateApplication(payload: InitializeEditorPayload) {
const { applicationId, mode, pageId } = payload;
const applicationCall: boolean = yield failFastApiCalls(
[fetchApplication({ pageId, applicationId, mode: APP_MODE.EDIT })],
[fetchApplication({ pageId, applicationId, mode })],
[
ReduxActionTypes.FETCH_APPLICATION_SUCCESS,
ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
],
[
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
ReduxActionErrorTypes.FETCH_PAGE_ERROR,
ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
],
);
@ -222,34 +220,73 @@ function* initiateEditorApplicationAndPages(payload: InitializeEditorPayload) {
const defaultPageId: string = yield select(getDefaultPageId);
toLoadPageId = toLoadPageId || defaultPageId;
yield call(initiateURLUpdate, toLoadPageId, APP_MODE.EDIT, payload.pageId);
yield call(initiateURLUpdate, toLoadPageId, mode, payload.pageId);
return toLoadPageId;
}
function* initiateEditorActions(toLoadPageId: string, applicationId: string) {
const initActionsCalls = [
fetchPage(toLoadPageId, true),
fetchActions({ applicationId }, []),
fetchJSCollections({ applicationId }),
fetchSelectedAppThemeAction(applicationId),
fetchAppThemesAction(applicationId),
];
const successActionEffects = [
ReduxActionTypes.FETCH_JS_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_APP_THEMES_SUCCESS,
ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS,
fetchPageSuccess().type,
];
const failureActionEffects = [
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_APP_THEMES_ERROR,
ReduxActionErrorTypes.FETCH_SELECTED_APP_THEME_ERROR,
ReduxActionErrorTypes.FETCH_PAGE_ERROR,
];
function* initiatePageAndAllActions(
toLoadPageId: string,
applicationId: string,
mode: APP_MODE,
) {
let initActionsCalls = [];
let successActionEffects = [];
let failureActionEffects = [];
switch (mode) {
case APP_MODE.EDIT:
{
initActionsCalls = [
fetchPage(toLoadPageId, true),
fetchActions({ applicationId }, []),
fetchJSCollections({ applicationId }),
fetchSelectedAppThemeAction(applicationId),
fetchAppThemesAction(applicationId),
];
successActionEffects = [
ReduxActionTypes.FETCH_JS_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_ACTIONS_SUCCESS,
ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS,
fetchPageSuccess().type,
ReduxActionTypes.FETCH_APP_THEMES_SUCCESS,
];
failureActionEffects = [
ReduxActionErrorTypes.FETCH_JS_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_ACTIONS_ERROR,
ReduxActionErrorTypes.FETCH_SELECTED_APP_THEME_ERROR,
ReduxActionErrorTypes.FETCH_PAGE_ERROR,
ReduxActionErrorTypes.FETCH_APP_THEMES_ERROR,
];
}
break;
case APP_MODE.PUBLISHED:
{
initActionsCalls = [
fetchPublishedPage(toLoadPageId, true, true),
fetchActionsForView({ applicationId }),
fetchJSCollectionsForView({ applicationId }),
fetchSelectedAppThemeAction(applicationId),
fetchAppThemesAction(applicationId),
];
successActionEffects = [
fetchPublishedPageSuccess().type,
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionTypes.FETCH_JS_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS,
ReduxActionTypes.FETCH_APP_THEMES_SUCCESS,
];
failureActionEffects = [
ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR,
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
ReduxActionErrorTypes.FETCH_SELECTED_APP_THEME_ERROR,
ReduxActionErrorTypes.FETCH_APP_THEMES_ERROR,
];
}
break;
default:
return false;
}
const allActionCalls: boolean = yield failFastApiCalls(
initActionsCalls,
successActionEffects,
@ -257,15 +294,15 @@ function* initiateEditorActions(toLoadPageId: string, applicationId: string) {
);
if (!allActionCalls) {
return;
return false;
} else {
yield put({
type: ReduxActionTypes.FETCH_PLUGIN_AND_JS_ACTIONS_SUCCESS,
});
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
return true;
}
}
// Editor mode only
function* initiatePluginsAndDatasources() {
const pluginsAndDatasourcesCalls: boolean = yield failFastApiCalls(
[fetchPlugins(), fetchDatasources(), fetchMockDatasources()],
@ -289,7 +326,7 @@ function* initiatePluginsAndDatasources() {
);
if (!pluginFormCall) return;
}
// Editor mode only
function* initiateGit(applicationId: string) {
const branchInStore: string = yield select(getCurrentGitBranch);
@ -316,17 +353,15 @@ function* initializeEditorSaga(
initializeEditorAction: ReduxAction<InitializeEditorPayload>,
) {
try {
const { payload } = initializeEditorAction;
const { branch } = payload;
yield call(bootstrapEditor, payload);
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.INIT_EDIT_APP,
);
const { payload } = initializeEditorAction;
const { branch, mode } = initializeEditorAction.payload;
const toLoadPageId = yield call(initiateEditorApplicationAndPages, payload);
yield call(bootstrap, payload);
const toLoadPageId = yield call(initiateApplication, payload);
if (!toLoadPageId) return;
const { id: applicationId, name }: ApplicationPayload = yield select(
@ -338,7 +373,8 @@ function* initializeEditorSaga(
);
yield all([
call(initiateEditorActions, toLoadPageId, applicationId),
call(initiatePageAndAllActions, toLoadPageId, applicationId, mode),
// only in edit mode
call(initiatePluginsAndDatasources),
call(populatePageDSLsSaga),
]);
@ -348,10 +384,14 @@ function* initializeEditorSaga(
appName: name,
});
// only in edit mode
yield call(initiateGit, applicationId);
yield put(fetchCommentThreadsInit());
// For omnibar to show all entities search
// only in edit mode
yield put({
type: ReduxActionTypes.INITIALIZE_EDITOR_SUCCESS,
});
@ -373,75 +413,34 @@ function* initializeEditorSaga(
}
export function* initializeAppViewerSaga(
action: ReduxAction<{
branch: string;
pageId: string;
applicationId: string;
}>,
action: ReduxAction<InitAppViewerPayload>,
) {
const { branch, pageId } = action.payload;
let { applicationId } = action.payload;
PerformanceTracker.startAsyncTracking(
PerformanceTransactionName.INIT_VIEW_APP,
);
const { payload } = action;
const { branch, mode } = payload;
yield put(updateBranchLocally(branch || ""));
yield call(bootstrap, payload);
yield put(setAppMode(APP_MODE.PUBLISHED));
const applicationCall: boolean = yield failFastApiCalls(
[fetchApplication({ applicationId, pageId, mode: APP_MODE.PUBLISHED })],
[
ReduxActionTypes.FETCH_APPLICATION_SUCCESS,
ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
],
[
ReduxActionErrorTypes.FETCH_APPLICATION_ERROR,
ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
],
const toLoadPageId = yield call(initiateApplication, payload);
// only in edit mode
const { id: applicationId }: ApplicationPayload = yield select(
getCurrentApplication,
);
if (!applicationCall) return;
applicationId = applicationId || (yield select(getCurrentApplicationId));
yield put(
updateAppPersistentStore(getPersistentAppStore(applicationId, branch)),
);
yield put({ type: ReduxActionTypes.START_EVALUATION });
const defaultPageId: string = yield select(getDefaultPageId);
const toLoadPageId: string = pageId || defaultPageId;
yield call(initiateURLUpdate, toLoadPageId, APP_MODE.PUBLISHED, pageId);
const resultOfPrimaryCalls: boolean = yield failFastApiCalls(
[
fetchActionsForView({ applicationId }),
fetchJSCollectionsForView({ applicationId }),
fetchSelectedAppThemeAction(applicationId),
fetchAppThemesAction(applicationId),
fetchPublishedPage(toLoadPageId, true, true),
],
[
ReduxActionTypes.FETCH_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionTypes.FETCH_JS_ACTIONS_VIEW_MODE_SUCCESS,
ReduxActionTypes.FETCH_APP_THEMES_SUCCESS,
ReduxActionTypes.FETCH_SELECTED_APP_THEME_SUCCESS,
fetchPublishedPageSuccess().type,
],
[
ReduxActionErrorTypes.FETCH_ACTIONS_VIEW_MODE_ERROR,
ReduxActionErrorTypes.FETCH_JS_ACTIONS_VIEW_MODE_ERROR,
ReduxActionErrorTypes.FETCH_APP_THEMES_ERROR,
ReduxActionErrorTypes.FETCH_SELECTED_APP_THEME_ERROR,
ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR,
],
const pageAndActionsFetch = yield call(
initiatePageAndAllActions,
toLoadPageId,
applicationId,
mode,
);
if (!resultOfPrimaryCalls) return;
if (!pageAndActionsFetch) return;
yield put(fetchAllPageEntityCompletion([executePageLoadActions()]));
yield put(fetchCommentThreadsInit());

View File

@ -0,0 +1,12 @@
import { useEffect, useRef } from "react";
// Make sure to use this hook at the start of functional component
const usePrevious = <T,>(value: T): T | undefined => {
const ref = useRef<T>();
useEffect(() => {
ref.current = value;
});
return ref.current;
};
export default usePrevious;

View File

@ -98,7 +98,7 @@ export const syntheticTestMouseEvent = (
export function MockApplication({ children }: any) {
editorInitializer();
const dispatch = useDispatch();
dispatch(initEditor({ pageId: "page_id" }));
dispatch(initEditor({ pageId: "page_id", mode: APP_MODE.EDIT }));
const mockResp: any = {
organizationId: "org_id",
pages: [{ id: "page_id", name: "Page1", isDefault: true }],