[](https://workerb.linearb.io/v2/badge/collaboration-page?magicLinkId=M7zehz4) ## Description Cleaning up three patterns of checks to enable wds and anvil into two. wds and anvil had to have different flags coz anvil had to play catch up with wds, now that's not the case so it does not make sense to have two flags. Old patterns - checking if the wds feature flag is enabled - checking if the anvil feature flag is enabled - checking if the layout system of the app is anvil New Pattern - checking if anvil feature flag is enabled (used only for creating an anvil app) - checking if layout system of the app is anvil Fixes #32590 _or_ Fixes `Issue URL` > [!WARNING] > _If no issue exists, please create an issue first, and check with the maintainers if the issue is valid._ ## Automation /ok-to-test tags="@tag.All" ### 🔍 Cypress test results <!-- This is an auto-generated comment: Cypress test results --> > [!TIP] > 🟢 🟢 🟢 All cypress tests have passed! 🎉 🎉 🎉 > Workflow run: <https://github.com/appsmithorg/appsmith/actions/runs/8663918496> > Commit: e10cc2a84ed680b29c49c5b2e8175df4c18da2f8 > Cypress dashboard url: <a href="https://internal.appsmith.com/app/cypress-dashboard/rundetails-65890b3c81d7400d08fa9ee5?branch=master&workflowId=8663918496&attempt=1" target="_blank">Click here!</a> <!-- end of auto-generated comment: Cypress test results --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Consolidated the usage of layout system checks across the application to use a unified Anvil layout selector, enhancing consistency in layout-related conditional logic. - **Bug Fixes** - Removed outdated feature flags related to the Anvil + WDS integration, ensuring the application's feature toggling aligns with the current development strategy. - **Tests** - Updated unit tests to align with the new method of layout system determination, ensuring test environments accurately reflect production behavior. - **Chores** - Cleaned up redundant code and feature flags that are no longer in use, simplifying the codebase and reducing potential for errors. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
368 lines
11 KiB
TypeScript
368 lines
11 KiB
TypeScript
import {
|
|
all,
|
|
call,
|
|
delay,
|
|
put,
|
|
select,
|
|
takeEvery,
|
|
takeLatest,
|
|
} from "redux-saga/effects";
|
|
|
|
import { generateReactKey } from "utils/generators";
|
|
import type { ModalWidgetResize, WidgetAddChild } from "actions/pageActions";
|
|
import { updateAndSaveLayout } from "actions/pageActions";
|
|
import {
|
|
GridDefaults,
|
|
MAIN_CONTAINER_WIDGET_ID,
|
|
} from "constants/WidgetConstants";
|
|
import type { ReduxAction } from "@appsmith/constants/ReduxActionConstants";
|
|
import {
|
|
ReduxActionErrorTypes,
|
|
ReduxActionTypes,
|
|
WidgetReduxActionTypes,
|
|
} from "@appsmith/constants/ReduxActionConstants";
|
|
|
|
import {
|
|
getWidget,
|
|
getWidgetByName,
|
|
getWidgetIdsByType,
|
|
getWidgetMetaProps,
|
|
getWidgets,
|
|
getWidgetsMeta,
|
|
} from "sagas/selectors";
|
|
import type {
|
|
CanvasWidgetsReduxState,
|
|
FlattenedWidgetProps,
|
|
} from "reducers/entityReducers/canvasWidgetsReducer";
|
|
import { updateWidgetMetaPropAndEval } from "actions/metaActions";
|
|
import { focusWidget, showModal } from "actions/widgetActions";
|
|
import log from "loglevel";
|
|
import { flatten } from "lodash";
|
|
import WidgetFactory from "WidgetProvider/factory";
|
|
import type { WidgetProps } from "widgets/BaseWidget";
|
|
import { selectWidgetInitAction } from "actions/widgetSelectionActions";
|
|
import { SelectionRequestType } from "./WidgetSelectUtils";
|
|
import { toast } from "design-system";
|
|
import { getIsAutoLayout } from "selectors/editorSelectors";
|
|
import { recalculateAutoLayoutColumnsAndSave } from "./AutoLayoutUpdateSagas";
|
|
import {
|
|
FlexLayerAlignment,
|
|
LayoutDirection,
|
|
} from "layoutSystems/common/utils/constants";
|
|
import { getModalWidgetType } from "selectors/widgetSelectors";
|
|
import { AnvilReduxActionTypes } from "layoutSystems/anvil/integrations/actions/actionTypes";
|
|
import { getWidgetSelectionBlock } from "selectors/ui";
|
|
import { getIsAnvilLayout } from "layoutSystems/anvil/integrations/selectors";
|
|
|
|
const WidgetTypes = WidgetFactory.widgetTypes;
|
|
|
|
export function* createModalSaga(action: ReduxAction<{ modalName: string }>) {
|
|
try {
|
|
const modalWidgetId = generateReactKey();
|
|
const isAutoLayout: boolean = yield select(getIsAutoLayout);
|
|
const modalWidgetType: string = yield select(getModalWidgetType);
|
|
const isAnvilLayout: boolean = yield select(getIsAnvilLayout);
|
|
const newWidget: WidgetAddChild = {
|
|
widgetId: MAIN_CONTAINER_WIDGET_ID,
|
|
widgetName: action.payload.modalName,
|
|
type: modalWidgetType,
|
|
newWidgetId: modalWidgetId,
|
|
parentRowSpace: 1,
|
|
parentColumnSpace: 1,
|
|
leftColumn: 0,
|
|
topRow: 0,
|
|
columns: 0,
|
|
rows: 0,
|
|
tabId: "",
|
|
};
|
|
|
|
if (isAutoLayout) {
|
|
const dropPayload = {
|
|
alignment: FlexLayerAlignment.Center,
|
|
index: 0,
|
|
isNewLayer: true,
|
|
layerIndex: 0,
|
|
rowIndex: 0,
|
|
};
|
|
newWidget.props = {
|
|
alignment: FlexLayerAlignment.Center,
|
|
};
|
|
|
|
yield put({
|
|
type: ReduxActionTypes.AUTOLAYOUT_ADD_NEW_WIDGETS,
|
|
payload: {
|
|
dropPayload,
|
|
newWidget,
|
|
parentId: MAIN_CONTAINER_WIDGET_ID,
|
|
direction: LayoutDirection.Vertical,
|
|
addToBottom: true,
|
|
},
|
|
});
|
|
} else if (isAnvilLayout) {
|
|
//TODO(#30604): Refactor to separate this logic from the anvil layout system
|
|
yield put({
|
|
type: AnvilReduxActionTypes.ANVIL_ADD_NEW_WIDGET,
|
|
payload: {
|
|
highlight: { alignment: "none", canvasId: "0" },
|
|
newWidget: { ...newWidget, detachFromLayout: true },
|
|
dragMeta: {
|
|
draggedWidgetTypes: "WIDGETS",
|
|
draggedOn: "MAIN_CANVAS",
|
|
},
|
|
},
|
|
});
|
|
} else {
|
|
yield put({
|
|
type: WidgetReduxActionTypes.WIDGET_ADD_CHILD,
|
|
payload: newWidget,
|
|
});
|
|
}
|
|
} catch (error) {
|
|
log.error(error);
|
|
yield put({
|
|
type: ReduxActionErrorTypes.CREATE_MODAL_ERROR,
|
|
payload: { error },
|
|
});
|
|
}
|
|
}
|
|
|
|
export function* showModalByNameSaga(
|
|
action: ReduxAction<{ modalName: string }>,
|
|
) {
|
|
const widgets: { [widgetId: string]: FlattenedWidgetProps } =
|
|
yield select(getWidgets);
|
|
const modal: FlattenedWidgetProps | undefined = Object.values(widgets).find(
|
|
(widget: FlattenedWidgetProps) =>
|
|
widget.widgetName === action.payload.modalName,
|
|
);
|
|
if (modal) {
|
|
yield put(showModal(modal.widgetId));
|
|
}
|
|
}
|
|
|
|
export function* showIfModalSaga(
|
|
action: ReduxAction<{ widgetId: string; type: string }>,
|
|
) {
|
|
if (action.payload.type === "MODAL_WIDGET") {
|
|
yield put(showModal(action.payload.widgetId));
|
|
}
|
|
}
|
|
|
|
export function* showModalSaga(action: ReduxAction<{ modalId: string }>) {
|
|
// First we close the currently open modals (if any)
|
|
// Notice the empty payload.
|
|
yield call(closeModalSaga, {
|
|
type: ReduxActionTypes.CLOSE_MODAL,
|
|
payload: {
|
|
exclude: action.payload.modalId,
|
|
},
|
|
});
|
|
|
|
yield put(focusWidget(action.payload.modalId));
|
|
|
|
const widgetLikeProps = {
|
|
widgetId: action.payload.modalId,
|
|
} as WidgetProps;
|
|
const metaProps: Record<string, unknown> = yield select(
|
|
getWidgetMetaProps,
|
|
widgetLikeProps,
|
|
);
|
|
if (!metaProps || !metaProps.isVisible) {
|
|
// Then show the modal we would like to show.
|
|
yield put(
|
|
updateWidgetMetaPropAndEval(action.payload.modalId, "isVisible", true),
|
|
);
|
|
yield delay(1000);
|
|
}
|
|
yield put({
|
|
type: ReduxActionTypes.SHOW_PROPERTY_PANE,
|
|
payload: {
|
|
widgetId: action.payload.modalId,
|
|
callForDragOrResize: undefined,
|
|
force: true,
|
|
},
|
|
});
|
|
}
|
|
|
|
export function* closeModalSaga(
|
|
action: ReduxAction<{ modalName?: string; exclude?: string }>,
|
|
) {
|
|
try {
|
|
const { modalName } = action.payload;
|
|
|
|
let widgetIds: string[] = [];
|
|
// If modalName is provided, we just want to close this modal
|
|
if (modalName) {
|
|
const widget: FlattenedWidgetProps | undefined = yield select(
|
|
getWidgetByName,
|
|
modalName,
|
|
);
|
|
widgetIds = widget ? [widget.widgetId] : [];
|
|
yield put({
|
|
type: ReduxActionTypes.SHOW_PROPERTY_PANE,
|
|
payload: {},
|
|
});
|
|
} else {
|
|
// If modalName is not provided, find all open modals
|
|
// Get all meta prop records
|
|
const metaProps: Record<string, any> = yield select(getWidgetsMeta);
|
|
const modalWidgetType: string = yield select(getModalWidgetType);
|
|
|
|
// Get widgetIds of all widgets of type MODAL_WIDGET
|
|
// Note: Not updating this code path for WDS_MODAL_WIDGET, as the functionality
|
|
// may require us to keep existing modals open.
|
|
// In this, the flow of switching back and forth between multiple modals is to be tested.
|
|
const modalWidgetIds: string[] = yield select(
|
|
getWidgetIdsByType,
|
|
modalWidgetType,
|
|
);
|
|
|
|
// Loop through all modal widgetIds
|
|
modalWidgetIds.forEach((widgetId: string) => {
|
|
// Check if modal is open
|
|
if (metaProps[widgetId] && metaProps[widgetId].isVisible) {
|
|
// Add to our list of widgetIds
|
|
widgetIds.push(widgetId);
|
|
}
|
|
});
|
|
}
|
|
widgetIds = action.payload.exclude
|
|
? widgetIds.filter((id: string) => id !== action.payload.exclude)
|
|
: widgetIds;
|
|
// If we have modals to close, set its isVisible to false to close.
|
|
if (widgetIds) {
|
|
yield all(
|
|
flatten(
|
|
widgetIds.map((widgetId: string) => {
|
|
return [
|
|
put(updateWidgetMetaPropAndEval(widgetId, "isVisible", false)),
|
|
];
|
|
}),
|
|
),
|
|
);
|
|
}
|
|
if (modalName) {
|
|
const isWidgetSelectionBlocked: boolean = yield select(
|
|
getWidgetSelectionBlock,
|
|
);
|
|
if (!isWidgetSelectionBlocked) {
|
|
yield put(selectWidgetInitAction(SelectionRequestType.Empty));
|
|
yield put(focusWidget(MAIN_CONTAINER_WIDGET_ID));
|
|
}
|
|
}
|
|
} catch (error) {
|
|
log.error(error);
|
|
}
|
|
}
|
|
|
|
export function* resizeModalSaga(resizeAction: ReduxAction<ModalWidgetResize>) {
|
|
try {
|
|
toast.dismiss();
|
|
const start = performance.now();
|
|
const { canvasWidgetId, height, widgetId, width } = resizeAction.payload;
|
|
|
|
const stateWidget: FlattenedWidgetProps = yield select(getWidget, widgetId);
|
|
const stateWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
|
|
const isAutoLayout: boolean = yield select(getIsAutoLayout);
|
|
|
|
let widget = { ...stateWidget };
|
|
const widgets = { ...stateWidgets };
|
|
|
|
widget = { ...widget, height, width };
|
|
widgets[widgetId] = widget;
|
|
|
|
if (canvasWidgetId) {
|
|
const bottomRow = getModalCanvasBottomRow(
|
|
widgets,
|
|
canvasWidgetId,
|
|
height,
|
|
);
|
|
const stateModalContainerWidget: FlattenedWidgetProps = yield select(
|
|
getWidget,
|
|
canvasWidgetId,
|
|
);
|
|
let modalContainerWidget = { ...stateModalContainerWidget };
|
|
|
|
modalContainerWidget = {
|
|
...modalContainerWidget,
|
|
bottomRow,
|
|
minHeight: height,
|
|
};
|
|
|
|
widgets[canvasWidgetId] = modalContainerWidget;
|
|
}
|
|
|
|
log.debug("resize computations took", performance.now() - start, "ms");
|
|
//TODO Identify the updated widgets and pass the values
|
|
if (isAutoLayout) {
|
|
yield call(recalculateAutoLayoutColumnsAndSave, widgets);
|
|
yield put({
|
|
type: ReduxActionTypes.PROCESS_AUTO_LAYOUT_DIMENSION_UPDATES,
|
|
});
|
|
} else {
|
|
yield put(updateAndSaveLayout(widgets));
|
|
}
|
|
} catch (error) {
|
|
yield put({
|
|
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
|
|
payload: {
|
|
action: WidgetReduxActionTypes.WIDGET_RESIZE,
|
|
error,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Note: returns bottomRow of the lowest widget on the canvas
|
|
* @param finalWidgets
|
|
* @param parentId
|
|
* @param height
|
|
*/
|
|
const getModalCanvasBottomRow = (
|
|
finalWidgets: CanvasWidgetsReduxState,
|
|
parentId: string,
|
|
height: number,
|
|
): number => {
|
|
if (
|
|
!finalWidgets[parentId] ||
|
|
finalWidgets[parentId].type !== WidgetTypes.CANVAS_WIDGET
|
|
) {
|
|
return height;
|
|
}
|
|
const lowestBottomRowHeight =
|
|
height -
|
|
GridDefaults.CANVAS_EXTENSION_OFFSET *
|
|
GridDefaults.DEFAULT_GRID_ROW_HEIGHT -
|
|
GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
|
|
|
let lowestBottomRow = Math.ceil(
|
|
lowestBottomRowHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
|
);
|
|
const childIds = finalWidgets[parentId].children || [];
|
|
|
|
// find lowest row
|
|
childIds.forEach((cId: string) => {
|
|
const child = finalWidgets[cId];
|
|
|
|
if (child.bottomRow > lowestBottomRow) {
|
|
lowestBottomRow = child.bottomRow;
|
|
}
|
|
});
|
|
return (
|
|
(lowestBottomRow + GridDefaults.CANVAS_EXTENSION_OFFSET) *
|
|
GridDefaults.DEFAULT_GRID_ROW_HEIGHT
|
|
);
|
|
};
|
|
|
|
export default function* modalSagas() {
|
|
yield all([
|
|
takeEvery(ReduxActionTypes.CLOSE_MODAL, closeModalSaga),
|
|
takeLatest(ReduxActionTypes.CREATE_MODAL_INIT, createModalSaga),
|
|
takeLatest(ReduxActionTypes.SHOW_MODAL, showModalSaga),
|
|
takeLatest(ReduxActionTypes.SHOW_MODAL_BY_NAME, showModalByNameSaga),
|
|
takeLatest(WidgetReduxActionTypes.WIDGET_CHILD_ADDED, showIfModalSaga),
|
|
takeLatest(WidgetReduxActionTypes.WIDGET_MODAL_RESIZE, resizeModalSaga),
|
|
]);
|
|
}
|