Browse Source

feat: import cvs from file (#2667)

* refactor: import file

* chore: fix tarui build
Nathan.fooo 1 year ago
parent
commit
188b36cae6
40 changed files with 552 additions and 616 deletions
  1. 1 1
      frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/insert_page_command.dart
  2. 1 1
      frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/board/board_view_menu_item.dart
  3. 1 1
      frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_view_menu_item.dart
  4. 1 1
      frontend/appflowy_flutter/lib/workspace/application/app/app_bloc.dart
  5. 2 2
      frontend/appflowy_flutter/lib/workspace/application/app/app_service.dart
  6. 40 0
      frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart
  7. 1 1
      frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart
  8. 30 14
      frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart
  9. 3 2
      frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/header.dart
  10. 43 16
      frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart
  11. 1 1
      frontend/appflowy_flutter/test/bloc_test/board_test/util.dart
  12. 1 1
      frontend/appflowy_flutter/test/bloc_test/grid_test/filter/filter_util.dart
  13. 1 1
      frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart
  14. 1 1
      frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/app/app_bd_svc.ts
  15. 1 1
      frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts
  16. 116 62
      frontend/rust-lib/flowy-core/src/deps_resolve/folder2_deps.rs
  17. 0 361
      frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs
  18. 0 2
      frontend/rust-lib/flowy-database2/src/entities/mod.rs
  19. 0 24
      frontend/rust-lib/flowy-database2/src/entities/share_entities.rs
  20. 1 26
      frontend/rust-lib/flowy-database2/src/event_handler.rs
  21. 3 9
      frontend/rust-lib/flowy-database2/src/event_map.rs
  22. 17 6
      frontend/rust-lib/flowy-database2/src/manager.rs
  23. 9 6
      frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs
  24. 2 1
      frontend/rust-lib/flowy-database2/tests/database/database_editor.rs
  25. 82 0
      frontend/rust-lib/flowy-folder2/src/entities/import.rs
  26. 2 0
      frontend/rust-lib/flowy-folder2/src/entities/mod.rs
  27. 17 0
      frontend/rust-lib/flowy-folder2/src/entities/parser/empty_str.rs
  28. 1 0
      frontend/rust-lib/flowy-folder2/src/entities/parser/mod.rs
  29. 6 6
      frontend/rust-lib/flowy-folder2/src/entities/view.rs
  30. 12 1
      frontend/rust-lib/flowy-folder2/src/event_handler.rs
  31. 18 14
      frontend/rust-lib/flowy-folder2/src/event_map.rs
  32. 1 0
      frontend/rust-lib/flowy-folder2/src/lib.rs
  33. 69 28
      frontend/rust-lib/flowy-folder2/src/manager.rs
  34. 17 0
      frontend/rust-lib/flowy-folder2/src/share/import.rs
  35. 3 0
      frontend/rust-lib/flowy-folder2/src/share/mod.rs
  36. 2 2
      frontend/rust-lib/flowy-folder2/src/test_helper.rs
  37. 5 5
      frontend/rust-lib/flowy-folder2/src/user_default.rs
  38. 37 15
      frontend/rust-lib/flowy-folder2/src/view_ext.rs
  39. 2 2
      frontend/rust-lib/flowy-folder2/tests/workspace/script.rs
  40. 2 2
      frontend/rust-lib/flowy-test/src/folder_event.rs

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/base/insert_page_command.dart

@@ -37,7 +37,7 @@ extension InsertDatabase on EditorState {
 
     final prefix = _referencedDatabasePrefix(viewPB.layout);
     final ref = await AppBackendService().createView(
-      appId: appPB.id,
+      parentViewId: appPB.id,
       name: "$prefix ${viewPB.name}",
       layoutType: viewPB.layout,
       ext: {

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/board/board_view_menu_item.dart

@@ -25,7 +25,7 @@ SelectionMenuItem boardViewMenuItem(DocumentBloc documentBloc) =>
         final service = AppBackendService();
 
         final result = (await service.createView(
-          appId: appId,
+          parentViewId: appId,
           name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
           layoutType: ViewLayoutPB.Board,
         ))

+ 1 - 1
frontend/appflowy_flutter/lib/plugins/document/presentation/editor_plugins/grid/grid_view_menu_item.dart

@@ -25,7 +25,7 @@ SelectionMenuItem gridViewMenuItem(DocumentBloc documentBloc) =>
         final service = AppBackendService();
 
         final result = (await service.createView(
-          appId: appId,
+          parentViewId: appId,
           name: LocaleKeys.menuAppHeader_defaultNewPageName.tr(),
           layoutType: ViewLayoutPB.Grid,
         ))

+ 1 - 1
frontend/appflowy_flutter/lib/workspace/application/app/app_bloc.dart

@@ -104,7 +104,7 @@ class AppBloc extends Bloc<AppEvent, AppState> {
 
   Future<void> _createView(CreateView value, Emitter<AppState> emit) async {
     final result = await appService.createView(
-      appId: state.view.id,
+      parentViewId: state.view.id,
       name: value.name,
       desc: value.desc ?? "",
       layoutType: value.pluginBuilder.layoutType!,

+ 2 - 2
frontend/appflowy_flutter/lib/workspace/application/app/app_service.dart

@@ -8,7 +8,7 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
 
 class AppBackendService {
   Future<Either<ViewPB, FlowyError>> createView({
-    required String appId,
+    required String parentViewId,
     required String name,
     String? desc,
     required ViewLayoutPB layoutType,
@@ -25,7 +25,7 @@ class AppBackendService {
     Map<String, String> ext = const {},
   }) {
     final payload = CreateViewPayloadPB.create()
-      ..belongToId = appId
+      ..parentViewId = parentViewId
       ..name = name
       ..desc = desc ?? ""
       ..layout = layoutType

+ 40 - 0
frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart

@@ -0,0 +1,40 @@
+import 'dart:convert';
+import 'dart:typed_data';
+
+import 'package:appflowy_backend/dispatch/dispatch.dart';
+import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-folder2/import.pb.dart';
+import 'package:appflowy_backend/protobuf/flowy-folder2/view.pbenum.dart';
+import 'package:dartz/dartz.dart';
+
+class ImportBackendService {
+  static Future<Either<Unit, FlowyError>> importHistoryDatabase(
+    String data,
+    String name,
+    String parentViewId,
+  ) async {
+    final payload = ImportPB.create()
+      ..data = utf8.encode(data)
+      ..parentViewId = parentViewId
+      ..viewLayout = ViewLayoutPB.Grid
+      ..name = name
+      ..importType = ImportTypePB.HistoryDatabase;
+
+    return await FolderEventImportData(payload).send();
+  }
+
+  static Future<Either<Unit, FlowyError>> importHistoryDocument(
+    Uint8List data,
+    String name,
+    String parentViewId,
+  ) async {
+    final payload = ImportPB.create()
+      ..data = data
+      ..parentViewId = parentViewId
+      ..viewLayout = ViewLayoutPB.Document
+      ..name = name
+      ..importType = ImportTypePB.HistoryDocument;
+
+    return await FolderEventImportData(payload).send();
+  }
+}

+ 1 - 1
frontend/appflowy_flutter/lib/workspace/application/workspace/workspace_service.dart

@@ -25,7 +25,7 @@ class WorkspaceService {
     String? desc,
   }) {
     final payload = CreateViewPayloadPB.create()
-      ..belongToId = workspaceId
+      ..parentViewId = workspaceId
       ..name = name
       ..desc = desc ?? ""
       ..layout = ViewLayoutPB.Document;

+ 30 - 14
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart

@@ -12,6 +12,7 @@ import 'package:appflowy/generated/locale_keys.g.dart';
 import 'package:easy_localization/easy_localization.dart';
 
 class AddButton extends StatelessWidget {
+  final String parentViewId;
   final Function(
     PluginBuilder,
     String? name,
@@ -20,6 +21,7 @@ class AddButton extends StatelessWidget {
   ) onSelected;
 
   const AddButton({
+    required this.parentViewId,
     Key? key,
     required this.onSelected,
   }) : super(key: key);
@@ -75,20 +77,34 @@ class AddButton extends StatelessWidget {
           onSelected(action.pluginBuilder, null, null, true);
         }
         if (action is ImportActionWrapper) {
-          showImportPanel(context, (type, name, initialDataBytes) {
-            if (initialDataBytes == null) {
-              return;
-            }
-            switch (type) {
-              case ImportType.historyDocument:
-              case ImportType.historyDatabase:
-                onSelected(action.pluginBuilder, name, initialDataBytes, false);
-                break;
-              case ImportType.markdownOrText:
-                onSelected(action.pluginBuilder, name, initialDataBytes, true);
-                break;
-            }
-          });
+          showImportPanel(
+            parentViewId,
+            context,
+            (type, name, initialDataBytes) {
+              if (initialDataBytes == null) {
+                return;
+              }
+              switch (type) {
+                case ImportType.historyDocument:
+                case ImportType.historyDatabase:
+                  onSelected(
+                    action.pluginBuilder,
+                    name,
+                    initialDataBytes,
+                    false,
+                  );
+                  break;
+                case ImportType.markdownOrText:
+                  onSelected(
+                    action.pluginBuilder,
+                    name,
+                    initialDataBytes,
+                    true,
+                  );
+                  break;
+              }
+            },
+          );
         }
         controller.close();
       },

+ 3 - 2
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/header.dart

@@ -17,9 +17,9 @@ import '../menu_app.dart';
 import 'add_button.dart';
 
 class MenuAppHeader extends StatelessWidget {
-  final ViewPB app;
+  final ViewPB parentView;
   const MenuAppHeader(
-    this.app, {
+    this.parentView, {
     Key? key,
   }) : super(key: key);
 
@@ -108,6 +108,7 @@ class MenuAppHeader extends StatelessWidget {
     return Tooltip(
       message: LocaleKeys.menuAppHeader_addPageTooltip.tr(),
       child: AddButton(
+        parentViewId: parentView.id,
         onSelected: (pluginBuilder, name, initialDataBytes, openAfterCreated) {
           context.read<AppBloc>().add(
                 AppEvent.createView(

+ 43 - 16
frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart

@@ -1,9 +1,11 @@
 import 'dart:io';
+import 'dart:typed_data';
 
 import 'package:appflowy/plugins/document/application/document_data_pb_extension.dart';
 import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/editor_migration.dart';
 import 'package:appflowy/startup/startup.dart';
 import 'package:appflowy/util/file_picker/file_picker_service.dart';
+import 'package:appflowy/workspace/application/settings/share/import_service.dart';
 import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:file_picker/file_picker.dart';
 import 'package:flowy_infra/image.dart';
@@ -21,6 +23,7 @@ typedef ImportCallback = void Function(
 );
 
 Future<void> showImportPanel(
+  String parentViewId,
   BuildContext context,
   ImportCallback callback,
 ) async {
@@ -33,7 +36,10 @@ Future<void> showImportPanel(
           fontSize: 20,
           color: Theme.of(context).colorScheme.tertiary,
         ),
-        content: _ImportPanel(importCallback: callback),
+        content: _ImportPanel(
+          parentViewId: parentViewId,
+          importCallback: callback,
+        ),
         contentPadding: const EdgeInsets.symmetric(
           vertical: 10.0,
           horizontal: 20.0,
@@ -112,9 +118,11 @@ enum ImportType {
 
 class _ImportPanel extends StatefulWidget {
   const _ImportPanel({
+    required this.parentViewId,
     required this.importCallback,
   });
 
+  final String parentViewId;
   final ImportCallback importCallback;
 
   @override
@@ -145,7 +153,7 @@ class _ImportPanelState extends State<_ImportPanel> {
                   overflow: TextOverflow.ellipsis,
                 ),
                 onTap: () async {
-                  await _importFile(e);
+                  await _importFile(widget.parentViewId, e);
                   if (mounted) {
                     Navigator.of(context).pop();
                   }
@@ -158,7 +166,7 @@ class _ImportPanelState extends State<_ImportPanel> {
     );
   }
 
-  Future<void> _importFile(ImportType importType) async {
+  Future<void> _importFile(String parentViewId, ImportType importType) async {
     final result = await getIt<FilePickerService>().pickFiles(
       allowMultiple: importType.allowMultiSelect,
       type: FileType.custom,
@@ -173,26 +181,45 @@ class _ImportPanelState extends State<_ImportPanel> {
       if (path == null) {
         continue;
       }
-      final plainText = await File(path).readAsString();
-      Document? document;
+      final data = await File(path).readAsString();
+      final name = p.basenameWithoutExtension(path);
+
       switch (importType) {
         case ImportType.markdownOrText:
-          document = markdownToDocument(plainText);
-          break;
         case ImportType.historyDocument:
-          document = EditorMigration.migrateDocument(plainText);
+          final bytes = _documentDataFrom(importType, data);
+          if (bytes != null) {
+            await ImportBackendService.importHistoryDocument(
+              bytes,
+              name,
+              parentViewId,
+            );
+          }
+          break;
+        case ImportType.historyDatabase:
+          await ImportBackendService.importHistoryDatabase(
+            data,
+            name,
+            parentViewId,
+          );
           break;
         default:
           assert(false, 'Unsupported Type $importType');
       }
-      if (document != null) {
-        final data = DocumentDataPBFromTo.fromDocument(document);
-        widget.importCallback(
-          importType,
-          p.basenameWithoutExtension(path),
-          data?.writeToBuffer(),
-        );
-      }
     }
   }
 }
+
+Uint8List? _documentDataFrom(ImportType importType, String data) {
+  switch (importType) {
+    case ImportType.markdownOrText:
+      final document = markdownToDocument(data);
+      return DocumentDataPBFromTo.fromDocument(document)?.writeToBuffer();
+    case ImportType.historyDocument:
+      final document = EditorMigration.migrateDocument(data);
+      return DocumentDataPBFromTo.fromDocument(document)?.writeToBuffer();
+    default:
+      assert(false, 'Unsupported Type $importType');
+      return null;
+  }
+}

+ 1 - 1
frontend/appflowy_flutter/test/bloc_test/board_test/util.dart

@@ -32,7 +32,7 @@ class AppFlowyBoardTest {
     final builder = BoardPluginBuilder();
     return AppBackendService()
         .createView(
-      appId: app.id,
+      parentViewId: app.id,
       name: "Test Board",
       layoutType: builder.layoutType!,
     )

+ 1 - 1
frontend/appflowy_flutter/test/bloc_test/grid_test/filter/filter_util.dart

@@ -10,7 +10,7 @@ Future<GridTestContext> createTestFilterGrid(AppFlowyGridTest gridTest) async {
   final builder = GridPluginBuilder();
   final context = await AppBackendService()
       .createView(
-    appId: app.id,
+    parentViewId: app.id,
     name: "Filter Grid",
     layoutType: builder.layoutType!,
   )

+ 1 - 1
frontend/appflowy_flutter/test/bloc_test/grid_test/util.dart

@@ -172,7 +172,7 @@ class AppFlowyGridTest {
     final builder = GridPluginBuilder();
     final context = await AppBackendService()
         .createView(
-      appId: app.id,
+      parentViewId: app.id,
       name: "Test Grid",
       layoutType: builder.layoutType!,
     )

+ 1 - 1
frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/app/app_bd_svc.ts

@@ -36,7 +36,7 @@ export class AppBackendService {
   }) => {
     const encoder = new TextEncoder();
     const payload = CreateViewPayloadPB.fromObject({
-      belong_to_id: this.appId,
+      parent_view_id: this.appId,
       name: params.name,
       desc: params.desc || '',
       layout: params.layoutType,

+ 1 - 1
frontend/appflowy_tauri/src/appflowy_app/stores/effects/folder/workspace/workspace_bd_svc.ts

@@ -19,7 +19,7 @@ export class WorkspaceBackendService {
 
   createApp = async (params: { name: string; desc?: string }) => {
     const payload = CreateViewPayloadPB.fromObject({
-      belong_to_id: this.workspaceId,
+      parent_view_id: this.workspaceId,
       name: params.name,
       desc: params.desc || '',
       layout: ViewLayoutPB.Document,

+ 116 - 62
frontend/rust-lib/flowy-core/src/deps_resolve/folder2_deps.rs

@@ -7,6 +7,7 @@ use appflowy_integrate::RocksCollabDB;
 use bytes::Bytes;
 
 use flowy_database2::entities::DatabaseLayoutPB;
+use flowy_database2::services::share::csv::CSVFormat;
 use flowy_database2::template::{make_default_board, make_default_calendar, make_default_grid};
 use flowy_database2::DatabaseManager2;
 use flowy_document2::document_data::DocumentDataWrapper;
@@ -16,7 +17,7 @@ use flowy_error::FlowyError;
 use flowy_folder2::deps::{FolderCloudService, FolderUser};
 use flowy_folder2::entities::ViewLayoutPB;
 use flowy_folder2::manager::Folder2Manager;
-use flowy_folder2::view_ext::{ViewDataProcessor, ViewDataProcessorMap};
+use flowy_folder2::view_ext::{FolderOperationHandler, FolderOperationHandlers};
 use flowy_folder2::ViewLayout;
 use flowy_user::services::UserSession;
 use lib_dispatch::prelude::ToBytes;
@@ -33,29 +34,28 @@ impl Folder2DepsResolver {
   ) -> Arc<Folder2Manager> {
     let user: Arc<dyn FolderUser> = Arc::new(FolderUserImpl(user_session.clone()));
 
-    let view_processors =
-      make_view_data_processor(document_manager.clone(), database_manager.clone());
+    let handlers = folder_operation_handlers(document_manager.clone(), database_manager.clone());
     Arc::new(
-      Folder2Manager::new(user.clone(), collab_builder, view_processors, folder_cloud)
+      Folder2Manager::new(user.clone(), collab_builder, handlers, folder_cloud)
         .await
         .unwrap(),
     )
   }
 }
 
-fn make_view_data_processor(
+fn folder_operation_handlers(
   document_manager: Arc<DocumentManager>,
   database_manager: Arc<DatabaseManager2>,
-) -> ViewDataProcessorMap {
-  let mut map: HashMap<ViewLayout, Arc<dyn ViewDataProcessor + Send + Sync>> = HashMap::new();
+) -> FolderOperationHandlers {
+  let mut map: HashMap<ViewLayout, Arc<dyn FolderOperationHandler + Send + Sync>> = HashMap::new();
 
-  let document_processor = Arc::new(DocumentViewDataProcessor(document_manager));
-  map.insert(ViewLayout::Document, document_processor);
+  let document_folder_operation = Arc::new(DocumentFolderOperation(document_manager));
+  map.insert(ViewLayout::Document, document_folder_operation);
 
-  let database_processor = Arc::new(DatabaseViewDataProcessor(database_manager));
-  map.insert(ViewLayout::Board, database_processor.clone());
-  map.insert(ViewLayout::Grid, database_processor.clone());
-  map.insert(ViewLayout::Calendar, database_processor);
+  let database_folder_operation = Arc::new(DatabaseFolderOperation(database_manager));
+  map.insert(ViewLayout::Board, database_folder_operation.clone());
+  map.insert(ViewLayout::Grid, database_folder_operation.clone());
+  map.insert(ViewLayout::Calendar, database_folder_operation);
   Arc::new(map)
 }
 
@@ -80,8 +80,8 @@ impl FolderUser for FolderUserImpl {
   }
 }
 
-struct DocumentViewDataProcessor(Arc<DocumentManager>);
-impl ViewDataProcessor for DocumentViewDataProcessor {
+struct DocumentFolderOperation(Arc<DocumentManager>);
+impl FolderOperationHandler for DocumentFolderOperation {
   /// Close the document view.
   fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError> {
     let manager = self.0.clone();
@@ -92,10 +92,7 @@ impl ViewDataProcessor for DocumentViewDataProcessor {
     })
   }
 
-  /// Get the view data.
-  ///
-  /// only use in the duplicate view.
-  fn get_view_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
+  fn duplicate_view(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
     let manager = self.0.clone();
     let view_id = view_id.to_string();
     FutureResult::new(async move {
@@ -106,49 +103,74 @@ impl ViewDataProcessor for DocumentViewDataProcessor {
     })
   }
 
-  /// Create a view with built-in data.
-  fn create_view_with_built_in_data(
+  fn create_view_with_view_data(
     &self,
     _user_id: i64,
     view_id: &str,
     _name: &str,
+    data: Vec<u8>,
     layout: ViewLayout,
     _ext: HashMap<String, String>,
   ) -> FutureResult<(), FlowyError> {
     debug_assert_eq!(layout, ViewLayout::Document);
-
+    // TODO: implement read the document data from custom data.
     let view_id = view_id.to_string();
     let manager = self.0.clone();
-    // TODO: implement read the document data from json.
     FutureResult::new(async move {
-      manager.create_document(view_id, DocumentDataWrapper::default())?;
+      let data = DocumentDataPB::try_from(Bytes::from(data))?;
+      manager.create_document(view_id, data.into())?;
       Ok(())
     })
   }
 
-  fn create_view_with_custom_data(
+  /// Create a view with built-in data.
+  fn create_built_in_view(
     &self,
     _user_id: i64,
     view_id: &str,
     _name: &str,
-    data: Vec<u8>,
     layout: ViewLayout,
     _ext: HashMap<String, String>,
   ) -> FutureResult<(), FlowyError> {
     debug_assert_eq!(layout, ViewLayout::Document);
-    // TODO: implement read the document data from custom data.
+
     let view_id = view_id.to_string();
     let manager = self.0.clone();
+    // TODO: implement read the document data from json.
     FutureResult::new(async move {
-      let data = DocumentDataPB::try_from(Bytes::from(data))?;
+      manager.create_document(view_id, DocumentDataWrapper::default())?;
+      Ok(())
+    })
+  }
+
+  fn import_from_bytes(
+    &self,
+    view_id: &str,
+    _name: &str,
+    bytes: Vec<u8>,
+  ) -> FutureResult<(), FlowyError> {
+    let view_id = view_id.to_string();
+    let manager = self.0.clone();
+    FutureResult::new(async move {
+      let data = DocumentDataPB::try_from(Bytes::from(bytes))?;
       manager.create_document(view_id, data.into())?;
       Ok(())
     })
   }
+
+  // will implement soon
+  fn import_from_file_path(
+    &self,
+    _view_id: &str,
+    _name: &str,
+    _path: String,
+  ) -> FutureResult<(), FlowyError> {
+    FutureResult::new(async move { Ok(()) })
+  }
 }
 
-struct DatabaseViewDataProcessor(Arc<DatabaseManager2>);
-impl ViewDataProcessor for DatabaseViewDataProcessor {
+struct DatabaseFolderOperation(Arc<DatabaseManager2>);
+impl FolderOperationHandler for DatabaseFolderOperation {
   fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError> {
     let database_manager = self.0.clone();
     let view_id = view_id.to_string();
@@ -158,7 +180,7 @@ impl ViewDataProcessor for DatabaseViewDataProcessor {
     })
   }
 
-  fn get_view_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
+  fn duplicate_view(&self, view_id: &str) -> FutureResult<Bytes, FlowyError> {
     let database_manager = self.0.clone();
     let view_id = view_id.to_owned();
     FutureResult::new(async move {
@@ -167,40 +189,10 @@ impl ViewDataProcessor for DatabaseViewDataProcessor {
     })
   }
 
-  /// Create a database view with build-in data.
-  /// If the ext contains the {"database_id": "xx"}, then it will link to
-  /// the existing database. The data of the database will be shared within
-  /// these references views.
-  fn create_view_with_built_in_data(
-    &self,
-    _user_id: i64,
-    view_id: &str,
-    name: &str,
-    layout: ViewLayout,
-    _ext: HashMap<String, String>,
-  ) -> FutureResult<(), FlowyError> {
-    let name = name.to_string();
-    let database_manager = self.0.clone();
-    let data = match layout {
-      ViewLayout::Grid => make_default_grid(view_id, &name),
-      ViewLayout::Board => make_default_board(view_id, &name),
-      ViewLayout::Calendar => make_default_calendar(view_id, &name),
-      ViewLayout::Document => {
-        return FutureResult::new(async move {
-          Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
-        });
-      },
-    };
-    FutureResult::new(async move {
-      database_manager.create_database_with_params(data).await?;
-      Ok(())
-    })
-  }
-
   /// Create a database view with duplicated data.
   /// If the ext contains the {"database_id": "xx"}, then it will link
   /// to the existing database.
-  fn create_view_with_custom_data(
+  fn create_view_with_view_data(
     &self,
     _user_id: i64,
     view_id: &str,
@@ -241,6 +233,68 @@ impl ViewDataProcessor for DatabaseViewDataProcessor {
       },
     }
   }
+
+  /// Create a database view with build-in data.
+  /// If the ext contains the {"database_id": "xx"}, then it will link to
+  /// the existing database. The data of the database will be shared within
+  /// these references views.
+  fn create_built_in_view(
+    &self,
+    _user_id: i64,
+    view_id: &str,
+    name: &str,
+    layout: ViewLayout,
+    _meta: HashMap<String, String>,
+  ) -> FutureResult<(), FlowyError> {
+    let name = name.to_string();
+    let database_manager = self.0.clone();
+    let data = match layout {
+      ViewLayout::Grid => make_default_grid(view_id, &name),
+      ViewLayout::Board => make_default_board(view_id, &name),
+      ViewLayout::Calendar => make_default_calendar(view_id, &name),
+      ViewLayout::Document => {
+        return FutureResult::new(async move {
+          Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
+        });
+      },
+    };
+    FutureResult::new(async move {
+      database_manager.create_database_with_params(data).await?;
+      Ok(())
+    })
+  }
+
+  fn import_from_bytes(
+    &self,
+    view_id: &str,
+    _name: &str,
+    bytes: Vec<u8>,
+  ) -> FutureResult<(), FlowyError> {
+    let database_manager = self.0.clone();
+    let view_id = view_id.to_string();
+    FutureResult::new(async move {
+      let content = String::from_utf8(bytes).map_err(|err| FlowyError::internal().context(err))?;
+      database_manager
+        .import_csv(view_id, content, CSVFormat::META)
+        .await?;
+      Ok(())
+    })
+  }
+
+  fn import_from_file_path(
+    &self,
+    _view_id: &str,
+    _name: &str,
+    path: String,
+  ) -> FutureResult<(), FlowyError> {
+    let database_manager = self.0.clone();
+    FutureResult::new(async move {
+      database_manager
+        .import_csv_from_file(path, CSVFormat::META)
+        .await?;
+      Ok(())
+    })
+  }
 }
 
 #[derive(Debug, serde::Deserialize)]

+ 0 - 361
frontend/rust-lib/flowy-core/src/deps_resolve/folder_deps.rs

@@ -1,361 +0,0 @@
-use bytes::Bytes;
-use flowy_sqlite::ConnectionPool;
-
-use database_model::BuildDatabaseContext;
-use flowy_client_ws::FlowyWebSocketConnect;
-use flowy_database::entities::LayoutTypePB;
-use flowy_database::manager::{create_new_database, link_existing_database, DatabaseManager};
-use flowy_database::util::{make_default_board, make_default_calendar, make_default_grid};
-use flowy_document::editor::make_transaction_from_document_content;
-use flowy_document::DocumentManager;
-
-use flowy_folder::entities::{ViewDataFormatPB, ViewLayoutTypePB, ViewPB};
-use flowy_folder::manager::{ViewDataProcessor, ViewDataProcessorMap};
-use flowy_folder::{
-  errors::{internal_error, FlowyError},
-  event_map::{FolderCouldServiceV1, WorkspaceDatabase, WorkspaceUser},
-  manager::FolderManager,
-};
-use flowy_net::ClientServerConfiguration;
-use flowy_net::{http_server::folder::FolderHttpCloudService, local_server::LocalServer};
-use flowy_revision::{RevisionWebSocket, WSStateReceiver};
-use flowy_user::services::UserSession;
-use futures_core::future::BoxFuture;
-use lib_infra::future::{BoxResultFuture, FutureResult};
-use lib_ws::{WSChannel, WSMessageReceiver, WebSocketRawMessage};
-use revision_model::Revision;
-use std::collections::HashMap;
-use std::convert::TryFrom;
-use std::{convert::TryInto, sync::Arc};
-use ws_model::ws_revision::ClientRevisionWSData;
-
-pub struct FolderDepsResolver();
-impl FolderDepsResolver {
-  pub async fn resolve(
-    local_server: Option<Arc<LocalServer>>,
-    user_session: Arc<UserSession>,
-    server_config: &ClientServerConfiguration,
-    ws_conn: &Arc<FlowyWebSocketConnect>,
-    text_block_manager: &Arc<DocumentManager>,
-    database_manager: &Arc<DatabaseManager>,
-  ) -> Arc<FolderManager> {
-    let user: Arc<dyn WorkspaceUser> = Arc::new(WorkspaceUserImpl(user_session.clone()));
-    let database: Arc<dyn WorkspaceDatabase> = Arc::new(WorkspaceDatabaseImpl(user_session));
-    let web_socket = Arc::new(FolderRevisionWebSocket(ws_conn.clone()));
-    let cloud_service: Arc<dyn FolderCouldServiceV1> = match local_server {
-      None => Arc::new(FolderHttpCloudService::new(server_config.clone())),
-      Some(local_server) => local_server,
-    };
-
-    let view_data_processor =
-      make_view_data_processor(text_block_manager.clone(), database_manager.clone());
-    let folder_manager = Arc::new(
-      FolderManager::new(
-        user.clone(),
-        cloud_service,
-        database,
-        view_data_processor,
-        web_socket,
-      )
-      .await,
-    );
-
-    // if let (Ok(user_id), Ok(token)) = (user.user_id(), user.token()) {
-    //   match folder_manager.initialize(&user_id, &token).await {
-    //     Ok(_) => {},
-    //     Err(e) => tracing::error!("Initialize folder manager failed: {}", e),
-    //   }
-    // }
-
-    let receiver = Arc::new(FolderWSMessageReceiverImpl(folder_manager.clone()));
-    ws_conn.add_ws_message_receiver(receiver).unwrap();
-    folder_manager
-  }
-}
-
-fn make_view_data_processor(
-  document_manager: Arc<DocumentManager>,
-  database_manager: Arc<DatabaseManager>,
-) -> ViewDataProcessorMap {
-  let mut map: HashMap<ViewDataFormatPB, Arc<dyn ViewDataProcessor + Send + Sync>> = HashMap::new();
-
-  let document_processor = Arc::new(DocumentViewDataProcessor(document_manager));
-  document_processor
-    .data_types()
-    .into_iter()
-    .for_each(|data_type| {
-      map.insert(data_type, document_processor.clone());
-    });
-
-  let grid_data_impl = Arc::new(DatabaseViewDataProcessor(database_manager));
-  grid_data_impl
-    .data_types()
-    .into_iter()
-    .for_each(|data_type| {
-      map.insert(data_type, grid_data_impl.clone());
-    });
-
-  Arc::new(map)
-}
-
-struct WorkspaceDatabaseImpl(Arc<UserSession>);
-impl WorkspaceDatabase for WorkspaceDatabaseImpl {
-  fn db_pool(&self) -> Result<Arc<ConnectionPool>, FlowyError> {
-    self
-      .0
-      .db_pool()
-      .map_err(|e| FlowyError::internal().context(e))
-  }
-}
-
-struct WorkspaceUserImpl(Arc<UserSession>);
-impl WorkspaceUser for WorkspaceUserImpl {
-  fn user_id(&self) -> Result<String, FlowyError> {
-    self
-      .0
-      .user_id()
-      .map_err(|e| FlowyError::internal().context(e))
-  }
-
-  fn token(&self) -> Result<Option<String>, FlowyError> {
-    self
-      .0
-      .token()
-      .map_err(|e| FlowyError::internal().context(e))
-  }
-}
-
-struct FolderRevisionWebSocket(Arc<FlowyWebSocketConnect>);
-impl RevisionWebSocket for FolderRevisionWebSocket {
-  fn send(&self, data: ClientRevisionWSData) -> BoxResultFuture<(), FlowyError> {
-    let bytes: Bytes = data.try_into().unwrap();
-    let msg = WebSocketRawMessage {
-      channel: WSChannel::Folder,
-      data: bytes.to_vec(),
-    };
-
-    let ws_conn = self.0.clone();
-    Box::pin(async move {
-      match ws_conn.web_socket().await? {
-        None => {},
-        Some(sender) => {
-          sender.send(msg).map_err(internal_error)?;
-        },
-      }
-      Ok(())
-    })
-  }
-
-  fn subscribe_state_changed(&self) -> BoxFuture<WSStateReceiver> {
-    let ws_conn = self.0.clone();
-    Box::pin(async move { ws_conn.subscribe_websocket_state().await })
-  }
-}
-
-struct FolderWSMessageReceiverImpl(Arc<FolderManager>);
-impl WSMessageReceiver for FolderWSMessageReceiverImpl {
-  fn source(&self) -> WSChannel {
-    WSChannel::Folder
-  }
-  fn receive_message(&self, msg: WebSocketRawMessage) {
-    let handler = self.0.clone();
-    tokio::spawn(async move {
-      handler.did_receive_ws_data(Bytes::from(msg.data)).await;
-    });
-  }
-}
-
-struct DocumentViewDataProcessor(Arc<DocumentManager>);
-impl ViewDataProcessor for DocumentViewDataProcessor {
-  fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError> {
-    let manager = self.0.clone();
-    let view_id = view_id.to_string();
-    FutureResult::new(async move {
-      manager.close_document_editor(view_id).await?;
-      Ok(())
-    })
-  }
-
-  fn get_view_data(&self, view: &ViewPB) -> FutureResult<Bytes, FlowyError> {
-    let view_id = view.id.clone();
-    let manager = self.0.clone();
-    FutureResult::new(async move {
-      let editor = manager.open_document_editor(view_id).await?;
-      let document_data = Bytes::from(editor.duplicate().await?);
-      Ok(document_data)
-    })
-  }
-
-  fn create_view_with_built_in_data(
-    &self,
-    user_id: &str,
-    view_id: &str,
-    _name: &str,
-    layout: ViewLayoutTypePB,
-    _data_format: ViewDataFormatPB,
-    _ext: HashMap<String, String>,
-  ) -> FutureResult<(), FlowyError> {
-    debug_assert_eq!(layout, ViewLayoutTypePB::Document);
-    let _user_id = user_id.to_string();
-    let view_id = view_id.to_string();
-    let manager = self.0.clone();
-    // todo: implement the default content
-    FutureResult::new(async move {
-      let delta_data = Bytes::from(document_content);
-      let revision = Revision::initial_revision(&view_id, delta_data);
-      manager.create_document(view_id, vec![revision]).await?;
-      Ok(())
-    })
-  }
-
-  fn create_view_with_custom_data(
-    &self,
-    _user_id: &str,
-    view_id: &str,
-    _name: &str,
-    data: Vec<u8>,
-    layout: ViewLayoutTypePB,
-    _ext: HashMap<String, String>,
-  ) -> FutureResult<(), FlowyError> {
-    debug_assert_eq!(layout, ViewLayoutTypePB::Document);
-    let view_data = match String::from_utf8(data) {
-      Ok(content) => match make_transaction_from_document_content(&content) {
-        Ok(transaction) => transaction.to_bytes().unwrap_or_else(|_| vec![]),
-        Err(_) => vec![],
-      },
-      Err(_) => vec![],
-    };
-
-    let revision = Revision::initial_revision(view_id, Bytes::from(view_data));
-    let view_id = view_id.to_string();
-    let manager = self.0.clone();
-
-    FutureResult::new(async move {
-      manager.create_document(view_id, vec![revision]).await?;
-      Ok(())
-    })
-  }
-
-  fn data_types(&self) -> Vec<ViewDataFormatPB> {
-    vec![ViewDataFormatPB::DeltaFormat, ViewDataFormatPB::NodeFormat]
-  }
-}
-
-struct DatabaseViewDataProcessor(Arc<DatabaseManager>);
-impl ViewDataProcessor for DatabaseViewDataProcessor {
-  fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError> {
-    let database_manager = self.0.clone();
-    let view_id = view_id.to_string();
-    FutureResult::new(async move {
-      database_manager.close_database_view(view_id).await?;
-      Ok(())
-    })
-  }
-
-  fn get_view_data(&self, view: &ViewPB) -> FutureResult<Bytes, FlowyError> {
-    let database_manager = self.0.clone();
-    let view_id = view.id.clone();
-    FutureResult::new(async move {
-      let editor = database_manager.open_database_view(&view_id).await?;
-      let delta_bytes = editor.duplicate_database(&view_id).await?;
-      Ok(delta_bytes.into())
-    })
-  }
-
-  /// Create a database view with build-in data.
-  /// If the ext contains the {"database_id": "xx"}, then it will link to
-  /// the existing database. The data of the database will be shared within
-  /// these references views.
-  fn create_view_with_built_in_data(
-    &self,
-    _user_id: &str,
-    view_id: &str,
-    name: &str,
-    layout: ViewLayoutTypePB,
-    data_format: ViewDataFormatPB,
-    ext: HashMap<String, String>,
-  ) -> FutureResult<(), FlowyError> {
-    debug_assert_eq!(data_format, ViewDataFormatPB::DatabaseFormat);
-    let view_id = view_id.to_string();
-    let name = name.to_string();
-    let database_manager = self.0.clone();
-    match DatabaseExtParams::from_map(ext).map(|params| params.database_id) {
-      None => {
-        let (build_context, layout) = match layout {
-          ViewLayoutTypePB::Grid => (make_default_grid(), LayoutTypePB::Grid),
-          ViewLayoutTypePB::Board => (make_default_board(), LayoutTypePB::Board),
-          ViewLayoutTypePB::Calendar => (make_default_calendar(), LayoutTypePB::Calendar),
-          ViewLayoutTypePB::Document => {
-            return FutureResult::new(async move {
-              Err(FlowyError::internal().context(format!("Can't handle {:?} layout type", layout)))
-            });
-          },
-        };
-        FutureResult::new(async move {
-          create_new_database(&view_id, name, layout, database_manager, build_context).await
-        })
-      },
-      Some(database_id) => {
-        let layout = layout_type_from_view_layout(layout);
-        FutureResult::new(async move {
-          link_existing_database(&view_id, name, &database_id, layout, database_manager).await
-        })
-      },
-    }
-  }
-
-  /// Create a database view with custom data.
-  /// If the ext contains the {"database_id": "xx"}, then it will link
-  /// to the existing database. The data of the database will be shared
-  /// within these references views.
-  fn create_view_with_custom_data(
-    &self,
-    _user_id: &str,
-    view_id: &str,
-    name: &str,
-    data: Vec<u8>,
-    layout: ViewLayoutTypePB,
-    ext: HashMap<String, String>,
-  ) -> FutureResult<(), FlowyError> {
-    let view_id = view_id.to_string();
-    let database_manager = self.0.clone();
-    let layout = layout_type_from_view_layout(layout);
-    let name = name.to_string();
-    match DatabaseExtParams::from_map(ext).map(|params| params.database_id) {
-      None => FutureResult::new(async move {
-        let bytes = Bytes::from(data);
-        let build_context = BuildDatabaseContext::try_from(bytes)?;
-        let _ = create_new_database(&view_id, name, layout, database_manager, build_context).await;
-        Ok(())
-      }),
-      Some(database_id) => FutureResult::new(async move {
-        link_existing_database(&view_id, name, &database_id, layout, database_manager).await
-      }),
-    }
-  }
-
-  fn data_types(&self) -> Vec<ViewDataFormatPB> {
-    vec![ViewDataFormatPB::DatabaseFormat]
-  }
-}
-
-pub fn layout_type_from_view_layout(layout: ViewLayoutTypePB) -> LayoutTypePB {
-  match layout {
-    ViewLayoutTypePB::Grid => LayoutTypePB::Grid,
-    ViewLayoutTypePB::Board => LayoutTypePB::Board,
-    ViewLayoutTypePB::Calendar => LayoutTypePB::Calendar,
-    ViewLayoutTypePB::Document => LayoutTypePB::Grid,
-  }
-}
-
-#[derive(Debug, serde::Deserialize)]
-struct DatabaseExtParams {
-  database_id: String,
-}
-
-impl DatabaseExtParams {
-  pub fn from_map(map: HashMap<String, String>) -> Option<Self> {
-    let value = serde_json::to_value(map).ok()?;
-    serde_json::from_value::<Self>(value).ok()
-  }
-}

+ 0 - 2
frontend/rust-lib/flowy-database2/src/entities/mod.rs

@@ -12,7 +12,6 @@ mod view_entities;
 
 #[macro_use]
 mod macros;
-mod share_entities;
 mod type_option_entities;
 
 pub use calendar_entities::*;
@@ -23,7 +22,6 @@ pub use filter_entities::*;
 pub use group_entities::*;
 pub use row_entities::*;
 pub use setting_entities::*;
-pub use share_entities::*;
 pub use sort_entities::*;
 pub use type_option_entities::*;
 pub use view_entities::*;

+ 0 - 24
frontend/rust-lib/flowy-database2/src/entities/share_entities.rs

@@ -1,24 +0,0 @@
-use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
-
-#[derive(Clone, Debug, ProtoBuf_Enum)]
-pub enum ImportTypePB {
-  CSV = 0,
-}
-
-impl Default for ImportTypePB {
-  fn default() -> Self {
-    Self::CSV
-  }
-}
-
-#[derive(Clone, Debug, ProtoBuf, Default)]
-pub struct DatabaseImportPB {
-  #[pb(index = 1, one_of)]
-  pub data: Option<String>,
-
-  #[pb(index = 2, one_of)]
-  pub uri: Option<String>,
-
-  #[pb(index = 3)]
-  pub import_type: ImportTypePB,
-}

+ 1 - 26
frontend/rust-lib/flowy-database2/src/event_handler.rs

@@ -5,7 +5,7 @@ use collab_database::rows::RowId;
 use collab_database::views::DatabaseLayout;
 use lib_infra::util::timestamp;
 
-use flowy_error::{ErrorCode, FlowyError, FlowyResult};
+use flowy_error::{FlowyError, FlowyResult};
 use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
 
 use crate::entities::*;
@@ -17,7 +17,6 @@ use crate::services::field::{
   type_option_data_from_pb_or_default, DateCellChangeset, SelectOptionCellChangeset,
 };
 use crate::services::group::{GroupChangeset, GroupSettingChangeset};
-use crate::services::share::csv::CSVFormat;
 
 #[tracing::instrument(level = "trace", skip_all, err)]
 pub(crate) async fn get_database_data_handler(
@@ -678,27 +677,3 @@ pub(crate) async fn get_calendar_event_handler(
     Some(event) => data_result_ok(event),
   }
 }
-
-#[tracing::instrument(level = "debug", skip(data, manager), err)]
-pub(crate) async fn import_data_handler(
-  data: AFPluginData<DatabaseImportPB>,
-  manager: AFPluginState<Arc<DatabaseManager2>>,
-) -> FlowyResult<()> {
-  let params = data.into_inner();
-
-  match params.import_type {
-    ImportTypePB::CSV => {
-      if let Some(data) = params.data {
-        manager.import_csv(data, CSVFormat::META).await?;
-      } else if let Some(uri) = params.uri {
-        manager.import_csv_from_uri(uri, CSVFormat::META).await?;
-      } else {
-        return Err(FlowyError::new(
-          ErrorCode::InvalidData,
-          "No data or uri provided",
-        ));
-      }
-    },
-  }
-  Ok(())
-}

+ 3 - 9
frontend/rust-lib/flowy-database2/src/event_map.rs

@@ -9,10 +9,10 @@ use crate::event_handler::*;
 use crate::manager::DatabaseManager2;
 
 pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
-  let mut plugin = AFPlugin::new()
+  let plugin = AFPlugin::new()
     .name(env!("CARGO_PKG_NAME"))
     .state(database_manager);
-  plugin = plugin
+  plugin
         .event(DatabaseEvent::GetDatabase, get_database_data_handler)
         .event(DatabaseEvent::GetDatabaseSetting, get_database_setting_handler)
         .event(DatabaseEvent::UpdateDatabaseSetting, update_database_setting_handler)
@@ -64,10 +64,7 @@ pub fn init(database_manager: Arc<DatabaseManager2>) -> AFPlugin {
         // Layout setting
         .event(DatabaseEvent::SetLayoutSetting, set_layout_setting_handler)
         .event(DatabaseEvent::GetLayoutSetting, get_layout_setting_handler)
-        // import
-        .event(DatabaseEvent::ImportCSV, import_data_handler);
-
-  plugin
+  // import
 }
 
 /// [DatabaseEvent] defines events that are used to interact with the Grid. You could check [this](https://appflowy.gitbook.io/docs/essential-documentation/contribute-to-appflowy/architecture/backend/protobuf)
@@ -278,7 +275,4 @@ pub enum DatabaseEvent {
 
   #[event(input = "MoveCalendarEventPB")]
   MoveCalendarEvent = 125,
-
-  #[event(input = "DatabaseImportPB")]
-  ImportCSV = 130,
 }

+ 17 - 6
frontend/rust-lib/flowy-database2/src/manager.rs

@@ -195,11 +195,17 @@ impl DatabaseManager2 {
     Ok(())
   }
 
-  pub async fn import_csv(&self, content: String, format: CSVFormat) -> FlowyResult<ImportResult> {
-    let params =
-      tokio::task::spawn_blocking(move || CSVImporter.import_csv_from_string(content, format))
-        .await
-        .map_err(internal_error)??;
+  pub async fn import_csv(
+    &self,
+    view_id: String,
+    content: String,
+    format: CSVFormat,
+  ) -> FlowyResult<ImportResult> {
+    let params = tokio::task::spawn_blocking(move || {
+      CSVImporter.import_csv_from_string(view_id, content, format)
+    })
+    .await
+    .map_err(internal_error)??;
     let result = ImportResult {
       database_id: params.database_id.clone(),
       view_id: params.view_id.clone(),
@@ -208,7 +214,12 @@ impl DatabaseManager2 {
     Ok(result)
   }
 
-  pub async fn import_csv_from_uri(&self, _uri: String, _format: CSVFormat) -> FlowyResult<()> {
+  // will implement soon
+  pub async fn import_csv_from_file(
+    &self,
+    _file_path: String,
+    _format: CSVFormat,
+  ) -> FlowyResult<()> {
     Ok(())
   }
 

+ 9 - 6
frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs

@@ -2,7 +2,7 @@ use crate::entities::FieldType;
 
 use crate::services::field::{default_type_option_data_from_type, CELL_DATA};
 use crate::services::share::csv::CSVFormat;
-use collab_database::database::{gen_database_id, gen_database_view_id, gen_field_id, gen_row_id};
+use collab_database::database::{gen_database_id, gen_field_id, gen_row_id};
 use collab_database::fields::Field;
 use collab_database::rows::{new_cell_builder, Cell, CreateRowParams};
 use collab_database::views::{CreateDatabaseParams, DatabaseLayout};
@@ -15,6 +15,7 @@ pub struct CSVImporter;
 impl CSVImporter {
   pub fn import_csv_from_file(
     &self,
+    view_id: &str,
     path: &str,
     style: CSVFormat,
   ) -> FlowyResult<CreateDatabaseParams> {
@@ -22,17 +23,18 @@ impl CSVImporter {
     let mut content = String::new();
     file.read_to_string(&mut content)?;
     let fields_with_rows = self.get_fields_and_rows(content)?;
-    let database_data = database_from_fields_and_rows(fields_with_rows, &style);
+    let database_data = database_from_fields_and_rows(view_id, fields_with_rows, &style);
     Ok(database_data)
   }
 
   pub fn import_csv_from_string(
     &self,
+    view_id: String,
     content: String,
     format: CSVFormat,
   ) -> FlowyResult<CreateDatabaseParams> {
     let fields_with_rows = self.get_fields_and_rows(content)?;
-    let database_data = database_from_fields_and_rows(fields_with_rows, &format);
+    let database_data = database_from_fields_and_rows(&view_id, fields_with_rows, &format);
     Ok(database_data)
   }
 
@@ -68,11 +70,11 @@ impl CSVImporter {
 }
 
 fn database_from_fields_and_rows(
+  view_id: &str,
   fields_and_rows: FieldsRows,
   format: &CSVFormat,
 ) -> CreateDatabaseParams {
   let (fields, rows) = fields_and_rows.split();
-  let view_id = gen_database_view_id();
   let database_id = gen_database_id();
 
   let fields = fields
@@ -125,7 +127,7 @@ fn database_from_fields_and_rows(
 
   CreateDatabaseParams {
     database_id,
-    view_id,
+    view_id: view_id.to_string(),
     name: "".to_string(),
     layout: DatabaseLayout::Grid,
     layout_settings: Default::default(),
@@ -167,6 +169,7 @@ pub struct ImportResult {
 #[cfg(test)]
 mod tests {
   use crate::services::share::csv::{CSVFormat, CSVImporter};
+  use collab_database::database::gen_database_view_id;
 
   #[test]
   fn test_import_csv_from_str() {
@@ -176,7 +179,7 @@ mod tests {
 ,,,,Yes,"#;
     let importer = CSVImporter;
     let result = importer
-      .import_csv_from_string(s.to_string(), CSVFormat::Original)
+      .import_csv_from_string(gen_database_view_id(), s.to_string(), CSVFormat::Original)
       .unwrap();
     assert_eq!(result.created_rows.len(), 3);
     assert_eq!(result.fields.len(), 6);

+ 2 - 1
frontend/rust-lib/flowy-database2/tests/database/database_editor.rs

@@ -1,3 +1,4 @@
+use collab_database::database::gen_database_view_id;
 use std::collections::HashMap;
 use std::sync::Arc;
 
@@ -262,7 +263,7 @@ impl DatabaseEditorTest {
     self
       .sdk
       .database_manager
-      .import_csv(s, format)
+      .import_csv(gen_database_view_id(), s, format)
       .await
       .unwrap()
   }

+ 82 - 0
frontend/rust-lib/flowy-folder2/src/entities/import.rs

@@ -0,0 +1,82 @@
+use crate::entities::parser::empty_str::NotEmptyStr;
+use crate::entities::ViewLayoutPB;
+use crate::share::{ImportParams, ImportType};
+
+use flowy_derive::{ProtoBuf, ProtoBuf_Enum};
+use flowy_error::FlowyError;
+
+#[derive(Clone, Debug, ProtoBuf_Enum)]
+pub enum ImportTypePB {
+  HistoryDocument = 0,
+  HistoryDatabase = 1,
+}
+
+impl From<ImportTypePB> for ImportType {
+  fn from(pb: ImportTypePB) -> Self {
+    match pb {
+      ImportTypePB::HistoryDocument => ImportType::HistoryDocument,
+      ImportTypePB::HistoryDatabase => ImportType::HistoryDatabase,
+    }
+  }
+}
+
+impl Default for ImportTypePB {
+  fn default() -> Self {
+    Self::HistoryDocument
+  }
+}
+
+#[derive(Clone, Debug, ProtoBuf, Default)]
+pub struct ImportPB {
+  #[pb(index = 1)]
+  pub parent_view_id: String,
+
+  #[pb(index = 2)]
+  pub name: String,
+
+  #[pb(index = 3, one_of)]
+  pub data: Option<Vec<u8>>,
+
+  #[pb(index = 4, one_of)]
+  pub file_path: Option<String>,
+
+  #[pb(index = 5)]
+  pub view_layout: ViewLayoutPB,
+
+  #[pb(index = 6)]
+  pub import_type: ImportTypePB,
+}
+
+impl TryInto<ImportParams> for ImportPB {
+  type Error = FlowyError;
+
+  fn try_into(self) -> Result<ImportParams, Self::Error> {
+    let parent_view_id = NotEmptyStr::parse(self.parent_view_id)
+      .map_err(|_| FlowyError::invalid_view_id())?
+      .0;
+
+    let name = if self.name.is_empty() {
+      "Untitled".to_string()
+    } else {
+      self.name
+    };
+
+    let file_path = match self.file_path {
+      None => None,
+      Some(file_path) => Some(
+        NotEmptyStr::parse(file_path)
+          .map_err(|_| FlowyError::invalid_data().context("The import file path is empty"))?
+          .0,
+      ),
+    };
+
+    Ok(ImportParams {
+      parent_view_id,
+      name,
+      data: self.data,
+      file_path,
+      view_layout: self.view_layout.into(),
+      import_type: self.import_type.into(),
+    })
+  }
+}

+ 2 - 0
frontend/rust-lib/flowy-folder2/src/entities/mod.rs

@@ -1,8 +1,10 @@
+mod import;
 mod parser;
 pub mod trash;
 pub mod view;
 pub mod workspace;
 
+pub use import::*;
 pub use trash::*;
 pub use view::*;
 pub use workspace::*;

+ 17 - 0
frontend/rust-lib/flowy-folder2/src/entities/parser/empty_str.rs

@@ -0,0 +1,17 @@
+#[derive(Debug)]
+pub struct NotEmptyStr(pub String);
+
+impl NotEmptyStr {
+  pub fn parse(s: String) -> Result<Self, String> {
+    if s.trim().is_empty() {
+      return Err("Input string is empty".to_owned());
+    }
+    Ok(Self(s))
+  }
+}
+
+impl AsRef<str> for NotEmptyStr {
+  fn as_ref(&self) -> &str {
+    &self.0
+  }
+}

+ 1 - 0
frontend/rust-lib/flowy-folder2/src/entities/parser/mod.rs

@@ -1,4 +1,5 @@
 // pub mod app;
+pub mod empty_str;
 pub mod trash;
 pub mod view;
 pub mod workspace;

+ 6 - 6
frontend/rust-lib/flowy-folder2/src/entities/view.rs

@@ -123,7 +123,7 @@ pub struct RepeatedViewIdPB {
 #[derive(Default, ProtoBuf)]
 pub struct CreateViewPayloadPB {
   #[pb(index = 1)]
-  pub belong_to_id: String,
+  pub parent_view_id: String,
 
   #[pb(index = 2)]
   pub name: String,
@@ -146,13 +146,13 @@ pub struct CreateViewPayloadPB {
 
 #[derive(Debug, Clone)]
 pub struct CreateViewParams {
-  pub belong_to_id: String,
+  pub parent_view_id: String,
   pub name: String,
   pub desc: String,
   pub layout: ViewLayoutPB,
   pub view_id: String,
   pub initial_data: Vec<u8>,
-  pub ext: HashMap<String, String>,
+  pub meta: HashMap<String, String>,
 }
 
 impl TryInto<CreateViewParams> for CreateViewPayloadPB {
@@ -160,17 +160,17 @@ impl TryInto<CreateViewParams> for CreateViewPayloadPB {
 
   fn try_into(self) -> Result<CreateViewParams, Self::Error> {
     let name = ViewName::parse(self.name)?.0;
-    let belong_to_id = ViewIdentify::parse(self.belong_to_id)?.0;
+    let belong_to_id = ViewIdentify::parse(self.parent_view_id)?.0;
     let view_id = gen_view_id();
 
     Ok(CreateViewParams {
-      belong_to_id,
+      parent_view_id: belong_to_id,
       name,
       desc: self.desc,
       layout: self.layout,
       view_id,
       initial_data: self.initial_data,
-      ext: self.ext,
+      meta: self.ext,
     })
   }
 }

+ 12 - 1
frontend/rust-lib/flowy-folder2/src/event_handler.rs

@@ -1,12 +1,13 @@
 use crate::entities::{
   view_pb_without_child_views, CreateViewParams, CreateViewPayloadPB, CreateWorkspaceParams,
-  CreateWorkspacePayloadPB, MoveFolderItemPayloadPB, MoveViewParams, RepeatedTrashIdPB,
+  CreateWorkspacePayloadPB, ImportPB, MoveFolderItemPayloadPB, MoveViewParams, RepeatedTrashIdPB,
   RepeatedTrashPB, RepeatedViewIdPB, RepeatedViewPB, RepeatedWorkspacePB, TrashIdPB,
   UpdateViewParams, UpdateViewPayloadPB, ViewIdPB, ViewPB, WorkspaceIdPB, WorkspacePB,
   WorkspaceSettingPB,
 };
 use crate::manager::Folder2Manager;
 
+use crate::share::ImportParams;
 use flowy_error::FlowyError;
 use lib_dispatch::prelude::{data_result_ok, AFPluginData, AFPluginState, DataResult};
 use std::sync::Arc;
@@ -203,3 +204,13 @@ pub(crate) async fn delete_all_trash_handler(
   folder.delete_all_trash().await;
   Ok(())
 }
+
+#[tracing::instrument(level = "debug", skip(data, folder), err)]
+pub(crate) async fn import_data_handler(
+  data: AFPluginData<ImportPB>,
+  folder: AFPluginState<Arc<Folder2Manager>>,
+) -> Result<(), FlowyError> {
+  let params: ImportParams = data.into_inner().try_into()?;
+  folder.import(params).await?;
+  Ok(())
+}

+ 18 - 14
frontend/rust-lib/flowy-folder2/src/event_map.rs

@@ -33,6 +33,7 @@ pub fn init(folder: Arc<Folder2Manager>) -> AFPlugin {
     .event(FolderEvent::DeleteTrash, delete_trash_handler)
     .event(FolderEvent::RestoreAllTrash, restore_all_trash_handler)
     .event(FolderEvent::DeleteAllTrash, delete_all_trash_handler)
+    .event(FolderEvent::ImportData, import_data_handler)
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Display, Hash, ProtoBuf_Enum, Flowy_Event)]
@@ -64,57 +65,60 @@ pub enum FolderEvent {
 
   /// Create a new view in the corresponding app
   #[event(input = "CreateViewPayloadPB", output = "ViewPB")]
-  CreateView = 201,
+  CreateView = 10,
 
   /// Return the view info
   #[event(input = "ViewIdPB", output = "ViewPB")]
-  ReadView = 202,
+  ReadView = 11,
 
   /// Update the view's properties including the name,description, etc.
   #[event(input = "UpdateViewPayloadPB", output = "ViewPB")]
-  UpdateView = 203,
+  UpdateView = 12,
 
   /// Move the view to the trash folder
   #[event(input = "RepeatedViewIdPB")]
-  DeleteView = 204,
+  DeleteView = 13,
 
   /// Duplicate the view
   #[event(input = "ViewPB")]
-  DuplicateView = 205,
+  DuplicateView = 14,
 
   /// Close and release the resources that are used by this view.
   /// It should get called when the 'View' page get destroy
   #[event(input = "ViewIdPB")]
-  CloseView = 206,
+  CloseView = 15,
 
   #[event()]
-  CopyLink = 220,
+  CopyLink = 20,
 
   /// Set the current visiting view
   #[event(input = "ViewIdPB")]
-  SetLatestView = 221,
+  SetLatestView = 21,
 
   /// Move the view or app to another place
   #[event(input = "MoveFolderItemPayloadPB")]
-  MoveItem = 230,
+  MoveItem = 22,
 
   /// Read the trash that was deleted by the user
   #[event(output = "RepeatedTrashPB")]
-  ReadTrash = 300,
+  ReadTrash = 23,
 
   /// Put back the trash to the origin folder
   #[event(input = "TrashIdPB")]
-  PutbackTrash = 301,
+  PutbackTrash = 24,
 
   /// Delete the trash from the disk
   #[event(input = "RepeatedTrashIdPB")]
-  DeleteTrash = 302,
+  DeleteTrash = 25,
 
   /// Put back all the trash to its original folder
   #[event()]
-  RestoreAllTrash = 303,
+  RestoreAllTrash = 26,
 
   /// Delete all the trash from the disk
   #[event()]
-  DeleteAllTrash = 304,
+  DeleteAllTrash = 27,
+
+  #[event(input = "ImportPB")]
+  ImportData = 30,
 }

+ 1 - 0
frontend/rust-lib/flowy-folder2/src/lib.rs

@@ -8,6 +8,7 @@ mod user_default;
 pub mod view_ext;
 
 pub mod deps;
+mod share;
 #[cfg(feature = "test_helper")]
 mod test_helper;
 

+ 69 - 28
frontend/rust-lib/flowy-folder2/src/manager.rs

@@ -1,4 +1,5 @@
 use std::collections::{HashMap, HashSet};
+
 use std::ops::Deref;
 use std::sync::Arc;
 
@@ -12,7 +13,7 @@ use parking_lot::Mutex;
 use tracing::{event, Level};
 
 use crate::deps::{FolderCloudService, FolderUser};
-use flowy_error::{FlowyError, FlowyResult};
+use flowy_error::{ErrorCode, FlowyError, FlowyResult};
 use lib_infra::util::timestamp;
 
 use crate::entities::{
@@ -23,16 +24,15 @@ use crate::notification::{
   send_notification, send_workspace_notification, send_workspace_setting_notification,
   FolderNotification,
 };
+use crate::share::ImportParams;
 use crate::user_default::DefaultFolderBuilder;
-use crate::view_ext::{
-  gen_view_id, view_from_create_view_params, ViewDataProcessor, ViewDataProcessorMap,
-};
+use crate::view_ext::{create_view, gen_view_id, FolderOperationHandler, FolderOperationHandlers};
 
 pub struct Folder2Manager {
   folder: Folder,
   collab_builder: Arc<AppFlowyCollabBuilder>,
   user: Arc<dyn FolderUser>,
-  view_processors: ViewDataProcessorMap,
+  operation_handlers: FolderOperationHandlers,
   cloud_service: Arc<dyn FolderCloudService>,
 }
 
@@ -43,7 +43,7 @@ impl Folder2Manager {
   pub async fn new(
     user: Arc<dyn FolderUser>,
     collab_builder: Arc<AppFlowyCollabBuilder>,
-    view_processors: ViewDataProcessorMap,
+    operation_handlers: FolderOperationHandlers,
     cloud_service: Arc<dyn FolderCloudService>,
   ) -> FlowyResult<Self> {
     let folder = Folder::default();
@@ -51,7 +51,7 @@ impl Folder2Manager {
       user,
       folder,
       collab_builder,
-      view_processors,
+      operation_handlers,
       cloud_service,
     };
 
@@ -117,7 +117,7 @@ impl Folder2Manager {
     let (folder_data, workspace_pb) = DefaultFolderBuilder::build(
       self.user.user_id()?,
       workspace_id.to_string(),
-      &self.view_processors,
+      &self.operation_handlers,
     )
     .await;
     self.with_folder((), |folder| {
@@ -187,14 +187,14 @@ impl Folder2Manager {
 
   pub async fn create_view_with_params(&self, params: CreateViewParams) -> FlowyResult<View> {
     let view_layout: ViewLayout = params.layout.clone().into();
-    let processor = self.get_data_processor(&view_layout)?;
+    let handler = self.get_handler(&view_layout)?;
     let user_id = self.user.user_id()?;
-    let ext = params.ext.clone();
+    let ext = params.meta.clone();
     match params.initial_data.is_empty() {
       true => {
         tracing::trace!("Create view with build-in data");
-        processor
-          .create_view_with_built_in_data(
+        handler
+          .create_built_in_view(
             user_id,
             &params.view_id,
             &params.name,
@@ -205,8 +205,8 @@ impl Folder2Manager {
       },
       false => {
         tracing::trace!("Create view with view data");
-        processor
-          .create_view_with_custom_data(
+        handler
+          .create_view_with_view_data(
             user_id,
             &params.view_id,
             &params.name,
@@ -217,7 +217,7 @@ impl Folder2Manager {
           .await?;
       },
     }
-    let view = view_from_create_view_params(params, view_layout);
+    let view = create_view(params, view_layout);
     self.with_folder((), |folder| {
       folder.insert_view(view.clone());
     });
@@ -233,12 +233,12 @@ impl Folder2Manager {
       .ok_or_else(|| {
         FlowyError::record_not_found().context("Can't find the view when closing the view")
       })?;
-    let processor = self.get_data_processor(&view.layout)?;
-    processor.close_view(view_id).await?;
+    let handler = self.get_handler(&view.layout)?;
+    handler.close_view(view_id).await?;
     Ok(())
   }
 
-  pub async fn create_view_data(
+  pub async fn create_view_with_data(
     &self,
     view_id: &str,
     name: &str,
@@ -246,9 +246,9 @@ impl Folder2Manager {
     data: Vec<u8>,
   ) -> FlowyResult<()> {
     let user_id = self.user.user_id()?;
-    let processor = self.get_data_processor(&view_layout)?;
-    processor
-      .create_view_with_custom_data(
+    let handler = self.get_handler(&view_layout)?;
+    handler
+      .create_view_with_view_data(
         user_id,
         view_id,
         name,
@@ -367,20 +367,20 @@ impl Folder2Manager {
       .with_folder(None, |folder| folder.views.get_view(view_id))
       .ok_or_else(|| FlowyError::record_not_found().context("Can't duplicate the view"))?;
 
-    let processor = self.get_data_processor(&view.layout)?;
-    let view_data = processor.get_view_data(&view.id).await?;
+    let handler = self.get_handler(&view.layout)?;
+    let view_data = handler.duplicate_view(&view.id).await?;
     let mut ext = HashMap::new();
     if let Some(database_id) = view.database_id {
       ext.insert("database_id".to_string(), database_id);
     }
     let duplicate_params = CreateViewParams {
-      belong_to_id: view.bid.clone(),
+      parent_view_id: view.bid.clone(),
       name: format!("{} (copy)", &view.name),
       desc: view.desc,
       layout: view.layout.into(),
       initial_data: view_data.to_vec(),
       view_id: gen_view_id(),
-      ext,
+      meta: ext,
     };
 
     let _ = self.create_view_with_params(duplicate_params).await?;
@@ -451,11 +451,52 @@ impl Folder2Manager {
       .send();
   }
 
-  fn get_data_processor(
+  pub(crate) async fn import(&self, import_data: ImportParams) -> FlowyResult<View> {
+    if import_data.data.is_none() && import_data.file_path.is_none() {
+      return Err(FlowyError::new(
+        ErrorCode::InvalidData,
+        "data or file_path is required",
+      ));
+    }
+
+    let handler = self.get_handler(&import_data.view_layout)?;
+    let view_id = gen_view_id();
+    if let Some(data) = import_data.data {
+      handler
+        .import_from_bytes(&view_id, &import_data.name, data)
+        .await?;
+    }
+
+    if let Some(file_path) = import_data.file_path {
+      handler
+        .import_from_file_path(&view_id, &import_data.name, file_path)
+        .await?;
+    }
+
+    let params = CreateViewParams {
+      parent_view_id: import_data.parent_view_id,
+      name: import_data.name,
+      desc: "".to_string(),
+      layout: import_data.view_layout.clone().into(),
+      initial_data: vec![],
+      view_id,
+      meta: Default::default(),
+    };
+
+    let view = create_view(params, import_data.view_layout);
+    self.with_folder((), |folder| {
+      folder.insert_view(view.clone());
+    });
+    notify_parent_view_did_change(self.folder.clone(), vec![view.bid.clone()]);
+    Ok(view)
+  }
+
+  /// Returns a handler that implements the [FolderOperationHandler] trait
+  fn get_handler(
     &self,
     view_layout: &ViewLayout,
-  ) -> FlowyResult<Arc<dyn ViewDataProcessor + Send + Sync>> {
-    match self.view_processors.get(view_layout) {
+  ) -> FlowyResult<Arc<dyn FolderOperationHandler + Send + Sync>> {
+    match self.operation_handlers.get(view_layout) {
       None => Err(FlowyError::internal().context(format!(
         "Get data processor failed. Unknown layout type: {:?}",
         view_layout

+ 17 - 0
frontend/rust-lib/flowy-folder2/src/share/import.rs

@@ -0,0 +1,17 @@
+use collab_folder::core::ViewLayout;
+
+#[derive(Clone, Debug)]
+pub enum ImportType {
+  HistoryDocument = 0,
+  HistoryDatabase = 1,
+}
+
+#[derive(Clone, Debug)]
+pub struct ImportParams {
+  pub parent_view_id: String,
+  pub name: String,
+  pub data: Option<Vec<u8>>,
+  pub file_path: Option<String>,
+  pub view_layout: ViewLayout,
+  pub import_type: ImportType,
+}

+ 3 - 0
frontend/rust-lib/flowy-folder2/src/share/mod.rs

@@ -0,0 +1,3 @@
+mod import;
+
+pub use import::*;

+ 2 - 2
frontend/rust-lib/flowy-folder2/src/test_helper.rs

@@ -36,13 +36,13 @@ impl Folder2Manager {
   ) -> String {
     let view_id = gen_view_id();
     let params = CreateViewParams {
-      belong_to_id: app_id.to_string(),
+      parent_view_id: app_id.to_string(),
       name: name.to_string(),
       desc: "".to_string(),
       layout,
       view_id: view_id.clone(),
       initial_data: vec![],
-      ext,
+      meta: ext,
     };
     self.create_view_with_params(params).await.unwrap();
     view_id

+ 5 - 5
frontend/rust-lib/flowy-folder2/src/user_default.rs

@@ -5,14 +5,14 @@ use collab_folder::core::{Belonging, Belongings, FolderData, View, ViewLayout, W
 use nanoid::nanoid;
 
 use crate::entities::{view_pb_with_child_views, WorkspacePB};
-use crate::view_ext::{gen_view_id, ViewDataProcessorMap};
+use crate::view_ext::{gen_view_id, FolderOperationHandlers};
 
 pub struct DefaultFolderBuilder();
 impl DefaultFolderBuilder {
   pub async fn build(
     uid: i64,
     workspace_id: String,
-    view_processors: &ViewDataProcessorMap,
+    handlers: &FolderOperationHandlers,
   ) -> (FolderData, WorkspacePB) {
     let time = Utc::now().timestamp();
     let view_id = gen_view_id();
@@ -33,9 +33,9 @@ impl DefaultFolderBuilder {
     // create the document
     // TODO: use the initial data from the view processor
     // let data = initial_read_me().into_bytes();
-    let processor = view_processors.get(&child_view_layout).unwrap();
-    processor
-      .create_view_with_built_in_data(
+    let handler = handlers.get(&child_view_layout).unwrap();
+    handler
+      .create_built_in_view(
         uid,
         &child_view.id,
         &child_view.name,

+ 37 - 15
frontend/rust-lib/flowy-folder2/src/view_ext.rs

@@ -8,40 +8,62 @@ use lib_infra::util::timestamp;
 use std::collections::HashMap;
 use std::sync::Arc;
 
-pub trait ViewDataProcessor {
+pub type ViewData = Bytes;
+
+/// The handler will be used to handler the folder operation for a specific
+/// view layout. Each [ViewLayout] will have a handler. So when creating a new
+/// view, the [ViewLayout] will be used to get the handler.
+///
+pub trait FolderOperationHandler {
   /// Closes the view and releases the resources that this view has in
   /// the backend
   fn close_view(&self, view_id: &str) -> FutureResult<(), FlowyError>;
 
-  /// Gets the data of the this view.
-  /// For example, the data can be used to duplicate the view.
-  fn get_view_data(&self, view_id: &str) -> FutureResult<Bytes, FlowyError>;
+  /// Returns the [ViewData] that can be used to create the same view.
+  fn duplicate_view(&self, view_id: &str) -> FutureResult<ViewData, FlowyError>;
 
-  /// Create a view with the pre-defined data.
-  /// For example, the initial data of the grid/calendar/kanban board when
-  /// you create a new view.
-  fn create_view_with_built_in_data(
+  /// Create a view with custom data
+  fn create_view_with_view_data(
     &self,
     user_id: i64,
     view_id: &str,
     name: &str,
+    data: Vec<u8>,
     layout: ViewLayout,
     ext: HashMap<String, String>,
   ) -> FutureResult<(), FlowyError>;
 
-  /// Create a view with custom data
-  fn create_view_with_custom_data(
+  /// Create a view with the pre-defined data.
+  /// For example, the initial data of the grid/calendar/kanban board when
+  /// you create a new view.
+  fn create_built_in_view(
     &self,
     user_id: i64,
     view_id: &str,
     name: &str,
-    data: Vec<u8>,
     layout: ViewLayout,
-    ext: HashMap<String, String>,
+    meta: HashMap<String, String>,
+  ) -> FutureResult<(), FlowyError>;
+
+  /// Create a view by importing data
+  fn import_from_bytes(
+    &self,
+    view_id: &str,
+    name: &str,
+    bytes: Vec<u8>,
+  ) -> FutureResult<(), FlowyError>;
+
+  /// Create a view by importing data from a file
+  fn import_from_file_path(
+    &self,
+    view_id: &str,
+    name: &str,
+    path: String,
   ) -> FutureResult<(), FlowyError>;
 }
 
-pub type ViewDataProcessorMap = Arc<HashMap<ViewLayout, Arc<dyn ViewDataProcessor + Send + Sync>>>;
+pub type FolderOperationHandlers =
+  Arc<HashMap<ViewLayout, Arc<dyn FolderOperationHandler + Send + Sync>>>;
 
 impl From<ViewLayoutPB> for ViewLayout {
   fn from(pb: ViewLayoutPB) -> Self {
@@ -54,11 +76,11 @@ impl From<ViewLayoutPB> for ViewLayout {
   }
 }
 
-pub fn view_from_create_view_params(params: CreateViewParams, layout: ViewLayout) -> View {
+pub(crate) fn create_view(params: CreateViewParams, layout: ViewLayout) -> View {
   let time = timestamp();
   View {
     id: params.view_id,
-    bid: params.belong_to_id,
+    bid: params.parent_view_id,
     name: params.name,
     desc: params.desc,
     belongings: Default::default(),

+ 2 - 2
frontend/rust-lib/flowy-folder2/tests/workspace/script.rs

@@ -212,7 +212,7 @@ pub async fn read_workspace(sdk: &FlowyCoreTest, workspace_id: Option<String>) -
 
 pub async fn create_app(sdk: &FlowyCoreTest, workspace_id: &str, name: &str, desc: &str) -> ViewPB {
   let create_view_request = CreateViewPayloadPB {
-    belong_to_id: workspace_id.to_owned(),
+    parent_view_id: workspace_id.to_owned(),
     name: name.to_string(),
     desc: desc.to_string(),
     thumbnail: None,
@@ -237,7 +237,7 @@ pub async fn create_view(
   layout: ViewLayout,
 ) -> ViewPB {
   let request = CreateViewPayloadPB {
-    belong_to_id: app_id.to_string(),
+    parent_view_id: app_id.to_string(),
     name: name.to_string(),
     desc: desc.to_string(),
     thumbnail: None,

+ 2 - 2
frontend/rust-lib/flowy-test/src/folder_event.rs

@@ -69,7 +69,7 @@ async fn open_workspace(sdk: &FlowyCoreTest, workspace_id: &str) {
 
 async fn create_app(sdk: &FlowyCoreTest, name: &str, desc: &str, workspace_id: &str) -> ViewPB {
   let create_app_request = CreateViewPayloadPB {
-    belong_to_id: workspace_id.to_owned(),
+    parent_view_id: workspace_id.to_owned(),
     name: name.to_string(),
     desc: desc.to_string(),
     thumbnail: None,
@@ -93,7 +93,7 @@ async fn create_view(
   data: Vec<u8>,
 ) -> ViewPB {
   let payload = CreateViewPayloadPB {
-    belong_to_id: app_id.to_string(),
+    parent_view_id: app_id.to_string(),
     name: "View A".to_string(),
     desc: "".to_string(),
     thumbnail: Some("http://1.png".to_string()),