diff --git a/CHANGELOG.md b/CHANGELOG.md
index b485eb1211..e0b58100e9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,27 @@
# Release Notes
+## Version 0.0.5.1 - 09/14/2022
+
+New features
+- Enable deleting a field in board
+- Fix some bugs
+
+
+## Version 0.0.5 - 09/08/2022
+New Features - Kanban Board like Notion and Trello beta
+Boards are the best way to manage projects & tasks. Use them to group your databases by select, multiselect, and checkbox.
+
+
+
+- Set up columns that represent a specific phase of the project cycle and use cards to represent each project / task
+- Drag and drop a card from one phase / column to another phase / column
+- Update database properties in the Board view by clicking on a property and making edits on the card
+
+### Other Features & Improvements
+- Settings allow users to change avatars
+- Click and drag the right edge to resize your sidebar
+- And many user interface improvements (link)
+
## Version 0.0.5 - beta.2 - beta.1 - 09/01/2022
New features
diff --git a/frontend/Makefile.toml b/frontend/Makefile.toml
index 185dac15ad..bdecb73712 100644
--- a/frontend/Makefile.toml
+++ b/frontend/Makefile.toml
@@ -22,7 +22,7 @@ CARGO_MAKE_EXTEND_WORKSPACE_MAKEFILE = true
CARGO_MAKE_CRATE_FS_NAME = "dart_ffi"
CARGO_MAKE_CRATE_NAME = "dart-ffi"
LIB_NAME = "dart_ffi"
-CURRENT_APP_VERSION = "0.0.5"
+CURRENT_APP_VERSION = "0.0.5.1"
FEATURES = "flutter"
PRODUCT_NAME = "AppFlowy"
# CRATE_TYPE: https://doc.rust-lang.org/reference/linkage.html
diff --git a/frontend/app_flowy/assets/translations/en.json b/frontend/app_flowy/assets/translations/en.json
index 141999b4fd..873bbc93ee 100644
--- a/frontend/app_flowy/assets/translations/en.json
+++ b/frontend/app_flowy/assets/translations/en.json
@@ -191,7 +191,8 @@
"optionTitle": "Options",
"addOption": "Add option",
"editProperty": "Edit property",
- "newColumn": "New column"
+ "newColumn": "New column",
+ "deleteFieldPromptMessage": "Are you sure? This property will be deleted"
},
"row": {
"duplicate": "Duplicate",
diff --git a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart
index 3ad8d670d7..3a34469cd7 100644
--- a/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart
+++ b/frontend/app_flowy/lib/plugins/board/presentation/board_page.dart
@@ -287,8 +287,13 @@ class _BoardContentState extends State {
);
}
- void _openCard(String gridId, GridFieldController fieldController,
- RowPB rowPB, GridRowCache rowCache, BuildContext context) {
+ void _openCard(
+ String gridId,
+ GridFieldController fieldController,
+ RowPB rowPB,
+ GridRowCache rowCache,
+ BuildContext context,
+ ) {
final rowInfo = RowInfo(
gridId: gridId,
fields: UnmodifiableListView(fieldController.fieldContexts),
diff --git a/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
index 6bf06c39b9..a64df31cc4 100644
--- a/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
+++ b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_setting.dart
@@ -2,11 +2,12 @@ import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:app_flowy/plugins/board/application/toolbar/board_setting_bloc.dart';
import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
+import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_group.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/toolbar/grid_property.dart';
+import 'package:appflowy_popover/popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/scrolling/styled_list.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
@@ -141,10 +142,12 @@ extension _GridSettingExtension on BoardSettingAction {
}
class BoardSettingListPopover extends StatefulWidget {
+ final PopoverController popoverController;
final BoardSettingContext settingContext;
const BoardSettingListPopover({
Key? key,
+ required this.popoverController,
required this.settingContext,
}) : super(key: key);
@@ -153,36 +156,33 @@ class BoardSettingListPopover extends StatefulWidget {
}
class _BoardSettingListPopoverState extends State {
- bool _showGridPropertyList = false;
+ BoardSettingAction? _action;
@override
Widget build(BuildContext context) {
- if (_showGridPropertyList) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(260, 400)),
- child: GridPropertyList(
- gridId: widget.settingContext.viewId,
- fieldController: widget.settingContext.fieldController,
- ),
- );
+ if (_action != null) {
+ switch (_action!) {
+ case BoardSettingAction.groups:
+ return GridGroupList(
+ viewId: widget.settingContext.viewId,
+ fieldController: widget.settingContext.fieldController,
+ onDismissed: () {
+ widget.popoverController.close();
+ },
+ );
+ case BoardSettingAction.properties:
+ return GridPropertyList(
+ gridId: widget.settingContext.viewId,
+ fieldController: widget.settingContext.fieldController,
+ );
+ }
}
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(140, 400)),
- child: BoardSettingList(
- settingContext: widget.settingContext,
- onAction: (action, settingContext) {
- switch (action) {
- case BoardSettingAction.groups:
- break;
- case BoardSettingAction.properties:
- setState(() {
- _showGridPropertyList = true;
- });
- break;
- }
- },
- ),
+ return BoardSettingList(
+ settingContext: widget.settingContext,
+ onAction: (action, settingContext) {
+ setState(() => _action = action);
+ },
);
}
}
diff --git a/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart
index f8cc0d30ca..27a59c69a6 100644
--- a/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart
+++ b/frontend/app_flowy/lib/plugins/board/presentation/toolbar/board_toolbar.dart
@@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/field/field_controller.dart';
import 'package:appflowy_popover/popover.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart';
import 'package:flutter/widgets.dart';
import 'package:provider/provider.dart';
@@ -40,15 +41,30 @@ class BoardToolbar extends StatelessWidget {
}
}
-class _SettingButton extends StatelessWidget {
+class _SettingButton extends StatefulWidget {
final BoardSettingContext settingContext;
const _SettingButton({required this.settingContext, Key? key})
: super(key: key);
+ @override
+ State<_SettingButton> createState() => _SettingButtonState();
+}
+
+class _SettingButtonState extends State<_SettingButton> {
+ late PopoverController popoverController;
+
+ @override
+ void initState() {
+ popoverController = PopoverController();
+ super.initState();
+ }
+
@override
Widget build(BuildContext context) {
final theme = context.read();
- return Popover(
+ return AppFlowyStylePopover(
+ controller: popoverController,
+ constraints: BoxConstraints.loose(const Size(260, 400)),
triggerActions: PopoverTriggerActionFlags.click,
child: FlowyIconButton(
hoverColor: theme.hover,
@@ -61,7 +77,8 @@ class _SettingButton extends StatelessWidget {
),
popupBuilder: (BuildContext popoverContext) {
return BoardSettingListPopover(
- settingContext: settingContext,
+ settingContext: widget.settingContext,
+ popoverController: popoverController,
);
},
);
diff --git a/frontend/app_flowy/lib/plugins/doc/application/doc_bloc.dart b/frontend/app_flowy/lib/plugins/doc/application/doc_bloc.dart
index ed12227af0..89c87db455 100644
--- a/frontend/app_flowy/lib/plugins/doc/application/doc_bloc.dart
+++ b/frontend/app_flowy/lib/plugins/doc/application/doc_bloc.dart
@@ -90,7 +90,7 @@ class DocumentBloc extends Bloc {
final result = await service.openDocument(docId: view.id);
result.fold(
(block) {
- document = _decodeJsonToDocument(block.deltaStr);
+ document = _decodeJsonToDocument(block.snapshot);
_subscription = document.changes.listen((event) {
final delta = event.item2;
final documentDelta = document.toDelta();
@@ -115,16 +115,12 @@ class DocumentBloc extends Bloc {
void _composeDelta(Delta composedDelta, Delta documentDelta) async {
final json = jsonEncode(composedDelta.toJson());
Log.debug("doc_id: $view.id - Send json: $json");
- final result = await service.composeDelta(docId: view.id, data: json);
+ final result = await service.applyEdit(docId: view.id, data: json);
- result.fold((rustDoc) {
- // final json = utf8.decode(doc.data);
- final rustDelta = Delta.fromJson(jsonDecode(rustDoc.deltaStr));
- if (documentDelta != rustDelta) {
- Log.error("Receive : $rustDelta");
- Log.error("Expected : $documentDelta");
- }
- }, (r) => null);
+ result.fold(
+ (_) {},
+ (r) => Log.error(r),
+ );
}
Document _decodeJsonToDocument(String data) {
diff --git a/frontend/app_flowy/lib/plugins/doc/application/doc_service.dart b/frontend/app_flowy/lib/plugins/doc/application/doc_service.dart
index 659a99e371..067d51a510 100644
--- a/frontend/app_flowy/lib/plugins/doc/application/doc_service.dart
+++ b/frontend/app_flowy/lib/plugins/doc/application/doc_service.dart
@@ -4,22 +4,28 @@ import 'package:flowy_sdk/dispatch/dispatch.dart';
import 'package:flowy_sdk/protobuf/flowy-folder/view.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-error/errors.pb.dart';
import 'package:flowy_sdk/protobuf/flowy-sync/text_block.pb.dart';
+import 'package:flowy_sdk/protobuf/flowy-text-block/entities.pb.dart';
class DocumentService {
- Future> openDocument({
+ Future> openDocument({
required String docId,
}) async {
await FolderEventSetLatestView(ViewIdPB(value: docId)).send();
final payload = TextBlockIdPB(value: docId);
- return TextBlockEventGetBlockData(payload).send();
+ return TextBlockEventGetTextBlock(payload).send();
}
- Future> composeDelta({required String docId, required String data}) {
- final payload = TextBlockDeltaPB.create()
- ..blockId = docId
- ..deltaStr = data;
- return TextBlockEventApplyDelta(payload).send();
+ Future> applyEdit({
+ required String docId,
+ required String data,
+ String operations = "",
+ }) {
+ final payload = EditPayloadPB.create()
+ ..textBlockId = docId
+ ..operations = operations
+ ..delta = data;
+ return TextBlockEventApplyEdit(payload).send();
}
Future> closeDocument({required String docId}) {
diff --git a/frontend/app_flowy/lib/plugins/doc/document.dart b/frontend/app_flowy/lib/plugins/doc/document.dart
index ba209ddbb7..31488d3d2e 100644
--- a/frontend/app_flowy/lib/plugins/doc/document.dart
+++ b/frontend/app_flowy/lib/plugins/doc/document.dart
@@ -186,7 +186,8 @@ class DocumentShareButton extends StatelessWidget {
'Exported to: ${LocaleKeys.notifications_export_path.tr()}');
break;
case ShareAction.copyLink:
- FlowyAlertDialog(title: LocaleKeys.shareAction_workInProgress.tr())
+ NavigatorAlertDialog(
+ title: LocaleKeys.shareAction_workInProgress.tr())
.show(context);
break;
}
diff --git a/frontend/app_flowy/lib/plugins/doc/presentation/toolbar/link_button.dart b/frontend/app_flowy/lib/plugins/doc/presentation/toolbar/link_button.dart
index 428c45e400..22c9f108ec 100644
--- a/frontend/app_flowy/lib/plugins/doc/presentation/toolbar/link_button.dart
+++ b/frontend/app_flowy/lib/plugins/doc/presentation/toolbar/link_button.dart
@@ -84,7 +84,7 @@ class FlowyLinkStyleButtonState extends State {
value = values.first;
}
- TextFieldDialog(
+ NavigatorTextFieldDialog(
title: 'URL',
value: value,
confirm: (newValue) {
diff --git a/frontend/app_flowy/lib/plugins/grid/application/field/field_editor_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/field/field_editor_bloc.dart
index d7b024d67d..af50f47d67 100644
--- a/frontend/app_flowy/lib/plugins/grid/application/field/field_editor_bloc.dart
+++ b/frontend/app_flowy/lib/plugins/grid/application/field/field_editor_bloc.dart
@@ -2,6 +2,7 @@ import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:async';
import 'package:dartz/dartz.dart';
+import 'field_service.dart';
import 'type_option/type_option_context.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -15,10 +16,11 @@ class FieldEditorBloc extends Bloc {
FieldEditorBloc({
required String gridId,
required String fieldName,
+ required bool isGroupField,
required IFieldTypeOptionLoader loader,
}) : dataController =
TypeOptionDataController(gridId: gridId, loader: loader),
- super(FieldEditorState.initial(gridId, fieldName)) {
+ super(FieldEditorState.initial(gridId, fieldName, isGroupField)) {
on(
(event, emit) async {
await event.when(
@@ -35,7 +37,23 @@ class FieldEditorBloc extends Bloc {
emit(state.copyWith(name: name));
},
didReceiveFieldChanged: (FieldPB field) {
- emit(state.copyWith(field: Some(field), name: field.name));
+ emit(state.copyWith(
+ field: Some(field),
+ name: field.name,
+ canDelete: field.isPrimary,
+ ));
+ },
+ deleteField: () {
+ state.field.fold(
+ () => null,
+ (field) {
+ final fieldService = FieldService(
+ gridId: gridId,
+ fieldId: field.id,
+ );
+ fieldService.deleteField();
+ },
+ );
},
);
},
@@ -52,6 +70,7 @@ class FieldEditorBloc extends Bloc {
class FieldEditorEvent with _$FieldEditorEvent {
const factory FieldEditorEvent.initial() = _InitialField;
const factory FieldEditorEvent.updateName(String name) = _UpdateName;
+ const factory FieldEditorEvent.deleteField() = _DeleteField;
const factory FieldEditorEvent.didReceiveFieldChanged(FieldPB field) =
_DidReceiveFieldChanged;
}
@@ -63,16 +82,21 @@ class FieldEditorState with _$FieldEditorState {
required String errorText,
required String name,
required Option field,
+ required bool canDelete,
+ required bool isGroupField,
}) = _FieldEditorState;
factory FieldEditorState.initial(
String gridId,
String fieldName,
+ bool isGroupField,
) =>
FieldEditorState(
gridId: gridId,
errorText: '',
field: none(),
+ canDelete: false,
name: fieldName,
+ isGroupField: isGroupField,
);
}
diff --git a/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart b/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart
index 6f59682f4d..10b4fe3cf8 100644
--- a/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart
+++ b/frontend/app_flowy/lib/plugins/grid/application/row/row_detail_bloc.dart
@@ -1,4 +1,5 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
+import 'package:app_flowy/plugins/grid/application/field/field_service.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'dart:async';
@@ -24,6 +25,13 @@ class RowDetailBloc extends Bloc {
didReceiveCellDatas: (_DidReceiveCellDatas value) {
emit(state.copyWith(gridCells: value.gridCells));
},
+ deleteField: (_DeleteField value) {
+ final fieldService = FieldService(
+ gridId: dataController.rowInfo.gridId,
+ fieldId: value.fieldId,
+ );
+ fieldService.deleteField();
+ },
);
},
);
@@ -49,6 +57,7 @@ class RowDetailBloc extends Bloc {
@freezed
class RowDetailEvent with _$RowDetailEvent {
const factory RowDetailEvent.initial() = _Initial;
+ const factory RowDetailEvent.deleteField(String fieldId) = _DeleteField;
const factory RowDetailEvent.didReceiveCellDatas(
List gridCells) = _DidReceiveCellDatas;
}
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart b/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart
index 42cf16edf7..a70fe840dd 100755
--- a/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/grid_page.dart
@@ -317,9 +317,7 @@ class _GridFooter extends StatelessWidget {
height: GridSize.footerHeight,
child: Padding(
padding: GridSize.footerContentInsets,
- child: const Expanded(
- child: SizedBox(height: 40, child: GridAddRowButton()),
- ),
+ child: const SizedBox(height: 40, child: GridAddRowButton()),
),
),
),
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart
index 2a5c79b2f0..7b289d4a21 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_cell.dart
@@ -1,3 +1,4 @@
+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';
@@ -62,10 +63,11 @@ class _DateCellState extends GridCellState {
value: _cellBloc,
child: BlocBuilder(
builder: (context, state) {
- return Popover(
+ return AppFlowyStylePopover(
controller: _popover,
offset: const Offset(0, 20),
direction: PopoverDirection.bottomWithLeftAligned,
+ constraints: BoxConstraints.loose(const Size(320, 500)),
child: SizedBox.expand(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart
index 9c57e7ed3b..1fe45f60c8 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/date_cell/date_editor.dart
@@ -64,12 +64,9 @@ class _DateCellEditor extends State {
return Container();
}
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(320, 500)),
- child: _CellCalendarWidget(
- cellContext: widget.cellController,
- dateTypeOptionPB: _dateTypeOptionPB!,
- ),
+ return _CellCalendarWidget(
+ cellContext: widget.cellController,
+ dateTypeOptionPB: _dateTypeOptionPB!,
);
}
}
@@ -302,10 +299,11 @@ class _DateTypeOptionButton extends StatelessWidget {
return BlocSelector(
selector: (state) => state.dateTypeOptionPB,
builder: (context, dateTypeOptionPB) {
- return Popover(
+ return AppFlowyStylePopover(
triggerActions:
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
offset: const Offset(20, 0),
+ constraints: BoxConstraints.loose(const Size(140, 100)),
child: FlowyButton(
text: FlowyText.medium(title, fontSize: 12),
hoverColor: theme.hover,
@@ -313,12 +311,9 @@ class _DateTypeOptionButton extends StatelessWidget {
rightIcon: svgWidget("grid/more", color: theme.iconColor),
),
popupBuilder: (BuildContext popContext) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(140, 100)),
- child: _CalDateTimeSetting(
- dateTypeOptionPB: dateTypeOptionPB,
- onEvent: (event) => context.read().add(event),
- ),
+ return _CalDateTimeSetting(
+ dateTypeOptionPB: dateTypeOptionPB,
+ onEvent: (event) => context.read().add(event),
);
},
);
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart
index df7ddb0d9e..2e65226289 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_cell.dart
@@ -3,7 +3,7 @@ import 'package:app_flowy/plugins/grid/application/prelude.dart';
import 'package:appflowy_popover/popover.dart';
import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
// ignore: unused_import
import 'package:flowy_sdk/log.dart';
@@ -194,8 +194,10 @@ class _SelectOptionWrapState extends State {
alignment: AlignmentDirectional.center,
fit: StackFit.expand,
children: [
- Popover(
+ AppFlowyStylePopover(
controller: _popover,
+ constraints: BoxConstraints.loose(
+ Size(SelectOptionCellEditor.editorPanelWidth, 300)),
offset: const Offset(0, 20),
direction: PopoverDirection.bottomWithLeftAligned,
// triggerActions: PopoverTriggerActionFlags.c,
@@ -203,18 +205,14 @@ class _SelectOptionWrapState extends State {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
widget.onFocus?.call(true);
});
- return OverlayContainer(
- constraints: BoxConstraints.loose(
- Size(SelectOptionCellEditor.editorPanelWidth, 300)),
- child: SizedBox(
- width: SelectOptionCellEditor.editorPanelWidth,
- child: SelectOptionCellEditor(
- cellController: widget.cellControllerBuilder.build()
- as GridSelectOptionCellController,
- onDismissed: () {
- widget.onFocus?.call(false);
- },
- ),
+ return SizedBox(
+ width: SelectOptionCellEditor.editorPanelWidth,
+ child: SelectOptionCellEditor(
+ cellController: widget.cellControllerBuilder.build()
+ as GridSelectOptionCellController,
+ onDismissed: () {
+ widget.onFocus?.call(false);
+ },
),
);
},
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart
index 897b6f81c3..1c0cad85b1 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/select_option_cell/select_option_editor.dart
@@ -251,9 +251,10 @@ class _SelectOptionCellState extends State<_SelectOptionCell> {
@override
Widget build(BuildContext context) {
final theme = context.watch();
- return Popover(
+ return AppFlowyStylePopover(
controller: _popoverController,
offset: const Offset(20, 0),
+ constraints: BoxConstraints.loose(const Size(200, 300)),
child: SizedBox(
height: GridSize.typeOptionItemHeight,
child: Row(
@@ -286,23 +287,20 @@ class _SelectOptionCellState extends State<_SelectOptionCell> {
),
),
popupBuilder: (BuildContext popoverContext) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(200, 300)),
- child: SelectOptionTypeOptionEditor(
- option: widget.option,
- onDeleted: () {
- context
- .read()
- .add(SelectOptionEditorEvent.deleteOption(widget.option));
- },
- onUpdated: (updatedOption) {
- context
- .read()
- .add(SelectOptionEditorEvent.updateOption(updatedOption));
- },
- key: ValueKey(widget.option
- .id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
- ),
+ return SelectOptionTypeOptionEditor(
+ option: widget.option,
+ onDeleted: () {
+ context
+ .read()
+ .add(SelectOptionEditorEvent.deleteOption(widget.option));
+ },
+ onUpdated: (updatedOption) {
+ context
+ .read()
+ .add(SelectOptionEditorEvent.updateOption(updatedOption));
+ },
+ key: ValueKey(widget.option
+ .id), // Use ValueKey to refresh the UI, otherwise, it will remain the old value.
);
},
);
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart
index e390104187..17a899f3a1 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/cell_editor.dart
@@ -1,6 +1,5 @@
import 'package:app_flowy/plugins/grid/application/cell/cell_service/cell_service.dart';
import 'package:app_flowy/plugins/grid/application/cell/url_cell_editor_bloc.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'dart:async';
@@ -79,15 +78,12 @@ class URLEditorPopover extends StatelessWidget {
@override
Widget build(BuildContext context) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(300, 160)),
- child: SizedBox(
- width: 200,
- child: Padding(
- padding: const EdgeInsets.all(6),
- child: URLCellEditor(
- cellController: cellController,
- ),
+ return SizedBox(
+ width: 200,
+ child: Padding(
+ padding: const EdgeInsets.all(6),
+ child: URLCellEditor(
+ cellController: cellController,
),
),
);
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart
index 27e7d4433c..155a75cae3 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/cell/url_cell/url_cell.dart
@@ -6,6 +6,7 @@ import 'package:appflowy_popover/popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
@@ -129,8 +130,9 @@ class _GridURLCellState extends GridCellState {
),
);
- return Popover(
+ return AppFlowyStylePopover(
controller: _popoverController,
+ constraints: BoxConstraints.loose(const Size(300, 160)),
direction: PopoverDirection.bottomWithLeftAligned,
offset: const Offset(0, 20),
child: SizedBox.expand(
@@ -214,7 +216,8 @@ class _EditURLAccessoryState extends State<_EditURLAccessory>
@override
Widget build(BuildContext context) {
final theme = context.watch();
- return Popover(
+ return AppFlowyStylePopover(
+ constraints: BoxConstraints.loose(const Size(300, 160)),
controller: _popoverController,
direction: PopoverDirection.bottomWithLeftAligned,
triggerActions: PopoverTriggerActionFlags.click,
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell_action_sheet.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell_action_sheet.dart
index 8f11bb008f..4d412ed530 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell_action_sheet.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_cell_action_sheet.dart
@@ -2,6 +2,7 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/type_option
import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_editor.dart';
import 'package:app_flowy/startup/startup.dart';
import 'package:app_flowy/plugins/grid/application/prelude.dart';
+import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -168,7 +169,7 @@ class FieldActionCell extends StatelessWidget {
}
},
leftIcon: svgWidget(action.iconName(),
- color: enable ? theme.iconColor : theme.disableIconColor),
+ color: enable ? theme.iconColor : theme.disableIconColor),
);
}
}
@@ -215,9 +216,15 @@ extension _FieldActionExtension on FieldAction {
.add(const FieldActionSheetEvent.duplicateField());
break;
case FieldAction.delete:
- context
- .read()
- .add(const FieldActionSheetEvent.deleteField());
+ NavigatorAlertDialog(
+ title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
+ confirm: () {
+ context
+ .read()
+ .add(const FieldActionSheetEvent.deleteField());
+ },
+ ).show(context);
+
break;
}
}
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart
index 8a8c445ec4..cc836809b8 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_editor.dart
@@ -2,8 +2,13 @@ import 'package:app_flowy/plugins/grid/application/field/field_editor_bloc.dart'
import 'package:app_flowy/plugins/grid/application/field/type_option/type_option_context.dart';
import 'package:appflowy_popover/popover.dart';
import 'package:easy_localization/easy_localization.dart';
+import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
+import 'package:flowy_infra/theme.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
+import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
+import 'package:flowy_sdk/log.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
@@ -13,14 +18,16 @@ import 'field_type_option_editor.dart';
class FieldEditor extends StatefulWidget {
final String gridId;
final String fieldName;
- final VoidCallback? onRemoved;
+ final bool isGroupField;
+ final Function(String)? onDeleted;
final IFieldTypeOptionLoader typeOptionLoader;
const FieldEditor({
required this.gridId,
this.fieldName = "",
required this.typeOptionLoader,
- this.onRemoved,
+ this.isGroupField = false,
+ this.onDeleted,
Key? key,
}) : super(key: key);
@@ -43,10 +50,10 @@ class _FieldEditorState extends State {
create: (context) => FieldEditorBloc(
gridId: widget.gridId,
fieldName: widget.fieldName,
+ isGroupField: widget.isGroupField,
loader: widget.typeOptionLoader,
)..add(const FieldEditorEvent.initial()),
child: BlocBuilder(
- buildWhen: (p, c) => false,
builder: (context, state) {
return ListView(
shrinkWrap: true,
@@ -56,6 +63,16 @@ class _FieldEditorState extends State {
const VSpace(10),
const _FieldNameCell(),
const VSpace(10),
+ _DeleteFieldButton(
+ popoverMutex: popoverMutex,
+ onDeleted: () {
+ state.field.fold(
+ () => Log.error('Can not delete the field'),
+ (field) => widget.onDeleted?.call(field.id),
+ );
+ },
+ ),
+ const VSpace(10),
_FieldTypeOptionCell(popoverMutex: popoverMutex),
],
);
@@ -114,3 +131,55 @@ class _FieldNameCell extends StatelessWidget {
);
}
}
+
+class _DeleteFieldButton extends StatelessWidget {
+ final PopoverMutex popoverMutex;
+ final VoidCallback? onDeleted;
+
+ const _DeleteFieldButton({
+ required this.popoverMutex,
+ required this.onDeleted,
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = context.watch();
+ return BlocBuilder(
+ buildWhen: (previous, current) => previous != current,
+ builder: (context, state) {
+ final enable = !state.canDelete && !state.isGroupField;
+ Widget button = FlowyButton(
+ text: FlowyText.medium(
+ LocaleKeys.grid_field_delete.tr(),
+ fontSize: 12,
+ color: enable ? null : theme.shader4,
+ ),
+ );
+ if (enable) button = _wrapPopover(button);
+ return button;
+ },
+ );
+ }
+
+ Widget _wrapPopover(Widget widget) {
+ return AppFlowyStylePopover(
+ triggerActions: PopoverTriggerActionFlags.click,
+ constraints: BoxConstraints.loose(const Size(400, 240)),
+ mutex: popoverMutex,
+ direction: PopoverDirection.center,
+ popupBuilder: (popupContext) {
+ return PopoverAlertView(
+ title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
+ cancel: () => popoverMutex.state?.close(),
+ confirm: () {
+ onDeleted?.call();
+ popoverMutex.state?.close();
+ },
+ popoverMutex: popoverMutex,
+ );
+ },
+ child: widget,
+ );
+ }
+}
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart
index de9b3a809f..0cb18d411b 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_list.dart
@@ -1,3 +1,4 @@
+import 'package:appflowy_popover/popover.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
@@ -25,7 +26,7 @@ class FieldTypeList extends StatelessWidget with FlowyOverlayDelegate {
fieldType: fieldType,
onSelectField: (fieldType) {
onSelectField(fieldType);
- FlowyOverlay.of(context).remove(FieldTypeList.identifier());
+ PopoverContainer.of(context).closeAll();
},
);
}).toList();
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart
index 4827a5ac6d..f541335844 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/field_type_option_editor.dart
@@ -64,19 +64,15 @@ class FieldTypeOptionEditor extends StatelessWidget {
final theme = context.watch();
return SizedBox(
height: GridSize.typeOptionItemHeight,
- child: Popover(
- triggerActions:
- PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
+ child: AppFlowyStylePopover(
+ constraints: BoxConstraints.loose(const Size(460, 440)),
+ triggerActions: PopoverTriggerActionFlags.click,
mutex: popoverMutex,
offset: const Offset(20, 0),
popupBuilder: (context) {
- final list = FieldTypeList(onSelectField: (newFieldType) {
+ return FieldTypeList(onSelectField: (newFieldType) {
dataController.switchToField(newFieldType);
});
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(460, 440)),
- child: list,
- );
},
child: FlowyButton(
text: FlowyText.medium(field.fieldType.title(), fontSize: 12),
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
index 154919d019..f9656e8dcc 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/grid_header.dart
@@ -7,7 +7,7 @@ import 'package:easy_localization/easy_localization.dart';
import 'package:appflowy_popover/popover.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_sdk/protobuf/flowy-grid/field_entities.pb.dart';
@@ -176,9 +176,10 @@ class CreateFieldButton extends StatelessWidget {
Widget build(BuildContext context) {
final theme = context.watch();
- return Popover(
+ return AppFlowyStylePopover(
triggerActions: PopoverTriggerActionFlags.click,
direction: PopoverDirection.bottomWithRightAligned,
+ constraints: BoxConstraints.loose(const Size(240, 200)),
child: FlowyButton(
text: FlowyText.medium(
LocaleKeys.grid_field_newColumn.tr(),
@@ -192,13 +193,10 @@ class CreateFieldButton extends StatelessWidget {
),
),
popupBuilder: (BuildContext popover) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(240, 200)),
- child: FieldEditor(
- gridId: gridId,
- fieldName: "",
- typeOptionLoader: NewFieldTypeOptionLoader(gridId: gridId),
- ),
+ return FieldEditor(
+ gridId: gridId,
+ fieldName: "",
+ typeOptionLoader: NewFieldTypeOptionLoader(gridId: gridId),
);
},
);
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart
index 02350f3a5e..f01efda675 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/builder.dart
@@ -54,9 +54,10 @@ Widget? makeTypeOptionWidget({
return builder.build(context);
}
-TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder(
- {required TypeOptionDataController dataController,
- required PopoverMutex popoverMutex}) {
+TypeOptionWidgetBuilder makeTypeOptionWidgetBuilder({
+ required TypeOptionDataController dataController,
+ required PopoverMutex popoverMutex,
+}) {
final gridId = dataController.gridId;
final fieldType = dataController.field.fieldType;
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart
index ae3dcf8682..e8cbb00fba 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/date.dart
@@ -62,23 +62,21 @@ class DateTypeOptionWidget extends TypeOptionWidget {
}
Widget _renderDateFormatButton(BuildContext context, DateFormat dataFormat) {
- return Popover(
+ return AppFlowyStylePopover(
mutex: popoverMutex,
triggerActions:
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
offset: const Offset(20, 0),
+ constraints: BoxConstraints.loose(const Size(460, 440)),
popupBuilder: (popoverContext) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(460, 440)),
- child: DateFormatList(
- selectedFormat: dataFormat,
- onSelected: (format) {
- context
- .read()
- .add(DateTypeOptionEvent.didSelectDateFormat(format));
- PopoverContainerState.of(popoverContext).closeAll();
- },
- ),
+ return DateFormatList(
+ selectedFormat: dataFormat,
+ onSelected: (format) {
+ context
+ .read()
+ .add(DateTypeOptionEvent.didSelectDateFormat(format));
+ PopoverContainer.of(popoverContext).closeAll();
+ },
);
},
child: const DateFormatButton(),
@@ -86,22 +84,21 @@ class DateTypeOptionWidget extends TypeOptionWidget {
}
Widget _renderTimeFormatButton(BuildContext context, TimeFormat timeFormat) {
- return Popover(
+ return AppFlowyStylePopover(
mutex: popoverMutex,
triggerActions:
PopoverTriggerActionFlags.hover | PopoverTriggerActionFlags.click,
offset: const Offset(20, 0),
+ constraints: BoxConstraints.loose(const Size(460, 440)),
popupBuilder: (BuildContext popoverContext) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(460, 440)),
- child: TimeFormatList(
- selectedFormat: timeFormat,
- onSelected: (format) {
- context
- .read()
- .add(DateTypeOptionEvent.didSelectTimeFormat(format));
- PopoverContainerState.of(popoverContext).closeAll();
- }),
+ return TimeFormatList(
+ selectedFormat: timeFormat,
+ onSelected: (format) {
+ context
+ .read()
+ .add(DateTypeOptionEvent.didSelectTimeFormat(format));
+ PopoverContainer.of(popoverContext).closeAll();
+ },
);
},
child: TimeFormatButton(timeFormat: timeFormat),
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/multi_select.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/multi_select.dart
index 75ac6eeaa0..2dc804111d 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/multi_select.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/multi_select.dart
@@ -41,7 +41,7 @@ class MultiSelectTypeOptionWidget extends TypeOptionWidget {
return SelectOptionTypeOptionWidget(
options: selectOptionAction.typeOption.options,
beginEdit: () {
- PopoverContainerState.of(context).closeAll();
+ PopoverContainer.of(context).closeAll();
},
popoverMutex: popoverMutex,
typeOptionAction: selectOptionAction,
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart
index cdc69eb4e6..80627d062d 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/number.dart
@@ -55,11 +55,12 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
listener: (context, state) =>
typeOptionContext.typeOption = state.typeOption,
builder: (context, state) {
- return Popover(
+ return AppFlowyStylePopover(
mutex: popoverMutex,
triggerActions: PopoverTriggerActionFlags.hover |
PopoverTriggerActionFlags.click,
offset: const Offset(20, 0),
+ constraints: BoxConstraints.loose(const Size(460, 440)),
child: FlowyButton(
margin: GridSize.typeOptionContentInsets,
hoverColor: theme.hover,
@@ -76,17 +77,14 @@ class NumberTypeOptionWidget extends TypeOptionWidget {
),
),
popupBuilder: (BuildContext popoverContext) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(460, 440)),
- child: NumberFormatList(
- onSelected: (format) {
- context
- .read()
- .add(NumberTypeOptionEvent.didSelectFormat(format));
- PopoverContainerState.of(popoverContext).closeAll();
- },
- selectedFormat: state.typeOption.format,
- ),
+ return NumberFormatList(
+ onSelected: (format) {
+ context
+ .read()
+ .add(NumberTypeOptionEvent.didSelectFormat(format));
+ PopoverContainer.of(popoverContext).closeAll();
+ },
+ selectedFormat: state.typeOption.format,
);
},
);
@@ -116,6 +114,7 @@ class NumberFormatList extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _FilterTextField(),
+ const VSpace(10),
BlocBuilder(
builder: (context, state) {
final cells = state.formats.map((format) {
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart
index 7b0be75515..f8cf09d076 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/select_option.dart
@@ -2,7 +2,7 @@ import 'package:app_flowy/plugins/grid/application/field/type_option/select_opti
import 'package:appflowy_popover/popover.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
+import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
@@ -180,10 +180,11 @@ class _OptionCellState extends State<_OptionCell> {
Widget build(BuildContext context) {
final theme = context.watch();
- return Popover(
+ return AppFlowyStylePopover(
controller: _popoverController,
mutex: widget.popoverMutex,
offset: const Offset(20, 0),
+ constraints: BoxConstraints.loose(const Size(460, 440)),
child: SizedBox(
height: GridSize.typeOptionItemHeight,
child: SelectOptionTagCell(
@@ -200,24 +201,21 @@ class _OptionCellState extends State<_OptionCell> {
),
),
popupBuilder: (BuildContext popoverContext) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(460, 440)),
- child: SelectOptionTypeOptionEditor(
- option: widget.option,
- onDeleted: () {
- context
- .read()
- .add(SelectOptionTypeOptionEvent.deleteOption(widget.option));
- PopoverContainerState.of(popoverContext).closeAll();
- },
- onUpdated: (updatedOption) {
- context
- .read()
- .add(SelectOptionTypeOptionEvent.updateOption(updatedOption));
- PopoverContainerState.of(popoverContext).closeAll();
- },
- key: ValueKey(widget.option.id),
- ),
+ return SelectOptionTypeOptionEditor(
+ option: widget.option,
+ onDeleted: () {
+ context
+ .read()
+ .add(SelectOptionTypeOptionEvent.deleteOption(widget.option));
+ PopoverContainer.of(popoverContext).closeAll();
+ },
+ onUpdated: (updatedOption) {
+ context
+ .read()
+ .add(SelectOptionTypeOptionEvent.updateOption(updatedOption));
+ PopoverContainer.of(popoverContext).closeAll();
+ },
+ key: ValueKey(widget.option.id),
);
},
);
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/single_select.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/single_select.dart
index 27ffabb286..477e573ff2 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/single_select.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/header/type_option/single_select.dart
@@ -40,7 +40,7 @@ class SingleSelectTypeOptionWidget extends TypeOptionWidget {
return SelectOptionTypeOptionWidget(
options: selectOptionAction.typeOption.options,
beginEdit: () {
- PopoverContainerState.of(context).closeAll();
+ PopoverContainer.of(context).closeAll();
},
popoverMutex: popoverMutex,
typeOptionAction: selectOptionAction,
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart
index 8c828ec627..e67908f544 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_action_sheet.dart
@@ -1,4 +1,5 @@
import 'package:app_flowy/plugins/grid/application/row/row_action_sheet_bloc.dart';
+import 'package:app_flowy/workspace/presentation/widgets/dialogs.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
import 'package:flowy_infra/image.dart';
@@ -150,9 +151,15 @@ extension _RowActionExtension on _RowAction {
.add(const RowActionSheetEvent.duplicateRow());
break;
case _RowAction.delete:
- context
- .read()
- .add(const RowActionSheetEvent.deleteRow());
+ NavigatorAlertDialog(
+ title: LocaleKeys.grid_field_deleteFieldPromptMessage.tr(),
+ confirm: () {
+ context
+ .read()
+ .add(const RowActionSheetEvent.deleteRow());
+ },
+ ).show(context);
+
break;
}
}
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
index fe148d9dcb..a1d65f89ae 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/row/row_detail.dart
@@ -133,6 +133,7 @@ class _PropertyList extends StatelessWidget {
),
),
),
+ const VSpace(10),
_CreateFieldButton(
viewId: viewId,
onClosed: () {
@@ -144,13 +145,16 @@ class _PropertyList extends StatelessWidget {
);
});
},
- onOpened: () {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(240, 200)),
- child: FieldEditor(
- gridId: viewId,
- typeOptionLoader: NewFieldTypeOptionLoader(gridId: viewId),
- ),
+ onOpened: (controller) {
+ return FieldEditor(
+ gridId: viewId,
+ typeOptionLoader: NewFieldTypeOptionLoader(gridId: viewId),
+ onDeleted: (fieldId) {
+ controller.close();
+ context
+ .read()
+ .add(RowDetailEvent.deleteField(fieldId));
+ },
);
},
),
@@ -161,10 +165,11 @@ class _PropertyList extends StatelessWidget {
}
}
-class _CreateFieldButton extends StatelessWidget {
+class _CreateFieldButton extends StatefulWidget {
final String viewId;
- final Widget Function() onOpened;
+ final Widget Function(PopoverController) onOpened;
final VoidCallback onClosed;
+
const _CreateFieldButton({
required this.viewId,
required this.onOpened,
@@ -172,16 +177,32 @@ class _CreateFieldButton extends StatelessWidget {
Key? key,
}) : super(key: key);
+ @override
+ State<_CreateFieldButton> createState() => _CreateFieldButtonState();
+}
+
+class _CreateFieldButtonState extends State<_CreateFieldButton> {
+ late PopoverController popoverController;
+
+ @override
+ void initState() {
+ popoverController = PopoverController();
+ super.initState();
+ }
+
@override
Widget build(BuildContext context) {
final theme = context.read();
- return Popover(
+ return AppFlowyStylePopover(
+ constraints: BoxConstraints.loose(const Size(240, 200)),
+ controller: popoverController,
triggerActions: PopoverTriggerActionFlags.click,
- direction: PopoverDirection.bottomWithLeftAligned,
- onClose: onClosed,
- child: SizedBox(
+ direction: PopoverDirection.topWithLeftAligned,
+ onClose: widget.onClosed,
+ child: Container(
height: 40,
+ decoration: _makeBoxDecoration(context),
child: FlowyButton(
text: FlowyText.medium(
LocaleKeys.grid_field_newColumn.tr(),
@@ -192,7 +213,17 @@ class _CreateFieldButton extends StatelessWidget {
leftIcon: svgWidget("home/add"),
),
),
- popupBuilder: (BuildContext context) => onOpened(),
+ popupBuilder: (BuildContext context) =>
+ widget.onOpened(popoverController),
+ );
+ }
+
+ BoxDecoration _makeBoxDecoration(BuildContext context) {
+ final theme = context.read();
+ final borderSide = BorderSide(color: theme.shader6, width: 1.0);
+ return BoxDecoration(
+ color: theme.surface,
+ border: Border(top: borderSide),
);
}
}
@@ -241,16 +272,23 @@ class _RowDetailCellState extends State<_RowDetailCell> {
child: Popover(
controller: popover,
offset: const Offset(20, 0),
- popupBuilder: (context) {
+ popupBuilder: (popoverContext) {
return OverlayContainer(
constraints: BoxConstraints.loose(const Size(240, 200)),
child: FieldEditor(
gridId: widget.cellId.gridId,
fieldName: widget.cellId.fieldContext.field.name,
+ isGroupField: widget.cellId.fieldContext.isGroupField,
typeOptionLoader: FieldTypeOptionLoader(
gridId: widget.cellId.gridId,
field: widget.cellId.fieldContext.field,
),
+ onDeleted: (fieldId) {
+ popover.close();
+ context
+ .read()
+ .add(RowDetailEvent.deleteField(fieldId));
+ },
),
);
},
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart
index b400cda0fe..8709cd3ea3 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_group.dart
@@ -3,7 +3,6 @@ import 'package:app_flowy/plugins/grid/presentation/layout/sizes.dart';
import 'package:app_flowy/plugins/grid/presentation/widgets/header/field_type_extension.dart';
import 'package:flowy_infra/image.dart';
import 'package:flowy_infra/theme.dart';
-import 'package:flowy_infra_ui/flowy_infra_ui.dart';
import 'package:flowy_infra_ui/style_widget/button.dart';
import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flowy_infra_ui/widget/spacing.dart';
@@ -15,9 +14,11 @@ import 'package:flutter_bloc/flutter_bloc.dart';
class GridGroupList extends StatelessWidget {
final String viewId;
final GridFieldController fieldController;
+ final VoidCallback onDismissed;
const GridGroupList({
required this.viewId,
required this.fieldController,
+ required this.onDismissed,
Key? key,
}) : super(key: key);
@@ -33,6 +34,7 @@ class GridGroupList extends StatelessWidget {
final cells = state.fieldContexts.map((fieldContext) {
Widget cell = _GridGroupCell(
fieldContext: fieldContext,
+ onSelected: () => onDismissed(),
key: ValueKey(fieldContext.id),
);
@@ -56,29 +58,16 @@ class GridGroupList extends StatelessWidget {
),
);
}
-
- void show(BuildContext context) {
- FlowyOverlay.of(context).insertWithAnchor(
- widget: OverlayContainer(
- constraints: BoxConstraints.loose(const Size(260, 400)),
- child: this,
- ),
- identifier: identifier(),
- anchorContext: context,
- anchorDirection: AnchorDirection.bottomRight,
- style: FlowyOverlayStyle(blur: false),
- );
- }
-
- static String identifier() {
- return (GridGroupList).toString();
- }
}
class _GridGroupCell extends StatelessWidget {
+ final VoidCallback onSelected;
final GridFieldContext fieldContext;
- const _GridGroupCell({required this.fieldContext, Key? key})
- : super(key: key);
+ const _GridGroupCell({
+ required this.fieldContext,
+ required this.onSelected,
+ Key? key,
+ }) : super(key: key);
@override
Widget build(BuildContext context) {
@@ -97,8 +86,10 @@ class _GridGroupCell extends StatelessWidget {
child: FlowyButton(
text: FlowyText.medium(fieldContext.name, fontSize: 12),
hoverColor: theme.hover,
- leftIcon: svgWidget(fieldContext.fieldType.iconName(),
- color: theme.iconColor),
+ leftIcon: svgWidget(
+ fieldContext.fieldType.iconName(),
+ color: theme.iconColor,
+ ),
rightIcon: rightIcon,
onTap: () {
context.read().add(
@@ -107,7 +98,8 @@ class _GridGroupCell extends StatelessWidget {
fieldContext.fieldType,
),
);
- FlowyOverlay.of(context).remove(GridGroupList.identifier());
+ onSelected();
+ // FlowyOverlay.of(context).remove(GridGroupList.identifier());
},
),
);
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart
index 5e38efd9cb..724ec74cc1 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_property.dart
@@ -116,10 +116,11 @@ class _GridPropertyCell extends StatelessWidget {
}
Widget _editFieldButton(AppTheme theme, BuildContext context) {
- return Popover(
+ return AppFlowyStylePopover(
mutex: popoverMutex,
triggerActions: PopoverTriggerActionFlags.click,
offset: const Offset(20, 0),
+ constraints: BoxConstraints.loose(const Size(240, 200)),
child: FlowyButton(
text: FlowyText.medium(fieldContext.name, fontSize: 12),
hoverColor: theme.hover,
@@ -127,14 +128,11 @@ class _GridPropertyCell extends StatelessWidget {
color: theme.iconColor),
),
popupBuilder: (BuildContext context) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(240, 200)),
- child: FieldEditor(
- gridId: gridId,
- fieldName: fieldContext.name,
- typeOptionLoader: FieldTypeOptionLoader(
- gridId: gridId, field: fieldContext.field),
- ),
+ return FieldEditor(
+ gridId: gridId,
+ fieldName: fieldContext.name,
+ typeOptionLoader:
+ FieldTypeOptionLoader(gridId: gridId, field: fieldContext.field),
);
},
);
diff --git a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart
index f5893c892f..7675eda943 100644
--- a/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart
+++ b/frontend/app_flowy/lib/plugins/grid/presentation/widgets/toolbar/grid_toolbar.dart
@@ -53,7 +53,8 @@ class _SettingButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final theme = context.watch();
- return Popover(
+ return AppFlowyStylePopover(
+ constraints: BoxConstraints.loose(const Size(260, 400)),
triggerActions: PopoverTriggerActionFlags.click,
offset: const Offset(0, 10),
child: FlowyIconButton(
@@ -87,25 +88,19 @@ class _GridSettingListPopoverState extends State<_GridSettingListPopover> {
@override
Widget build(BuildContext context) {
if (_action == GridSettingAction.properties) {
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(260, 400)),
- child: GridPropertyList(
- gridId: widget.settingContext.gridId,
- fieldController: widget.settingContext.fieldController,
- ),
+ return GridPropertyList(
+ gridId: widget.settingContext.gridId,
+ fieldController: widget.settingContext.fieldController,
);
}
- return OverlayContainer(
- constraints: BoxConstraints.loose(const Size(140, 400)),
- child: GridSettingList(
- settingContext: widget.settingContext,
- onAction: (action, settingContext) {
- setState(() {
- _action = action;
- });
- },
- ),
+ return GridSettingList(
+ settingContext: widget.settingContext,
+ onAction: (action, settingContext) {
+ setState(() {
+ _action = action;
+ });
+ },
);
}
}
diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/create_button.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/create_button.dart
index 503980ae86..bd6ab040e1 100644
--- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/create_button.dart
+++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/create_button.dart
@@ -30,7 +30,7 @@ class NewAppButton extends StatelessWidget {
}
Future _showCreateAppDialog(BuildContext context) async {
- return TextFieldDialog(
+ return NavigatorTextFieldDialog(
title: LocaleKeys.newPageText.tr(),
value: "",
confirm: (newValue) {
diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart
index 9d02f1a013..48f4f0d399 100644
--- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart
+++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/header/header.dart
@@ -126,7 +126,7 @@ class MenuAppHeader extends StatelessWidget {
action.fold(() {}, (action) {
switch (action) {
case AppDisclosureAction.rename:
- TextFieldDialog(
+ NavigatorTextFieldDialog(
title: LocaleKeys.menuAppHeader_renameDialog.tr(),
value: context.read().state.app.name,
confirm: (newValue) {
diff --git a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart
index cb3146a70c..9c9b5a0895 100644
--- a/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart
+++ b/frontend/app_flowy/lib/workspace/presentation/home/menu/app/section/item.dart
@@ -109,7 +109,7 @@ class ViewSectionItem extends StatelessWidget {
action.foldRight({}, (action, previous) {
switch (action) {
case ViewDisclosureAction.rename:
- TextFieldDialog(
+ NavigatorTextFieldDialog(
title: LocaleKeys.disclosureAction_rename.tr(),
value: context.read().state.view.name,
confirm: (newValue) {
diff --git a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart
index 3a5c1c79e5..4cb763e50d 100644
--- a/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart
+++ b/frontend/app_flowy/lib/workspace/presentation/widgets/dialogs.dart
@@ -1,3 +1,4 @@
+import 'package:appflowy_popover/popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/text_style.dart';
import 'package:flowy_infra/theme.dart';
@@ -15,13 +16,13 @@ import 'package:textstyle_extensions/textstyle_extensions.dart';
export 'package:flowy_infra_ui/widget/dialog/styled_dialogs.dart';
import 'package:app_flowy/generated/locale_keys.g.dart';
-class TextFieldDialog extends StatefulWidget {
+class NavigatorTextFieldDialog extends StatefulWidget {
final String value;
final String title;
final void Function()? cancel;
final void Function(String) confirm;
- const TextFieldDialog({
+ const NavigatorTextFieldDialog({
required this.title,
required this.value,
required this.confirm,
@@ -30,10 +31,10 @@ class TextFieldDialog extends StatefulWidget {
}) : super(key: key);
@override
- State createState() => _CreateTextFieldDialog();
+ State createState() => _CreateTextFieldDialog();
}
-class _CreateTextFieldDialog extends State {
+class _CreateTextFieldDialog extends State {
String newValue = "";
@override
@@ -56,7 +57,8 @@ class _CreateTextFieldDialog extends State {
FlowyFormTextInput(
hintText: LocaleKeys.dialogCreatePageNameHint.tr(),
initialValue: widget.value,
- textStyle: const TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
+ textStyle:
+ const TextStyle(fontSize: 24, fontWeight: FontWeight.w400),
autoFocus: true,
onChanged: (text) {
newValue = text;
@@ -70,11 +72,13 @@ class _CreateTextFieldDialog extends State {
OkCancelButton(
onOkPressed: () {
widget.confirm(newValue);
+ Navigator.of(context).pop();
},
onCancelPressed: () {
if (widget.cancel != null) {
widget.cancel!();
}
+ Navigator.of(context).pop();
},
)
],
@@ -83,12 +87,14 @@ class _CreateTextFieldDialog extends State {
}
}
-class FlowyAlertDialog extends StatefulWidget {
+class PopoverAlertView extends StatelessWidget {
+ final PopoverMutex popoverMutex;
final String title;
final void Function()? cancel;
final void Function()? confirm;
- const FlowyAlertDialog({
+ const PopoverAlertView({
+ required this.popoverMutex,
required this.title,
this.confirm,
this.cancel,
@@ -96,10 +102,46 @@ class FlowyAlertDialog extends StatefulWidget {
}) : super(key: key);
@override
- State createState() => _CreateFlowyAlertDialog();
+ Widget build(BuildContext context) {
+ final theme = context.watch();
+ return StyledDialog(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ...[
+ FlowyText.medium(title, color: theme.shader4),
+ ],
+ if (confirm != null) ...[
+ const VSpace(20),
+ OkCancelButton(
+ onOkPressed: confirm,
+ onCancelPressed: cancel,
+ )
+ ]
+ ],
+ ),
+ );
+ }
}
-class _CreateFlowyAlertDialog extends State {
+class NavigatorAlertDialog extends StatefulWidget {
+ final String title;
+ final void Function()? cancel;
+ final void Function()? confirm;
+
+ const NavigatorAlertDialog({
+ required this.title,
+ this.confirm,
+ this.cancel,
+ Key? key,
+ }) : super(key: key);
+
+ @override
+ State createState() => _CreateFlowyAlertDialog();
+}
+
+class _CreateFlowyAlertDialog extends State {
@override
void initState() {
super.initState();
@@ -118,10 +160,13 @@ class _CreateFlowyAlertDialog extends State {
],
if (widget.confirm != null) ...[
const VSpace(20),
- OkCancelButton(
- onOkPressed: widget.confirm!,
- onCancelPressed: widget.confirm,
- )
+ OkCancelButton(onOkPressed: () {
+ widget.confirm?.call();
+ Navigator.of(context).pop();
+ }, onCancelPressed: () {
+ widget.cancel?.call();
+ Navigator.of(context).pop();
+ })
]
],
),
@@ -129,7 +174,7 @@ class _CreateFlowyAlertDialog extends State {
}
}
-class OkCancelDialog extends StatelessWidget {
+class NavigatorOkCancelDialog extends StatelessWidget {
final VoidCallback? onOkPressed;
final VoidCallback? onCancelPressed;
final String? okTitle;
@@ -138,7 +183,7 @@ class OkCancelDialog extends StatelessWidget {
final String message;
final double? maxWidth;
- const OkCancelDialog(
+ const NavigatorOkCancelDialog(
{Key? key,
this.onOkPressed,
this.onCancelPressed,
@@ -158,7 +203,7 @@ class OkCancelDialog extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title != null) ...[
- Text(title!.toUpperCase(), style: TextStyles.T1.textColor(theme.shader1)),
+ FlowyText.medium(title!.toUpperCase(), color: theme.shader1),
VSpace(Insets.sm * 1.5),
Container(color: theme.bg1, height: 1),
VSpace(Insets.m * 1.5),
@@ -166,8 +211,14 @@ class OkCancelDialog extends StatelessWidget {
Text(message, style: TextStyles.Body1.textHeight(1.5)),
SizedBox(height: Insets.l),
OkCancelButton(
- onOkPressed: onOkPressed,
- onCancelPressed: onCancelPressed,
+ onOkPressed: () {
+ onOkPressed?.call();
+ Navigator.of(context).pop();
+ },
+ onCancelPressed: () {
+ onCancelPressed?.call();
+ Navigator.of(context).pop();
+ },
okTitle: okTitle?.toUpperCase(),
cancelTitle: cancelTitle?.toUpperCase(),
)
@@ -185,7 +236,12 @@ class OkCancelButton extends StatelessWidget {
final double? minHeight;
const OkCancelButton(
- {Key? key, this.onOkPressed, this.onCancelPressed, this.okTitle, this.cancelTitle, this.minHeight})
+ {Key? key,
+ this.onOkPressed,
+ this.onCancelPressed,
+ this.okTitle,
+ this.cancelTitle,
+ this.minHeight})
: super(key: key);
@override
@@ -198,20 +254,14 @@ class OkCancelButton extends StatelessWidget {
if (onCancelPressed != null)
SecondaryTextButton(
cancelTitle ?? LocaleKeys.button_Cancel.tr(),
- onPressed: () {
- onCancelPressed!();
- AppGlobals.nav.pop();
- },
+ onPressed: onCancelPressed,
bigMode: true,
),
HSpace(Insets.m),
if (onOkPressed != null)
PrimaryTextButton(
okTitle ?? LocaleKeys.button_OK.tr(),
- onPressed: () {
- onOkPressed!();
- AppGlobals.nav.pop();
- },
+ onPressed: onOkPressed,
bigMode: true,
),
],
diff --git a/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj b/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj
index 219b81efe0..5634f236ea 100644
--- a/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj
+++ b/frontend/app_flowy/macos/Runner.xcodeproj/project.pbxproj
@@ -416,6 +416,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
+ ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
@@ -549,6 +550,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
+ ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
@@ -573,6 +575,7 @@
isa = XCBuildConfiguration;
baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
buildSettings = {
+ ARCHS = arm64;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
diff --git a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart
index bcba14db11..eafee79a6d 100644
--- a/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart
+++ b/frontend/app_flowy/packages/appflowy_editor/lib/src/service/shortcut_event/built_in_shortcut_events.dart
@@ -58,122 +58,153 @@ List builtInShortcutEvents = [
key: 'Move cursor top',
command: 'meta+arrow up',
windowsCommand: 'ctrl+arrow up',
+ linuxCommand: 'ctrl+arrow up',
handler: cursorTop,
),
ShortcutEvent(
key: 'Move cursor bottom',
command: 'meta+arrow down',
windowsCommand: 'ctrl+arrow down',
+ linuxCommand: 'ctrl+arrow down',
handler: cursorBottom,
),
ShortcutEvent(
key: 'Move cursor begin',
command: 'meta+arrow left',
windowsCommand: 'ctrl+arrow left',
+ linuxCommand: 'ctrl+arrow left',
handler: cursorBegin,
),
ShortcutEvent(
key: 'Move cursor end',
command: 'meta+arrow right',
windowsCommand: 'ctrl+arrow right',
+ linuxCommand: 'ctrl+arrow right',
handler: cursorEnd,
),
ShortcutEvent(
key: 'Cursor top select',
command: 'meta+shift+arrow up',
windowsCommand: 'ctrl+shift+arrow up',
+ linuxCommand: 'ctrl+shift+arrow up',
handler: cursorTopSelect,
),
ShortcutEvent(
key: 'Cursor bottom select',
command: 'meta+shift+arrow down',
windowsCommand: 'ctrl+shift+arrow down',
+ linuxCommand: 'ctrl+shift+arrow down',
handler: cursorBottomSelect,
),
ShortcutEvent(
key: 'Cursor begin select',
command: 'meta+shift+arrow left',
windowsCommand: 'ctrl+shift+arrow left',
+ linuxCommand: 'ctrl+shift+arrow left',
handler: cursorBeginSelect,
),
ShortcutEvent(
key: 'Cursor end select',
command: 'meta+shift+arrow right',
windowsCommand: 'ctrl+shift+arrow right',
+ linuxCommand: 'ctrl+shift+arrow right',
handler: cursorEndSelect,
),
ShortcutEvent(
key: 'Redo',
command: 'meta+shift+z',
windowsCommand: 'ctrl+shift+z',
+ linuxCommand: 'ctrl+shift+z',
handler: redoEventHandler,
),
ShortcutEvent(
key: 'Undo',
command: 'meta+z',
windowsCommand: 'ctrl+z',
+ linuxCommand: 'ctrl+z',
handler: undoEventHandler,
),
ShortcutEvent(
key: 'Format bold',
command: 'meta+b',
windowsCommand: 'ctrl+b',
+ linuxCommand: 'ctrl+b',
handler: formatBoldEventHandler,
),
ShortcutEvent(
key: 'Format italic',
command: 'meta+i',
windowsCommand: 'ctrl+i',
+ linuxCommand: 'ctrl+i',
handler: formatItalicEventHandler,
),
ShortcutEvent(
key: 'Format underline',
command: 'meta+u',
windowsCommand: 'ctrl+u',
+ linuxCommand: 'ctrl+u',
handler: formatUnderlineEventHandler,
),
ShortcutEvent(
key: 'Format strikethrough',
command: 'meta+shift+s',
windowsCommand: 'ctrl+shift+s',
+ linuxCommand: 'ctrl+shift+s',
handler: formatStrikethroughEventHandler,
),
ShortcutEvent(
key: 'Format highlight',
command: 'meta+shift+h',
windowsCommand: 'ctrl+shift+h',
+ linuxCommand: 'ctrl+shift+h',
handler: formatHighlightEventHandler,
),
ShortcutEvent(
key: 'Format embed code',
command: 'meta+e',
windowsCommand: 'ctrl+e',
+ linuxCommand: 'ctrl+e',
handler: formatEmbedCodeEventHandler,
),
ShortcutEvent(
key: 'Format link',
command: 'meta+k',
windowsCommand: 'ctrl+k',
+ linuxCommand: 'ctrl+k',
handler: formatLinkEventHandler,
),
ShortcutEvent(
key: 'Copy',
command: 'meta+c',
windowsCommand: 'ctrl+c',
+ linuxCommand: 'ctrl+c',
handler: copyEventHandler,
),
ShortcutEvent(
key: 'Paste',
command: 'meta+v',
windowsCommand: 'ctrl+v',
+ linuxCommand: 'ctrl+v',
handler: pasteEventHandler,
),
ShortcutEvent(
- key: 'Paste',
+ key: 'Cut',
command: 'meta+x',
windowsCommand: 'ctrl+x',
+ linuxCommand: 'ctrl+x',
handler: cutEventHandler,
),
+ ShortcutEvent(
+ key: 'Home',
+ command: 'home',
+ handler: cursorBegin,
+ ),
+ ShortcutEvent(
+ key: 'End',
+ command: 'end',
+ handler: cursorEnd,
+ ),
+
// TODO: split the keys.
ShortcutEvent(
key: 'Delete Text',
@@ -199,6 +230,7 @@ List builtInShortcutEvents = [
key: 'select all',
command: 'meta+a',
windowsCommand: 'ctrl+a',
+ linuxCommand: 'ctrl+a',
handler: selectAllHandler,
),
ShortcutEvent(
diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart
index 5000c3ec93..b9b163f489 100644
--- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart
+++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/arrow_keys_handler_test.dart
@@ -287,7 +287,7 @@ void main() async {
LogicalKeyboardKey.arrowDown,
isShiftPressed: true,
);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowRight,
isShiftPressed: true,
@@ -321,7 +321,7 @@ void main() async {
LogicalKeyboardKey.arrowUp,
isShiftPressed: true,
);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowLeft,
isShiftPressed: true,
@@ -398,7 +398,7 @@ Future _testPressArrowKeyWithMetaInSelection(
}
}
await editor.updateSelection(selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowLeft,
isControlPressed: true,
@@ -415,7 +415,7 @@ Future _testPressArrowKeyWithMetaInSelection(
Selection.single(path: [0], startOffset: 0),
);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowRight,
isControlPressed: true,
@@ -432,7 +432,7 @@ Future _testPressArrowKeyWithMetaInSelection(
Selection.single(path: [0], startOffset: text.length),
);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowUp,
isControlPressed: true,
@@ -449,7 +449,7 @@ Future _testPressArrowKeyWithMetaInSelection(
Selection.single(path: [0], startOffset: 0),
);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowDown,
isControlPressed: true,
diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart
index 44ffb074d8..7240c186f3 100644
--- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart
+++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/format_style_handler_test.dart
@@ -94,7 +94,7 @@ Future _testUpdateTextStyleByCommandX(
var selection =
Selection.single(path: [1], startOffset: 2, endOffset: text.length - 2);
await editor.updateSelection(selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
key,
isShiftPressed: isShiftPressed,
@@ -121,7 +121,7 @@ Future _testUpdateTextStyleByCommandX(
selection =
Selection.single(path: [1], startOffset: 0, endOffset: text.length);
await editor.updateSelection(selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
key,
isShiftPressed: isShiftPressed,
@@ -146,7 +146,7 @@ Future _testUpdateTextStyleByCommandX(
true);
await editor.updateSelection(selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
key,
isShiftPressed: isShiftPressed,
@@ -168,7 +168,7 @@ Future _testUpdateTextStyleByCommandX(
end: Position(path: [2], offset: text.length),
);
await editor.updateSelection(selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
key,
isShiftPressed: isShiftPressed,
@@ -203,7 +203,7 @@ Future _testUpdateTextStyleByCommandX(
await editor.updateSelection(selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
key,
isShiftPressed: isShiftPressed,
@@ -249,7 +249,7 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async {
expect(find.byType(ToolbarWidget), findsOneWidget);
// trigger the link menu
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(LogicalKeyboardKey.keyK, isControlPressed: true);
} else {
await editor.pressLogicKey(LogicalKeyboardKey.keyK, isMetaPressed: true);
@@ -272,7 +272,7 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async {
true);
await editor.updateSelection(selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(LogicalKeyboardKey.keyK, isControlPressed: true);
} else {
await editor.pressLogicKey(LogicalKeyboardKey.keyK, isMetaPressed: true);
@@ -289,7 +289,7 @@ Future _testLinkMenuInSingleTextSelection(WidgetTester tester) async {
expect(find.byType(LinkMenu), findsNothing);
// Remove link
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(LogicalKeyboardKey.keyK, isControlPressed: true);
} else {
await editor.pressLogicKey(LogicalKeyboardKey.keyK, isMetaPressed: true);
diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart
index 6fa98e9997..b165f279e7 100644
--- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart
+++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/redo_undo_handler_test.dart
@@ -43,7 +43,7 @@ Future _testBackspaceUndoRedo(
await editor.pressLogicKey(LogicalKeyboardKey.backspace);
expect(editor.documentLength, 2);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.keyZ,
isControlPressed: true,
@@ -59,7 +59,7 @@ Future _testBackspaceUndoRedo(
expect((editor.nodeAtPath([1]) as TextNode).toRawString(), text);
expect(editor.documentSelection, selection);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.keyZ,
isControlPressed: true,
diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart
index 6efd2064e9..937988da58 100644
--- a/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart
+++ b/frontend/app_flowy/packages/appflowy_editor/test/service/internal_key_event_handlers/select_all_handler_test.dart
@@ -28,7 +28,7 @@ Future _testSelectAllHandler(WidgetTester tester, int lines) async {
editor.insertTextNode(text);
}
await editor.startTesting();
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(LogicalKeyboardKey.keyA, isControlPressed: true);
} else {
await editor.pressLogicKey(LogicalKeyboardKey.keyA, isMetaPressed: true);
diff --git a/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart b/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart
index 79b2f4d26c..034d91a93e 100644
--- a/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart
+++ b/frontend/app_flowy/packages/appflowy_editor/test/service/shortcut_event/shortcut_event_test.dart
@@ -39,7 +39,7 @@ void main() async {
await editor.updateSelection(
Selection.single(path: [1], startOffset: text.length),
);
- if (Platform.isWindows) {
+ if (Platform.isWindows || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowLeft,
isControlPressed: true,
@@ -62,11 +62,12 @@ void main() async {
if (event.key == 'Move cursor begin') {
event.updateCommand(
windowsCommand: 'alt+arrow left',
+ linuxCommand: 'alt+arrow left',
macOSCommand: 'alt+arrow left',
);
}
}
- if (Platform.isWindows || Platform.isMacOS) {
+ if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
await editor.pressLogicKey(
LogicalKeyboardKey.arrowLeft,
isAltPressed: true,
@@ -84,5 +85,139 @@ void main() async {
tester.pumpAndSettle();
});
+
+ testWidgets('redefine move cursor end command', (tester) async {
+ const text = 'Welcome to Appflowy 😁';
+ final editor = tester.editor
+ ..insertTextNode(text)
+ ..insertTextNode(text);
+ await editor.startTesting();
+ await editor.updateSelection(
+ Selection.single(path: [1], startOffset: 0),
+ );
+ if (Platform.isWindows || Platform.isLinux) {
+ await editor.pressLogicKey(
+ LogicalKeyboardKey.arrowRight,
+ isControlPressed: true,
+ );
+ } else {
+ await editor.pressLogicKey(
+ LogicalKeyboardKey.arrowRight,
+ isMetaPressed: true,
+ );
+ }
+ expect(
+ editor.documentSelection,
+ Selection.single(path: [1], startOffset: text.length),
+ );
+ await editor.updateSelection(
+ Selection.single(path: [1], startOffset: 0),
+ );
+
+ for (final event in builtInShortcutEvents) {
+ if (event.key == 'Move cursor end') {
+ event.updateCommand(
+ windowsCommand: 'alt+arrow right',
+ linuxCommand: 'alt+arrow right',
+ macOSCommand: 'alt+arrow right',
+ );
+ }
+ }
+ await editor.pressLogicKey(
+ LogicalKeyboardKey.arrowRight,
+ isAltPressed: true,
+ );
+ expect(
+ editor.documentSelection,
+ Selection.single(path: [1], startOffset: text.length),
+ );
+ });
+
+ testWidgets('Test Home Key to move to start of current text',
+ (tester) async {
+ const text = 'Welcome to Appflowy 😁';
+ final editor = tester.editor
+ ..insertTextNode(text)
+ ..insertTextNode(text);
+ await editor.startTesting();
+ await editor.updateSelection(
+ Selection.single(path: [1], startOffset: text.length),
+ );
+ if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
+ await editor.pressLogicKey(
+ LogicalKeyboardKey.home,
+ );
+ }
+
+ expect(
+ editor.documentSelection,
+ Selection.single(path: [1], startOffset: 0),
+ );
+ await editor.updateSelection(
+ Selection.single(path: [1], startOffset: text.length),
+ );
+
+ for (final event in builtInShortcutEvents) {
+ if (event.key == 'Move cursor begin') {
+ event.updateCommand(
+ windowsCommand: 'home',
+ linuxCommand: 'home',
+ macOSCommand: 'home',
+ );
+ }
+ }
+ if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
+ await editor.pressLogicKey(
+ LogicalKeyboardKey.home,
+ );
+ }
+ expect(
+ editor.documentSelection,
+ Selection.single(path: [1], startOffset: 0),
+ );
+ });
+
+ testWidgets('Test End Key to move to end of current text', (tester) async {
+ const text = 'Welcome to Appflowy 😁';
+ final editor = tester.editor
+ ..insertTextNode(text)
+ ..insertTextNode(text);
+ await editor.startTesting();
+ await editor.updateSelection(
+ Selection.single(path: [1], startOffset: text.length),
+ );
+ if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
+ await editor.pressLogicKey(
+ LogicalKeyboardKey.end,
+ );
+ }
+
+ expect(
+ editor.documentSelection,
+ Selection.single(path: [1], startOffset: text.length),
+ );
+ await editor.updateSelection(
+ Selection.single(path: [1], startOffset: 0),
+ );
+
+ for (final event in builtInShortcutEvents) {
+ if (event.key == 'Move cursor end') {
+ event.updateCommand(
+ windowsCommand: 'end',
+ linuxCommand: 'end',
+ macOSCommand: 'end',
+ );
+ }
+ }
+ if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) {
+ await editor.pressLogicKey(
+ LogicalKeyboardKey.end,
+ );
+ }
+ expect(
+ editor.documentSelection,
+ Selection.single(path: [1], startOffset: text.length),
+ );
+ });
});
}
diff --git a/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart b/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart
index 2fc26aeb0c..6f9f4e9f33 100644
--- a/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart
+++ b/frontend/app_flowy/packages/appflowy_popover/lib/popover.dart
@@ -59,7 +59,7 @@ class Popover extends StatefulWidget {
final Decoration? maskDecoration;
/// The function used to build the popover.
- final Widget Function(BuildContext context) popupBuilder;
+ final Widget? Function(BuildContext context) popupBuilder;
final int triggerActions;
@@ -265,7 +265,7 @@ class _PopoverMaskState extends State<_PopoverMask> {
}
class PopoverContainer extends StatefulWidget {
- final Widget Function(BuildContext context) popupBuilder;
+ final Widget? Function(BuildContext context) popupBuilder;
final PopoverDirection direction;
final PopoverLink popoverLink;
final Offset offset;
@@ -284,6 +284,15 @@ class PopoverContainer extends StatefulWidget {
@override
State createState() => PopoverContainerState();
+
+ static PopoverContainerState of(BuildContext context) {
+ if (context is StatefulElement && context.state is PopoverContainerState) {
+ return context.state as PopoverContainerState;
+ }
+ final PopoverContainerState? result =
+ context.findAncestorStateOfType();
+ return result!;
+ }
}
class PopoverContainerState extends State {
@@ -302,13 +311,4 @@ class PopoverContainerState extends State {
close() => widget.onClose();
closeAll() => widget.onCloseAll();
-
- static PopoverContainerState of(BuildContext context) {
- if (context is StatefulElement && context.state is PopoverContainerState) {
- return context.state as PopoverContainerState;
- }
- final PopoverContainerState? result =
- context.findAncestorStateOfType();
- return result!;
- }
}
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart
index a656077eb5..adc7dd1f9b 100644
--- a/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart
+++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/flowy_infra_ui.dart
@@ -9,4 +9,4 @@ export 'src/flowy_overlay/flowy_overlay.dart';
export 'src/flowy_overlay/list_overlay.dart';
export 'src/flowy_overlay/option_overlay.dart';
export 'src/flowy_overlay/flowy_dialog.dart';
-export 'src/flowy_overlay/flowy_popover.dart';
+export 'src/flowy_overlay/appflowy_stype_popover.dart';
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_stype_popover.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_stype_popover.dart
new file mode 100644
index 0000000000..663a8c0646
--- /dev/null
+++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/appflowy_stype_popover.dart
@@ -0,0 +1,48 @@
+import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
+import 'package:appflowy_popover/popover.dart';
+import 'package:flutter/material.dart';
+
+class AppFlowyStylePopover extends StatelessWidget {
+ final Widget child;
+ final PopoverController? controller;
+ final Widget Function(BuildContext context) popupBuilder;
+ final PopoverDirection direction;
+ final int triggerActions;
+ final BoxConstraints? constraints;
+ final void Function()? onClose;
+ final PopoverMutex? mutex;
+ final Offset? offset;
+
+ const AppFlowyStylePopover({
+ Key? key,
+ required this.child,
+ required this.popupBuilder,
+ this.direction = PopoverDirection.rightWithTopAligned,
+ this.onClose,
+ this.constraints,
+ this.mutex,
+ this.triggerActions = 0,
+ this.offset,
+ this.controller,
+ }) : super(key: key);
+
+ @override
+ Widget build(BuildContext context) {
+ return Popover(
+ controller: controller,
+ onClose: onClose,
+ direction: direction,
+ mutex: mutex,
+ triggerActions: triggerActions,
+ popupBuilder: (context) {
+ final child = popupBuilder(context);
+ debugPrint('$child popover');
+ return OverlayContainer(
+ constraints: constraints,
+ child: popupBuilder(context),
+ );
+ },
+ child: child,
+ );
+ }
+}
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_popover.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_popover.dart
deleted file mode 100644
index d1a52e50d9..0000000000
--- a/frontend/app_flowy/packages/flowy_infra_ui/lib/src/flowy_overlay/flowy_popover.dart
+++ /dev/null
@@ -1,59 +0,0 @@
-import 'package:flowy_infra_ui/flowy_infra_ui_web.dart';
-import 'package:flowy_infra_ui/style_widget/decoration.dart';
-import 'package:flowy_infra/theme.dart';
-import 'package:provider/provider.dart';
-import 'package:flutter/material.dart';
-import './flowy_popover_layout.dart';
-
-const _overlayContainerPadding = EdgeInsets.all(12);
-
-class FlowyPopover extends StatefulWidget {
- final Widget Function(BuildContext context) builder;
- final ShapeBorder? shape;
- final Rect anchorRect;
- final AnchorDirection? anchorDirection;
- final EdgeInsets padding;
- final BoxConstraints? constraints;
-
- const FlowyPopover({
- Key? key,
- required this.builder,
- required this.anchorRect,
- this.shape,
- this.padding = _overlayContainerPadding,
- this.anchorDirection,
- this.constraints,
- }) : super(key: key);
-
- @override
- State createState() => _FlowyPopoverState();
-}
-
-class _FlowyPopoverState extends State {
- final preRenderKey = GlobalKey();
- Size? size;
-
- @override
- Widget build(BuildContext context) {
- final theme =
- context.watch() ?? AppTheme.fromType(ThemeType.light);
- return Material(
- type: MaterialType.transparency,
- child: CustomSingleChildLayout(
- delegate: PopoverLayoutDelegate(
- anchorRect: widget.anchorRect,
- anchorDirection:
- widget.anchorDirection ?? AnchorDirection.rightWithTopAligned,
- overlapBehaviour: OverlapBehaviour.stretch,
- ),
- child: Container(
- padding: widget.padding,
- constraints: widget.constraints ??
- BoxConstraints.loose(const Size(280, 400)),
- decoration: FlowyDecoration.decoration(
- theme.surface, theme.shadowColor.withOpacity(0.15)),
- key: preRenderKey,
- child: widget.builder(context),
- )));
- }
-}
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart
index 2c0725288c..63e9ce2698 100644
--- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart
+++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/primary_button.dart
@@ -1,22 +1,30 @@
+import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:flowy_infra/size.dart';
-import 'package:flowy_infra/text_style.dart';
import 'package:flowy_infra/theme.dart';
import 'base_styled_button.dart';
-import 'package:textstyle_extensions/textstyle_extensions.dart';
class PrimaryTextButton extends StatelessWidget {
final String label;
final VoidCallback? onPressed;
final bool bigMode;
- const PrimaryTextButton(this.label, {Key? key, this.onPressed, this.bigMode = false}) : super(key: key);
+ const PrimaryTextButton(this.label,
+ {Key? key, this.onPressed, this.bigMode = false})
+ : super(key: key);
@override
Widget build(BuildContext context) {
- TextStyle txtStyle = TextStyles.Btn.textColor(Colors.white);
- return PrimaryButton(bigMode: bigMode, onPressed: onPressed, child: Text(label, style: txtStyle));
+ final theme = context.watch();
+ return PrimaryButton(
+ bigMode: bigMode,
+ onPressed: onPressed,
+ child: FlowyText.regular(
+ label,
+ color: theme.surface,
+ ),
+ );
}
}
@@ -25,14 +33,16 @@ class PrimaryButton extends StatelessWidget {
final VoidCallback? onPressed;
final bool bigMode;
- const PrimaryButton({Key? key, required this.child, this.onPressed, this.bigMode = false}) : super(key: key);
+ const PrimaryButton(
+ {Key? key, required this.child, this.onPressed, this.bigMode = false})
+ : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch();
return BaseStyledButton(
- minWidth: bigMode ? 170 : 78,
- minHeight: bigMode ? 48 : 28,
+ minWidth: bigMode ? 100 : 80,
+ minHeight: bigMode ? 40 : 38,
contentPadding: EdgeInsets.zero,
bgColor: theme.main1,
hoverColor: theme.main1,
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart
index 9e6f7d331d..ef7a6e6051 100644
--- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart
+++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/buttons/secondary_button.dart
@@ -1,9 +1,8 @@
+import 'package:flowy_infra_ui/style_widget/text.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
// ignore: import_of_legacy_library_into_null_safe
-import 'package:textstyle_extensions/textstyle_extensions.dart';
import 'package:flowy_infra/size.dart';
-import 'package:flowy_infra/text_style.dart';
import 'package:flowy_infra/theme.dart';
import 'base_styled_button.dart';
@@ -12,13 +11,21 @@ class SecondaryTextButton extends StatelessWidget {
final VoidCallback? onPressed;
final bool bigMode;
- const SecondaryTextButton(this.label, {Key? key, this.onPressed, this.bigMode = false}) : super(key: key);
+ const SecondaryTextButton(this.label,
+ {Key? key, this.onPressed, this.bigMode = false})
+ : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch();
- TextStyle txtStyle = TextStyles.Btn.textColor(theme.main1);
- return SecondaryButton(bigMode: bigMode, onPressed: onPressed, child: Text(label, style: txtStyle));
+ return SecondaryButton(
+ bigMode: bigMode,
+ onPressed: onPressed,
+ child: FlowyText.regular(
+ label,
+ color: theme.main1,
+ ),
+ );
}
}
@@ -27,14 +34,16 @@ class SecondaryButton extends StatelessWidget {
final VoidCallback? onPressed;
final bool bigMode;
- const SecondaryButton({Key? key, required this.child, this.onPressed, this.bigMode = false}) : super(key: key);
+ const SecondaryButton(
+ {Key? key, required this.child, this.onPressed, this.bigMode = false})
+ : super(key: key);
@override
Widget build(BuildContext context) {
final theme = context.watch();
return BaseStyledButton(
- minWidth: bigMode ? 170 : 78,
- minHeight: bigMode ? 48 : 28,
+ minWidth: bigMode ? 100 : 80,
+ minHeight: bigMode ? 40 : 38,
contentPadding: EdgeInsets.zero,
bgColor: theme.shader7,
hoverColor: theme.hover,
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/dialog_size.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/dialog_size.dart
index e4a95b7b5f..b0a13d6bd3 100644
--- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/dialog_size.dart
+++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/dialog_size.dart
@@ -1,3 +1,3 @@
class DialogSize {
- static double get minDialogWidth => 480;
+ static double get minDialogWidth => 400;
}
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/styled_dialogs.dart b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/styled_dialogs.dart
index ecd8cfb4ae..5cf1641649 100644
--- a/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/styled_dialogs.dart
+++ b/frontend/app_flowy/packages/flowy_infra_ui/lib/widget/dialog/styled_dialogs.dart
@@ -65,7 +65,7 @@ class StyledDialog extends StatelessWidget {
return FocusTraversalGroup(
child: Container(
- margin: margin ?? EdgeInsets.all(Insets.lGutter * 2),
+ margin: margin ?? EdgeInsets.all(Insets.sm * 2),
alignment: Alignment.center,
child: Container(
constraints: BoxConstraints(
@@ -133,7 +133,9 @@ class StyledDialogRoute extends PopupRoute {
super(settings: settings, filter: barrier.filter);
@override
- bool get barrierDismissible => barrier.dismissible;
+ bool get barrierDismissible {
+ return barrier.dismissible;
+ }
@override
String get barrierLabel => barrier.label;
diff --git a/frontend/app_flowy/packages/flowy_infra_ui/pubspec.yaml b/frontend/app_flowy/packages/flowy_infra_ui/pubspec.yaml
index c95ba1119e..9d9b25efab 100644
--- a/frontend/app_flowy/packages/flowy_infra_ui/pubspec.yaml
+++ b/frontend/app_flowy/packages/flowy_infra_ui/pubspec.yaml
@@ -27,6 +27,8 @@ dependencies:
path: flowy_infra_ui_platform_interface
flowy_infra_ui_web:
path: flowy_infra_ui_web
+ appflowy_popover:
+ path: ../appflowy_popover
# Flowy packages
flowy_infra:
diff --git a/frontend/rust-lib/flowy-net/src/http_server/document.rs b/frontend/rust-lib/flowy-net/src/http_server/document.rs
index ca9cb47955..b286e5c102 100644
--- a/frontend/rust-lib/flowy-net/src/http_server/document.rs
+++ b/frontend/rust-lib/flowy-net/src/http_server/document.rs
@@ -4,7 +4,7 @@ use crate::{
};
use flowy_error::FlowyError;
use flowy_sync::entities::text_block::{CreateTextBlockParams, DocumentPB, ResetTextBlockParams, TextBlockIdPB};
-use flowy_text_block::BlockCloudService;
+use flowy_text_block::TextEditorCloudService;
use http_flowy::response::FlowyResponse;
use lazy_static::lazy_static;
use lib_infra::future::FutureResult;
@@ -20,20 +20,20 @@ impl BlockHttpCloudService {
}
}
-impl BlockCloudService for BlockHttpCloudService {
- fn create_block(&self, token: &str, params: CreateTextBlockParams) -> FutureResult<(), FlowyError> {
+impl TextEditorCloudService for BlockHttpCloudService {
+ fn create_text_block(&self, token: &str, params: CreateTextBlockParams) -> FutureResult<(), FlowyError> {
let token = token.to_owned();
let url = self.config.doc_url();
FutureResult::new(async move { create_document_request(&token, params, &url).await })
}
- fn read_block(&self, token: &str, params: TextBlockIdPB) -> FutureResult, FlowyError> {
+ fn read_text_block(&self, token: &str, params: TextBlockIdPB) -> FutureResult , FlowyError> {
let token = token.to_owned();
let url = self.config.doc_url();
FutureResult::new(async move { read_document_request(&token, params, &url).await })
}
- fn update_block(&self, token: &str, params: ResetTextBlockParams) -> FutureResult<(), FlowyError> {
+ fn update_text_block(&self, token: &str, params: ResetTextBlockParams) -> FutureResult<(), FlowyError> {
let token = token.to_owned();
let url = self.config.doc_url();
FutureResult::new(async move { reset_doc_request(&token, params, &url).await })
diff --git a/frontend/rust-lib/flowy-net/src/local_server/server.rs b/frontend/rust-lib/flowy-net/src/local_server/server.rs
index 387ff4e885..060abf08bf 100644
--- a/frontend/rust-lib/flowy-net/src/local_server/server.rs
+++ b/frontend/rust-lib/flowy-net/src/local_server/server.rs
@@ -261,7 +261,7 @@ use flowy_folder::entities::{
use flowy_folder_data_model::revision::{
gen_app_id, gen_workspace_id, AppRevision, TrashRevision, ViewRevision, WorkspaceRevision,
};
-use flowy_text_block::BlockCloudService;
+use flowy_text_block::TextEditorCloudService;
use flowy_user::entities::{
SignInParams, SignInResponse, SignUpParams, SignUpResponse, UpdateUserProfileParams, UserProfilePB,
};
@@ -414,12 +414,12 @@ impl UserCloudService for LocalServer {
}
}
-impl BlockCloudService for LocalServer {
- fn create_block(&self, _token: &str, _params: CreateTextBlockParams) -> FutureResult<(), FlowyError> {
+impl TextEditorCloudService for LocalServer {
+ fn create_text_block(&self, _token: &str, _params: CreateTextBlockParams) -> FutureResult<(), FlowyError> {
FutureResult::new(async { Ok(()) })
}
- fn read_block(&self, _token: &str, params: TextBlockIdPB) -> FutureResult , FlowyError> {
+ fn read_text_block(&self, _token: &str, params: TextBlockIdPB) -> FutureResult , FlowyError> {
let doc = DocumentPB {
block_id: params.value,
text: initial_quill_delta_string(),
@@ -429,7 +429,7 @@ impl BlockCloudService for LocalServer {
FutureResult::new(async { Ok(Some(doc)) })
}
- fn update_block(&self, _token: &str, _params: ResetTextBlockParams) -> FutureResult<(), FlowyError> {
+ fn update_text_block(&self, _token: &str, _params: ResetTextBlockParams) -> FutureResult<(), FlowyError> {
FutureResult::new(async { Ok(()) })
}
}
diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs
index 0dc51c1a09..79b5468a56 100644
--- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs
+++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/folder_deps.rs
@@ -18,7 +18,7 @@ use flowy_revision::{RevisionWebSocket, WSStateReceiver};
use flowy_sync::client_document::default::initial_quill_delta_string;
use flowy_sync::entities::revision::{RepeatedRevision, Revision};
use flowy_sync::entities::ws_data::ClientRevisionWSData;
-use flowy_text_block::TextBlockManager;
+use flowy_text_block::TextEditorManager;
use flowy_user::services::UserSession;
use futures_core::future::BoxFuture;
use lib_infra::future::{BoxResultFuture, FutureResult};
@@ -34,7 +34,7 @@ impl FolderDepsResolver {
user_session: Arc,
server_config: &ClientServerConfiguration,
ws_conn: &Arc,
- text_block_manager: &Arc,
+ text_block_manager: &Arc,
grid_manager: &Arc,
) -> Arc {
let user: Arc = Arc::new(WorkspaceUserImpl(user_session.clone()));
@@ -63,7 +63,7 @@ impl FolderDepsResolver {
}
fn make_view_data_processor(
- text_block_manager: Arc,
+ text_block_manager: Arc,
grid_manager: Arc,
) -> ViewDataProcessorMap {
let mut map: HashMap> = HashMap::new();
@@ -135,7 +135,7 @@ impl WSMessageReceiver for FolderWSMessageReceiverImpl {
}
}
-struct TextBlockViewDataProcessor(Arc);
+struct TextBlockViewDataProcessor(Arc);
impl ViewDataProcessor for TextBlockViewDataProcessor {
fn initialize(&self) -> FutureResult<(), FlowyError> {
let manager = self.0.clone();
@@ -147,7 +147,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
let view_id = view_id.to_string();
let manager = self.0.clone();
FutureResult::new(async move {
- let _ = manager.create_block(view_id, repeated_revision).await?;
+ let _ = manager.create_text_block(view_id, repeated_revision).await?;
Ok(())
})
}
@@ -156,7 +156,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
let manager = self.0.clone();
let view_id = view_id.to_string();
FutureResult::new(async move {
- let _ = manager.delete_block(view_id)?;
+ let _ = manager.close_text_editor(view_id)?;
Ok(())
})
}
@@ -165,7 +165,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
let manager = self.0.clone();
let view_id = view_id.to_string();
FutureResult::new(async move {
- let _ = manager.close_block(view_id)?;
+ let _ = manager.close_text_editor(view_id)?;
Ok(())
})
}
@@ -174,7 +174,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
let view_id = view_id.to_string();
let manager = self.0.clone();
FutureResult::new(async move {
- let editor = manager.open_block(view_id).await?;
+ let editor = manager.open_text_editor(view_id).await?;
let delta_bytes = Bytes::from(editor.delta_str().await?);
Ok(delta_bytes)
})
@@ -195,7 +195,7 @@ impl ViewDataProcessor for TextBlockViewDataProcessor {
let delta_data = Bytes::from(view_data);
let repeated_revision: RepeatedRevision =
Revision::initial_revision(&user_id, &view_id, delta_data.clone()).into();
- let _ = manager.create_block(view_id, repeated_revision).await?;
+ let _ = manager.create_text_block(view_id, repeated_revision).await?;
Ok(delta_data)
})
}
diff --git a/frontend/rust-lib/flowy-sdk/src/deps_resolve/text_block_deps.rs b/frontend/rust-lib/flowy-sdk/src/deps_resolve/text_block_deps.rs
index 7b80e637ab..9fa5667bb1 100644
--- a/frontend/rust-lib/flowy-sdk/src/deps_resolve/text_block_deps.rs
+++ b/frontend/rust-lib/flowy-sdk/src/deps_resolve/text_block_deps.rs
@@ -8,7 +8,7 @@ use flowy_revision::{RevisionWebSocket, WSStateReceiver};
use flowy_sync::entities::ws_data::ClientRevisionWSData;
use flowy_text_block::{
errors::{internal_error, FlowyError},
- BlockCloudService, TextBlockManager, TextBlockUser,
+ TextEditorCloudService, TextEditorManager, TextEditorUser,
};
use flowy_user::services::UserSession;
use futures_core::future::BoxFuture;
@@ -23,15 +23,15 @@ impl TextBlockDepsResolver {
ws_conn: Arc,
user_session: Arc,
server_config: &ClientServerConfiguration,
- ) -> Arc {
+ ) -> Arc {
let user = Arc::new(BlockUserImpl(user_session));
let rev_web_socket = Arc::new(TextBlockWebSocket(ws_conn.clone()));
- let cloud_service: Arc = match local_server {
+ let cloud_service: Arc = match local_server {
None => Arc::new(BlockHttpCloudService::new(server_config.clone())),
Some(local_server) => local_server,
};
- let manager = Arc::new(TextBlockManager::new(cloud_service, user, rev_web_socket));
+ let manager = Arc::new(TextEditorManager::new(cloud_service, user, rev_web_socket));
let receiver = Arc::new(DocumentWSMessageReceiverImpl(manager.clone()));
ws_conn.add_ws_message_receiver(receiver).unwrap();
@@ -40,7 +40,7 @@ impl TextBlockDepsResolver {
}
struct BlockUserImpl(Arc);
-impl TextBlockUser for BlockUserImpl {
+impl TextEditorUser for BlockUserImpl {
fn user_dir(&self) -> Result {
let dir = self.0.user_dir().map_err(|e| FlowyError::unauthorized().context(e))?;
@@ -90,7 +90,7 @@ impl RevisionWebSocket for TextBlockWebSocket {
}
}
-struct DocumentWSMessageReceiverImpl(Arc);
+struct DocumentWSMessageReceiverImpl(Arc);
impl WSMessageReceiver for DocumentWSMessageReceiverImpl {
fn source(&self) -> WSChannel {
WSChannel::Document
diff --git a/frontend/rust-lib/flowy-sdk/src/lib.rs b/frontend/rust-lib/flowy-sdk/src/lib.rs
index e0bd601987..e780f9f937 100644
--- a/frontend/rust-lib/flowy-sdk/src/lib.rs
+++ b/frontend/rust-lib/flowy-sdk/src/lib.rs
@@ -11,7 +11,7 @@ use flowy_net::{
local_server::LocalServer,
ws::connection::{listen_on_websocket, FlowyWebSocketConnect},
};
-use flowy_text_block::TextBlockManager;
+use flowy_text_block::TextEditorManager;
use flowy_user::services::{notifier::UserStatus, UserSession, UserSessionConfig};
use lib_dispatch::prelude::*;
use lib_dispatch::runtime::tokio_default_runtime;
@@ -89,7 +89,7 @@ pub struct FlowySDK {
#[allow(dead_code)]
config: FlowySDKConfig,
pub user_session: Arc,
- pub text_block_manager: Arc,
+ pub text_block_manager: Arc,
pub folder_manager: Arc,
pub grid_manager: Arc,
pub dispatcher: Arc,
diff --git a/frontend/rust-lib/flowy-sdk/src/module.rs b/frontend/rust-lib/flowy-sdk/src/module.rs
index cbb1673a17..2f6a20d6c6 100644
--- a/frontend/rust-lib/flowy-sdk/src/module.rs
+++ b/frontend/rust-lib/flowy-sdk/src/module.rs
@@ -1,7 +1,7 @@
use flowy_folder::manager::FolderManager;
use flowy_grid::manager::GridManager;
use flowy_net::ws::connection::FlowyWebSocketConnect;
-use flowy_text_block::TextBlockManager;
+use flowy_text_block::TextEditorManager;
use flowy_user::services::UserSession;
use lib_dispatch::prelude::Module;
use std::sync::Arc;
@@ -11,7 +11,7 @@ pub fn mk_modules(
folder_manager: &Arc,
grid_manager: &Arc,
user_session: &Arc,
- text_block_manager: &Arc,
+ text_block_manager: &Arc,
) -> Vec {
let user_module = mk_user_module(user_session.clone());
let folder_module = mk_folder_module(folder_manager.clone());
@@ -43,6 +43,6 @@ fn mk_grid_module(grid_manager: Arc) -> Module {
flowy_grid::event_map::create(grid_manager)
}
-fn mk_text_block_module(text_block_manager: Arc) -> Module {
+fn mk_text_block_module(text_block_manager: Arc) -> Module {
flowy_text_block::event_map::create(text_block_manager)
}
diff --git a/frontend/rust-lib/flowy-text-block/src/editor.rs b/frontend/rust-lib/flowy-text-block/src/editor.rs
index 8f75a67d88..8ca41f3f5a 100644
--- a/frontend/rust-lib/flowy-text-block/src/editor.rs
+++ b/frontend/rust-lib/flowy-text-block/src/editor.rs
@@ -2,7 +2,7 @@ use crate::web_socket::EditorCommandSender;
use crate::{
errors::FlowyError,
queue::{EditBlockQueue, EditorCommand},
- TextBlockUser,
+ TextEditorUser,
};
use bytes::Bytes;
use flowy_error::{internal_error, FlowyResult};
@@ -24,7 +24,6 @@ use tokio::sync::{mpsc, oneshot};
pub struct TextBlockEditor {
pub doc_id: String,
- #[allow(dead_code)]
rev_manager: Arc,
#[cfg(feature = "sync")]
ws_manager: Arc,
@@ -35,7 +34,7 @@ impl TextBlockEditor {
#[allow(unused_variables)]
pub(crate) async fn new(
doc_id: &str,
- user: Arc,
+ user: Arc,
mut rev_manager: RevisionManager,
rev_web_socket: Arc,
cloud_service: Arc,
@@ -194,7 +193,7 @@ impl std::ops::Drop for TextBlockEditor {
// The edit queue will exit after the EditorCommandSender was dropped.
fn spawn_edit_queue(
- user: Arc,
+ user: Arc,
rev_manager: Arc,
delta: TextDelta,
) -> EditorCommandSender {
diff --git a/frontend/rust-lib/flowy-text-block/src/entities.rs b/frontend/rust-lib/flowy-text-block/src/entities.rs
index ec8767285b..d7cc1d8665 100644
--- a/frontend/rust-lib/flowy-text-block/src/entities.rs
+++ b/frontend/rust-lib/flowy-text-block/src/entities.rs
@@ -29,6 +29,52 @@ impl std::convert::From for ExportType {
}
}
+#[derive(Default, ProtoBuf)]
+pub struct EditPayloadPB {
+ #[pb(index = 1)]
+ pub text_block_id: String,
+
+ // Encode in JSON format
+ #[pb(index = 2)]
+ pub operations: String,
+
+ // Encode in JSON format
+ #[pb(index = 3)]
+ pub delta: String,
+}
+
+#[derive(Default)]
+pub struct EditParams {
+ pub text_block_id: String,
+
+ // Encode in JSON format
+ pub operations: String,
+
+ // Encode in JSON format
+ pub delta: String,
+}
+
+impl TryInto for EditPayloadPB {
+ type Error = ErrorCode;
+ fn try_into(self) -> Result {
+ Ok(EditParams {
+ text_block_id: self.text_block_id,
+ operations: self.operations,
+ delta: self.delta,
+ })
+ }
+}
+
+#[derive(Default, ProtoBuf)]
+pub struct TextBlockPB {
+ #[pb(index = 1)]
+ pub text_block_id: String,
+
+ /// Encode in JSON format
+ #[pb(index = 2)]
+ pub snapshot: String,
+}
+
#[derive(Default, ProtoBuf)]
pub struct ExportPayloadPB {
#[pb(index = 1)]
diff --git a/frontend/rust-lib/flowy-text-block/src/event_handler.rs b/frontend/rust-lib/flowy-text-block/src/event_handler.rs
index dc9812d862..98ca7e19fe 100644
--- a/frontend/rust-lib/flowy-text-block/src/event_handler.rs
+++ b/frontend/rust-lib/flowy-text-block/src/event_handler.rs
@@ -1,39 +1,40 @@
-use crate::entities::{ExportDataPB, ExportParams, ExportPayloadPB};
-use crate::TextBlockManager;
+use crate::entities::{EditParams, EditPayloadPB, ExportDataPB, ExportParams, ExportPayloadPB, TextBlockPB};
+use crate::TextEditorManager;
use flowy_error::FlowyError;
-use flowy_sync::entities::text_block::{TextBlockDeltaPB, TextBlockIdPB};
+use flowy_sync::entities::text_block::TextBlockIdPB;
use lib_dispatch::prelude::{data_result, AppData, Data, DataResult};
use std::convert::TryInto;
use std::sync::Arc;
-pub(crate) async fn get_block_data_handler(
+pub(crate) async fn get_text_block_handler(
data: Data,
- manager: AppData>,
-) -> DataResult {
- let block_id: TextBlockIdPB = data.into_inner();
- let editor = manager.open_block(&block_id).await?;
+ manager: AppData>,
+) -> DataResult {
+ let text_block_id: TextBlockIdPB = data.into_inner();
+ let editor = manager.open_text_editor(&text_block_id).await?;
let delta_str = editor.delta_str().await?;
- data_result(TextBlockDeltaPB {
- block_id: block_id.into(),
- delta_str,
+ data_result(TextBlockPB {
+ text_block_id: text_block_id.into(),
+ snapshot: delta_str,
})
}
-pub(crate) async fn apply_delta_handler(
- data: Data,
- manager: AppData>,
-) -> DataResult {
- let block_delta = manager.receive_local_delta(data.into_inner()).await?;
- data_result(block_delta)
+pub(crate) async fn apply_edit_handler(
+ data: Data,
+ manager: AppData>,
+) -> Result<(), FlowyError> {
+ let params: EditParams = data.into_inner().try_into()?;
+ let _ = manager.apply_edit(params).await?;
+ Ok(())
}
#[tracing::instrument(level = "debug", skip(data, manager), err)]
pub(crate) async fn export_handler(
data: Data,
- manager: AppData>,
+ manager: AppData>,
) -> DataResult {
let params: ExportParams = data.into_inner().try_into()?;
- let editor = manager.open_block(¶ms.view_id).await?;
+ let editor = manager.open_text_editor(¶ms.view_id).await?;
let delta_json = editor.delta_str().await?;
data_result(ExportDataPB {
data: delta_json,
diff --git a/frontend/rust-lib/flowy-text-block/src/event_map.rs b/frontend/rust-lib/flowy-text-block/src/event_map.rs
index cfc06bf32c..d66ee490ee 100644
--- a/frontend/rust-lib/flowy-text-block/src/event_map.rs
+++ b/frontend/rust-lib/flowy-text-block/src/event_map.rs
@@ -1,16 +1,16 @@
use crate::event_handler::*;
-use crate::TextBlockManager;
+use crate::TextEditorManager;
use flowy_derive::{Flowy_Event, ProtoBuf_Enum};
use lib_dispatch::prelude::Module;
use std::sync::Arc;
use strum_macros::Display;
-pub fn create(block_manager: Arc) -> Module {
+pub fn create(block_manager: Arc) -> Module {
let mut module = Module::new().name(env!("CARGO_PKG_NAME")).data(block_manager);
module = module
- .event(TextBlockEvent::GetBlockData, get_block_data_handler)
- .event(TextBlockEvent::ApplyDelta, apply_delta_handler)
+ .event(TextBlockEvent::GetTextBlock, get_text_block_handler)
+ .event(TextBlockEvent::ApplyEdit, apply_edit_handler)
.event(TextBlockEvent::ExportDocument, export_handler);
module
@@ -19,11 +19,11 @@ pub fn create(block_manager: Arc) -> Module {
#[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
#[event_err = "FlowyError"]
pub enum TextBlockEvent {
- #[event(input = "TextBlockIdPB", output = "TextBlockDeltaPB")]
- GetBlockData = 0,
+ #[event(input = "TextBlockIdPB", output = "TextBlockPB")]
+ GetTextBlock = 0,
- #[event(input = "TextBlockDeltaPB", output = "TextBlockDeltaPB")]
- ApplyDelta = 1,
+ #[event(input = "EditPayloadPB")]
+ ApplyEdit = 1,
#[event(input = "ExportPayloadPB", output = "ExportDataPB")]
ExportDocument = 2,
diff --git a/frontend/rust-lib/flowy-text-block/src/lib.rs b/frontend/rust-lib/flowy-text-block/src/lib.rs
index 37ddf6ea1e..b3f9839fde 100644
--- a/frontend/rust-lib/flowy-text-block/src/lib.rs
+++ b/frontend/rust-lib/flowy-text-block/src/lib.rs
@@ -18,10 +18,10 @@ use crate::errors::FlowyError;
use flowy_sync::entities::text_block::{CreateTextBlockParams, DocumentPB, ResetTextBlockParams, TextBlockIdPB};
use lib_infra::future::FutureResult;
-pub trait BlockCloudService: Send + Sync {
- fn create_block(&self, token: &str, params: CreateTextBlockParams) -> FutureResult<(), FlowyError>;
+pub trait TextEditorCloudService: Send + Sync {
+ fn create_text_block(&self, token: &str, params: CreateTextBlockParams) -> FutureResult<(), FlowyError>;
- fn read_block(&self, token: &str, params: TextBlockIdPB) -> FutureResult, FlowyError>;
+ fn read_text_block(&self, token: &str, params: TextBlockIdPB) -> FutureResult , FlowyError>;
- fn update_block(&self, token: &str, params: ResetTextBlockParams) -> FutureResult<(), FlowyError>;
+ fn update_text_block(&self, token: &str, params: ResetTextBlockParams) -> FutureResult<(), FlowyError>;
}
diff --git a/frontend/rust-lib/flowy-text-block/src/manager.rs b/frontend/rust-lib/flowy-text-block/src/manager.rs
index 57e875668e..2e10873523 100644
--- a/frontend/rust-lib/flowy-text-block/src/manager.rs
+++ b/frontend/rust-lib/flowy-text-block/src/manager.rs
@@ -1,5 +1,6 @@
+use crate::entities::EditParams;
use crate::queue::TextBlockRevisionCompactor;
-use crate::{editor::TextBlockEditor, errors::FlowyError, BlockCloudService};
+use crate::{editor::TextBlockEditor, errors::FlowyError, TextEditorCloudService};
use bytes::Bytes;
use dashmap::DashMap;
use flowy_database::ConnectionPool;
@@ -16,30 +17,30 @@ use flowy_sync::entities::{
use lib_infra::future::FutureResult;
use std::{convert::TryInto, sync::Arc};
-pub trait TextBlockUser: Send + Sync {
+pub trait TextEditorUser: Send + Sync {
fn user_dir(&self) -> Result;
fn user_id(&self) -> Result;
fn token(&self) -> Result;
fn db_pool(&self) -> Result, FlowyError>;
}
-pub struct TextBlockManager {
- cloud_service: Arc,
+pub struct TextEditorManager {
+ cloud_service: Arc,
rev_web_socket: Arc,
- editor_map: Arc,
- user: Arc,
+ editor_map: Arc,
+ user: Arc,
}
-impl TextBlockManager {
+impl TextEditorManager {
pub fn new(
- cloud_service: Arc,
- text_block_user: Arc,
+ cloud_service: Arc,
+ text_block_user: Arc,
rev_web_socket: Arc,
) -> Self {
Self {
cloud_service,
rev_web_socket,
- editor_map: Arc::new(TextBlockEditorMap::new()),
+ editor_map: Arc::new(TextEditorMap::new()),
user: text_block_user,
}
}
@@ -50,45 +51,47 @@ impl TextBlockManager {
Ok(())
}
- #[tracing::instrument(level = "trace", skip(self, block_id), fields(block_id), err)]
- pub async fn open_block>(&self, block_id: T) -> Result, FlowyError> {
- let block_id = block_id.as_ref();
- tracing::Span::current().record("block_id", &block_id);
- self.get_block_editor(block_id).await
+ #[tracing::instrument(level = "trace", skip(self, editor_id), fields(editor_id), err)]
+ pub async fn open_text_editor>(&self, editor_id: T) -> Result, FlowyError> {
+ let editor_id = editor_id.as_ref();
+ tracing::Span::current().record("editor_id", &editor_id);
+ self.get_text_editor(editor_id).await
}
- #[tracing::instrument(level = "trace", skip(self, block_id), fields(block_id), err)]
- pub fn close_block>(&self, block_id: T) -> Result<(), FlowyError> {
- let block_id = block_id.as_ref();
- tracing::Span::current().record("block_id", &block_id);
- self.editor_map.remove(block_id);
+ #[tracing::instrument(level = "trace", skip(self, editor_id), fields(editor_id), err)]
+ pub fn close_text_editor>(&self, editor_id: T) -> Result<(), FlowyError> {
+ let editor_id = editor_id.as_ref();
+ tracing::Span::current().record("editor_id", &editor_id);
+ self.editor_map.remove(editor_id);
Ok(())
}
- #[tracing::instrument(level = "debug", skip(self, doc_id), fields(doc_id), err)]
- pub fn delete_block>(&self, doc_id: T) -> Result<(), FlowyError> {
- let doc_id = doc_id.as_ref();
- tracing::Span::current().record("doc_id", &doc_id);
- self.editor_map.remove(doc_id);
- Ok(())
- }
-
- #[tracing::instrument(level = "debug", skip(self, delta), fields(doc_id = %delta.block_id), err)]
+ #[tracing::instrument(level = "debug", skip(self, delta), err)]
pub async fn receive_local_delta(&self, delta: TextBlockDeltaPB) -> Result {
- let editor = self.get_block_editor(&delta.block_id).await?;
+ let editor = self.get_text_editor(&delta.text_block_id).await?;
let _ = editor.compose_local_delta(Bytes::from(delta.delta_str)).await?;
- let document_json = editor.delta_str().await?;
+ let delta_str = editor.delta_str().await?;
Ok(TextBlockDeltaPB {
- block_id: delta.block_id.clone(),
- delta_str: document_json,
+ text_block_id: delta.text_block_id.clone(),
+ delta_str,
})
}
- pub async fn create_block>(&self, doc_id: T, revisions: RepeatedRevision) -> FlowyResult<()> {
- let doc_id = doc_id.as_ref().to_owned();
+ pub async fn apply_edit(&self, params: EditParams) -> FlowyResult<()> {
+ let editor = self.get_text_editor(¶ms.text_block_id).await?;
+ let _ = editor.compose_local_delta(Bytes::from(params.delta)).await?;
+ Ok(())
+ }
+
+ pub async fn create_text_block>(
+ &self,
+ text_block_id: T,
+ revisions: RepeatedRevision,
+ ) -> FlowyResult<()> {
+ let doc_id = text_block_id.as_ref().to_owned();
let db_pool = self.user.db_pool()?;
// Maybe we could save the block to disk without creating the RevisionManager
- let rev_manager = self.make_rev_manager(&doc_id, db_pool)?;
+ let rev_manager = self.make_text_block_rev_manager(&doc_id, db_pool)?;
let _ = rev_manager.reset_object(revisions).await?;
Ok(())
}
@@ -110,26 +113,26 @@ impl TextBlockManager {
}
}
-impl TextBlockManager {
- async fn get_block_editor(&self, block_id: &str) -> FlowyResult> {
+impl TextEditorManager {
+ async fn get_text_editor(&self, block_id: &str) -> FlowyResult> {
match self.editor_map.get(block_id) {
None => {
let db_pool = self.user.db_pool()?;
- self.make_text_block_editor(block_id, db_pool).await
+ self.make_text_editor(block_id, db_pool).await
}
Some(editor) => Ok(editor),
}
}
#[tracing::instrument(level = "trace", skip(self, pool), err)]
- async fn make_text_block_editor(
+ async fn make_text_editor(
&self,
block_id: &str,
pool: Arc,
) -> Result, FlowyError> {
let user = self.user.clone();
let token = self.user.token()?;
- let rev_manager = self.make_rev_manager(block_id, pool.clone())?;
+ let rev_manager = self.make_text_block_rev_manager(block_id, pool.clone())?;
let cloud_service = Arc::new(TextBlockRevisionCloudService {
token,
server: self.cloud_service.clone(),
@@ -140,7 +143,11 @@ impl TextBlockManager {
Ok(doc_editor)
}
- fn make_rev_manager(&self, doc_id: &str, pool: Arc) -> Result {
+ fn make_text_block_rev_manager(
+ &self,
+ doc_id: &str,
+ pool: Arc,
+ ) -> Result {
let user_id = self.user.user_id()?;
let disk_cache = SQLiteTextBlockRevisionPersistence::new(&user_id, pool.clone());
let rev_persistence = RevisionPersistence::new(&user_id, doc_id, disk_cache);
@@ -161,7 +168,7 @@ impl TextBlockManager {
struct TextBlockRevisionCloudService {
token: String,
- server: Arc,
+ server: Arc,
}
impl RevisionCloudService for TextBlockRevisionCloudService {
@@ -173,7 +180,7 @@ impl RevisionCloudService for TextBlockRevisionCloudService {
let user_id = user_id.to_string();
FutureResult::new(async move {
- match server.read_block(&token, params).await? {
+ match server.read_text_block(&token, params).await? {
None => Err(FlowyError::record_not_found().context("Remote doesn't have this document")),
Some(doc) => {
let delta_data = Bytes::from(doc.text.clone());
@@ -193,36 +200,36 @@ impl RevisionCloudService for TextBlockRevisionCloudService {
}
}
-pub struct TextBlockEditorMap {
+pub struct TextEditorMap {
inner: DashMap>,
}
-impl TextBlockEditorMap {
+impl TextEditorMap {
fn new() -> Self {
Self { inner: DashMap::new() }
}
- pub(crate) fn insert(&self, block_id: &str, doc: &Arc) {
- if self.inner.contains_key(block_id) {
- log::warn!("Doc:{} already exists in cache", block_id);
+ pub(crate) fn insert(&self, editor_id: &str, doc: &Arc) {
+ if self.inner.contains_key(editor_id) {
+ log::warn!("Doc:{} already exists in cache", editor_id);
}
- self.inner.insert(block_id.to_string(), doc.clone());
+ self.inner.insert(editor_id.to_string(), doc.clone());
}
- pub(crate) fn get(&self, block_id: &str) -> Option> {
- Some(self.inner.get(block_id)?.clone())
+ pub(crate) fn get(&self, editor_id: &str) -> Option> {
+ Some(self.inner.get(editor_id)?.clone())
}
- pub(crate) fn remove(&self, block_id: &str) {
- if let Some(editor) = self.get(block_id) {
+ pub(crate) fn remove(&self, editor_id: &str) {
+ if let Some(editor) = self.get(editor_id) {
editor.stop()
}
- self.inner.remove(block_id);
+ self.inner.remove(editor_id);
}
}
#[tracing::instrument(level = "trace", skip(web_socket, handlers))]
-fn listen_ws_state_changed(web_socket: Arc, handlers: Arc) {
+fn listen_ws_state_changed(web_socket: Arc, handlers: Arc) {
tokio::spawn(async move {
let mut notify = web_socket.subscribe_state_changed().await;
while let Ok(state) = notify.recv().await {
diff --git a/frontend/rust-lib/flowy-text-block/src/queue.rs b/frontend/rust-lib/flowy-text-block/src/queue.rs
index d81461d25e..510205c1ba 100644
--- a/frontend/rust-lib/flowy-text-block/src/queue.rs
+++ b/frontend/rust-lib/flowy-text-block/src/queue.rs
@@ -1,5 +1,5 @@
use crate::web_socket::EditorCommandReceiver;
-use crate::TextBlockUser;
+use crate::TextEditorUser;
use async_stream::stream;
use bytes::Bytes;
use flowy_error::{FlowyError, FlowyResult};
@@ -23,14 +23,14 @@ use tokio::sync::{oneshot, RwLock};
// serial.
pub(crate) struct EditBlockQueue {
document: Arc>,
- user: Arc,
+ user: Arc,
rev_manager: Arc,
receiver: Option,
}
impl EditBlockQueue {
pub(crate) fn new(
- user: Arc,
+ user: Arc,
rev_manager: Arc,
delta: TextDelta,
receiver: EditorCommandReceiver,
diff --git a/frontend/rust-lib/flowy-text-block/tests/document/script.rs b/frontend/rust-lib/flowy-text-block/tests/document/script.rs
index 254b94958e..ae3209020a 100644
--- a/frontend/rust-lib/flowy-text-block/tests/document/script.rs
+++ b/frontend/rust-lib/flowy-text-block/tests/document/script.rs
@@ -27,7 +27,7 @@ impl TextBlockEditorTest {
let sdk = FlowySDKTest::default();
let _ = sdk.init_user().await;
let test = ViewTest::new_text_block_view(&sdk).await;
- let editor = sdk.text_block_manager.open_block(&test.view.id).await.unwrap();
+ let editor = sdk.text_block_manager.open_text_editor(&test.view.id).await.unwrap();
Self { sdk, editor }
}
diff --git a/shared-lib/flowy-sync/src/entities/text_block.rs b/shared-lib/flowy-sync/src/entities/text_block.rs
index d65ec683ee..cca7222bd0 100644
--- a/shared-lib/flowy-sync/src/entities/text_block.rs
+++ b/shared-lib/flowy-sync/src/entities/text_block.rs
@@ -69,7 +69,7 @@ pub struct ResetTextBlockParams {
#[derive(ProtoBuf, Default, Debug, Clone)]
pub struct TextBlockDeltaPB {
#[pb(index = 1)]
- pub block_id: String,
+ pub text_block_id: String,
#[pb(index = 2)]
pub delta_str: String,
diff --git a/shared-lib/lib-ot/Cargo.toml b/shared-lib/lib-ot/Cargo.toml
index 3d0bd5aadd..81b75507a0 100644
--- a/shared-lib/lib-ot/Cargo.toml
+++ b/shared-lib/lib-ot/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2018"
[dependencies]
bytecount = "0.6.0"
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0", features = ["derive", "rc"] }
#protobuf = {version = "2.18.0"}
#flowy-derive = { path = "../flowy-derive" }
tokio = { version = "1", features = ["sync"] }
diff --git a/shared-lib/lib-ot/src/core/document/operation.rs b/shared-lib/lib-ot/src/core/document/operation.rs
deleted file mode 100644
index f9e772e5d0..0000000000
--- a/shared-lib/lib-ot/src/core/document/operation.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-use crate::core::attributes::Attributes;
-use crate::core::document::path::Path;
-use crate::core::{NodeBodyChangeset, NodeData};
-use crate::errors::OTError;
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Serialize, Deserialize)]
-#[serde(tag = "op")]
-pub enum NodeOperation {
- #[serde(rename = "insert")]
- Insert { path: Path, nodes: Vec },
-
- #[serde(rename = "update")]
- UpdateAttributes {
- path: Path,
- attributes: Attributes,
- #[serde(rename = "oldAttributes")]
- old_attributes: Attributes,
- },
-
- #[serde(rename = "update-body")]
- // #[serde(serialize_with = "serialize_edit_body")]
- // #[serde(deserialize_with = "deserialize_edit_body")]
- UpdateBody { path: Path, changeset: NodeBodyChangeset },
-
- #[serde(rename = "delete")]
- Delete { path: Path, nodes: Vec },
-}
-
-impl NodeOperation {
- pub fn path(&self) -> &Path {
- match self {
- NodeOperation::Insert { path, .. } => path,
- NodeOperation::UpdateAttributes { path, .. } => path,
- NodeOperation::Delete { path, .. } => path,
- NodeOperation::UpdateBody { path, .. } => path,
- }
- }
- pub fn invert(&self) -> NodeOperation {
- match self {
- NodeOperation::Insert { path, nodes } => NodeOperation::Delete {
- path: path.clone(),
- nodes: nodes.clone(),
- },
- NodeOperation::UpdateAttributes {
- path,
- attributes,
- old_attributes,
- } => NodeOperation::UpdateAttributes {
- path: path.clone(),
- attributes: old_attributes.clone(),
- old_attributes: attributes.clone(),
- },
- NodeOperation::Delete { path, nodes } => NodeOperation::Insert {
- path: path.clone(),
- nodes: nodes.clone(),
- },
- NodeOperation::UpdateBody { path, changeset: body } => NodeOperation::UpdateBody {
- path: path.clone(),
- changeset: body.inverted(),
- },
- }
- }
- pub fn clone_with_new_path(&self, path: Path) -> NodeOperation {
- match self {
- NodeOperation::Insert { nodes, .. } => NodeOperation::Insert {
- path,
- nodes: nodes.clone(),
- },
- NodeOperation::UpdateAttributes {
- attributes,
- old_attributes,
- ..
- } => NodeOperation::UpdateAttributes {
- path,
- attributes: attributes.clone(),
- old_attributes: old_attributes.clone(),
- },
- NodeOperation::Delete { nodes, .. } => NodeOperation::Delete {
- path,
- nodes: nodes.clone(),
- },
- NodeOperation::UpdateBody { path, changeset } => NodeOperation::UpdateBody {
- path: path.clone(),
- changeset: changeset.clone(),
- },
- }
- }
- pub fn transform(a: &NodeOperation, b: &NodeOperation) -> NodeOperation {
- match a {
- NodeOperation::Insert { path: a_path, nodes } => {
- let new_path = Path::transform(a_path, b.path(), nodes.len() as i64);
- b.clone_with_new_path(new_path)
- }
- NodeOperation::Delete { path: a_path, nodes } => {
- let new_path = Path::transform(a_path, b.path(), nodes.len() as i64);
- b.clone_with_new_path(new_path)
- }
- _ => b.clone(),
- }
- }
-}
-
-#[derive(Serialize, Deserialize, Default)]
-pub struct NodeOperationList {
- operations: Vec,
-}
-
-impl NodeOperationList {
- pub fn into_inner(self) -> Vec {
- self.operations
- }
-}
-
-impl std::ops::Deref for NodeOperationList {
- type Target = Vec;
-
- fn deref(&self) -> &Self::Target {
- &self.operations
- }
-}
-
-impl std::ops::DerefMut for NodeOperationList {
- fn deref_mut(&mut self) -> &mut Self::Target {
- &mut self.operations
- }
-}
-
-impl NodeOperationList {
- pub fn new(operations: Vec) -> Self {
- Self { operations }
- }
-
- pub fn from_bytes(bytes: Vec) -> Result {
- let operation_list = serde_json::from_slice(&bytes).map_err(|err| OTError::serde().context(err))?;
- Ok(operation_list)
- }
-
- pub fn to_bytes(&self) -> Result, OTError> {
- let bytes = serde_json::to_vec(self).map_err(|err| OTError::serde().context(err))?;
- Ok(bytes)
- }
-}
diff --git a/shared-lib/lib-ot/src/core/document/path.rs b/shared-lib/lib-ot/src/core/document/path.rs
deleted file mode 100644
index 245abde36c..0000000000
--- a/shared-lib/lib-ot/src/core/document/path.rs
+++ /dev/null
@@ -1,127 +0,0 @@
-use serde::{Deserialize, Serialize};
-
-#[derive(Clone, Serialize, Deserialize, Eq, PartialEq, Debug)]
-pub struct Path(pub Vec);
-
-impl std::ops::Deref for Path {
- type Target = Vec;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl std::convert::From for Path {
- fn from(val: usize) -> Self {
- Path(vec![val])
- }
-}
-
-impl std::convert::From<&usize> for Path {
- fn from(val: &usize) -> Self {
- Path(vec![*val])
- }
-}
-
-impl std::convert::From<&Path> for Path {
- fn from(path: &Path) -> Self {
- path.clone()
- }
-}
-
-impl From> for Path {
- fn from(v: Vec) -> Self {
- Path(v)
- }
-}
-
-impl From<&Vec> for Path {
- fn from(values: &Vec) -> Self {
- Path(values.clone())
- }
-}
-
-impl From<&[usize]> for Path {
- fn from(values: &[usize]) -> Self {
- Path(values.to_vec())
- }
-}
-
-impl Path {
- // delta is default to be 1
- pub fn transform(pre_insert_path: &Path, b: &Path, offset: i64) -> Path {
- if pre_insert_path.len() > b.len() {
- return b.clone();
- }
- if pre_insert_path.is_empty() || b.is_empty() {
- return b.clone();
- }
- // check the prefix
- for i in 0..(pre_insert_path.len() - 1) {
- if pre_insert_path.0[i] != b.0[i] {
- return b.clone();
- }
- }
- let mut prefix: Vec = pre_insert_path.0[0..(pre_insert_path.len() - 1)].into();
- let mut suffix: Vec = b.0[pre_insert_path.0.len()..].into();
- let prev_insert_last: usize = *pre_insert_path.0.last().unwrap();
- let b_at_index = b.0[pre_insert_path.0.len() - 1];
- if prev_insert_last <= b_at_index {
- prefix.push(((b_at_index as i64) + offset) as usize);
- } else {
- prefix.push(b_at_index);
- }
- prefix.append(&mut suffix);
-
- Path(prefix)
- }
-}
-
-#[cfg(test)]
-mod tests {
- use crate::core::Path;
- #[test]
- fn path_transform_test_1() {
- assert_eq!(
- { Path::transform(&Path(vec![0, 1]), &Path(vec![0, 1]), 1) }.0,
- vec![0, 2]
- );
-
- assert_eq!(
- { Path::transform(&Path(vec![0, 1]), &Path(vec![0, 1]), 5) }.0,
- vec![0, 6]
- );
- }
-
- #[test]
- fn path_transform_test_2() {
- assert_eq!(
- { Path::transform(&Path(vec![0, 1]), &Path(vec![0, 2]), 1) }.0,
- vec![0, 3]
- );
- }
-
- #[test]
- fn path_transform_test_3() {
- assert_eq!(
- { Path::transform(&Path(vec![0, 1]), &Path(vec![0, 2, 7, 8, 9]), 1) }.0,
- vec![0, 3, 7, 8, 9]
- );
- }
-
- #[test]
- fn path_transform_no_changed_test() {
- assert_eq!(
- { Path::transform(&Path(vec![0, 1, 2]), &Path(vec![0, 0, 7, 8, 9]), 1) }.0,
- vec![0, 0, 7, 8, 9]
- );
- assert_eq!(
- { Path::transform(&Path(vec![0, 1, 2]), &Path(vec![0, 1]), 1) }.0,
- vec![0, 1]
- );
- assert_eq!(
- { Path::transform(&Path(vec![1, 1]), &Path(vec![1, 0]), 1) }.0,
- vec![1, 0]
- );
- }
-}
diff --git a/shared-lib/lib-ot/src/core/mod.rs b/shared-lib/lib-ot/src/core/mod.rs
index e8253e6ebc..7d038c8b5d 100644
--- a/shared-lib/lib-ot/src/core/mod.rs
+++ b/shared-lib/lib-ot/src/core/mod.rs
@@ -1,12 +1,12 @@
pub mod attributes;
mod delta;
-mod document;
mod interval;
+mod node_tree;
mod ot_str;
pub use attributes::*;
pub use delta::operation::*;
pub use delta::*;
-pub use document::*;
pub use interval::*;
+pub use node_tree::*;
pub use ot_str::*;
diff --git a/shared-lib/lib-ot/src/core/document/mod.rs b/shared-lib/lib-ot/src/core/node_tree/mod.rs
similarity index 84%
rename from shared-lib/lib-ot/src/core/document/mod.rs
rename to shared-lib/lib-ot/src/core/node_tree/mod.rs
index b60d8c0251..1f7205201d 100644
--- a/shared-lib/lib-ot/src/core/document/mod.rs
+++ b/shared-lib/lib-ot/src/core/node_tree/mod.rs
@@ -2,14 +2,14 @@
mod node;
mod node_serde;
-mod node_tree;
mod operation;
mod operation_serde;
mod path;
mod transaction;
+mod tree;
pub use node::*;
-pub use node_tree::*;
pub use operation::*;
pub use path::*;
pub use transaction::*;
+pub use tree::*;
diff --git a/shared-lib/lib-ot/src/core/document/node.rs b/shared-lib/lib-ot/src/core/node_tree/node.rs
similarity index 98%
rename from shared-lib/lib-ot/src/core/document/node.rs
rename to shared-lib/lib-ot/src/core/node_tree/node.rs
index 18651a6062..908cfa3f18 100644
--- a/shared-lib/lib-ot/src/core/document/node.rs
+++ b/shared-lib/lib-ot/src/core/node_tree/node.rs
@@ -6,7 +6,7 @@ use crate::errors::OTError;
use crate::text_delta::TextDelta;
use serde::{Deserialize, Serialize};
-#[derive(Default, Clone, Serialize, Deserialize, Eq, PartialEq)]
+#[derive(Default, Debug, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct NodeData {
#[serde(rename = "type")]
pub node_type: String,
diff --git a/shared-lib/lib-ot/src/core/document/node_serde.rs b/shared-lib/lib-ot/src/core/node_tree/node_serde.rs
similarity index 100%
rename from shared-lib/lib-ot/src/core/document/node_serde.rs
rename to shared-lib/lib-ot/src/core/node_tree/node_serde.rs
diff --git a/shared-lib/lib-ot/src/core/node_tree/operation.rs b/shared-lib/lib-ot/src/core/node_tree/operation.rs
new file mode 100644
index 0000000000..cdd2b99c7b
--- /dev/null
+++ b/shared-lib/lib-ot/src/core/node_tree/operation.rs
@@ -0,0 +1,174 @@
+use crate::core::attributes::Attributes;
+use crate::core::{NodeBodyChangeset, NodeData, Path};
+use crate::errors::OTError;
+use serde::{Deserialize, Serialize};
+use std::rc::Rc;
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+#[serde(tag = "op")]
+pub enum NodeOperation {
+ #[serde(rename = "insert")]
+ Insert { path: Path, nodes: Vec