Browse Source

chore: (unstable) using controllers

ascarbek 2 years ago
parent
commit
11649ef16d

+ 9 - 5
frontend/appflowy_tauri/src/appflowy_app/components/TestApiButton/TestGrid.tsx

@@ -5,7 +5,7 @@ import {
   SingleSelectTypeOptionPB,
   ViewLayoutTypePB,
 } from '../../../services/backend';
-import { Log } from '../../utils/log';
+import {Log} from '../../utils/log';
 import {
   assertFieldName,
   assertNumberOfFields,
@@ -20,10 +20,10 @@ import {
   SelectOptionBackendService,
   SelectOptionCellBackendService,
 } from '../../stores/effects/database/cell/select_option_bd_svc';
-import { TypeOptionController } from '../../stores/effects/database/field/type_option/type_option_controller';
-import { None, Some } from 'ts-results';
-import { RowBackendService } from '../../stores/effects/database/row/row_bd_svc';
-import { makeSingleSelectTypeOptionContext } from '../../stores/effects/database/field/type_option/type_option_context';
+import {TypeOptionController} from '../../stores/effects/database/field/type_option/type_option_controller';
+import {None, Some} from 'ts-results';
+import {RowBackendService} from '../../stores/effects/database/row/row_bd_svc';
+import {makeSingleSelectTypeOptionContext} from '../../stores/effects/database/field/type_option/type_option_context';
 
 export const TestCreateGrid = () => {
   async function createBuildInGrid() {
@@ -180,6 +180,7 @@ export const TestEditField = () => {
     await controller.initialize();
     const newName = 'hello world';
     await controller.setFieldName(newName);
+    await controller.switchToField(FieldType.MultiSelect);
 
     await assertFieldName(view.id, firstFieldInfo.field.id, firstFieldInfo.field.field_type, newName);
     await databaseController.dispose();
@@ -199,6 +200,9 @@ export const TestCreateNewField = () => {
     const controller = new TypeOptionController(view.id, None);
     await controller.initialize();
     await assertNumberOfFields(view.id, 4);
+
+
+
     await databaseController.dispose();
   }
 

+ 99 - 0
frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/loadField.ts

@@ -0,0 +1,99 @@
+import { TypeOptionController } from '../../../stores/effects/database/field/type_option/type_option_controller';
+import { Some } from 'ts-results';
+import { IDatabaseField, ISelectOption } from '../../../stores/reducers/database/slice';
+import {
+  ChecklistTypeOptionPB,
+  DateFormat,
+  FieldType,
+  MultiSelectTypeOptionPB,
+  NumberFormat,
+  SingleSelectTypeOptionPB,
+  TimeFormat,
+} from '../../../../services/backend';
+import {
+  makeChecklistTypeOptionContext,
+  makeDateTypeOptionContext,
+  makeMultiSelectTypeOptionContext,
+  makeNumberTypeOptionContext,
+  makeSingleSelectTypeOptionContext,
+} from '../../../stores/effects/database/field/type_option/type_option_context';
+import { boardActions } from '../../../stores/reducers/board/slice';
+import { FieldInfo } from '../../../stores/effects/database/field/field_controller';
+import { AppDispatch } from '../../../stores/store';
+
+export default async function (viewId: string, fieldInfo: FieldInfo, dispatch?: AppDispatch): Promise<IDatabaseField> {
+  const field = fieldInfo.field;
+  const typeOptionController = new TypeOptionController(viewId, Some(fieldInfo));
+
+  let selectOptions: ISelectOption[] | undefined;
+  let numberFormat: NumberFormat | undefined;
+  let dateFormat: DateFormat | undefined;
+  let timeFormat: TimeFormat | undefined;
+  let includeTime: boolean | undefined;
+
+  // temporary hack to set grouping field
+  let groupingFieldSelected = false;
+
+  switch (field.field_type) {
+    case FieldType.SingleSelect:
+    case FieldType.MultiSelect:
+    case FieldType.Checklist:
+      {
+        let typeOption: SingleSelectTypeOptionPB | MultiSelectTypeOptionPB | ChecklistTypeOptionPB | undefined;
+
+        if (field.field_type === FieldType.SingleSelect) {
+          typeOption = (await makeSingleSelectTypeOptionContext(typeOptionController).getTypeOption()).unwrap();
+          if (!groupingFieldSelected) {
+            if (dispatch) {
+              dispatch(boardActions.setGroupingFieldId({ fieldId: field.id }));
+            }
+            groupingFieldSelected = true;
+          }
+        }
+        if (field.field_type === FieldType.MultiSelect) {
+          typeOption = (await makeMultiSelectTypeOptionContext(typeOptionController).getTypeOption()).unwrap();
+        }
+        if (field.field_type === FieldType.Checklist) {
+          typeOption = (await makeChecklistTypeOptionContext(typeOptionController).getTypeOption()).unwrap();
+        }
+
+        if (typeOption) {
+          selectOptions = typeOption.options.map<ISelectOption>((option) => {
+            return {
+              selectOptionId: option.id,
+              title: option.name,
+              color: option.color,
+            };
+          });
+        }
+      }
+      break;
+    case FieldType.Number:
+      {
+        const typeOption = (await makeNumberTypeOptionContext(typeOptionController).getTypeOption()).unwrap();
+        numberFormat = typeOption.format;
+      }
+      break;
+    case FieldType.DateTime:
+      {
+        const typeOption = (await makeDateTypeOptionContext(typeOptionController).getTypeOption()).unwrap();
+        dateFormat = typeOption.date_format;
+        timeFormat = typeOption.time_format;
+        includeTime = typeOption.include_time;
+      }
+      break;
+  }
+
+  return {
+    fieldId: field.id,
+    title: field.name,
+    fieldType: field.field_type,
+    fieldOptions: {
+      selectOptions,
+      numberFormat,
+      dateFormat,
+      timeFormat,
+      includeTime,
+    },
+  };
+}

+ 40 - 0
frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useCell.ts

@@ -0,0 +1,40 @@
+import { CellIdentifier } from '../../../stores/effects/database/cell/cell_bd_svc';
+import { CellCache } from '../../../stores/effects/database/cell/cell_cache';
+import { FieldController } from '../../../stores/effects/database/field/field_controller';
+import { CellControllerBuilder } from '../../../stores/effects/database/cell/controller_builder';
+import { DateCellDataPB, FieldType, SelectOptionCellDataPB } from '../../../../services/backend';
+import { useState } from 'react';
+
+export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fieldController: FieldController) => {
+  const [data, setData] = useState<string[]>([]);
+
+  const loadCell = async () => {
+    const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController);
+    const cellController = builder.build();
+    cellController.subscribeChanged({
+      onCellChanged: (value) => {
+        if (
+          cellIdentifier.fieldType === FieldType.Checklist ||
+          cellIdentifier.fieldType === FieldType.MultiSelect ||
+          cellIdentifier.fieldType === FieldType.SingleSelect
+        ) {
+          const v = value.unwrap() as SelectOptionCellDataPB;
+          setData(v.select_options.map((option) => option.id));
+        } else if (cellIdentifier.fieldType === FieldType.DateTime) {
+          const v = value.unwrap() as DateCellDataPB;
+          setData([v.date]);
+        } else {
+          const v = value.unwrap() as string;
+          setData([v]);
+        }
+      },
+    });
+
+    cellController.getCellData();
+  };
+
+  return {
+    loadCell,
+    data,
+  };
+};

+ 50 - 0
frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useDatabase.ts

@@ -0,0 +1,50 @@
+import { useEffect, useState } from 'react';
+import { DatabaseController } from '../../../stores/effects/database/database_controller';
+import {
+  databaseActions,
+  DatabaseFieldMap,
+  IDatabaseColumn,
+  IDatabaseRow,
+} from '../../../stores/reducers/database/slice';
+import { useAppDispatch, useAppSelector } from '../../../stores/store';
+import loadField from './loadField';
+import { FieldInfo } from '../../../stores/effects/database/field/field_controller';
+
+export const useDatabase = (viewId: string) => {
+  const dispatch = useAppDispatch();
+  const databaseStore = useAppSelector((state) => state.database);
+  const boardStore = useAppSelector((state) => state.board);
+  const [controller, setController] = useState<DatabaseController>();
+
+  useEffect(() => {
+    if (!viewId.length) return;
+    const c = new DatabaseController(viewId);
+    setController(c);
+
+    // on unmount dispose the controller
+    return () => void c.dispose();
+  }, [viewId]);
+
+  const loadFields = async (fieldInfos: readonly FieldInfo[]) => {
+    const fields: DatabaseFieldMap = {};
+    const columns: IDatabaseColumn[] = [];
+
+    for (const fieldInfo of fieldInfos) {
+      const fieldPB = fieldInfo.field;
+      columns.push({
+        fieldId: fieldPB.id,
+        sort: 'none',
+        visible: fieldPB.visibility,
+      });
+
+      const field = await loadField(viewId, fieldInfo, dispatch);
+      fields[field.fieldId] = field;
+    }
+
+    dispatch(databaseActions.updateFields({ fields }));
+    dispatch(databaseActions.updateColumns({ columns }));
+    console.log(fields, columns);
+  };
+
+  return { loadFields, controller };
+};

+ 32 - 0
frontend/appflowy_tauri/src/appflowy_app/components/_shared/database-hooks/useRow.ts

@@ -0,0 +1,32 @@
+import { DatabaseController } from '../../../stores/effects/database/database_controller';
+import { RowController } from '../../../stores/effects/database/row/row_controller';
+import { RowInfo } from '../../../stores/effects/database/row/row_cache';
+import { CellIdentifier } from '../../../stores/effects/database/cell/cell_bd_svc';
+import { useState } from 'react';
+
+export const useRow = (viewId: string, databaseController: DatabaseController, rowInfo: RowInfo) => {
+  const [cells, setCells] = useState<{ fieldId: string; cellIdentifier: CellIdentifier }[]>([]);
+
+  const rowCache = databaseController.databaseViewCache.getRowCache();
+  const fieldController = databaseController.fieldController;
+  const rowController = new RowController(rowInfo, fieldController, rowCache);
+
+  const loadRow = async () => {
+    const cellsPB = await rowController.loadCells();
+    const loadingCells: { fieldId: string; cellIdentifier: CellIdentifier }[] = [];
+
+    for (const [fieldId, cellIdentifier] of cellsPB.entries()) {
+      loadingCells.push({
+        fieldId,
+        cellIdentifier,
+      });
+    }
+
+    setCells(loadingCells);
+  };
+
+  return {
+    loadRow: loadRow,
+    cells: cells,
+  };
+};

+ 17 - 15
frontend/appflowy_tauri/src/appflowy_app/components/board/Board.hooks.ts

@@ -1,7 +1,7 @@
 import { useEffect, useState } from 'react';
 import { useAppDispatch, useAppSelector } from '../../stores/store';
 import { boardActions } from '../../stores/reducers/board/slice';
-import { ICellData, IDatabase, IDatabaseRow, ISelectOption } from '../../stores/reducers/database/slice';
+import { IDatabase, IDatabaseRow, ISelectOption } from '../../stores/reducers/database/slice';
 
 export const useBoard = () => {
   const dispatch = useAppDispatch();
@@ -15,20 +15,22 @@ export const useBoard = () => {
 
   useEffect(() => {
     setTitle(database.title);
-    setBoardColumns(
-      database.fields[groupingFieldId].fieldOptions.selectOptions?.map((groupFieldItem) => {
-        const rows = database.rows
-          .filter((row) => row.cells[groupingFieldId].optionIds?.some((so) => so === groupFieldItem.selectOptionId))
-          .map((row) => ({
-            ...row,
-            isGhost: false,
-          }));
-        return {
-          ...groupFieldItem,
-          rows: rows,
-        };
-      }) || []
-    );
+    if (database.fields[groupingFieldId]) {
+      setBoardColumns(
+        database.fields[groupingFieldId].fieldOptions.selectOptions?.map((groupFieldItem) => {
+        /*  const rows = database.rows
+            .filter((row) => row.cells[groupingFieldId].data?.some((so) => so === groupFieldItem.selectOptionId))
+            .map((row) => ({
+              ...row,
+              isGhost: false,
+            }));*/
+          return {
+            ...groupFieldItem,
+            rows: [],
+          };
+        }) || []
+      );
+    }
   }, [database, groupingFieldId]);
 
   const changeGroupingField = (fieldId: string) => {

+ 28 - 9
frontend/appflowy_tauri/src/appflowy_app/components/board/Board.tsx

@@ -1,13 +1,15 @@
 import { SettingsSvg } from '../_shared/svg/SettingsSvg';
 import { SearchInput } from '../_shared/SearchInput';
-import { useDatabase } from '../_shared/Database.hooks';
 import { BoardBlock } from './BoardBlock';
 import { NewBoardBlock } from './NewBoardBlock';
-import { IDatabaseRow } from '../../stores/reducers/database/slice';
 import { useBoard } from './Board.hooks';
+import { useDatabase } from '../_shared/database-hooks/useDatabase';
+import { useEffect, useState } from 'react';
+import { RowInfo } from '../../stores/effects/database/row/row_cache';
+
+export const Board = ({ viewId }: { viewId: string }) => {
+  const { controller, loadFields } = useDatabase(viewId);
 
-export const Board = () => {
-  const { database, newField, renameField, newRow } = useDatabase();
   const {
     title,
     boardColumns,
@@ -20,6 +22,24 @@ export const Board = () => {
     ghostLocation,
   } = useBoard();
 
+  const [rows, setRows] = useState<readonly RowInfo[]>([]);
+
+  useEffect(() => {
+    if (!controller) return;
+
+    void (async () => {
+      controller.subscribe({
+        onRowsChanged: (rowInfos) => {
+          setRows(rowInfos);
+        },
+        onFieldsChanged: (fieldInfos) => {
+          void loadFields(fieldInfos);
+        },
+      });
+      await controller.open();
+    })();
+  }, [controller]);
+
   return (
     <>
       <div className='flex w-full items-center justify-between'>
@@ -36,16 +56,15 @@ export const Board = () => {
       </div>
       <div className={'relative w-full flex-1 overflow-auto'}>
         <div className={'absolute flex h-full flex-shrink-0 items-start justify-start gap-4'}>
-          {database &&
+          {controller &&
             boardColumns?.map((column, index) => (
               <BoardBlock
+                viewId={viewId}
+                controller={controller}
                 key={index}
                 title={column.title}
+                rows={rows}
                 groupingFieldId={groupingFieldId}
-                count={column.rows.length}
-                fields={database.fields}
-                columns={database.columns}
-                rows={column.rows}
                 startMove={startMove}
                 endMove={endMove}
               />

+ 17 - 15
frontend/appflowy_tauri/src/appflowy_app/components/board/BoardBlock.tsx

@@ -1,24 +1,26 @@
 import { Details2Svg } from '../_shared/svg/Details2Svg';
 import AddSvg from '../_shared/svg/AddSvg';
-import { DatabaseFieldMap, ICellData, IDatabaseColumn, IDatabaseRow } from '../../stores/reducers/database/slice';
-import { BoardBlockItem } from './BoardBlockItem';
+import { DatabaseFieldMap, IDatabaseColumn, IDatabaseRow } from '../../stores/reducers/database/slice';
+import { BoardCard } from './BoardCard';
+import { RowInfo } from '../../stores/effects/database/row/row_cache';
+import { useEffect } from 'react';
+import { useRow } from '../_shared/database-hooks/useRow';
+import { DatabaseController } from '../../stores/effects/database/database_controller';
 
 export const BoardBlock = ({
+  viewId,
+  controller,
   title,
   groupingFieldId,
-  count,
-  fields,
-  columns,
   rows,
   startMove,
   endMove,
 }: {
+  viewId: string;
+  controller: DatabaseController;
   title: string;
   groupingFieldId: string;
-  count: number;
-  fields: DatabaseFieldMap;
-  columns: IDatabaseColumn[];
-  rows: IDatabaseRow[];
+  rows: readonly RowInfo[];
   startMove: (id: string) => void;
   endMove: () => void;
 }) => {
@@ -27,7 +29,7 @@ export const BoardBlock = ({
       <div className={'flex items-center justify-between p-4'}>
         <div className={'flex items-center gap-2'}>
           <span>{title}</span>
-          <span className={'text-shade-4'}>({count})</span>
+          <span className={'text-shade-4'}>()</span>
         </div>
         <div className={'flex items-center gap-2'}>
           <button className={'h-5 w-5 rounded hover:bg-surface-2'}>
@@ -40,15 +42,15 @@ export const BoardBlock = ({
       </div>
       <div className={'flex flex-1 flex-col gap-1 overflow-auto px-2'}>
         {rows.map((row, index) => (
-          <BoardBlockItem
+          <BoardCard
+            viewId={viewId}
+            controller={controller}
             key={index}
             groupingFieldId={groupingFieldId}
-            fields={fields}
-            columns={columns}
             row={row}
-            startMove={() => startMove(row.rowId)}
+            startMove={() => startMove(row.row.id)}
             endMove={() => endMove()}
-          ></BoardBlockItem>
+          ></BoardCard>
         ))}
       </div>
       <div className={'p-2'}>

+ 31 - 31
frontend/appflowy_tauri/src/appflowy_app/components/board/BoardBlockItem.tsx → frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx

@@ -3,22 +3,32 @@ import { Details2Svg } from '../_shared/svg/Details2Svg';
 import { FieldType } from '../../../services/backend';
 import { getBgColor } from '../_shared/getColor';
 import { MouseEventHandler, useEffect, useRef, useState } from 'react';
+import { RowInfo } from '../../stores/effects/database/row/row_cache';
+import { useRow } from '../_shared/database-hooks/useRow';
+import { DatabaseController } from '../../stores/effects/database/database_controller';
+import { useAppSelector } from '../../stores/store';
+import { BoardCell } from './BoardCell';
 
-export const BoardBlockItem = ({
+export const BoardCard = ({
+  viewId,
+  controller,
   groupingFieldId,
-  fields,
-  columns,
+  // fields,
+  // columns,
   row,
   startMove,
   endMove,
 }: {
+  viewId: string;
+  controller: DatabaseController;
   groupingFieldId: string;
-  fields: DatabaseFieldMap;
-  columns: IDatabaseColumn[];
-  row: IDatabaseRow;
+  // fields: DatabaseFieldMap;
+  // columns: IDatabaseColumn[];
+  row: RowInfo;
   startMove: () => void;
   endMove: () => void;
 }) => {
+  const databaseStore = useAppSelector((state) => state.database);
   const [isMoving, setIsMoving] = useState(false);
   const [isDown, setIsDown] = useState(false);
   const [ghostWidth, setGhostWidth] = useState(0);
@@ -43,6 +53,13 @@ export const BoardBlockItem = ({
     }
   }, [el, isMoving]);
 
+  const { loadRow, cells } = useRow(viewId, controller, row);
+  useEffect(() => {
+    void (async () => {
+      await loadRow();
+    })();
+  }, []);
+
   const onMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
     setGhostLeft(ghostLeft + e.movementX);
     setGhostTop(ghostTop + e.movementY);
@@ -74,31 +91,14 @@ export const BoardBlockItem = ({
           <Details2Svg></Details2Svg>
         </button>
         <div className={'flex flex-col gap-3'}>
-          {columns
-            .filter((column) => column.fieldId !== groupingFieldId)
-            .map((column, index) => {
-              switch (fields[column.fieldId].fieldType) {
-                case FieldType.MultiSelect:
-                  return (
-                    <div key={index} className={'flex flex-wrap items-center gap-2'}>
-                      {row.cells[column.fieldId].optionIds?.map((option, indexOption) => {
-                        const selectOptions = fields[column.fieldId].fieldOptions.selectOptions;
-                        const selectedOption = selectOptions?.find((so) => so.selectOptionId === option);
-                        return (
-                          <div
-                            key={indexOption}
-                            className={`rounded px-1 py-0.5 text-sm ${getBgColor(selectedOption?.color)}`}
-                          >
-                            {selectedOption?.title}
-                          </div>
-                        );
-                      })}
-                    </div>
-                  );
-                default:
-                  return <div key={index}>{row.cells[column.fieldId].data}</div>;
-              }
-            })}
+          {cells.map((cell, index) => (
+            <BoardCell
+              key={index}
+              cellIdentifier={cell.cellIdentifier}
+              cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
+              fieldController={controller.fieldController}
+            ></BoardCell>
+          ))}
         </div>
       </div>
       {isMoving && (

+ 23 - 0
frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCell.tsx

@@ -0,0 +1,23 @@
+import { useCell } from '../_shared/database-hooks/useCell';
+import { CellIdentifier } from '../../stores/effects/database/cell/cell_bd_svc';
+import { CellCache } from '../../stores/effects/database/cell/cell_cache';
+import { FieldController } from '../../stores/effects/database/field/field_controller';
+import {useEffect} from "react";
+
+export const BoardCell = ({
+  cellIdentifier,
+  cellCache,
+  fieldController,
+}: {
+  cellIdentifier: CellIdentifier;
+  cellCache: CellCache;
+  fieldController: FieldController;
+}) => {
+  const { loadCell, data } = useCell(cellIdentifier, cellCache, fieldController);
+  useEffect(() => {
+    void (async () => {
+      await loadCell()
+    })();
+  }, [])
+  return <div>{data}</div>;
+};

+ 2 - 1
frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts

@@ -3,7 +3,8 @@ import { FieldController, FieldInfo } from './field/field_controller';
 import { DatabaseViewCache } from './view/database_view_cache';
 import { DatabasePB } from '../../../../services/backend/models/flowy-database/grid_entities';
 import { RowChangedReason, RowInfo } from './row/row_cache';
-import { Err, Ok } from 'ts-results';
+import { Err, Ok, Result } from 'ts-results';
+import { FlowyError, RowPB } from '../../../../services/backend';
 
 export type SubscribeCallback = {
   onViewChanged?: (data: DatabasePB) => void;

+ 1 - 1
frontend/appflowy_tauri/src/appflowy_app/stores/reducers/board/slice.ts

@@ -1,6 +1,6 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
 
-const initialState = 'field1';
+const initialState = '';
 
 export const boardSlice = createSlice({
   name: 'board',

+ 47 - 193
frontend/appflowy_tauri/src/appflowy_app/stores/reducers/database/slice.ts

@@ -1,5 +1,4 @@
 import { createSlice, PayloadAction } from '@reduxjs/toolkit';
-import { nanoid } from 'nanoid';
 import { FieldType } from '../../../../services/backend/models/flowy-database/field_entities';
 import { DateFormat, NumberFormat, SelectOptionColorPB, TimeFormat } from '../../../../services/backend';
 
@@ -27,24 +26,22 @@ export interface IDatabaseField {
 export interface IDatabaseColumn {
   fieldId: string;
   sort: 'none' | 'asc' | 'desc';
-  filter?: any;
   visible: boolean;
 }
 
-export interface ICellData {
+/*export interface ICellData {
   rowId: string;
   fieldId: string;
   cellId: string;
-  data: string | number;
-  optionIds?: string[];
-}
+  data: string[];
+}*/
 
-export type DatabaseCellMap = { [keys: string]: ICellData };
+// export type DatabaseCellMap = { [keys: string]: ICellData };
 
 export interface IDatabaseRow {
   rowId: string;
   // key(fieldId) -> value(Cell)
-  cells: DatabaseCellMap;
+  // cells: DatabaseCellMap;
 }
 
 export type DatabaseFieldMap = { [keys: string]: IDatabaseField };
@@ -56,190 +53,47 @@ export interface IDatabase {
   columns: IDatabaseColumn[];
 }
 
-// key(databaseId) -> value(IDatabase)
 const initialState: IDatabase = {
   title: 'Database One',
-  columns: [
-    {
-      visible: true,
-      fieldId: 'field1',
-      sort: 'none',
-    },
-    {
-      visible: true,
-      fieldId: 'field2',
-      sort: 'none',
-    },
-    {
-      visible: true,
-      fieldId: 'field3',
-      sort: 'none',
-    },
-    {
-      visible: true,
-      fieldId: 'field4',
-      sort: 'none',
-    },
-  ],
-  fields: {
-    field1: {
-      title: 'status',
-      fieldId: 'field1',
-      fieldType: FieldType.SingleSelect,
-      fieldOptions: {
-        selectOptions: [
-          {
-            selectOptionId: 'so1',
-            title: 'To Do',
-            color: SelectOptionColorPB.Orange,
-          },
-          {
-            selectOptionId: 'so2',
-            title: 'In Progress',
-            color: SelectOptionColorPB.Green,
-          },
-          {
-            selectOptionId: 'so3',
-            title: 'Done',
-            color: SelectOptionColorPB.Blue,
-          },
-        ],
-      },
-    },
-    field2: {
-      title: 'name',
-      fieldId: 'field2',
-      fieldType: FieldType.RichText,
-      fieldOptions: {},
-    },
-    field3: {
-      title: 'percent',
-      fieldId: 'field3',
-      fieldType: FieldType.Number,
-      fieldOptions: {
-        numberFormat: NumberFormat.Num,
-      },
-    },
-    field4: {
-      title: 'tags',
-      fieldId: 'field4',
-      fieldType: FieldType.MultiSelect,
-      fieldOptions: {
-        selectOptions: [
-          {
-            selectOptionId: 'f4so1',
-            title: 'type1',
-            color: SelectOptionColorPB.Blue,
-          },
-          {
-            selectOptionId: 'f4so2',
-            title: 'type2',
-            color: SelectOptionColorPB.Aqua,
-          },
-          {
-            selectOptionId: 'f4so3',
-            title: 'type3',
-            color: SelectOptionColorPB.Purple,
-          },
-          {
-            selectOptionId: 'f4so4',
-            title: 'type4',
-            color: SelectOptionColorPB.Purple,
-          },
-          {
-            selectOptionId: 'f4so5',
-            title: 'type5',
-            color: SelectOptionColorPB.Purple,
-          },
-          {
-            selectOptionId: 'f4so6',
-            title: 'type6',
-            color: SelectOptionColorPB.Purple,
-          },
-          {
-            selectOptionId: 'f4so7',
-            title: 'type7',
-            color: SelectOptionColorPB.Purple,
-          },
-        ],
-      },
-    },
-  },
-  rows: [
-    {
-      rowId: 'row1',
-      cells: {
-        field1: {
-          rowId: 'row1',
-          fieldId: 'field1',
-          cellId: 'cell11',
-          data: '',
-          optionIds: ['so1'],
-        },
-        field2: {
-          rowId: 'row1',
-          fieldId: 'field2',
-          cellId: 'cell12',
-          data: 'Card 1',
-        },
-        field3: {
-          rowId: 'row1',
-          fieldId: 'field3',
-          cellId: 'cell13',
-          data: 10,
-        },
-        field4: {
-          rowId: 'row1',
-          fieldId: 'field4',
-          cellId: 'cell14',
-          data: '',
-          optionIds: ['f4so2', 'f4so3', 'f4so4', 'f4so5', 'f4so6', 'f4so7'],
-        },
-      },
-    },
-    {
-      rowId: 'row2',
-      cells: {
-        field1: {
-          rowId: 'row2',
-          fieldId: 'field1',
-          cellId: 'cell21',
-          data: '',
-          optionIds: ['so1'],
-        },
-        field2: {
-          rowId: 'row2',
-          fieldId: 'field2',
-          cellId: 'cell22',
-          data: 'Card 2',
-        },
-        field3: {
-          rowId: 'row2',
-          fieldId: 'field3',
-          cellId: 'cell23',
-          data: 20,
-        },
-        field4: {
-          rowId: 'row2',
-          fieldId: 'field4',
-          cellId: 'cell24',
-          data: '',
-          optionIds: ['f4so1'],
-        },
-      },
-    },
-  ],
+  columns: [],
+  fields: {},
+  rows: [],
 };
 
 export const databaseSlice = createSlice({
   name: 'database',
   initialState: initialState,
   reducers: {
+    clear: () => {
+      return initialState;
+    },
+
+    updateRows: (state, action: PayloadAction<{ rows: IDatabaseRow[] }>) => {
+      return {
+        ...state,
+        rows: action.payload.rows,
+      };
+    },
+
+    updateFields: (state, action: PayloadAction<{ fields: DatabaseFieldMap }>) => {
+      return {
+        ...state,
+        fields: action.payload.fields,
+      };
+    },
+
+    updateColumns: (state, action: PayloadAction<{ columns: IDatabaseColumn[] }>) => {
+      return {
+        ...state,
+        columns: action.payload.columns,
+      };
+    },
+
     updateTitle: (state, action: PayloadAction<{ title: string }>) => {
       state.title = action.payload.title;
     },
 
-    addField: (state, action: PayloadAction<{ field: IDatabaseField }>) => {
+    /*addField: (state, action: PayloadAction<{ field: IDatabaseField }>) => {
       const { field } = action.payload;
 
       state.fields[field.fieldId] = field;
@@ -253,7 +107,7 @@ export const databaseSlice = createSlice({
         cells[field.fieldId] = {
           rowId: r.rowId,
           fieldId: field.fieldId,
-          data: '',
+          data: [''],
           cellId: nanoid(6),
         };
         return {
@@ -261,15 +115,15 @@ export const databaseSlice = createSlice({
           cells: cells,
         };
       });
-    },
+    },*/
 
-    updateField: (state, action: PayloadAction<{ field: IDatabaseField }>) => {
+    /*updateField: (state, action: PayloadAction<{ field: IDatabaseField }>) => {
       const { field } = action.payload;
 
       state.fields[field.fieldId] = field;
-    },
+    },*/
 
-    addFieldSelectOption: (state, action: PayloadAction<{ fieldId: string; option: ISelectOption }>) => {
+    /*addFieldSelectOption: (state, action: PayloadAction<{ fieldId: string; option: ISelectOption }>) => {
       const { fieldId, option } = action.payload;
 
       const field = state.fields[fieldId];
@@ -283,9 +137,9 @@ export const databaseSlice = createSlice({
           selectOptions: [option],
         };
       }
-    },
+    },*/
 
-    updateFieldSelectOption: (state, action: PayloadAction<{ fieldId: string; option: ISelectOption }>) => {
+    /*updateFieldSelectOption: (state, action: PayloadAction<{ fieldId: string; option: ISelectOption }>) => {
       const { fieldId, option } = action.payload;
 
       const field = state.fields[fieldId];
@@ -293,16 +147,16 @@ export const databaseSlice = createSlice({
       if (selectOptions) {
         selectOptions[selectOptions.findIndex((o) => o.selectOptionId === option.selectOptionId)] = option;
       }
-    },
+    },*/
 
-    addRow: (state) => {
+    /*addRow: (state) => {
       const rowId = nanoid(6);
       const cells: { [keys: string]: ICellData } = {};
       Object.keys(state.fields).forEach((id) => {
         cells[id] = {
           rowId: rowId,
           fieldId: id,
-          data: '',
+          data: [''],
           cellId: nanoid(6),
         };
       });
@@ -312,15 +166,15 @@ export const databaseSlice = createSlice({
       };
 
       state.rows.push(newRow);
-    },
+    },*/
 
-    updateCellValue: (source, action: PayloadAction<{ cell: ICellData }>) => {
+    /*updateCellValue: (source, action: PayloadAction<{ cell: ICellData }>) => {
       const { cell } = action.payload;
       const row = source.rows.find((r) => r.rowId === cell.rowId);
       if (row) {
         row.cells[cell.fieldId] = cell;
       }
-    },
+    },*/
   },
 });
 

+ 5 - 5
frontend/appflowy_tauri/src/appflowy_app/views/BoardPage.tsx

@@ -4,19 +4,19 @@ import { Board } from '../components/board/Board';
 
 export const BoardPage = () => {
   const params = useParams();
-  const [databaseId, setDatabaseId] = useState('');
+  const [viewId, setViewId] = useState('');
 
   useEffect(() => {
     if (params?.id?.length) {
-      // setDatabaseId(params.id);
-      setDatabaseId('testDb');
+      setViewId(params.id);
+      // setDatabaseId('testDb');
     }
   }, [params]);
 
   return (
     <div className='flex h-full flex-col gap-8 px-8 pt-8'>
-      <h1 className='text-4xl font-bold'>Board</h1>
-      {databaseId?.length && <Board />}
+      <h1 className='text-4xl font-bold'>Board: {viewId}</h1>
+      {viewId?.length && <Board viewId={viewId} />}
     </div>
   );
 };