PromucFlow_constructor/app/client/src/sagas/PageSagas.tsx
2020-05-05 12:16:51 +00:00

424 lines
12 KiB
TypeScript

import CanvasWidgetsNormalizer from "normalizers/CanvasWidgetsNormalizer";
import { AppState } from "reducers";
import {
ReduxActionTypes,
ReduxActionErrorTypes,
ReduxAction,
UpdateCanvasPayload,
PageListPayload,
FetchPageListPayload,
} from "constants/ReduxActionConstants";
import {
updateCanvas,
savePageSuccess,
fetchPageSuccess,
updateWidgetNameSuccess,
deletePageSuccess,
updateCurrentPage,
fetchPublishedPageSuccess,
} from "actions/pageActions";
import PageApi, {
FetchPageResponse,
SavePageResponse,
FetchPageRequest,
FetchPublishedPageRequest,
FetchPublishedPageResponse,
CreatePageRequest,
FetchPageListResponse,
UpdatePageRequest,
DeletePageRequest,
UpdateWidgetNameRequest,
UpdateWidgetNameResponse,
} from "api/PageApi";
import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
import {
call,
select,
put,
takeLatest,
all,
debounce,
} from "redux-saga/effects";
import history from "utils/history";
import { PAGE_LIST_EDITOR_URL } from "constants/routes";
import { extractCurrentDSL } from "utils/WidgetPropsUtils";
import { getEditorConfigs, getWidgets, getAllPageIds } from "./selectors";
import { validateResponse } from "./ErrorSagas";
import { executePageLoadActions } from "actions/widgetActions";
import { ApiResponse } from "api/ApiResponses";
import {
getCurrentPageId,
getCurrentLayoutId,
getCurrentApplicationId,
getCurrentPageName,
} from "selectors/editorSelectors";
import { fetchActionsForPage } from "actions/actionActions";
import {
getExistingWidgetNames,
getExistingPageNames,
getExistingActionNames,
} from "./selectors";
import { clearCaches } from "utils/DynamicBindingUtils";
const getWidgetName = (state: AppState, widgetId: string) =>
state.entities.canvasWidgets[widgetId];
export function* fetchPageListSaga(
fetchPageListAction: ReduxAction<FetchPageListPayload>,
) {
try {
const { applicationId } = fetchPageListAction.payload;
const response: FetchPageListResponse = yield call(
PageApi.fetchPageList,
applicationId,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const pages: PageListPayload = response.data.map(page => ({
pageName: page.name,
pageId: page.id,
isDefault: page.isDefault,
}));
yield put({
type: ReduxActionTypes.FETCH_PAGE_LIST_SUCCESS,
payload: {
pages,
applicationId,
},
});
return;
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_PAGE_LIST_ERROR,
payload: {
error,
},
});
}
}
const getCanvasWidgetsPayload = (
pageResponse: FetchPageResponse,
): UpdateCanvasPayload => {
const normalizedResponse = CanvasWidgetsNormalizer.normalize(
extractCurrentDSL(pageResponse),
);
return {
pageWidgetId: normalizedResponse.result,
currentPageName: pageResponse.data.name,
currentPageId: pageResponse.data.id,
widgets: normalizedResponse.entities.canvasWidgets,
currentLayoutId: pageResponse.data.layouts[0].id, // TODO(abhinav): Handle for multiple layouts
currentApplicationId: pageResponse.data.applicationId,
pageActions: pageResponse.data.layouts[0].layoutOnLoadActions || [],
};
};
export function* fetchPageSaga(
pageRequestAction: ReduxAction<FetchPageRequest>,
) {
try {
const { pageId } = pageRequestAction.payload;
const fetchPageResponse: FetchPageResponse = yield call(PageApi.fetchPage, {
pageId,
});
const isValidResponse = yield validateResponse(fetchPageResponse);
if (isValidResponse) {
// Clear any existing caches
clearCaches();
// Get Canvas payload
const canvasWidgetsPayload = getCanvasWidgetsPayload(fetchPageResponse);
// Update the canvas
yield put(updateCanvas(canvasWidgetsPayload));
// set current page
yield put(updateCurrentPage(pageId));
// dispatch fetch page success
yield put(fetchPageSuccess());
// Execute page load actions
yield put(executePageLoadActions(canvasWidgetsPayload.pageActions));
}
} catch (error) {
console.log(error);
yield put({
type: ReduxActionErrorTypes.FETCH_PAGE_ERROR,
payload: {
error,
},
});
}
}
export function* fetchPublishedPageSaga(
pageRequestAction: ReduxAction<{ pageId: string; bustCache: boolean }>,
) {
try {
const { pageId, bustCache } = pageRequestAction.payload;
const request: FetchPublishedPageRequest = {
pageId,
bustCache,
};
const response: FetchPublishedPageResponse = yield call(
PageApi.fetchPublishedPage,
request,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
// Clear any existing caches
clearCaches();
const canvasWidgetsPayload = getCanvasWidgetsPayload(response);
yield put(updateCanvas(canvasWidgetsPayload));
yield put(updateCurrentPage(pageId));
yield put(
fetchPublishedPageSuccess({
dsl: response.data.layouts[0].dsl,
pageId: request.pageId,
pageWidgetId: canvasWidgetsPayload.pageWidgetId,
}),
);
// Execute page load actions
yield put(executePageLoadActions(canvasWidgetsPayload.pageActions));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.FETCH_PUBLISHED_PAGE_ERROR,
payload: {
error,
},
});
}
}
export function* fetchAllPublishedPagesSaga() {
try {
const pageIds = yield select(getAllPageIds);
yield all(
pageIds.map((pageId: string) => {
return call(PageApi.fetchPublishedPage, { pageId });
}),
);
} catch (error) {
console.log({ error });
}
}
function* savePageSaga() {
const widgets = yield select(getWidgets);
const editorConfigs = yield select(getEditorConfigs) as any;
const savePageRequest = getLayoutSavePayload(widgets, editorConfigs);
try {
const savePageResponse: SavePageResponse = yield call(
PageApi.savePage,
savePageRequest,
);
const isValidResponse = yield validateResponse(savePageResponse);
if (isValidResponse) {
yield put(savePageSuccess(savePageResponse));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.SAVE_PAGE_ERROR,
payload: {
error,
},
});
}
}
function getLayoutSavePayload(
widgets: {
[widgetId: string]: FlattenedWidgetProps;
},
editorConfigs: any,
) {
const denormalizedDSL = CanvasWidgetsNormalizer.denormalize(
Object.keys(widgets)[0],
{ canvasWidgets: widgets },
);
return {
...editorConfigs,
dsl: denormalizedDSL,
};
}
export function* saveLayoutSaga() {
try {
yield put({
type: ReduxActionTypes.SAVE_PAGE_INIT,
});
} catch (error) {
yield put({
type: ReduxActionErrorTypes.SAVE_PAGE_ERROR,
payload: {
error,
},
});
}
}
export function* createPageSaga(
createPageAction: ReduxAction<CreatePageRequest>,
) {
try {
const request: CreatePageRequest = createPageAction.payload;
const response: FetchPageResponse = yield call(PageApi.createPage, request);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.CREATE_PAGE_SUCCESS,
payload: {
pageId: response.data.id,
pageName: response.data.name,
layoutId: response.data.layouts[0].id,
},
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.CREATE_PAGE_ERROR,
payload: {
error,
},
});
}
}
export function* updatePageSaga(action: ReduxAction<UpdatePageRequest>) {
try {
const request: UpdatePageRequest = action.payload;
const response: ApiResponse = yield call(PageApi.updatePage, request);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put({
type: ReduxActionTypes.UPDATE_PAGE_SUCCESS,
payload: action.payload,
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.UPDATE_PAGE_ERROR,
payload: {
error,
},
});
}
}
export function* deletePageSaga(action: ReduxAction<DeletePageRequest>) {
try {
const request: DeletePageRequest = action.payload;
const defaultPageId = yield select(
(state: AppState) => state.entities.pageList.defaultPageId,
);
const applicationId = yield select(
(state: AppState) => state.entities.pageList.applicationId,
);
if (defaultPageId === request.pageId) {
throw Error("Cannot delete the home page.");
} else {
const response: ApiResponse = yield call(PageApi.deletePage, request);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
yield put(deletePageSuccess());
}
history.push(PAGE_LIST_EDITOR_URL(applicationId, defaultPageId));
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.DELETE_PAGE_ERROR,
payload: {
error,
},
});
}
}
export function* updateWidgetNameSaga(
action: ReduxAction<{ widgetId: string; newName: string }>,
) {
try {
const { widgetName } = yield select(getWidgetName, action.payload.widgetId);
const layoutId = yield select(getCurrentLayoutId);
const pageId = yield select(getCurrentPageId);
const existingWidgetNames = yield select(getExistingWidgetNames);
const existingActionNames = yield select(getExistingActionNames);
const existingPageNames = yield select(getExistingPageNames);
const hasWidgetNameConflict =
existingWidgetNames.indexOf(action.payload.newName) > -1 ||
existingActionNames.indexOf(action.payload.newName) > -1 ||
existingPageNames.indexOf(action.payload.newName) > -1;
if (!hasWidgetNameConflict) {
const request: UpdateWidgetNameRequest = {
newName: action.payload.newName,
oldName: widgetName,
pageId,
layoutId,
};
const response: UpdateWidgetNameResponse = yield call(
PageApi.updateWidgetName,
request,
);
const isValidResponse = yield validateResponse(response);
if (isValidResponse) {
const normalizedWidgets = CanvasWidgetsNormalizer.normalize(
response.data.dsl,
);
const currentPageName = yield select(getCurrentPageName);
const applicationId = yield select(getCurrentApplicationId);
const canvasWidgetsPayload: UpdateCanvasPayload = {
pageWidgetId: normalizedWidgets.result,
currentPageName,
currentPageId: pageId,
currentLayoutId: layoutId,
currentApplicationId: applicationId,
pageActions: response.data.layoutOnLoadActions,
widgets: normalizedWidgets.entities.canvasWidgets,
};
yield put(updateCanvas(canvasWidgetsPayload));
yield put(fetchActionsForPage(pageId));
yield put(updateWidgetNameSuccess());
}
} else {
yield put({
type: ReduxActionErrorTypes.UPDATE_WIDGET_NAME_ERROR,
payload: {
error: {
message: `Entity name: ${action.payload.newName} is already being used.`,
},
},
});
}
} catch (error) {
yield put({
type: ReduxActionErrorTypes.UPDATE_WIDGET_NAME_ERROR,
payload: {
error,
},
});
}
}
export default function* pageSagas() {
yield all([
takeLatest(ReduxActionTypes.FETCH_PAGE_INIT, fetchPageSaga),
takeLatest(
ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
fetchPublishedPageSaga,
),
takeLatest(ReduxActionTypes.UPDATE_LAYOUT, saveLayoutSaga),
takeLatest(ReduxActionTypes.CREATE_PAGE_INIT, createPageSaga),
takeLatest(ReduxActionTypes.FETCH_PAGE_LIST_INIT, fetchPageListSaga),
takeLatest(ReduxActionTypes.UPDATE_PAGE_INIT, updatePageSaga),
takeLatest(ReduxActionTypes.DELETE_PAGE_INIT, deletePageSaga),
debounce(500, ReduxActionTypes.SAVE_PAGE_INIT, savePageSaga),
takeLatest(ReduxActionTypes.UPDATE_WIDGET_NAME_INIT, updateWidgetNameSaga),
takeLatest(
ReduxActionTypes.FETCH_ALL_PUBLISHED_PAGES,
fetchAllPublishedPagesSaga,
),
]);
}