DUTconfig: dome: Use optimistic update

1. Use optimistic update for update/upload operations
2. Modify some indent

TEST=manually
BUG=b:116767069

Change-Id: I8fd46f43332ca803981926ea8e28c08f924bcbdd
Reviewed-on: https://chromium-review.googlesource.com/1409148
Commit-Ready: Hsin-Yi Wang <hsinyi@chromium.org>
Tested-by: Hsin-Yi Wang <hsinyi@chromium.org>
Reviewed-by: Pi-Hsun Shih <pihsun@chromium.org>
diff --git a/py/dome/frontend/src/parameters/actions.ts b/py/dome/frontend/src/parameters/actions.ts
index c4b2dd5..e769d18 100644
--- a/py/dome/frontend/src/parameters/actions.ts
+++ b/py/dome/frontend/src/parameters/actions.ts
@@ -18,6 +18,7 @@
   RENAME_PARAMETER_FORM,
   UPDATE_PARAMETER_FORM,
 } from './constants';
+import {getParameterDirs, getParameters} from './selector';
 import {
   CreateDirectoryRequest,
   Parameter,
@@ -43,81 +44,127 @@
 const updateParameterDir = createAction('UPDATE_PARAMETER_DIR', (resolve) =>
   (parameterDir: ParameterDirectory) => resolve({parameterDir}));
 
-const setLoading = createAction('LOADING');
-
 export const basicActions = {
   receiveParameters,
   updateParameter,
   receiveParameterDirs,
   updateParameterDir,
-  setLoading,
 };
 
 export const startCreateDirectory = (data: CreateDirectoryRequest) =>
-    async (dispatch: Dispatch, getState: () => RootState) => {
-  dispatch(formDialog.actions.closeForm(CREATE_DIRECTORY_FORM));
-  // send the request
-  const description = `Create Directory "${data.name}"`;
-  const parameterDir = await dispatch(task.actions.runTask<ParameterDirectory>(
-      description, 'POST', `${baseURL(getState)}/parameters/dirs/`, data));
-  dispatch(updateParameterDir(parameterDir));
-};
+  async (dispatch: Dispatch, getState: () => RootState) => {
+    dispatch(formDialog.actions.closeForm(CREATE_DIRECTORY_FORM));
 
-export const startUpdateParameter =
-    (data: UpdateParameterRequest) =>
-    async (dispatch: Dispatch, getState: () => RootState) => {
-  dispatch(formDialog.actions.closeForm(UPDATE_PARAMETER_FORM));
+    const parameterDirs = getParameterDirs(getState());
+    const optimisticUpdate = () => {
+      let newParameterDir = parameterDirs.find((d) => (
+        d.name === data.name && d.parentId === data.parentId));
+      if (!newParameterDir) {
+        newParameterDir = {
+          id: parameterDirs.length,
+          name: data.name,
+          parentId: data.parentId,
+        };
+      }
+      dispatch(updateParameterDir(newParameterDir));
+    };
 
-  // send the request
-  const description = `Update parameter "${data.name}"`;
-  dispatch(setLoading());
-  const parameterComponent = await dispatch(task.actions.runTask<Parameter>(
-      description, 'POST', `${baseURL(getState)}/parameters/files/`, data));
-  dispatch(updateParameter(parameterComponent));
-};
+    // send the request
+    const description = `Create Directory "${data.name}"`;
+    const parameterDir =
+      await dispatch(task.actions.runTask<ParameterDirectory>(
+        description, 'POST', `${baseURL(getState)}/parameters/dirs/`, data,
+        optimisticUpdate));
+    dispatch(updateParameterDir(parameterDir));
+  };
+
+export const startUpdateParameter = (data: UpdateParameterRequest) =>
+  async (dispatch: Dispatch, getState: () => RootState) => {
+    dispatch(formDialog.actions.closeForm(UPDATE_PARAMETER_FORM));
+
+    const parameters = getParameters(getState());
+    const optimisticUpdate = () => {
+      let newParameter = null;
+      if (data.id == null) {
+        newParameter = parameters.find((p) => (
+          p.name === data.name && p.dirId === data.dirId));
+        if (!newParameter) {
+          newParameter = {
+            id: parameters.length,
+            dirId: data.dirId,
+            name: data.name,
+            usingVer: 0,
+            revisions: [],
+          };
+        }
+      } else {
+        newParameter = parameters[data.id];
+      }
+      dispatch(updateParameter(newParameter));
+    };
+
+    // send the request
+    const description = `Update parameter "${data.name}"`;
+    const parameterComponent = await dispatch(task.actions.runTask<Parameter>(
+      description, 'POST', `${baseURL(getState)}/parameters/files/`, data,
+      optimisticUpdate));
+    dispatch(updateParameter(parameterComponent));
+  };
 
 export const startUpdateComponentVersion =
-    (data: UpdateParameterVersionRequest) =>
-        async (dispatch: Dispatch, getState: () => RootState) => {
+  (data: UpdateParameterVersionRequest) =>
+    async (dispatch: Dispatch, getState: () => RootState) => {
       // send the request
       const description = `Update parameter "${data.name}"  version`;
       const parameterComponent = await dispatch(task.actions.runTask<Parameter>(
-          description, 'POST', `${baseURL(getState)}/parameters/files/`, data));
+        description, 'POST', `${baseURL(getState)}/parameters/files/`, data,
+        () => {
+          dispatch(updateParameter({
+            ...getParameters(getState())[data.id], usingVer: data.usingVer}));
+        }));
       dispatch(updateParameter(parameterComponent));
     };
 
 export const startRenameParameter = (data: RenameRequest) =>
