fix: create document (#2701)

* fix: create a new document

* fix: the banner don't show after deleteing the page

* fix: inserting a divider through the slash menu the cursor should stay active in the next line

* fix: the overlay doesn't dismiss after selecting a page

* fix: typo

* fix: delete the page in document if it has been deleted

* chore: l10n

* chore: rename events

* ci: rm install_diesel in ci

* fix: cover color not working

* ci: fix tauri build

---------

Co-authored-by: nathan <nathan@appflowy.io>
This commit is contained in:
Lucas.Xu
2023-06-05 13:10:14 +08:00
committed by GitHub
parent 2bd90ba7ab
commit 80a273edae
37 changed files with 352 additions and 165 deletions

View File

@ -231,7 +231,7 @@
"addFilter": "Add Filter", "addFilter": "Add Filter",
"deleteFilter": "Delete filter", "deleteFilter": "Delete filter",
"filterBy": "Filter by...", "filterBy": "Filter by...",
"typeAValue": "Type a value...", "typeAValue": "Type a value...",
"layout": "Layout", "layout": "Layout",
"databaseLayout": "Layout" "databaseLayout": "Layout"
}, },
@ -414,7 +414,13 @@
}, },
"optionAction": { "optionAction": {
"click": "Click", "click": "Click",
"toOpenMenu": " to open menu" "toOpenMenu": " to open menu",
"delete": "Delete",
"duplicate": "Duplicate",
"turnInto": "Turn into",
"moveUp": "Move up",
"moveDown": "Move down",
"color": "Color"
} }
} }
}, },

View File

@ -0,0 +1,90 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'util/util.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
group('document', () {
const location = 'appflowy';
setUp(() async {
await TestFolder.cleanTestLocation(location);
await TestFolder.setTestLocation(location);
});
tearDown(() async {
await TestFolder.cleanTestLocation(location);
});
tearDownAll(() async {
await TestFolder.cleanTestLocation(null);
});
testWidgets('create a new document when launching app in first time',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// create a new document
await tester.tapAddButton();
await tester.tapCreateDocumentButton();
await tester.pumpAndSettle();
// expect to see a new document
tester.expectToSeePageName(
LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
);
// and with one paragraph block
expect(find.byType(TextBlockComponentWidget), findsOneWidget);
});
testWidgets('delete the readme page and restore it', (tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// delete the readme page
await tester.hoverOnPageName(readme);
await tester.tapDeletePageButton();
// the banner should show up and the readme page should be gone
tester.expectToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
// restore the readme page
await tester.tapRestoreButton();
// the banner should be gone and the readme page should be back
tester.expectNotToSeeDocumentBanner();
tester.expectToSeePageName(readme);
});
testWidgets('delete the readme page and delete it permanently',
(tester) async {
await tester.initializeAppFlowy();
await tester.tapGoButton();
// delete the readme page
await tester.hoverOnPageName(readme);
await tester.tapDeletePageButton();
// the banner should show up and the readme page should be gone
tester.expectToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
// delete the page permanently
await tester.tapDeletePermanentlyButton();
// the banner should be gone and the readme page should be gone
tester.expectNotToSeeDocumentBanner();
tester.expectNotToSeePageName(readme);
});
});
}

View File

@ -4,6 +4,7 @@ import 'board_test.dart' as board_test;
import 'switch_folder_test.dart' as switch_folder_test; import 'switch_folder_test.dart' as switch_folder_test;
import 'empty_document_test.dart' as empty_document_test; import 'empty_document_test.dart' as empty_document_test;
import 'open_ai_smart_menu_test.dart' as smart_menu_test; import 'open_ai_smart_menu_test.dart' as smart_menu_test;
import 'document_test.dart' as document_test;
/// The main task runner for all integration tests in AppFlowy. /// The main task runner for all integration tests in AppFlowy.
/// ///
@ -18,4 +19,5 @@ void main() {
board_test.main(); board_test.main();
empty_document_test.main(); empty_document_test.main();
smart_menu_test.main(); smart_menu_test.main();
document_test.main();
} }

View File

