Merge branch 'release' of https://github.com/appsmithorg/appsmith into release

This commit is contained in:
Automated Github Action 2020-09-30 13:06:34 +00:00
commit b027c57ad0
6 changed files with 136 additions and 73 deletions

View File

@ -8,7 +8,7 @@ import {
SavePageSuccessPayload,
FetchPageListPayload,
} from "constants/ReduxActionConstants";
import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import { ContainerWidgetProps } from "widgets/ContainerWidget";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { APP_MODE, UrlDataState } from "reducers/entityReducers/appReducer";
@ -85,7 +85,7 @@ export const deletePageSuccess = () => {
};
};
export const updateAndSaveLayout = (widgets: FlattenedWidgetProps) => {
export const updateAndSaveLayout = (widgets: CanvasWidgetsReduxState) => {
return {
type: ReduxActionTypes.UPDATE_LAYOUT,
payload: { widgets },

View File

@ -139,7 +139,7 @@ export class DataTreeFactory {
widget.type,
);
const derivedProps: any = {};
const dynamicBindings = widget.dynamicBindings || {};
const dynamicBindings = { ...widget.dynamicBindings } || {};
Object.keys(dynamicBindings).forEach(propertyName => {
if (_.isObject(widget[propertyName])) {
// Stringify this because composite controls may have bindings in the sub controls

View File

@ -1,4 +1,4 @@
import { createReducer } from "utils/AppsmithUtils";
import { createImmerReducer } from "utils/AppsmithUtils";
import {
ReduxActionTypes,
UpdateCanvasPayload,
@ -13,31 +13,25 @@ export type FlattenedWidgetProps = WidgetProps & {
children?: string[];
};
const canvasWidgetsReducer = createReducer(initialState, {
const canvasWidgetsReducer = createImmerReducer(initialState, {
[ReduxActionTypes.UPDATE_CANVAS]: (
state: CanvasWidgetsReduxState,
action: ReduxAction<UpdateCanvasPayload>,
) => {
return { ...action.payload.widgets };
return action.payload.widgets;
},
[ReduxActionTypes.UPDATE_LAYOUT]: (
state: CanvasWidgetsReduxState,
action: ReduxAction<UpdateCanvasPayload>,
) => {
return { ...action.payload.widgets };
return action.payload.widgets;
},
[ReduxActionTypes.UPDATE_WIDGET_PROPERTY]: (
state: CanvasWidgetsReduxState,
action: ReduxAction<UpdateWidgetPropertyPayload>,
) => {
const widget = state[action.payload.widgetId];
return {
...state,
[action.payload.widgetId]: {
...widget,
[action.payload.propertyName]: action.payload.propertyValue,
},
};
state[action.payload.widgetId][action.payload.propertyName] =
action.payload.propertyValue;
},
});

View File

@ -10,6 +10,7 @@ import { ApiResponse } from "api/ApiResponses";
import { put, takeLatest, call } from "redux-saga/effects";
import { ERROR_401, ERROR_500, ERROR_0 } from "constants/messages";
import { ToastType } from "react-toastify";
import log from "loglevel";
export function* callAPI(apiCall: any, requestPayload: any) {
try {
@ -84,7 +85,8 @@ export function* errorSaga(
) {
// Just a pass through for now.
// Add procedures to customize errors here
console.log({ error: errorAction });
log.debug(`Error in action ${errorAction.type}`);
log.error(errorAction.payload.error);
// Show a toast when the error occurs
const {
type,

View File

@ -158,26 +158,32 @@ function* generateChildWidgets(
export function* addChildSaga(addChildAction: ReduxAction<WidgetAddChild>) {
try {
const start = performance.now();
AppToaster.clear();
const { widgetId } = addChildAction.payload;
// Get the current parent widget whose child will be the new widget.
const parent: FlattenedWidgetProps = yield select(getWidget, widgetId);
const stateParent: FlattenedWidgetProps = yield select(getWidget, widgetId);
// const parent = Object.assign({}, stateParent);
// Get all the widgets from the canvasWidgetsReducer
const widgets = yield select(getWidgets);
const stateWidgets = yield select(getWidgets);
const widgets = Object.assign({}, stateWidgets);
// Generate the full WidgetProps of the widget to be added.
const childWidgetPayload: GeneratedWidgetPayload = yield generateChildWidgets(
parent,
stateParent,
addChildAction.payload,
widgets,
);
// Update widgets to put back in the canvasWidgetsReducer
// TODO(abhinav): This won't work if dont already have an empty children: []
if (parent.children) parent.children.push(childWidgetPayload.widgetId);
const parent = {
...stateParent,
children: [...stateParent.children, childWidgetPayload.widgetId],
};
widgets[parent.widgetId] = parent;
log.debug("add child computations took", performance.now() - start, "ms");
yield put(updateAndSaveLayout(widgets));
} catch (error) {
yield put({
@ -199,7 +205,8 @@ export function* addChildrenSaga(
) {
try {
const { widgetId, children } = addChildrenAction.payload;
const widgets = yield select(getWidgets);
const stateWidgets = yield select(getWidgets);
const widgets = { ...stateWidgets };
const widgetNames = Object.keys(widgets).map(w => widgets[w].widgetName);
children.forEach(child => {
@ -215,12 +222,13 @@ export function* addChildrenSaga(
widgetName: newWidgetName,
renderMode: RenderModes.CANVAS,
};
if (
widgets[widgetId].children &&
Array.isArray(widgets[widgetId].children)
) {
widgets[widgetId].children?.push(child.widgetId);
} else widgets[widgetId].children = [child.widgetId];
const existingChildren = widgets[widgetId].children || [];
widgets[widgetId] = {
...widgets[widgetId],
children: [...existingChildren, child.widgetId],
};
}
});
@ -265,9 +273,15 @@ export function* deleteSaga(deleteAction: ReduxAction<WidgetDelete>) {
}
if (widgetId && parentId) {
const widgets = yield select(getWidgets);
const widget = yield select(getWidget, widgetId);
const parent: FlattenedWidgetProps = yield select(getWidget, parentId);
const stateWidgets = yield select(getWidgets);
const widgets = { ...stateWidgets };
const stateWidget = yield select(getWidget, widgetId);
const widget = { ...stateWidget };
const stateParent: FlattenedWidgetProps = yield select(
getWidget,
parentId,
);
let parent = { ...stateParent };
const analyticsEvent = isShortcut
? "WIDGET_DELETE_VIA_SHORTCUT"
@ -281,9 +295,10 @@ export function* deleteSaga(deleteAction: ReduxAction<WidgetDelete>) {
// Remove entry from parent's children
if (parent.children) {
const indexOfChild = parent.children.indexOf(widgetId);
if (indexOfChild > -1) delete parent.children[indexOfChild];
parent.children = parent.children.filter(Boolean);
parent = {
...parent,
children: parent.children.filter(c => c !== widgetId),
};
}
widgets[parentId] = parent;
@ -319,11 +334,12 @@ export function* deleteSaga(deleteAction: ReduxAction<WidgetDelete>) {
}, WIDGET_DELETE_UNDO_TIMEOUT);
}
otherWidgetsToDelete.forEach(widget => {
delete widgets[widget.widgetId];
});
const finalWidgets = _.omit(
widgets,
otherWidgetsToDelete.map(widgets => widgets.widgetId),
);
yield put(updateAndSaveLayout(widgets));
yield put(updateAndSaveLayout(finalWidgets));
}
} catch (error) {
yield put({
@ -337,13 +353,18 @@ export function* deleteSaga(deleteAction: ReduxAction<WidgetDelete>) {
}
export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) {
// Get the list of widget and its children which were deleted
const deletedWidgets: FlattenedWidgetProps[] = yield getDeletedWidgets(
action.payload.widgetId,
);
// Find the parent in the list of deleted widgets
const deletedWidget = deletedWidgets.find(
widget => widget.widgetId === action.payload.widgetId,
);
// If the deleted widget is infact available.
if (deletedWidget) {
// Log an undo event
AnalyticsUtil.logEvent("WIDGET_DELETE_UNDO", {
widgetName: deletedWidget.widgetName,
widgetType: deletedWidget.type,
@ -351,13 +372,18 @@ export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) {
}
if (deletedWidgets) {
const widgets = yield select(getWidgets);
// Get the current list of widgets from reducer
const stateWidgets = yield select(getWidgets);
let widgets = { ...stateWidgets };
// For each deleted widget
deletedWidgets.forEach(widget => {
// Add it to the widgets list we fetched from reducer
widgets[widget.widgetId] = widget;
// If the widget in question is the deleted widget
if (widget.widgetId === action.payload.widgetId) {
//SPECIAL HANDLING FOR TAB IN A TABS WIDGET
if (widget.tabId && widget.type === WidgetTypes.CANVAS_WIDGET) {
const parent = widgets[widget.parentId];
const parent = { ...widgets[widget.parentId] };
if (parent.tabs) {
try {
const tabs = _.isString(parent.tabs)
@ -368,7 +394,13 @@ export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) {
widgetId: widget.widgetId,
label: widget.tabName || widget.widgetName,
});
widgets[widget.parentId].tabs = JSON.stringify(tabs);
widgets = {
...widgets,
[widget.parentId]: {
...widgets[widget.parentId],
tabs: JSON.stringify(tabs),
},
};
} catch (error) {
log.debug("Error deleting tabs widget: ", { error });
}
@ -380,11 +412,24 @@ export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) {
label: widget.tabName || widget.widgetName,
},
]);
widgets = {
...widgets,
[widget.parentId]: parent,
};
}
}
if (widgets[widget.parentId].children)
widgets[widget.parentId].children?.push(widget.widgetId);
else widgets[widget.parentId].children = [widget.widgetId];
let newChildren = [widget.widgetId];
if (widgets[widget.parentId].children) {
// Concatenate the list of paren't children with the current widgetId
newChildren = newChildren.concat(widgets[widget.parentId].children);
}
widgets = {
...widgets,
[widget.parentId]: {
...widgets[widget.parentId],
children: newChildren,
},
};
}
});
@ -396,7 +441,7 @@ export function* undoDeleteSaga(action: ReduxAction<{ widgetId: string }>) {
export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
try {
AppToaster.clear();
const start = performance.now();
const {
widgetId,
leftColumn,
@ -404,20 +449,25 @@ export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
parentId,
newParentId,
} = moveAction.payload;
let widget: FlattenedWidgetProps = yield select(getWidget, widgetId);
const stateWidget: FlattenedWidgetProps = yield select(getWidget, widgetId);
let widget = Object.assign({}, stateWidget);
// Get all widgets from DSL/Redux Store
const widgets = yield select(getWidgets) as any;
const stateWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
const widgets = Object.assign({}, stateWidgets);
// Get parent from DSL/Redux Store
const parent: FlattenedWidgetProps = yield select(getWidget, parentId);
const stateParent: FlattenedWidgetProps = yield select(getWidget, parentId);
const parent = { ...stateParent, children: [...stateParent.children] };
// Update position of widget
widget = updateWidgetPosition(widget, leftColumn, topRow);
const updatedPosition = updateWidgetPosition(widget, leftColumn, topRow);
widget = { ...widget, ...updatedPosition };
// Replace widget with update widget props
widgets[widgetId] = widget;
// If the parent has changed i.e parentWidgetId is not parent.widgetId
if (parent.widgetId !== newParentId && widgetId !== newParentId) {
// Remove from the previous parent
if (parent.children) {
if (parent.children && Array.isArray(parent.children)) {
const indexOfChild = parent.children.indexOf(widgetId);
if (indexOfChild > -1) delete parent.children[indexOfChild];
parent.children = parent.children.filter(Boolean);
@ -426,16 +476,17 @@ export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
// Add to new parent
widgets[parent.widgetId] = parent;
if (
widgets[newParentId].children &&
Array.isArray(widgets[newParentId].children)
) {
widgets[newParentId].children?.push(widgetId);
} else {
widgets[newParentId].children = [widgetId];
}
const newParent = {
...widgets[newParentId],
children: widgets[newParentId].children
? [...widgets[newParentId].children, widgetId]
: [widgetId],
};
widgets[widgetId].parentId = newParentId;
widgets[newParentId] = newParent;
}
log.debug("move computations took", performance.now() - start, "ms");
yield put(updateAndSaveLayout(widgets));
} catch (error) {
yield put({
@ -451,7 +502,7 @@ export function* moveSaga(moveAction: ReduxAction<WidgetMove>) {
export function* resizeSaga(resizeAction: ReduxAction<WidgetResize>) {
try {
AppToaster.clear();
const start = performance.now();
const {
widgetId,
leftColumn,
@ -460,11 +511,14 @@ export function* resizeSaga(resizeAction: ReduxAction<WidgetResize>) {
bottomRow,
} = resizeAction.payload;
let widget: FlattenedWidgetProps = yield select(getWidget, widgetId);
const widgets = yield select(getWidgets);
const stateWidget: FlattenedWidgetProps = yield select(getWidget, widgetId);
let widget = { ...stateWidget };
const stateWidgets = yield select(getWidgets);
const widgets = { ...stateWidgets };
widget = { ...widget, leftColumn, rightColumn, topRow, bottomRow };
widgets[widgetId] = widget;
log.debug("resize computations took", performance.now() - start, "ms");
yield put(updateAndSaveLayout(widgets));
} catch (error) {
yield put({
@ -513,7 +567,8 @@ function* updateDynamicBindings(
stringProp = JSON.stringify(propertyValue);
}
const isDynamic = isDynamicValue(stringProp);
let dynamicBindings: Record<string, boolean> = widget.dynamicBindings || {};
let dynamicBindings: Record<string, boolean> =
{ ...widget.dynamicBindings } || {};
if (!isDynamic && propertyName in dynamicBindings) {
dynamicBindings = _.omit(dynamicBindings, propertyName);
}
@ -531,7 +586,8 @@ function* updateWidgetPropertySaga(
const {
payload: { propertyValue, propertyName, widgetId },
} = updateAction;
const widget: WidgetProps = yield select(getWidget, widgetId);
const stateWidget: WidgetProps = yield select(getWidget, widgetId);
const widget = { ...stateWidget };
const dynamicTriggersUpdated = yield updateDynamicTriggers(
widget,
@ -542,7 +598,8 @@ function* updateWidgetPropertySaga(
yield updateDynamicBindings(widget, propertyName, propertyValue);
yield put(updateWidgetProperty(widgetId, propertyName, propertyValue));
const widgets = yield select(getWidgets);
const stateWidgets = yield select(getWidgets);
const widgets = { ...stateWidgets, [widgetId]: widget };
yield put(updateAndSaveLayout(widgets));
}
@ -623,7 +680,6 @@ function* updateCanvasSize(
function* copyWidgetSaga(action: ReduxAction<{ isShortcut: boolean }>) {
const selectedWidget = yield select(getSelectedWidget);
console.log({ selectedWidget });
if (!selectedWidget) return;
const widgets = yield select(getWidgets);
const widgetsToStore = getAllWidgetsInTree(selectedWidget.widgetId, widgets);
@ -696,7 +752,8 @@ function* pasteWidgetSaga() {
widgetType: copiedWidget.type,
});
const widgets = yield select(getWidgets);
const stateWidgets = yield select(getWidgets);
let widgets = { ...stateWidgets };
const selectedWidget = yield select(getSelectedWidget);
let newWidgetParentId = MAIN_CONTAINER_WIDGET_ID;
@ -808,14 +865,23 @@ function* pasteWidgetSaga() {
widget.parentId = newWidgetParentId;
// Also, update the parent widget in the canvas widgets
// to include this new copied widget's id in the parent's children
let parentChildren = [widget.widgetId];
if (
widgets[newWidgetParentId].children &&
Array.isArray(widgets[newWidgetParentId].children)
) {
widgets[newWidgetParentId].children.push(widget.widgetId);
} else {
widgets[newWidgetParentId].children = [widget.widgetId];
// Add the new child to existing children
parentChildren = parentChildren.concat(
widgets[newWidgetParentId].children,
);
}
widgets = {
...widgets,
[newWidgetParentId]: {
...widgets[newWidgetParentId],
children: parentChildren,
},
};
// If the copied widget's boundaries exceed the parent's
// Make the parent scrollable
if (
@ -824,9 +890,11 @@ function* pasteWidgetSaga() {
widget.bottomRow * widget.parentRowSpace
) {
if (widget.parentId !== MAIN_CONTAINER_WIDGET_ID) {
widgets[
widgets[newWidgetParentId].parentId
].shouldScrollContents = true;
const parent = widgets[widgets[newWidgetParentId].parentId];
widgets[widgets[newWidgetParentId].parentId] = {
...parent,
shouldScrollContents: true,
};
}
}
} else {

View File

@ -461,7 +461,6 @@ export const updateWidgetPosition = (
};
return {
...widget,
...newPositions,
};
};