diff --git a/frontend/appflowy_tauri/src-tauri/src/init.rs b/frontend/appflowy_tauri/src-tauri/src/init.rs index a09c80c875..c2fdddf85c 100644 --- a/frontend/appflowy_tauri/src-tauri/src/init.rs +++ b/frontend/appflowy_tauri/src-tauri/src/init.rs @@ -10,7 +10,7 @@ pub fn init_flowy_core() -> AppFlowyCore { } data_path.push("data"); - std::env::set_var("RUST_LOG", "debug"); + std::env::set_var("RUST_LOG", "trace"); let server_config = get_client_server_configuration().unwrap(); let config = AppFlowyCoreConfig::new( data_path.to_str().unwrap(), diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx index abf0b714e7..780120164e 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx @@ -108,7 +108,11 @@ export const EditRow = ({ const onDragEnd: OnDragEndResponder = (result) => { if (!result.destination?.index) return; - void controller.moveField(result.source.droppableId, result.source.index, result.destination.index); + void controller.moveField({ + fieldId: result.source.droppableId, + fromIndex: result.source.index, + toIndex: result.destination.index, + }); }; return ( diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts index f3b0f175e4..7ab6cdd5ce 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/DatabaseTestHelper.ts @@ -12,6 +12,7 @@ import { RowInfo } from '../../stores/effects/database/row/row_cache'; import { RowController } from '../../stores/effects/database/row/row_controller'; import { CellControllerBuilder, + CheckboxCellController, DateCellController, NumberCellController, SelectOptionCellController, @@ -126,6 +127,17 @@ export async function makeDateCellController( return Some(builder.build() as DateCellController); } +export async function makeCheckboxCellController( + fieldId: string, + rowInfo: RowInfo, + databaseController: DatabaseController +): Promise> { + const builder = await makeCellControllerBuilder(fieldId, rowInfo, FieldType.Checkbox, databaseController).then( + (result) => result.unwrap() + ); + return Some(builder.build() as CheckboxCellController); +} + export async function makeURLCellController( fieldId: string, rowInfo: RowInfo, diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx index a45d82fa38..62daf865d1 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestAPI.tsx @@ -8,10 +8,13 @@ import { TestDeleteField, TestDeleteRow, TestEditCell, + TestEditCheckboxCell, + TestEditDateCell, TestEditField, TestEditTextCell, TestEditURLCell, TestGetSingleSelectFieldData, + TestMoveField, TestSwitchFromMultiSelectToText, TestSwitchFromSingleSelectToNumber, } from './TestGrid'; @@ -37,9 +40,12 @@ export const TestAPI = () => { + + + diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx index 3f7cf70341..1a3925c82c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/tests/TestGrid.tsx @@ -8,6 +8,7 @@ import { } from '@/services/backend'; import { Log } from '$app/utils/log'; import { + assert, assertFieldName, assertNumberOfFields, assertNumberOfRows, @@ -16,6 +17,8 @@ import { createTestDatabaseView, editTextCell, findFirstFieldInfoWithFieldType, + makeCheckboxCellController, + makeDateCellController, makeMultiSelectCellController, makeSingleSelectCellController, makeTextCellController, @@ -27,6 +30,8 @@ import { TypeOptionController } from '$app/stores/effects/database/field/type_op import { None, Some } from 'ts-results'; import { RowBackendService } from '$app/stores/effects/database/row/row_bd_svc'; import { makeNumberTypeOptionContext } from '$app/stores/effects/database/field/type_option/type_option_context'; +import { CalendarData } from '$app/stores/effects/database/cell/controller_builder'; +import { DatabaseEventMoveField } from '@/services/backend/events/flowy-database'; export const RunAllGridTests = () => { async function run() { @@ -138,6 +143,57 @@ async function testEditURLCell() { await new Promise((resolve) => setTimeout(resolve, 200)); } +async function testEditDateCell() { + const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); + const databaseController = await openTestDatabase(view.id); + await databaseController.open().then((result) => result.unwrap()); + + const typeOptionController = new TypeOptionController(view.id, None, FieldType.DateTime); + await typeOptionController.initialize(); + + const row = databaseController.databaseViewCache.rowInfos[0]; + const dateCellController = await makeDateCellController(typeOptionController.fieldId, row, databaseController).then( + (result) => result.unwrap() + ); + + dateCellController.subscribeChanged({ + onCellChanged: (content) => { + const pb = content.unwrap(); + Log.info('Receive date data:', pb.date, pb.time); + }, + }); + + const date = new CalendarData(new Date(), true, '13:00'); + await dateCellController.saveCellData(date); + await new Promise((resolve) => setTimeout(resolve, 200)); +} + +async function testCheckboxCell() { + const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); + const databaseController = await openTestDatabase(view.id); + await databaseController.open().then((result) => result.unwrap()); + + const typeOptionController = new TypeOptionController(view.id, None, FieldType.Checkbox); + await typeOptionController.initialize(); + + const row = databaseController.databaseViewCache.rowInfos[0]; + const checkboxCellController = await makeCheckboxCellController( + typeOptionController.fieldId, + row, + databaseController + ).then((result) => result.unwrap()); + + checkboxCellController.subscribeChanged({ + onCellChanged: (content) => { + const pb = content.unwrap(); + Log.info('Receive checkbox data:', pb); + }, + }); + + await checkboxCellController.saveCellData('true'); + await new Promise((resolve) => setTimeout(resolve, 200)); +} + async function testCreateRow() { const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); const databaseController = await openTestDatabase(view.id); @@ -196,6 +252,24 @@ async function testCreateOptionInCell() { await databaseController.dispose(); } +async function testMoveField() { + const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); + const databaseController = await openTestDatabase(view.id); + await databaseController.open().then((result) => result.unwrap()); + + databaseController.subscribe({ + onFieldsChanged: (value) => { + Log.info('Receive fields data:', value); + }, + }); + + const fieldInfos = [...databaseController.fieldController.fieldInfos]; + const field_id = fieldInfos[0].field.id; + await databaseController.moveField({ fieldId: field_id, fromIndex: 0, toIndex: 1 }); + await new Promise((resolve) => setTimeout(resolve, 200)); + assert(databaseController.fieldController.fieldInfos[1].field.id === field_id); +} + async function testGetSingleSelectFieldData() { const view = await createTestDatabaseView(ViewLayoutTypePB.Grid); const databaseController = await openTestDatabase(view.id); @@ -360,6 +434,12 @@ export const TestEditTextCell = () => { export const TestEditURLCell = () => { return TestButton('Test editing URL cell', testEditURLCell); }; +export const TestEditDateCell = () => { + return TestButton('Test editing date cell', testEditDateCell); +}; +export const TestEditCheckboxCell = () => { + return TestButton('Test editing checkbox cell', testCheckboxCell); +}; export const TestCreateRow = () => { return TestButton('Test create row', testCreateRow); }; @@ -382,6 +462,9 @@ export const TestSwitchFromMultiSelectToText = () => { return TestButton('Test switch from multi-select to text column', testSwitchFromMultiSelectToRichText); }; +export const TestMoveField = () => { + return TestButton('Test move field', testMoveField); +}; export const TestEditField = () => { return TestButton('Test edit the column name', testEditField); }; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts index 7858f5a938..83f8bcd9ae 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/controller_builder.ts @@ -23,7 +23,7 @@ export type SelectOptionCellController = CellController; export class CalendarData { - constructor(public readonly date: Date, public readonly time?: string) {} + constructor(public readonly date: Date, public readonly includeTime: boolean, public readonly time?: string) {} } export type URLCellController = CellController; diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts index c6eb7288ec..affa40c7f5 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/cell/data_persistence.ts @@ -25,11 +25,12 @@ export class DateCellDataPersistence extends CellDataPersistence { save(data: CalendarData): Promise> { const payload = DateChangesetPB.fromObject({ cell_path: _makeCellPath(this.cellIdentifier) }); - payload.date = data.date.getUTCMilliseconds.toString(); + payload.date = ((data.date.getTime() / 1000) | 0).toString(); payload.is_utc = true; if (data.time !== undefined) { payload.time = data.time; } + payload.include_time = data.includeTime; return DatabaseEventUpdateDateCell(payload); } } diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_bd_svc.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_bd_svc.ts index 005aceee99..bce7783ddc 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_bd_svc.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_bd_svc.ts @@ -98,16 +98,6 @@ export class DatabaseBackendService { return DatabaseEventMoveGroup(payload); }; - moveField = (fieldId: string, fromIndex: number, toIndex: number) => { - const payload = MoveFieldPayloadPB.fromObject({ - view_id: this.viewId, - field_id: fieldId, - from_index: fromIndex, - to_index: toIndex, - }); - return DatabaseEventMoveField(payload); - }; - /// Get all fields in database getFields = async (fieldIds?: FieldIdPB[]) => { const payload = GetFieldPayloadPB.fromObject({ view_id: this.viewId }); @@ -125,6 +115,16 @@ export class DatabaseBackendService { return DatabaseEventGetGroup(payload); }; + moveField = (params: { fieldId: string; fromIndex: number; toIndex: number }) => { + const payload = MoveFieldPayloadPB.fromObject({ + view_id: this.viewId, + field_id: params.fieldId, + from_index: params.fromIndex, + to_index: params.toIndex, + }); + return DatabaseEventMoveField(payload); + }; + /// Get all groups in database /// It should only call once after the board open loadGroups = () => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts index 0930e3d8a0..918be53b65 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/database_controller.ts @@ -1,7 +1,7 @@ import { DatabaseBackendService } from './database_bd_svc'; import { FieldController, FieldInfo } from './field/field_controller'; import { DatabaseViewCache } from './view/database_view_cache'; -import { DatabasePB, FlowyError, GroupPB } from '@/services/backend'; +import { DatabasePB, GroupPB, FlowyError } from '@/services/backend'; import { RowChangedReason, RowInfo } from './row/row_cache'; import { Err, Ok } from 'ts-results'; import { DatabaseGroupController } from './group/group_controller'; @@ -102,8 +102,8 @@ export class DatabaseController { return this.backendService.moveGroup(fromGroupId, toGroupId); }; - moveField = (fieldId: string, fromIndex: number, toIndex: number) => { - return this.backendService.moveField(fieldId, fromIndex, toIndex); + moveField = (params: { fieldId: string; fromIndex: number; toIndex: number }) => { + return this.backendService.moveField(params); }; private loadGroup = async () => { diff --git a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts index 24ee55b968..c1e126e7d9 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts +++ b/frontend/appflowy_tauri/src/appflowy_app/stores/effects/database/field/field_controller.ts @@ -36,8 +36,8 @@ export class FieldController { } }; - subscribe = (callbacks: { onNumOfFieldsChanged?: (fieldInfos: readonly FieldInfo[]) => void}) => { - this.numOfFieldsNotifier.observer.subscribe((fieldInfos) => { + subscribe = (callbacks: { onNumOfFieldsChanged?: (fieldInfos: readonly FieldInfo[]) => void }) => { + this.numOfFieldsNotifier.observer.subscribe((fieldInfos) => { callbacks.onNumOfFieldsChanged?.(fieldInfos); }); }; @@ -63,12 +63,10 @@ export class FieldController { } const deletedFieldIds = deletedFields.map((field) => field.field_id); - const predicate = (element: FieldInfo) => { - !deletedFieldIds.includes(element.field.id); + const predicate = (element: FieldInfo): boolean => { + return !deletedFieldIds.includes(element.field.id); }; - const newFieldInfos = [...this.fieldInfos]; - newFieldInfos.filter(predicate); - this.numOfFieldsNotifier.fieldInfos = newFieldInfos; + this.numOfFieldsNotifier.fieldInfos = [...this.fieldInfos].filter(predicate); }; private _insertFields = (insertedFields: IndexFieldPB[]) => {