@ -51,7 +51,7 @@ void main() {
// Click create button again // Click create button again
await tester.tapCreateButton(); await tester.tapCreateButton();
await tester.expectToSeeWelcomePage(); tester.expectToSeeWelcomePage();
await TestFolder.cleanTestLocation(folderName); await TestFolder.cleanTestLocation(folderName);
}); });
@ -69,7 +69,7 @@ void main() {
await tester.tapOpenFolderButton(); await tester.tapOpenFolderButton();
await tester.wait(1000); await tester.wait(1000);
await tester.expectToSeeWelcomePage(); tester.expectToSeeWelcomePage();
await TestFolder.cleanTestLocation(folderName); await TestFolder.cleanTestLocation(folderName);
}); });
@ -84,7 +84,7 @@ void main() {
await tester.initializeAppFlowy(); await tester.initializeAppFlowy();
await tester.tapGoButton(); await tester.tapGoButton();
await tester.expectToSeeWelcomePage(); tester.expectToSeeWelcomePage();
// switch to user B // switch to user B
{ {
@ -99,7 +99,7 @@ void main() {
await mockGetDirectoryPath(userB); await mockGetDirectoryPath(userB);
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.expectToSeeWelcomePage(); tester.expectToSeeWelcomePage();
} }
// switch to the userA // switch to the userA
@ -116,7 +116,7 @@ void main() {
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.expectToSeeWelcomePage(); tester.expectToSeeWelcomePage();
expect(find.textContaining(userA), findsOneWidget); expect(find.textContaining(userA), findsOneWidget);
} }
@ -131,7 +131,7 @@ void main() {
await tester.tapCustomLocationButton(); await tester.tapCustomLocationButton();
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.expectToSeeWelcomePage(); tester.expectToSeeWelcomePage();
expect(find.textContaining(userB), findsOneWidget); expect(find.textContaining(userB), findsOneWidget);
} }
@ -145,7 +145,7 @@ void main() {
await tester.tapGoButton(); await tester.tapGoButton();
// home and readme document // home and readme document
await tester.expectToSeeWelcomePage(); tester.expectToSeeWelcomePage();
// open settings and restore the location // open settings and restore the location
await tester.openSettings(); await tester.openSettings();

View File

@ -91,7 +91,7 @@ extension AppFlowyTestBase on WidgetTester {
String tr, { String tr, {
int milliseconds = 500, int milliseconds = 500,
}) async { }) async {
final button = find.text(tr); final button = find.text(tr, findRichText: true);
await tapButton( await tapButton(
button, button,
milliseconds: milliseconds, milliseconds: milliseconds,

View File

@ -1,23 +1,100 @@
import 'dart:ui';
import 'package:appflowy/generated/locale_keys.g.dart'; import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/presentation/banner.dart';
import 'package:appflowy/user/presentation/skip_log_in_screen.dart';
import 'package:appflowy/workspace/presentation/home/home_stack.dart'; import 'package:appflowy/workspace/presentation/home/home_stack.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/add_button.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/section/item.dart';
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'base.dart'; import 'base.dart';
const String readme = 'Read me';
extension AppFlowyLaunch on WidgetTester { extension AppFlowyLaunch on WidgetTester {
Future<void> tapGoButton() async { Future<void> tapGoButton() async {
await tapButtonWithName(LocaleKeys.letsGoButtonText.tr()); final goButton = find.byType(GoButton);
return; await tapButton(goButton);
} }
Future<void> tapCreateButton() async { Future<void> tapCreateButton() async {
await tapButtonWithName(LocaleKeys.settings_files_create.tr()); await tapButtonWithName(LocaleKeys.settings_files_create.tr());
return;
} }
Future<void> expectToSeeWelcomePage() async { void expectToSeeWelcomePage() {
expect(find.byType(HomeStack), findsOneWidget); expect(find.byType(HomeStack), findsOneWidget);
expect(find.textContaining('Read me'), findsNWidgets(2)); expect(find.textContaining('Read me'), findsNWidgets(2));
} }
Future<void> tapAddButton() async {
final addButton = find.byType(AddButton);
await tapButton(addButton);
}
Future<void> tapCreateDocumentButton() async {
await tapButtonWithName(LocaleKeys.document_menuName.tr());
}
Finder findPageName(String name) {
return find.byWidgetPredicate(
(widget) => widget is ViewSectionItem && widget.view.name == name,
);
}
void expectToSeePageName(String name) {
final pageName = findPageName(name);
expect(pageName, findsOneWidget);
}
void expectNotToSeePageName(String name) {
final pageName = findPageName(name);
expect(pageName, findsNothing);
}
Future<void> hoverOnPageName(String name) async {
final pageName = find.byWidgetPredicate(
(widget) => widget is ViewSectionItem && widget.view.name == name,
);
final gesture = await createGesture(kind: PointerDeviceKind.mouse);
await gesture.addPointer(location: Offset.zero);
addTearDown(gesture.removePointer);
await pump();
await gesture.moveTo(getCenter(pageName));
await pumpAndSettle();
}
Future<void> tapPageOptionButton() async {
final optionButton = find.byType(ViewDisclosureButton);
await tapButton(optionButton);
}
Future<void> tapDeletePageButton() async {
await tapPageOptionButton();
await tapButtonWithName(ViewDisclosureAction.delete.name);
}
void expectToSeeDocumentBanner() {
expect(find.byType(DocumentBanner), findsOneWidget);
}
void expectNotToSeeDocumentBanner() {
expect(find.byType(DocumentBanner), findsNothing);
}
Future<void> tapRestoreButton() async {
final restoreButton = find.textContaining(
LocaleKeys.deletePagePrompt_restore.tr(),
);
await tapButton(restoreButton);
}
Future<void> tapDeletePermanentlyButton() async {
final restoreButton = find.textContaining(
LocaleKeys.deletePagePrompt_deletePermanent.tr(),
);
await tapButton(restoreButton);
}
} }

View File

@ -66,7 +66,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
await _subscribe(state); await _subscribe(state);
emit(state); emit(state);
}, },
deleted: (Deleted value) async { moveToTrash: (MoveToTrash value) async {
emit(state.copyWith(isDeleted: true)); emit(state.copyWith(isDeleted: true));
}, },
restore: (Restore value) async { restore: (Restore value) async {
@ -74,11 +74,13 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
}, },
deletePermanently: (DeletePermanently value) async { deletePermanently: (DeletePermanently value) async {
final result = await _trashService.deleteViews([view.id]); final result = await _trashService.deleteViews([view.id]);
emit(state.copyWith(forceClose: result.swap().isLeft())); final forceClose = result.fold((l) => true, (r) => false);
emit(state.copyWith(forceClose: forceClose));
}, },
restorePage: (RestorePage value) async { restorePage: (RestorePage value) async {
final result = await _trashService.putback(view.id); final result = await _trashService.putback(view.id);
emit(state.copyWith(isDeleted: result.swap().isRight())); final isDeleted = result.fold((l) => false, (r) => true);
emit(state.copyWith(isDeleted: isDeleted));
}, },
); );
} }
@ -98,8 +100,12 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
/// subscribe to the view(document page) change /// subscribe to the view(document page) change
void _onViewChanged() { void _onViewChanged() {
_viewListener.start( _viewListener.start(
onViewDeleted: (r) => onViewMoveToTrash: (r) {
r.swap().map((r) => add(const DocumentEvent.deleted())), r.swap().map((r) => add(const DocumentEvent.moveToTrash()));
},
onViewDeleted: (r) {
r.swap().map((r) => add(const DocumentEvent.moveToTrash()));
},
onViewRestored: (r) => onViewRestored: (r) =>
r.swap().map((r) => add(const DocumentEvent.restore())), r.swap().map((r) => add(const DocumentEvent.restore())),
); );
@ -161,7 +167,7 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
@freezed @freezed
class DocumentEvent with _$DocumentEvent { class DocumentEvent with _$DocumentEvent {
const factory DocumentEvent.initial() = Initial; const factory DocumentEvent.initial() = Initial;
const factory DocumentEvent.deleted() = Deleted; const factory DocumentEvent.moveToTrash() = MoveToTrash;
const factory DocumentEvent.restore() = Restore; const factory DocumentEvent.restore() = Restore;
const factory DocumentEvent.restorePage() = RestorePage; const factory DocumentEvent.restorePage() = RestorePage;
const factory DocumentEvent.deletePermanently() = DeletePermanently; const factory DocumentEvent.deletePermanently() = DeletePermanently;

View File

@ -88,35 +88,22 @@ extension DocumentDataPBFromTo on DocumentDataPB {
} }
} }
class _BackendKeys {
const _BackendKeys._();
static const String text = 'text';
}
extension BlockToNode on BlockPB { extension BlockToNode on BlockPB {
Node toNode({ Node toNode({
Iterable<Node>? children, Iterable<Node>? children,
}) { }) {
return Node( return Node(
id: id, id: id,
type: _typeAdapter(ty), type: ty,
attributes: _dataAdapter(ty, data), attributes: _dataAdapter(ty, data),
children: children ?? [], children: children ?? [],
); );
} }
String _typeAdapter(String ty) {
final adapter = {
_BackendKeys.text: ParagraphBlockKeys.type,
};
return adapter[ty] ?? ty;
}
Attributes _dataAdapter(String ty, String data) { Attributes _dataAdapter(String ty, String data) {
final map = Attributes.from(jsonDecode(data)); final map = Attributes.from(jsonDecode(data));
final adapter = { final adapter = {
'text': (Attributes map) => map ParagraphBlockKeys.type: (Attributes map) => map
..putIfAbsent( ..putIfAbsent(
'delta', 'delta',
() => Delta().toJson(), () => Delta().toJson(),
@ -134,7 +121,7 @@ extension NodeToBlock on Node {
assert(id.isNotEmpty); assert(id.isNotEmpty);
final block = BlockPB.create() final block = BlockPB.create()
..id = id ..id = id
..ty = _typeAdapter(type) ..ty = type
..data = _dataAdapter(type, attributes); ..data = _dataAdapter(type, attributes);
if (childrenId != null && childrenId.isNotEmpty) { if (childrenId != null && childrenId.isNotEmpty) {
block.childrenId = childrenId; block.childrenId = childrenId;
@ -145,13 +132,6 @@ extension NodeToBlock on Node {
return block; return block;
} }
String _typeAdapter(String type) {
final adapter = {
'paragraph': 'text',
};
return adapter[type] ?? type;
}
String _dataAdapter(String type, Attributes attributes) { String _dataAdapter(String type, Attributes attributes) {
return jsonEncode(attributes); return jsonEncode(attributes);
} }

View File

@ -112,6 +112,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
child: ConstrainedBox( child: ConstrainedBox(
constraints: const BoxConstraints( constraints: const BoxConstraints(
maxWidth: double.infinity, maxWidth: double.infinity,
maxHeight: double.infinity,
), ),
child: FloatingToolbar( child: FloatingToolbar(
style: styleCustomizer.floatingToolbarStyleBuilder(), style: styleCustomizer.floatingToolbarStyleBuilder(),
@ -225,7 +226,7 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage> {
]; ];
final colorAction = [ final colorAction = [
// OptionAction.divider, OptionAction.divider,
OptionAction.color, OptionAction.color,
]; ];

View File

@ -1,9 +1,11 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart'; import 'package:appflowy/plugins/database_view/grid/presentation/widgets/header/type_option/select_option_editor.dart';
import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart'; import 'package:appflowy/plugins/database_view/widgets/row/cells/select_option_cell/extension.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart'; import 'package:appflowy_backend/protobuf/flowy-database2/protobuf.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg; import 'package:appflowy_editor/appflowy_editor.dart' hide FlowySvg;
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:styled_widget/styled_widget.dart'; import 'package:styled_widget/styled_widget.dart';
@ -45,7 +47,7 @@ class ColorOptionAction extends PopoverActionCell {
@override @override
String get name { String get name {
return 'Color'; // todo: l10n return LocaleKeys.toolbar_color.tr();
} }
@override @override
@ -55,7 +57,6 @@ class ColorOptionAction extends PopoverActionCell {
if (selection == null) { if (selection == null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
// TODO: should we support multiple selection?
final node = editorState.getNodeAtPath(selection.start.path); final node = editorState.getNodeAtPath(selection.start.path);
if (node == null) { if (node == null) {
return const SizedBox.shrink(); return const SizedBox.shrink();
@ -70,6 +71,8 @@ class ColorOptionAction extends PopoverActionCell {
return SelectOptionColorList( return SelectOptionColorList(
selectedColor: selectedColor, selectedColor: selectedColor,
onSelectedColor: (color) { onSelectedColor: (color) {
controller.close();
final nodes = editorState.getNodesInSelection(selection); final nodes = editorState.getNodesInSelection(selection);
final transaction = editorState.transaction; final transaction = editorState.transaction;
for (final node in nodes) { for (final node in nodes) {
@ -79,8 +82,6 @@ class ColorOptionAction extends PopoverActionCell {
}); });
} }
editorState.apply(transaction); editorState.apply(transaction);
controller.close();
}, },
); );
}; };
@ -141,22 +142,22 @@ class OptionActionWrapper extends ActionCell {
switch (inner) { switch (inner) {
// TODO: l10n // TODO: l10n
case OptionAction.delete: case OptionAction.delete:
description = 'Delete'; description = LocaleKeys.document_plugins_optionAction_delete.tr();
break; break;
case OptionAction.duplicate: case OptionAction.duplicate:
description = 'Duplicate'; description = LocaleKeys.document_plugins_optionAction_duplicate.tr();
break; break;
case OptionAction.turnInto: case OptionAction.turnInto:
description = 'Turn into'; description = LocaleKeys.document_plugins_optionAction_turnInto.tr();
break; break;
case OptionAction.moveUp: case OptionAction.moveUp:
description = 'Move up'; description = LocaleKeys.document_plugins_optionAction_moveUp.tr();
break; break;
case OptionAction.moveDown: case OptionAction.moveDown:
description = 'Move down'; description = LocaleKeys.document_plugins_optionAction_moveDown.tr();
break; break;
case OptionAction.color: case OptionAction.color:
description = 'Color'; description = LocaleKeys.document_plugins_optionAction_color.tr();
break; break;
case OptionAction.divider: case OptionAction.divider:
throw UnimplementedError(); throw UnimplementedError();

View File

@ -69,6 +69,10 @@ class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
return _build(context, page); return _build(context, page);
} }
if (snapshot.connectionState == ConnectionState.done) { if (snapshot.connectionState == ConnectionState.done) {
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
// just delete the page if it is not found
_deletePage();
});
return const Center( return const Center(
child: FlowyText('Cannot load the page'), child: FlowyText('Cannot load the page'),
); );
@ -174,6 +178,12 @@ class _BuiltInPageWidgetState extends State<BuiltInPageWidget> {
), ),
); );
} }
Future<void> _deletePage() async {
final transaction = widget.editorState.transaction;
transaction.deleteNode(widget.node);
widget.editorState.apply(transaction);
}
} }
enum _ActionType { enum _ActionType {

View File

@ -23,7 +23,8 @@ void showLinkToPageMenu(
final top = alignment == Alignment.bottomLeft ? offset.dy : null; final top = alignment == Alignment.bottomLeft ? offset.dy : null;
final bottom = alignment == Alignment.topLeft ? offset.dy : null; final bottom = alignment == Alignment.topLeft ? offset.dy : null;
final linkToPageMenuEntry = FullScreenOverlayEntry( late OverlayEntry linkToPageMenuEntry;
linkToPageMenuEntry = FullScreenOverlayEntry(
top: top, top: top,
bottom: bottom, bottom: bottom,
left: offset.dx, left: offset.dx,
@ -35,6 +36,7 @@ void showLinkToPageMenu(
hintText: pageType.toHintText(), hintText: pageType.toHintText(),
onSelected: (appPB, viewPB) { onSelected: (appPB, viewPB) {
editorState.insertPage(appPB, viewPB); editorState.insertPage(appPB, viewPB);
linkToPageMenuEntry.remove();
}, },
), ),
), ),

View File

@ -465,17 +465,11 @@ class _CoverColorPickerState extends State<CoverColorPicker> {
}, },
platform: TargetPlatform.windows, platform: TargetPlatform.windows,
), ),
child: ListView.builder( child: SingleChildScrollView(
controller: scrollController, child: _buildColorItems(
shrinkWrap: true, widget.backgroundColorOptions,
itemCount: widget.backgroundColorOptions.length, widget.selectedBackgroundColorHex,
scrollDirection: Axis.horizontal, ),
itemBuilder: (context, index) {
return _buildColorItems(
widget.backgroundColorOptions,
widget.selectedBackgroundColorHex,
);
},
), ),
), ),
); );

View File

@ -303,10 +303,11 @@ class _CoverImageState extends State<_CoverImage> {
CoverSelectionType get selectionType => CoverSelectionType.fromString( CoverSelectionType get selectionType => CoverSelectionType.fromString(
widget.node.attributes[CoverBlockKeys.selectionType], widget.node.attributes[CoverBlockKeys.selectionType],
); );
Color get color => Color( Color get color {
int.tryParse(widget.node.attributes[CoverBlockKeys.selection]) ?? final hex = widget.node.attributes[CoverBlockKeys.selection] as String?;
0xFFFFFFFF, return hex?.toColor() ?? Colors.white;
); }
bool get hasIcon => bool get hasIcon =>
widget.node.attributes[CoverBlockKeys.iconSelection] == null widget.node.attributes[CoverBlockKeys.iconSelection] == null
? false ? false

View File

@ -35,7 +35,7 @@ CharacterShortcutEventHandler _convertMinusesToDividerHandler =
..insertNode(path, dividerNode()) ..insertNode(path, dividerNode())
..insertNode(path, paragraphNode()) ..insertNode(path, paragraphNode())
..deleteNode(node) ..deleteNode(node)
..afterSelection = Selection.collapse(path.next, 0); ..afterSelection = Selection.collapse(path, 0);
editorState.apply(transaction); editorState.apply(transaction);
return true; return true;
}; };
@ -61,7 +61,9 @@ SelectionMenuItem dividerMenuItem = SelectionMenuItem(
} }
final insertedPath = delta.isEmpty ? path : path.next; final insertedPath = delta.isEmpty ? path : path.next;
final transaction = editorState.transaction final transaction = editorState.transaction
..insertNode(insertedPath, dividerNode()); ..insertNode(insertedPath, dividerNode())
..insertNode(insertedPath, paragraphNode())
..afterSelection = Selection.collapse(insertedPath.next, 0);
editorState.apply(transaction); editorState.apply(transaction);
}, },
); );