-    async (dispatch: Dispatch, getState: () => RootState) => {
-  dispatch(formDialog.actions.closeForm(RENAME_PARAMETER_FORM));
-  // send the request
-  const description = `Rename parameter "${data.name}"`;
-  const parameterComponent = await dispatch(task.actions.runTask<Parameter>(
-      description, 'POST', `${baseURL(getState)}/parameters/files/`, data));
-  dispatch(updateParameter(parameterComponent));
-};
+  async (dispatch: Dispatch, getState: () => RootState) => {
+    dispatch(formDialog.actions.closeForm(RENAME_PARAMETER_FORM));
+    // send the request
+    const description = `Rename parameter "${data.name}"`;
+    const parameterComponent = await dispatch(task.actions.runTask<Parameter>(
+      description, 'POST', `${baseURL(getState)}/parameters/files/`, data,
+      () => {
+        dispatch(updateParameter({
+          ...getParameters(getState())[data.id], name: data.name}));
+      }));
+    dispatch(updateParameter(parameterComponent));
+  };
 
 export const startRenameDirectory = (data: RenameRequest) =>
-    async (dispatch: Dispatch, getState: () => RootState) => {
-  dispatch(formDialog.actions.closeForm(RENAME_DIRECTORY_FORM));
-  // send the request
-  const description = `Rename directory "${data.name}"`;
-  const directoryComponent =
-    await dispatch(task.actions.runTask<ParameterDirectory>(
-      description, 'POST', `${baseURL(getState)}/parameters/dirs/`, data));
-  dispatch(updateParameterDir(directoryComponent));
-};
+  async (dispatch: Dispatch, getState: () => RootState) => {
+    dispatch(formDialog.actions.closeForm(RENAME_DIRECTORY_FORM));
+    // send the request
+    const description = `Rename directory "${data.name}"`;
+    const directoryComponent =
+      await dispatch(task.actions.runTask<ParameterDirectory>(
+        description, 'POST', `${baseURL(getState)}/parameters/dirs/`, data,
+        () => {
+          dispatch(updateParameterDir({
+            ...getParameterDirs(getState())[data.id], name: data.name}));
+        }));
+    dispatch(updateParameterDir(directoryComponent));
+  };
 
 export const fetchParameters = () =>
