Browse Source

chore: config date cell ui

appflowy 3 years ago
parent
commit
8a94644add

+ 0 - 1
frontend/app_flowy/lib/startup/deps_resolver.dart

@@ -200,7 +200,6 @@ void _resolveGridDeps(GetIt getIt) {
 
   getIt.registerFactoryParam<DateCellBloc, CellData, void>(
     (cellData, _) => DateCellBloc(
-      service: CellService(),
       cellData: cellData,
     ),
   );

+ 26 - 20
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/checkbox_cell_bloc.dart

@@ -26,15 +26,10 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
             _startListening();
           },
           select: (_Selected value) async {
-            _service.updateCell(
-              gridId: state.cellData.gridId,
-              fieldId: state.cellData.field.id,
-              rowId: state.cellData.rowId,
-              data: !state.isSelected ? "Yes" : "No",
-            );
+            _updateCellData();
           },
           didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
-            emit(state.copyWith(isSelected: isSelected(value.cell)));
+            emit(state.copyWith(isSelected: _isSelected(value.cell)));
           },
         );
       },
@@ -50,22 +45,33 @@ class CheckboxCellBloc extends Bloc<CheckboxCellEvent, CheckboxCellState> {
   void _startListening() {
     _listener.updateCellNotifier.addPublishListener((result) {
       result.fold(
-        (notificationData) async {
-          final result = await _service.getCell(
-            gridId: state.cellData.gridId,
-            fieldId: state.cellData.field.id,
-            rowId: state.cellData.rowId,
-          );
-          result.fold(
-            (cell) => add(CheckboxCellEvent.didReceiveCellUpdate(cell)),
-            (err) => Log.error(err),
-          );
-        },
+        (notificationData) async => await _loadCellData(),
         (err) => Log.error(err),
       );
     });
     _listener.start();
   }
+
+  Future<void> _loadCellData() async {
+    final result = await _service.getCell(
+      gridId: state.cellData.gridId,
+      fieldId: state.cellData.field.id,
+      rowId: state.cellData.rowId,
+    );
+    result.fold(
+      (cell) => add(CheckboxCellEvent.didReceiveCellUpdate(cell)),
+      (err) => Log.error(err),
+    );
+  }
+
+  void _updateCellData() {
+    _service.updateCell(
+      gridId: state.cellData.gridId,
+      fieldId: state.cellData.field.id,
+      rowId: state.cellData.rowId,
+      data: !state.isSelected ? "Yes" : "No",
+    );
+  }
 }
 
 @freezed
@@ -83,11 +89,11 @@ class CheckboxCellState with _$CheckboxCellState {
   }) = _CheckboxCellState;
 
   factory CheckboxCellState.initial(CellData cellData) {
-    return CheckboxCellState(cellData: cellData, isSelected: isSelected(cellData.cell));
+    return CheckboxCellState(cellData: cellData, isSelected: _isSelected(cellData.cell));
   }
 }
 