View File

@ -70,7 +70,7 @@ class UserBackendService {
Future<Either<List<WorkspacePB>, FlowyError>> getWorkspaces() { Future<Either<List<WorkspacePB>, FlowyError>> getWorkspaces() {
final request = WorkspaceIdPB.create(); final request = WorkspaceIdPB.create();
return FolderEventReadWorkspaces(request).send().then((result) { return FolderEventReadAllWorkspaces(request).send().then((result) {
return result.fold( return result.fold(
(workspaces) => left(workspaces.items), (workspaces) => left(workspaces.items),
(error) => right(error), (error) => right(error),

View File

@ -50,7 +50,7 @@ class AuthRouter {
BuildContext context, BuildContext context,
UserProfilePB userProfile, UserProfilePB userProfile,
) async { ) async {
final result = await FolderEventReadCurrentWorkspace().send(); final result = await FolderEventGetCurrentWorkspace().send();
result.fold( result.fold(
(workspaceSettingPB) => pushHomeScreenWithWorkSpace( (workspaceSettingPB) => pushHomeScreenWithWorkSpace(
context, context,
@ -75,7 +75,7 @@ class SplashRoute {
), ),
); );
FolderEventReadCurrentWorkspace().send().then((result) { FolderEventGetCurrentWorkspace().send().then((result) {
result.fold( result.fold(
(workspaceSettingPB) => (workspaceSettingPB) =>
pushHomeScreen(context, userProfile, workspaceSettingPB), pushHomeScreen(context, userProfile, workspaceSettingPB),

View File

@ -126,7 +126,7 @@ class _SkipLogInScreenState extends State<SkipLogInScreen> {
Log.error(error); Log.error(error);
}, },
(user) { (user) {
FolderEventReadCurrentWorkspace().send().then((result) { FolderEventGetCurrentWorkspace().send().then((result) {
_openCurrentWorkspace(context, user, result); _openCurrentWorkspace(context, user, result);
}); });
}, },

View File

@ -70,7 +70,7 @@ class SplashScreen extends StatelessWidget {
Authenticated authenticated, Authenticated authenticated,
) async { ) async {
final userProfile = authenticated.userProfile; final userProfile = authenticated.userProfile;
final result = await FolderEventReadCurrentWorkspace().send(); final result = await FolderEventGetCurrentWorkspace().send();
result.fold( result.fold(
(workspaceSetting) { (workspaceSetting) {
getIt<SplashRoute>().pushHomeScreen( getIt<SplashRoute>().pushHomeScreen(

View File

@ -122,7 +122,7 @@ class ViewBackendService {
ViewLayoutPB layoutType, ViewLayoutPB layoutType,
) async { ) async {
final result = <(ViewPB, List<ViewPB>)>[]; final result = <(ViewPB, List<ViewPB>)>[];
return FolderEventReadCurrentWorkspace().send().then((value) async { return FolderEventGetCurrentWorkspace().send().then((value) async {
final workspaces = value.getLeftOrNull<WorkspaceSettingPB>(); final workspaces = value.getLeftOrNull<WorkspaceSettingPB>();
if (workspaces != null) { if (workspaces != null) {
final views = workspaces.workspace.views; final views = workspaces.workspace.views;

View File

@ -35,7 +35,7 @@ class WorkspaceService {
Future<Either<WorkspacePB, FlowyError>> getWorkspace() { Future<Either<WorkspacePB, FlowyError>> getWorkspace() {
final payload = WorkspaceIdPB.create()..value = workspaceId; final payload = WorkspaceIdPB.create()..value = workspaceId;
return FolderEventReadWorkspaces(payload).send().then((result) { return FolderEventReadAllWorkspaces(payload).send().then((result) {
return result.fold( return result.fold(
(workspaces) { (workspaces) {
assert(workspaces.items.length == 1); assert(workspaces.items.length == 1);

View File

@ -35,7 +35,7 @@ class _FileExporterWidgetState extends State<FileExporterWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FutureBuilder<dartz.Either<WorkspaceSettingPB, FlowyError>>( return FutureBuilder<dartz.Either<WorkspaceSettingPB, FlowyError>>(
future: FolderEventReadCurrentWorkspace().send(), future: FolderEventGetCurrentWorkspace().send(),
builder: (context, snapshot) { builder: (context, snapshot) {
if (snapshot.hasData && if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) { snapshot.connectionState == ConnectionState.done) {

View File

@ -179,7 +179,6 @@ class PopoverActionCellWidget<T extends PopoverAction> extends StatelessWidget {
final rightIcon = final rightIcon =
actionCell.rightIcon(Theme.of(context).colorScheme.onSurface); actionCell.rightIcon(Theme.of(context).colorScheme.onSurface);
return AppFlowyPopover( return AppFlowyPopover(
// mutex: mutex,
controller: popoverController, controller: popoverController,
asBarrier: true, asBarrier: true,
popupBuilder: (context) => actionCell.builder( popupBuilder: (context) => actionCell.builder(

View File

@ -127,7 +127,7 @@ void main() {
..add(const DocumentEvent.initial()); ..add(const DocumentEvent.initial());
await blocResponseFuture(); await blocResponseFuture();
final workspaceSetting = await FolderEventReadCurrentWorkspace() final workspaceSetting = await FolderEventGetCurrentWorkspace()
.send() .send()
.then((result) => result.fold((l) => l, (r) => throw Exception())); .then((result) => result.fold((l) => l, (r) => throw Exception()));
workspaceSetting.latestView.id == document1.id; workspaceSetting.latestView.id == document1.id;
@ -148,7 +148,7 @@ void main() {
final grid = bloc.state.latestCreatedView; final grid = bloc.state.latestCreatedView;
assert(grid!.name == "grid 2"); assert(grid!.name == "grid 2");
var workspaceSetting = await FolderEventReadCurrentWorkspace() var workspaceSetting = await FolderEventGetCurrentWorkspace()
.send() .send()
.then((result) => result.fold((l) => l, (r) => throw Exception())); .then((result) => result.fold((l) => l, (r) => throw Exception()));
workspaceSetting.latestView.id == grid!.id; workspaceSetting.latestView.id == grid!.id;
@ -159,7 +159,7 @@ void main() {
..add(const DocumentEvent.initial()); ..add(const DocumentEvent.initial());
await blocResponseFuture(); await blocResponseFuture();
workspaceSetting = await FolderEventReadCurrentWorkspace() workspaceSetting = await FolderEventGetCurrentWorkspace()
.send() .send()
.then((result) => result.fold((l) => l, (r) => throw Exception())); .then((result) => result.fold((l) => l, (r) => throw Exception()));
workspaceSetting.latestView.id == document.id; workspaceSetting.latestView.id == document.id;

View File

@ -15,7 +15,7 @@ void main() {
}); });
test('initi home screen', () async { test('initi home screen', () async {
final workspaceSetting = await FolderEventReadCurrentWorkspace() final workspaceSetting = await FolderEventGetCurrentWorkspace()
.send() .send()
.then((result) => result.fold((l) => l, (r) => throw Exception())); .then((result) => result.fold((l) => l, (r) => throw Exception()));
await blocResponseFuture(); await blocResponseFuture();
@ -28,7 +28,7 @@ void main() {
}); });
test('open the document', () async { test('open the document', () async {
final workspaceSetting = await FolderEventReadCurrentWorkspace() final workspaceSetting = await FolderEventGetCurrentWorkspace()
.send() .send()
.then((result) => result.fold((l) => l, (r) => throw Exception())); .then((result) => result.fold((l) => l, (r) => throw Exception()));
await blocResponseFuture(); await blocResponseFuture();

View File

@ -99,7 +99,7 @@ checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8"
[[package]] [[package]]
name = "appflowy-integrate" name = "appflowy-integrate"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1024,7 +1024,7 @@ dependencies = [
[[package]] [[package]]
name = "collab" name = "collab"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@ -1042,7 +1042,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-client-ws" name = "collab-client-ws"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab-sync", "collab-sync",
@ -1060,7 +1060,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-database" name = "collab-database"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1086,7 +1086,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-derive" name = "collab-derive"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1098,7 +1098,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-document" name = "collab-document"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1115,7 +1115,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-folder" name = "collab-folder"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"collab", "collab",
@ -1134,7 +1134,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-persistence" name = "collab-persistence"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"bincode", "bincode",
"chrono", "chrono",
@ -1154,7 +1154,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-plugins" name = "collab-plugins"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"async-trait", "async-trait",
@ -1184,7 +1184,7 @@ dependencies = [
[[package]] [[package]]
name = "collab-sync" name = "collab-sync"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=12e373#12e373f972d893d5f34b18794e77d3b49783ddcf" source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=13b178#13b17802de31e75255b4303914042bdbb04361b2"
dependencies = [ dependencies = [
"bytes", "bytes",
"collab", "collab",

View File

@ -2,7 +2,7 @@ import { currentUserActions } from '../../stores/reducers/current-user/slice';
import { useAppDispatch, useAppSelector } from '../../stores/store'; import { useAppDispatch, useAppSelector } from '../../stores/store';
import { UserProfilePB } from '../../../services/backend/events/flowy-user'; import { UserProfilePB } from '../../../services/backend/events/flowy-user';
import { AuthBackendService, UserBackendService } from '../../stores/effects/user/user_bd_svc'; import { AuthBackendService, UserBackendService } from '../../stores/effects/user/user_bd_svc';
import { FolderEventReadCurrentWorkspace } from '../../../services/backend/events/flowy-folder2'; import { FolderEventGetCurrentWorkspace } from '../../../services/backend/events/flowy-folder2';
import { WorkspaceSettingPB } from '../../../services/backend/models/flowy-folder2/workspace'; import { WorkspaceSettingPB } from '../../../services/backend/models/flowy-folder2/workspace';
import { Log } from '../../utils/log'; import { Log } from '../../utils/log';
@ -90,7 +90,7 @@ export const useAuth = () => {
} }
async function _openWorkspace() { async function _openWorkspace() {
return FolderEventReadCurrentWorkspace(); return FolderEventGetCurrentWorkspace();
} }
return { currentUser, checkUser, register, login, logout }; return { currentUser, checkUser, register, login, logout };

View File

@ -5,7 +5,7 @@ import {
ViewPB, ViewPB,
WorkspaceSettingPB, WorkspaceSettingPB,
} from '../../../services/backend'; } from '../../../services/backend';
import { FolderEventReadCurrentWorkspace } from '../../../services/backend/events/flowy-folder2'; import { FolderEventGetCurrentWorkspace } from '../../../services/backend/events/flowy-folder2';
import { AppBackendService } from '../../stores/effects/folder/app/app_bd_svc'; import { AppBackendService } from '../../stores/effects/folder/app/app_bd_svc';
import { DatabaseController } from '../../stores/effects/database/database_controller'; import { DatabaseController } from '../../stores/effects/database/database_controller';
import { RowInfo } from '../../stores/effects/database/row/row_cache'; import { RowInfo } from '../../stores/effects/database/row/row_cache';
@ -31,7 +31,7 @@ import { Log } from '$app/utils/log';
// Create a database view for specific layout type // Create a database view for specific layout type
// Do not use it production code. Just for testing // Do not use it production code. Just for testing
export async function createTestDatabaseView(layout: ViewLayoutPB): Promise<ViewPB> { export async function createTestDatabaseView(layout: ViewLayoutPB): Promise<ViewPB> {
const workspaceSetting: WorkspaceSettingPB = await FolderEventReadCurrentWorkspace().then((result) => result.unwrap()); const workspaceSetting: WorkspaceSettingPB = await FolderEventGetCurrentWorkspace().then((result) => result.unwrap());
const appService = new AppBackendService(workspaceSetting.workspace.id); const appService = new AppBackendService(workspaceSetting.workspace.id);
return await appService.createView({ name: 'New Grid', layoutType: layout }); return await appService.createView({ name: 'New Grid', layoutType: layout });
} }

View File

@ -1,9 +1,9 @@
import { ViewLayoutPB, WorkspaceSettingPB } from '@/services/backend'; import { ViewLayoutPB, WorkspaceSettingPB } from '@/services/backend';
import { FolderEventReadCurrentWorkspace } from '@/services/backend/events/flowy-folder2'; import { FolderEventGetCurrentWorkspace } from '@/services/backend/events/flowy-folder2';
import { AppBackendService } from '$app/stores/effects/folder/app/app_bd_svc'; import { AppBackendService } from '$app/stores/effects/folder/app/app_bd_svc';
export async function createTestDocument() { export async function createTestDocument() {
const workspaceSetting: WorkspaceSettingPB = await FolderEventReadCurrentWorkspace().then((result) => result.unwrap()); const workspaceSetting: WorkspaceSettingPB = await FolderEventGetCurrentWorkspace().then((result) => result.unwrap());
const app = workspaceSetting.workspace.views[0]; const app = workspaceSetting.workspace.views[0];
const appService = new AppBackendService(app.id); const appService = new AppBackendService(app.id);
return await appService.createView({ name: 'New Document', layoutType: ViewLayoutPB.Document }); return await appService.createView({ name: 'New Document', layoutType: ViewLayoutPB.Document });

View File

@ -3,7 +3,7 @@ import {
FolderEventCreateView, FolderEventCreateView,
FolderEventMoveItem, FolderEventMoveItem,
FolderEventReadWorkspaceViews, FolderEventReadWorkspaceViews,
FolderEventReadWorkspaces, FolderEventReadAllWorkspaces,
} from '@/services/backend/events/flowy-folder2'; } from '@/services/backend/events/flowy-folder2';
import { import {
CreateViewPayloadPB, CreateViewPayloadPB,
@ -35,7 +35,7 @@ export class WorkspaceBackendService {
getWorkspace = () => { getWorkspace = () => {
const payload = WorkspaceIdPB.fromObject({ value: this.workspaceId }); const payload = WorkspaceIdPB.fromObject({ value: this.workspaceId });
return FolderEventReadWorkspaces(payload).then((result) => { return FolderEventReadAllWorkspaces(payload).then((result) => {
if (result.ok) { if (result.ok) {
const workspaces = result.val.items; const workspaces = result.val.items;
if (workspaces.length === 0) { if (workspaces.length === 0) {

View File

@ -21,8 +21,8 @@ import {
import { import {
FolderEventCreateWorkspace, FolderEventCreateWorkspace,
FolderEventOpenWorkspace, FolderEventOpenWorkspace,
FolderEventReadCurrentWorkspace, FolderEventGetCurrentWorkspace,
FolderEventReadWorkspaces, FolderEventReadAllWorkspaces,
} from '@/services/backend/events/flowy-folder2'; } from '@/services/backend/events/flowy-folder2';
export class UserBackendService { export class UserBackendService {
@ -54,7 +54,7 @@ export class UserBackendService {
}; };
getCurrentWorkspace = async (): Promise<WorkspaceSettingPB> => { getCurrentWorkspace = async (): Promise<WorkspaceSettingPB> => {
const result = await FolderEventReadCurrentWorkspace(); const result = await FolderEventGetCurrentWorkspace();
if (result.ok) { if (result.ok) {
return result.val; return result.val;
} else { } else {
@ -64,7 +64,7 @@ export class UserBackendService {
getWorkspaces = () => { getWorkspaces = () => {
const payload = WorkspaceIdPB.fromObject({}); const payload = WorkspaceIdPB.fromObject({});
return FolderEventReadWorkspaces(payload); return FolderEventReadAllWorkspaces(payload);
}; };
openWorkspace = (workspaceId: string) => { openWorkspace = (workspaceId: string) => {

View File

@ -12,10 +12,10 @@ pub fn init(folder: Arc<Folder2Manager>) -> AFPlugin {
// Workspace // Workspace
.event(FolderEvent::CreateWorkspace, create_workspace_handler) .event(FolderEvent::CreateWorkspace, create_workspace_handler)
.event( .event(
FolderEvent::ReadCurrentWorkspace, FolderEvent::GetCurrentWorkspace,
read_cur_workspace_setting_handler, read_cur_workspace_setting_handler,
) )
.event(FolderEvent::ReadWorkspaces, read_workspaces_handler) .event(FolderEvent::ReadAllWorkspaces, read_workspaces_handler)
.event(FolderEvent::OpenWorkspace, open_workspace_handler) .event(FolderEvent::OpenWorkspace, open_workspace_handler)
.event(FolderEvent::ReadWorkspaceViews, read_workspace_views_handler) .event(FolderEvent::ReadWorkspaceViews, read_workspace_views_handler)
// View // View
@ -45,11 +45,11 @@ pub enum FolderEvent {
/// Read the current opening workspace. Currently, we only support one workspace /// Read the current opening workspace. Currently, we only support one workspace
#[event(output = "WorkspaceSettingPB")] #[event(output = "WorkspaceSettingPB")]
ReadCurrentWorkspace = 1, GetCurrentWorkspace = 1,
/// Return a list of workspaces that the current user can access /// Return a list of workspaces that the current user can access.
#[event(input = "WorkspaceIdPB", output = "RepeatedWorkspacePB")] #[event(input = "WorkspaceIdPB", output = "RepeatedWorkspacePB")]
ReadWorkspaces = 2, ReadAllWorkspaces = 2,
/// Delete the workspace /// Delete the workspace
#[event(input = "WorkspaceIdPB")] #[event(input = "WorkspaceIdPB")]
@ -59,7 +59,8 @@ pub enum FolderEvent {
#[event(input = "WorkspaceIdPB", output = "WorkspacePB")] #[event(input = "WorkspaceIdPB", output = "WorkspacePB")]
OpenWorkspace = 4, OpenWorkspace = 4,
/// Return a list of views that belong to this workspace. /// Return a list of views of the current workspace.
/// Only the first level of child views are included.
#[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")] #[event(input = "WorkspaceIdPB", output = "RepeatedViewPB")]
ReadWorkspaceViews = 5, ReadWorkspaceViews = 5,

View File

@ -18,8 +18,8 @@ use lib_infra::util::timestamp;
use crate::deps::{FolderCloudService, FolderUser}; use crate::deps::{FolderCloudService, FolderUser};
use crate::entities::{ use crate::entities::{
view_pb_with_child_views, CreateViewParams, CreateWorkspaceParams, RepeatedTrashPB, view_pb_with_child_views, CreateViewParams, CreateWorkspaceParams, DeletedViewPB,
RepeatedViewPB, RepeatedWorkspacePB, UpdateViewParams, ViewPB, RepeatedTrashPB, RepeatedViewPB, RepeatedWorkspacePB, UpdateViewParams, ViewPB,
}; };
use crate::notification::{ use crate::notification::{
send_notification, send_workspace_notification, send_workspace_setting_notification, send_notification, send_workspace_notification, send_workspace_setting_notification,
@ -68,6 +68,8 @@ impl Folder2Manager {
} }
} }
/// Return a list of views of the current workspace.
/// Only the first level of child views are included.
pub async fn get_current_workspace_views(&self) -> FlowyResult<Vec<ViewPB>> { pub async fn get_current_workspace_views(&self) -> FlowyResult<Vec<ViewPB>> {
let workspace_id = self let workspace_id = self
.mutex_folder .mutex_folder
@ -315,6 +317,14 @@ impl Folder2Manager {
folder.set_current_view(""); folder.set_current_view("");
} }
} }
// notify the parent view that the view is moved to trash
send_notification(view_id, FolderNotification::DidMoveViewToTrash)
.payload(DeletedViewPB {
view_id: view_id.to_string(),
index: None,
})
.send();
}); });
Ok(()) Ok(())

View File

@ -4,14 +4,14 @@ use flowy_folder2::entities::CreateWorkspacePayloadPB;
use flowy_test::{event_builder::*, FlowyCoreTest}; use flowy_test::{event_builder::*, FlowyCoreTest};
#[tokio::test] #[tokio::test]
async fn workspace_read_all() { async fn read_all_workspace_test() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
test.run_scripts(vec![ReadAllWorkspaces]).await; test.run_scripts(vec![ReadAllWorkspaces]).await;
assert!(!test.all_workspace.is_empty()); assert!(!test.all_workspace.is_empty());
} }
#[tokio::test] #[tokio::test]
async fn workspace_create() { async fn create_workspace_test() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
let name = "My new workspace".to_owned(); let name = "My new workspace".to_owned();
let desc = "Daily routines".to_owned(); let desc = "Daily routines".to_owned();
@ -34,7 +34,7 @@ async fn workspace_create() {
} }
#[tokio::test] #[tokio::test]
async fn workspace_read() { async fn get_workspace_test() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
let workspace = test.workspace.clone(); let workspace = test.workspace.clone();
@ -47,21 +47,21 @@ async fn workspace_read() {
} }
#[tokio::test] #[tokio::test]
async fn workspace_create_with_apps() { async fn create_parent_view_test() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
test test
.run_scripts(vec![CreateApp { .run_scripts(vec![CreateParentView {
name: "App".to_string(), name: "App".to_string(),
desc: "App description".to_string(), desc: "App description".to_string(),
}]) }])
.await; .await;
let app = test.parent_view.clone(); let app = test.parent_view.clone();
test.run_scripts(vec![RefreshRootView(app.id)]).await; test.run_scripts(vec![ReloadParentView(app.id)]).await;
} }
#[tokio::test] #[tokio::test]
async fn workspace_create_with_invalid_name() { async fn create_parent_view_with_invalid_name() {
for (name, code) in invalid_workspace_name_test_case() { for (name, code) in invalid_workspace_name_test_case() {
let sdk = FlowyCoreTest::new(); let sdk = FlowyCoreTest::new();
let request = CreateWorkspacePayloadPB { let request = CreateWorkspacePayloadPB {
@ -84,46 +84,46 @@ async fn workspace_create_with_invalid_name() {
#[tokio::test] #[tokio::test]
#[should_panic] #[should_panic]
async fn app_delete() { async fn delete_parent_view_test() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
let app = test.parent_view.clone(); let parent_view = test.parent_view.clone();
test test
.run_scripts(vec![DeleteRootView, RefreshRootView(app.id)]) .run_scripts(vec![DeleteParentView, ReloadParentView(parent_view.id)])
.await; .await;
} }
#[tokio::test] #[tokio::test]
async fn app_delete_then_restore() { async fn delete_parent_view_then_restore() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
test test
.run_scripts(vec![RefreshRootView(test.parent_view.id.clone())]) .run_scripts(vec![ReloadParentView(test.parent_view.id.clone())])
.await; .await;
let parent_view = test.parent_view.clone(); let parent_view = test.parent_view.clone();
test test
.run_scripts(vec![ .run_scripts(vec![
DeleteRootView, DeleteParentView,
RestoreAppFromTrash, RestoreAppFromTrash,
RefreshRootView(parent_view.id.clone()), ReloadParentView(parent_view.id.clone()),
AssertRootView(parent_view), AssertParentView(parent_view),
]) ])
.await; .await;
} }
#[tokio::test] #[tokio::test]
async fn app_update() { async fn update_parent_view_test() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
let app = test.parent_view.clone(); let parent_view = test.parent_view.clone();
let new_name = "😁 hell world".to_owned(); let new_name = "😁 hell world".to_owned();
assert_ne!(app.name, new_name); assert_ne!(parent_view.name, new_name);
test test
.run_scripts(vec![ .run_scripts(vec![
UpdateRootView { UpdateParentView {
name: Some(new_name.clone()), name: Some(new_name.clone()),
desc: None, desc: None,
}, },
RefreshRootView(app.id), ReloadParentView(parent_view.id),
]) ])
.await; .await;
assert_eq!(test.parent_view.name, new_name); assert_eq!(test.parent_view.name, new_name);
@ -145,7 +145,7 @@ async fn app_create_with_view() {
desc: "Grid description".to_owned(), desc: "Grid description".to_owned(),
layout: ViewLayout::Grid, layout: ViewLayout::Grid,
}, },
RefreshRootView(app.id), ReloadParentView(app.id),
]) ])
.await; .await;
@ -212,7 +212,7 @@ async fn view_delete_all() {
desc: "Grid description".to_owned(), desc: "Grid description".to_owned(),
layout: ViewLayout::Grid, layout: ViewLayout::Grid,
}, },
RefreshRootView(parent_view.id.clone()), ReloadParentView(parent_view.id.clone()),
]) ])
.await; .await;
@ -230,7 +230,7 @@ async fn view_delete_all() {
test test
.run_scripts(vec![ .run_scripts(vec![
DeleteViews(view_ids), DeleteViews(view_ids),
RefreshRootView(parent_view.id), ReloadParentView(parent_view.id),
ReadTrash, ReadTrash,
]) ])
.await; .await;
@ -242,7 +242,7 @@ async fn view_delete_all() {
#[tokio::test] #[tokio::test]
async fn view_delete_all_permanent() { async fn view_delete_all_permanent() {
let mut test = FolderTest::new().await; let mut test = FolderTest::new().await;
let app = test.parent_view.clone(); let parent_view = test.parent_view.clone();
test test
.run_scripts(vec![ .run_scripts(vec![
CreateView { CreateView {
@ -250,7 +250,7 @@ async fn view_delete_all_permanent() {
desc: "View A description".to_owned(), desc: "View A description".to_owned(),
layout: ViewLayout::Document, layout: ViewLayout::Document,
}, },
RefreshRootView(app.id.clone()), ReloadParentView(parent_view.id.clone()),
]) ])
.await; .await;
@ -263,7 +263,7 @@ async fn view_delete_all_permanent() {
test test
.run_scripts(vec![ .run_scripts(vec![
DeleteViews(view_ids), DeleteViews(view_ids),
RefreshRootView(app.id), ReloadParentView(parent_view.id),
DeleteAllTrash, DeleteAllTrash,
ReadTrash, ReadTrash,
]) ])

View File

@ -16,17 +16,17 @@ pub enum FolderScript {
ReadWorkspace(Option<String>), ReadWorkspace(Option<String>),
// App // App
CreateApp { CreateParentView {
name: String, name: String,
desc: String, desc: String,
}, },
AssertRootView(ViewPB), AssertParentView(ViewPB),
RefreshRootView(String), ReloadParentView(String),
UpdateRootView { UpdateParentView {
name: Option<String>, name: Option<String>,
desc: Option<String>, desc: Option<String>,
}, },
DeleteRootView, DeleteParentView,
// View // View
CreateView { CreateView {
@ -107,24 +107,23 @@ impl FolderTest {
let workspace = read_workspace(sdk, workspace_id).await.pop().unwrap(); let workspace = read_workspace(sdk, workspace_id).await.pop().unwrap();
self.workspace = workspace; self.workspace = workspace;
}, },
FolderScript::CreateApp { name, desc } => { FolderScript::CreateParentView { name, desc } => {
let app = create_app(sdk, &self.workspace.id, &name, &desc).await; let app = create_app(sdk, &self.workspace.id, &name, &desc).await;
self.parent_view = app; self.parent_view = app;
}, },
FolderScript::AssertRootView(app) => { FolderScript::AssertParentView(app) => {
assert_eq!(self.parent_view, app, "App not equal"); assert_eq!(self.parent_view, app, "App not equal");
}, },
FolderScript::RefreshRootView(app_id) => { FolderScript::ReloadParentView(parent_view_id) => {
let app = read_view(sdk, &app_id).await; let parent_view = read_view(sdk, &parent_view_id).await;
self.parent_view = app; self.parent_view = parent_view;
}, },
FolderScript::UpdateRootView { name, desc } => { FolderScript::UpdateParentView { name, desc } => {
update_view(sdk, &self.parent_view.id, name, desc).await; update_view(sdk, &self.parent_view.id, name, desc).await;
}, },
FolderScript::DeleteRootView => { FolderScript::DeleteParentView => {
delete_view(sdk, vec![self.parent_view.id.clone()]).await; delete_view(sdk, vec![self.parent_view.id.clone()]).await;
}, },
FolderScript::CreateView { name, desc, layout } => { FolderScript::CreateView { name, desc, layout } => {
let view = create_view(sdk, &self.parent_view.id, &name, &desc, layout).await; let view = create_view(sdk, &self.parent_view.id, &name, &desc, layout).await;
self.child_view = view; self.child_view = view;
@ -189,7 +188,7 @@ pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option<String>) -
value: workspace_id, value: workspace_id,
}; };
let repeated_workspace = EventBuilder::new(sdk.clone()) let repeated_workspace = EventBuilder::new(sdk.clone())
.event(ReadWorkspaces) .event(ReadAllWorkspaces)
.payload(request.clone()) .payload(request.clone())
.async_send() .async_send()
.await .await
@ -256,7 +255,7 @@ pub async fn create_view(
} }
pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB { pub async fn read_view(sdk: &FlowyCoreTest, view_id: &str) -> ViewPB {
let view_id: ViewIdPB = view_id.into(); let view_id = ViewIdPB::from(view_id);
EventBuilder::new(sdk.clone()) EventBuilder::new(sdk.clone())
.event(ReadView) .event(ReadView)
.payload(view_id) .payload(view_id)

View File

@ -1,6 +1,6 @@
use crate::user::supabase_test::helper::get_supabase_config; use crate::user::supabase_test::helper::get_supabase_config;
use flowy_folder2::entities::WorkspaceSettingPB; use flowy_folder2::entities::WorkspaceSettingPB;
use flowy_folder2::event_map::FolderEvent::ReadCurrentWorkspace; use flowy_folder2::event_map::FolderEvent::GetCurrentWorkspace;
use flowy_test::{event_builder::EventBuilder, FlowyCoreTest}; use flowy_test::{event_builder::EventBuilder, FlowyCoreTest};
use flowy_user::entities::{AuthTypePB, ThirdPartyAuthPB, UserProfilePB}; use flowy_user::entities::{AuthTypePB, ThirdPartyAuthPB, UserProfilePB};
@ -27,7 +27,7 @@ async fn initial_workspace_test() {
.parse::<UserProfilePB>(); .parse::<UserProfilePB>();
let workspace_settings = EventBuilder::new(test.clone()) let workspace_settings = EventBuilder::new(test.clone())
.event(ReadCurrentWorkspace) .event(GetCurrentWorkspace)
.async_send() .async_send()
.await .await
.parse::<WorkspaceSettingPB>(); .parse::<WorkspaceSettingPB>();

View File

@ -1,8 +1,14 @@
[tasks.appflowy-flutter-deps-tools] [tasks.appflowy-flutter-deps-tools]
run_task = { name = ["install_flutter_prerequests","install_diesel"] } run_task = { name = ["install_flutter_prerequests"] }
[tasks.appflowy-tauri-deps-tools] [tasks.appflowy-tauri-deps-tools]
run_task = { name = ["install_tauri_prerequests","install_diesel"] } run_task = { name = ["install_tauri_prerequests"] }
[tasks.appflowy-flutter-dev-tools]
run_task = { name = ["appflowy-flutter-deps-tools","install_diesel"] }
[tasks.appflowy-tauri-dev-tools]
run_task = { name = ["appflowy-tauri-deps-tools","install_diesel"] }
[tasks.install_windows_deps.windows] [tasks.install_windows_deps.windows]
dependencies=["check_duckscript_installation", "check_visual_studio_installation", "check_vcpkg", "install_vcpkg_sqlite", "install_rust_vcpkg_cli"] dependencies=["check_duckscript_installation", "check_visual_studio_installation", "check_vcpkg", "install_vcpkg_sqlite", "install_rust_vcpkg_cli"]
@ -78,9 +84,9 @@ dependencies = ["check_vcpkg"]
[tasks.install_targets.mac] [tasks.install_targets.mac]
script = """ script = """
rustup target add x86_64-apple-ios #rustup target add x86_64-apple-ios
rustup target add x86_64-apple-darwin #rustup target add x86_64-apple-darwin
rustup target add aarch64-apple-ios #rustup target add aarch64-apple-ios
rustup target add aarch64-apple-darwin rustup target add aarch64-apple-darwin
""" """