-    async (dispatch: Dispatch, getState: () => RootState) => {
-  try {
-    const components = await authorizedAxios().get<Parameter[]>(
+  async (dispatch: Dispatch, getState: () => RootState) => {
+    try {
+      const components = await authorizedAxios().get<Parameter[]>(
         `${baseURL(getState)}/parameters/files.json`);
-    dispatch(receiveParameters(components.data));
-    const directories = await authorizedAxios().get<ParameterDirectory[]>(
+      dispatch(receiveParameters(components.data));
+      const directories = await authorizedAxios().get<ParameterDirectory[]>(
         `${baseURL(getState)}/parameters/dirs.json`);
-    dispatch(receiveParameterDirs(directories.data));
-  } catch (err) {
-    dispatch(error.actions.setAndShowErrorDialog(
+      dispatch(receiveParameterDirs(directories.data));
+    } catch (err) {
+      dispatch(error.actions.setAndShowErrorDialog(
         `error fetching parameters or dirs\n\n${err.message}`));
-  }
-};
+    }
+  };
diff --git a/py/dome/frontend/src/parameters/components/parameter_app.tsx b/py/dome/frontend/src/parameters/components/parameter_app.tsx
index 44a4c0f..56b4dcc 100644
--- a/py/dome/frontend/src/parameters/components/parameter_app.tsx
+++ b/py/dome/frontend/src/parameters/components/parameter_app.tsx
@@ -19,11 +19,9 @@
 import {connect} from 'react-redux';
 
 import formDialog from '@app/form_dialog';
-import {RootState} from '@app/types';
 import {DispatchProps} from '@common/types';
 
 import {CREATE_DIRECTORY_FORM, UPDATE_PARAMETER_FORM} from '../constants';
-import {getLoadingStatus} from '../selector';
 
 import CreateDirectoryForm from './create_directory_form';
 import ParameterList from './parameter_list';
@@ -47,7 +45,6 @@
 
 type ParameterAppProps =
   WithStyles<typeof styles> &
-  ReturnType<typeof mapStateToProps> &
   DispatchProps<typeof mapDispatchToProps>;
 
 class ParameterApp extends React.Component<ParameterAppProps, ParameterState> {
@@ -89,12 +86,10 @@
             </div>
           </CardContent>
           <CardContent>
-            {this.props.loading ? 'LOADING' : (
-              <ParameterList
-                currentDirId={currentDirId}
-                dirClicked={this.setCurrentDirId}
-              />
-            )}
+            <ParameterList
+              currentDirId={currentDirId}
+              dirClicked={this.setCurrentDirId}
+            />
           </CardContent>
         </Card>
       </>
@@ -103,10 +98,6 @@
 
 }
 
-const mapStateToProps = (state: RootState) => ({
-  loading: getLoadingStatus(state),
-});
-
 const mapDispatchToProps = {
   updateComponent:
       (dirId: number | null, name: string, multiple: boolean) =>
@@ -115,5 +106,5 @@
   createDirectory: () => formDialog.actions.openForm(CREATE_DIRECTORY_FORM),
 };
 
-export default connect(mapStateToProps, mapDispatchToProps)(
+export default connect(null, mapDispatchToProps)(
   withStyles(styles)(ParameterApp));
diff --git a/py/dome/frontend/src/parameters/reducer.ts b/py/dome/frontend/src/parameters/reducer.ts
index 26afe32..b44fa8f 100644
--- a/py/dome/frontend/src/parameters/reducer.ts
+++ b/py/dome/frontend/src/parameters/reducer.ts
@@ -11,7 +11,6 @@
 export interface ParameterState {
   files: Parameter[];
   dirs: ParameterDirectory[];
-  loading: boolean;
 }
 
 type ParameterAction = ActionType<typeof actions>;
@@ -19,44 +18,34 @@
 const INITIAL_STATE = {
   files: [],
   dirs: [],
-  loading: false,
 };
 
 export default produce<ParameterState, ParameterAction>((draft, action) => {
   switch (action.type) {
     case getType(actions.receiveParameters): {
       const {parameters} = action.payload;
-      draft.loading = false;
       draft.files = parameters;
       return;
     }
 
     case getType(actions.updateParameter): {
       const {parameter} = action.payload;
-      draft.loading = false;
       draft.files[parameter.id] = parameter;
       return;
     }
 
     case getType(actions.receiveParameterDirs): {
       const {parameterDirs} = action.payload;
-      draft.loading = false;
       draft.dirs = parameterDirs;
       return;
     }
 
     case getType(actions.updateParameterDir): {
       const {parameterDir} = action.payload;
-      draft.loading = false;
       draft.dirs[parameterDir.id] = parameterDir;
       return;
     }
 
-    case getType(actions.setLoading): {
-      draft.loading = true;
-      return;
-    }
-
     default:
       return;
   }
diff --git a/py/dome/frontend/src/parameters/selector.ts b/py/dome/frontend/src/parameters/selector.ts
index 48ff0d2..3d58889 100644
--- a/py/dome/frontend/src/parameters/selector.ts
+++ b/py/dome/frontend/src/parameters/selector.ts
@@ -18,6 +18,3 @@
 
 export const getParameterDirs =
   (state: RootState): ParameterDirectory[] => localState(state).dirs;
-
-export const getLoadingStatus =
-  (state: RootState): boolean => localState(state).loading;
diff --git a/py/dome/frontend/src/parameters/types.d.ts b/py/dome/frontend/src/parameters/types.d.ts
index 3ff7cfe..882666e 100644
--- a/py/dome/frontend/src/parameters/types.d.ts
+++ b/py/dome/frontend/src/parameters/types.d.ts
@@ -38,7 +38,7 @@
 
 export interface ParameterDirectory {
   id: number;
-  parentId: number;
+  parentId: number | null;
   name: string;
 }