-bool isSelected(Cell? cell) {
+bool _isSelected(Cell? cell) {
   final content = cell?.content ?? "";
   return content == "Yes";
 }

+ 64 - 11
frontend/app_flowy/lib/workspace/application/grid/cell_bloc/date_cell_bloc.dart

@@ -1,5 +1,7 @@
+import 'package:app_flowy/workspace/application/grid/cell_bloc/cell_listener.dart';
 import 'package:app_flowy/workspace/application/grid/row/row_service.dart';
-import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
+import 'package:flowy_sdk/log.dart';
+import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart' show Cell;
 import 'package:flutter_bloc/flutter_bloc.dart';
 import 'package:freezed_annotation/freezed_annotation.dart';
 import 'dart:async';
@@ -8,17 +10,28 @@ import 'cell_service.dart';
 part 'date_cell_bloc.freezed.dart';
 
 class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
-  final CellService service;
-  final CellData cellData;
+  final CellService _service;
+  final CellListener _listener;
 
-  DateCellBloc({
-    required this.service,
-    required this.cellData,
-  }) : super(DateCellState.initial()) {
+  DateCellBloc({required CellData cellData})
+      : _service = CellService(),
+        _listener = CellListener(rowId: cellData.rowId, fieldId: cellData.field.id),
+        super(DateCellState.initial(cellData)) {
     on<DateCellEvent>(
       (event, emit) async {
-        await event.map(
-          initial: (_InitialCell value) async {},
+        event.map(
+          initial: (_InitialCell value) {
+            _startListening();
+          },
+          selectDay: (_SelectDay value) {
+            _updateCellData(value.day);
+          },
+          didReceiveCellUpdate: (_DidReceiveCellUpdate value) {
+            emit(state.copyWith(
+              cellData: state.cellData.copyWith(cell: value.cell),
+              content: value.cell.content,
+            ));
+          },
         );
       },
     );
@@ -26,20 +39,60 @@ class DateCellBloc extends Bloc<DateCellEvent, DateCellState> {
 
   @override
   Future<void> close() async {
+    await _listener.stop();
     return super.close();
   }
+
+  void _startListening() {
+    _listener.updateCellNotifier.addPublishListener((result) {
+      result.fold(
+        (notificationData) => _loadCellData(),
+        (err) => Log.error(err),
+      );
+    });
+    _listener.start();
+  }
+
+  Future<void> _loadCellData() async {
+    final result = await _service.getCell(
+      gridId: state.cellData.gridId,
+      fieldId: state.cellData.field.id,
+      rowId: state.cellData.rowId,
+    );
+    result.fold(
+      (cell) => add(DateCellEvent.didReceiveCellUpdate(cell)),
+      (err) => Log.error(err),
+    );
+  }
+
+  void _updateCellData(DateTime day) {
+    final data = day.millisecondsSinceEpoch ~/ 1000;
+    _service.updateCell(
+      gridId: state.cellData.gridId,
+      fieldId: state.cellData.field.id,
+      rowId: state.cellData.rowId,
+      data: data.toString(),
+    );
+  }
 }
 
 @freezed
 class DateCellEvent with _$DateCellEvent {
   const factory DateCellEvent.initial() = _InitialCell;
+  const factory DateCellEvent.selectDay(DateTime day) = _SelectDay;
+  const factory DateCellEvent.didReceiveCellUpdate(Cell cell) = _DidReceiveCellUpdate;
 }
 
 @freezed
 class DateCellState with _$DateCellState {
   const factory DateCellState({
-    Cell? cell,
+    required CellData cellData,
+    required String content,
+    DateTime? selectedDay,
   }) = _DateCellState;
 
-  factory DateCellState.initial() => const DateCellState();
+  factory DateCellState.initial(CellData cellData) => DateCellState(
+        cellData: cellData,
+        content: cellData.cell?.content ?? "",
+      );
 }

+ 11 - 15
frontend/app_flowy/lib/workspace/application/grid/row/row_service.dart

@@ -4,6 +4,9 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
 import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid-data-model/grid.pb.dart';
 import 'package:flowy_sdk/protobuf/flowy-grid/row_entities.pb.dart';
+import 'package:freezed_annotation/freezed_annotation.dart';
+
+part 'row_service.freezed.dart';
 
 class RowService {
   final String gridId;
@@ -29,19 +32,12 @@ class RowService {
   }
 }
 
-class CellData extends Equatable {
-  final String gridId;
-  final String rowId;
-  final Field field;
-  final Cell? cell;
-
-  const CellData({
-    required this.rowId,
-    required this.gridId,
-    required this.field,
-    required this.cell,
-  });
-
-  @override
-  List<Object?> get props => [cell, field];
+@freezed
+class CellData with _$CellData {
+  const factory CellData({
+    required String gridId,
+    required String rowId,
+    required Field field,
+    Cell? cell,
+  }) = _CellData;
 }

+ 89 - 2
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/date_cell.dart

@@ -1,7 +1,11 @@
 import 'package:app_flowy/startup/startup.dart';
 import 'package:app_flowy/workspace/application/grid/prelude.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/text.dart';
 import 'package:flutter/widgets.dart';
 import 'package:flutter_bloc/flutter_bloc.dart';
+import 'package:table_calendar/table_calendar.dart';
+import 'package:window_size/window_size.dart';
 
 class DateCell extends StatefulWidget {
   final CellData cellData;
@@ -20,7 +24,7 @@ class _DateCellState extends State<DateCell> {
 
   @override
   void initState() {
-    _cellBloc = getIt<DateCellBloc>(param1: widget.cellData);
+    _cellBloc = getIt<DateCellBloc>(param1: widget.cellData)..add(const DateCellEvent.initial());
     super.initState();
   }
 
@@ -30,7 +34,20 @@ class _DateCellState extends State<DateCell> {
       value: _cellBloc,
       child: BlocBuilder<DateCellBloc, DateCellState>(
         builder: (context, state) {
-          return Container();
+          return SizedBox.expand(
+            child: GestureDetector(
+              behavior: HitTestBehavior.opaque,
+              onTap: () => _CellCalendar.show(
+                context,
+                onSelected: (day) => context.read<DateCellBloc>().add(DateCellEvent.selectDay(day)),
+              ),
+              child: MouseRegion(
+                opaque: false,
+                cursor: SystemMouseCursors.click,
+                child: FlowyText.medium(state.content, fontSize: 12),
+              ),
+            ),
+          );
         },
       ),
     );
@@ -42,3 +59,73 @@ class _DateCellState extends State<DateCell> {
     super.dispose();
   }
 }
+
+final kToday = DateTime.now();
+final kFirstDay = DateTime(kToday.year, kToday.month - 3, kToday.day);
+final kLastDay = DateTime(kToday.year, kToday.month + 3, kToday.day);
+
+class _CellCalendar extends StatefulWidget {
+  final void Function(DateTime) onSelected;
+  const _CellCalendar({required this.onSelected, Key? key}) : super(key: key);
+
+  @override
+  State<_CellCalendar> createState() => _CellCalendarState();
+
+  static Future<void> show(BuildContext context, {required void Function(DateTime) onSelected}) async {
+    _CellCalendar.remove(context);
+    final window = await getWindowInfo();
+    final calendar = _CellCalendar(onSelected: onSelected);
+    const size = Size(460, 400);
+    FlowyOverlay.of(context).insertWithRect(
+      widget: OverlayContainer(
+        child: calendar,
+        constraints: BoxConstraints.loose(const Size(460, 400)),
+      ),
+      identifier: _CellCalendar.identifier(),
+      anchorPosition: Offset(-size.width / 2.0, -size.height / 2.0),
+      anchorSize: window.frame.size,
+      anchorDirection: AnchorDirection.center,
+      style: FlowyOverlayStyle(blur: false),
+    );
+  }
+
+  static void remove(BuildContext context) {
+    FlowyOverlay.of(context).remove(identifier());
+  }
+
+  static String identifier() {
+    return (_CellCalendar).toString();
+  }
+}
+
+class _CellCalendarState extends State<_CellCalendar> {
+  DateTime _focusedDay = DateTime.now();
+  DateTime? _selectedDay;
+
+  @override
+  Widget build(BuildContext context) {
+    return TableCalendar(
+      firstDay: kFirstDay,
+      lastDay: kLastDay,
+      focusedDay: _focusedDay,
+      calendarFormat: CalendarFormat.month,
+      selectedDayPredicate: (day) {
+        return isSameDay(_selectedDay, day);
+      },
+      onDaySelected: (selectedDay, focusedDay) {
+        if (!isSameDay(_selectedDay, selectedDay)) {
+          // Call `setState()` when updating the selected day
+          setState(() {
+            _selectedDay = selectedDay;
+            _focusedDay = focusedDay;
+            widget.onSelected(selectedDay);
+          });
+        }
+      },
+      onFormatChanged: (format) {},
+      onPageChanged: (focusedDay) {
+        _focusedDay = focusedDay;
+      },
+    );
+  }
+}

+ 6 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_cell.dart

@@ -86,7 +86,12 @@ class _MultiSelectCellState extends State<MultiSelectCell> {
           return SizedBox.expand(
             child: InkWell(
               onTap: () {
-                SelectOptionCellEditor.show(context, state.cellData, state.options, state.selectedOptions);
+                SelectOptionCellEditor.show(
+                  context,
+                  state.cellData,
+                  state.options,
+                  state.selectedOptions,
+                );
               },
               child: Row(children: children),
             ),

+ 4 - 4
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/cell/selection_cell/selection_editor.dart

@@ -36,10 +36,6 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
     Key? key,
   }) : super(key: key);
 
-  static String identifier() {
-    return (SelectOptionCellEditor).toString();
-  }
-
   @override
   Widget build(BuildContext context) {
     return BlocProvider(
@@ -96,6 +92,10 @@ class SelectOptionCellEditor extends StatelessWidget with FlowyOverlayDelegate {
     FlowyOverlay.of(context).remove(identifier());
   }
 
+  static String identifier() {
+    return (SelectOptionCellEditor).toString();
+  }
+
   @override
   bool asBarrier() => true;
 }

+ 1 - 1
frontend/app_flowy/lib/workspace/presentation/plugins/grid/src/widgets/header/field_type_list.dart

@@ -19,7 +19,7 @@ class FieldTypeList extends StatelessWidget with FlowyOverlayDelegate {
 
   @override
   Widget build(BuildContext context) {
-    final cells = FieldType.values.where((ty) => ty != FieldType.DateTime).map((fieldType) {
+    final cells = FieldType.values.map((fieldType) {
       return FieldTypeCell(
         fieldType: fieldType,
         onSelectField: (fieldType) {

+ 8 - 1
frontend/app_flowy/packages/flowy_infra/pubspec.lock

@@ -95,6 +95,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.12.11"
+  material_color_utilities:
+    dependency: transitive
+    description:
+      name: material_color_utilities
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.1.3"
   meta:
     dependency: transitive
     description:
@@ -176,7 +183,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.3"
+    version: "0.4.8"
   textstyle_extensions:
     dependency: "direct main"
     description:

+ 3 - 3
frontend/app_flowy/packages/flowy_sdk/example/pubspec.lock

@@ -7,7 +7,7 @@ packages:
       name: archive
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "3.1.8"
+    version: "3.1.6"
   async:
     dependency: transitive
     description:
@@ -200,7 +200,7 @@ packages:
       name: path
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.1"
+    version: "1.8.0"
   platform:
     dependency: transitive
     description:
@@ -275,7 +275,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.9"
+    version: "0.4.8"
   typed_data:
     dependency: transitive
     description:

+ 0 - 8
frontend/app_flowy/packages/flowy_sdk/example/windows/flutter/generated_plugins.cmake

@@ -6,9 +6,6 @@ list(APPEND FLUTTER_PLUGIN_LIST
   flowy_sdk
 )
 
-list(APPEND FLUTTER_FFI_PLUGIN_LIST
-)
-
 set(PLUGIN_BUNDLED_LIBRARIES)
 
 foreach(plugin ${FLUTTER_PLUGIN_LIST})
@@ -17,8 +14,3 @@ foreach(plugin ${FLUTTER_PLUGIN_LIST})
   list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
   list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
 endforeach(plugin)
-
-foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
-  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})
-  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
-endforeach(ffi_plugin)

+ 2 - 2
frontend/app_flowy/packages/flowy_sdk/pubspec.lock

@@ -346,7 +346,7 @@ packages:
       name: path
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.1"
+    version: "1.8.0"
   pedantic:
     dependency: transitive
     description:
@@ -456,7 +456,7 @@ packages:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.4.9"
+    version: "0.4.8"
   timing:
     dependency: transitive
     description:

+ 15 - 1
frontend/app_flowy/pubspec.lock

@@ -512,7 +512,7 @@ packages:
       name: fluttertoast
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "8.0.8"
+    version: "8.0.9"
   freezed:
     dependency: "direct dev"
     description:
@@ -1031,6 +1031,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.0.1"
+  simple_gesture_detector:
+    dependency: transitive
+    description:
+      name: simple_gesture_detector
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.2.0"
   sized_context:
     dependency: "direct main"
     description:
@@ -1120,6 +1127,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "0.3.1+2"
+  table_calendar:
+    dependency: "direct main"
+    description:
+      name: table_calendar
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "3.0.5"
   term_glyph:
     dependency: transitive
     description:

+ 1 - 0
frontend/app_flowy/pubspec.yaml

@@ -73,6 +73,7 @@ dependencies:
   cupertino_icons: ^1.0.2
   device_info_plus: ^3.2.1
   fluttertoast: ^8.0.8
+  table_calendar: ^3.0.5
 
 dev_dependencies:
   flutter_lints: ^1.0.0

+ 2 - 3
frontend/rust-lib/flowy-grid/src/services/field/type_options/date_type_option.rs

@@ -69,9 +69,8 @@ impl CellDataOperation for DateTypeOption {
         _cell_meta: Option<CellMeta>,
     ) -> Result<String, FlowyError> {
         let changeset = changeset.into();
-        if let Err(e) = changeset.parse::<i64>() {
-            tracing::error!("Parse {} to i64 failed: {}", changeset.to_string(), e);
-            return Err(FlowyError::internal().context(e));
+        if changeset.parse::<f64>().is_err() || changeset.parse::<i64>().is_err() {
+            return Err(FlowyError::internal().context(format!("Parse {} failed", changeset.to_string())));
         };
 
         Ok(TypeOptionCellData::new(changeset, self.field_type()).json())

+ 1 - 1
frontend/rust-lib/flowy-sdk/src/lib.rs

@@ -74,7 +74,7 @@ fn crate_log_filter(level: String) -> String {
     filters.push(format!("lib_ot={}", level));
     filters.push(format!("lib_ws={}", level));
     filters.push(format!("lib_infra={}", level));
-    filters.push(format!("flowy_sync={}", level));
+    filters.push(format!("flowy_sync={}", "debug"));
 
     filters.push(format!("dart_ffi={}", "info"));
     filters.push(format!("flowy_database={}", "info"));

+ 9 - 2
shared-lib/flowy-grid-data-model/src/entities/meta.rs

@@ -8,7 +8,6 @@ use std::collections::HashMap;
 use strum_macros::{Display, EnumCount as EnumCountMacro, EnumIter, EnumString};
 
 pub const DEFAULT_ROW_HEIGHT: i32 = 42;
-pub const DEFAULT_FIELD_WIDTH: i32 = 150;
 
 #[derive(Debug, Clone, Default, Serialize, Deserialize, ProtoBuf)]
 pub struct GridMeta {
@@ -110,6 +109,7 @@ pub struct FieldMeta {
 
 impl FieldMeta {
     pub fn new(name: &str, desc: &str, field_type: FieldType) -> Self {
+        let width = field_type.default_cell_width();
         Self {
             id: uuid::Uuid::new_v4().to_string(),
             name: name.to_string(),
@@ -117,7 +117,7 @@ impl FieldMeta {
             field_type,
             frozen: false,
             visibility: true,
-            width: DEFAULT_FIELD_WIDTH,
+            width,
             type_options: Default::default(),
         }
     }
@@ -270,6 +270,13 @@ impl FieldType {
         let ty = self.clone();
         format!("{}", ty as u8)
     }
+
+    pub fn default_cell_width(&self) -> i32 {
+        match self {
+            FieldType::DateTime => 180,
+            _ => 150,
+        }
+    }
 }
 
 #[derive(Debug, Clone, Serialize, Deserialize, Default, ProtoBuf)]

+ 1 - 1
shared-lib/flowy-sync/src/client_grid/grid_block_meta_pad.rs

@@ -156,7 +156,7 @@ impl GridBlockMetaPad {
                 match cal_diff::<PlainTextAttributes>(old, new) {
                     None => Ok(None),
                     Some(delta) => {
-                        tracing::trace!("[GridBlockMeta] Composing change {}", delta.to_delta_str());
+                        tracing::debug!("[GridBlockMeta] Composing change {}", delta.to_delta_str());
                         self.delta = self.delta.compose(&delta)?;
                         Ok(Some(GridBlockMetaChange { delta, md5: self.md5() }))
                     }