From e24a8aabebe914b9550822bcfb45bfa66ae9c2bc Mon Sep 17 00:00:00 2001 From: "Nathan.fooo" <86001920+appflowy@users.noreply.github.com> Date: Mon, 5 Jun 2023 18:29:52 +0800 Subject: [PATCH] chore: implement import csv ui (#2710) * chore: implement import csv ui * feat: support importing CSV --------- Co-authored-by: Lucas.Xu --- .../settings/share/import_service.dart | 41 ++++++++--------- .../home/menu/app/header/add_button.dart | 1 + .../menu/app/header/import/import_panel.dart | 44 ++++++++++++++----- .../src/deps_resolve/folder2_deps.rs | 10 ++++- .../src/services/share/csv/import.rs | 14 ++++-- .../flowy-folder2/src/entities/import.rs | 2 + frontend/rust-lib/flowy-folder2/src/lib.rs | 2 +- .../rust-lib/flowy-folder2/src/manager.rs | 2 +- .../flowy-folder2/src/share/import.rs | 1 + .../flowy-folder2/src/view_operation.rs | 2 + 10 files changed, 77 insertions(+), 42 deletions(-) diff --git a/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart b/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart index 78c012e01c..9b74c45457 100644 --- a/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart +++ b/frontend/appflowy_flutter/lib/workspace/application/settings/share/import_service.dart @@ -1,6 +1,3 @@ -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'; @@ -8,33 +5,31 @@ import 'package:appflowy_backend/protobuf/flowy-folder2/view.pbenum.dart'; import 'package:dartz/dartz.dart'; class ImportBackendService { - static Future> 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> importHistoryDocument( - Uint8List data, + static Future> importData( + List data, String name, String parentViewId, + ImportTypePB importType, ) async { final payload = ImportPB.create() ..data = data ..parentViewId = parentViewId - ..viewLayout = ViewLayoutPB.Document + ..viewLayout = importType.toLayout() ..name = name - ..importType = ImportTypePB.HistoryDocument; - + ..importType = importType; return await FolderEventImportData(payload).send(); } } + +extension on ImportTypePB { + ViewLayoutPB toLayout() { + switch (this) { + case ImportTypePB.HistoryDocument: + return ViewLayoutPB.Document; + case ImportTypePB.HistoryDatabase || ImportTypePB.CSV: + return ViewLayoutPB.Grid; + default: + throw UnimplementedError('Unsupported import type $this'); + } + } +} diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart index a9b667586d..8db09ca5fe 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/add_button.dart @@ -87,6 +87,7 @@ class AddButton extends StatelessWidget { switch (type) { case ImportType.historyDocument: case ImportType.historyDatabase: + case ImportType.databaseCSV: onSelected( action.pluginBuilder, name, diff --git a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart index ab03b48955..49ca7c3c38 100644 --- a/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart +++ b/frontend/appflowy_flutter/lib/workspace/presentation/home/menu/app/header/import/import_panel.dart @@ -1,3 +1,4 @@ +import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; @@ -6,6 +7,7 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/migration/ 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_backend/protobuf/flowy-folder2/protobuf.dart'; import 'package:appflowy_editor/appflowy_editor.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flowy_infra/image.dart'; @@ -52,7 +54,8 @@ Future showImportPanel( enum ImportType { historyDocument, historyDatabase, - markdownOrText; + markdownOrText, + databaseCSV; @override String toString() { @@ -63,6 +66,8 @@ enum ImportType { return 'Database from v0.1'; case ImportType.markdownOrText: return 'Text & Markdown'; + case ImportType.databaseCSV: + return 'CSV'; default: assert(false, 'Unsupported Type $this'); return ''; @@ -70,22 +75,24 @@ enum ImportType { } Widget? Function(BuildContext context) get icon => (context) { + var name = ''; switch (this) { case ImportType.historyDocument: + name = 'editor/board'; case ImportType.historyDatabase: - return svgWidget( - 'editor/documents', - color: Theme.of(context).iconTheme.color, - ); + name = 'editor/documents'; + case ImportType.databaseCSV: + name = 'editor/board'; case ImportType.markdownOrText: - return svgWidget( - 'editor/documents', - color: Theme.of(context).iconTheme.color, - ); + name = 'editor/text'; default: assert(false, 'Unsupported Type $this'); return null; } + return svgWidget( + name, + color: Theme.of(context).iconTheme.color, + ); }; List get allowedExtensions { @@ -96,6 +103,8 @@ enum ImportType { return ['afdb']; case ImportType.markdownOrText: return ['md', 'txt']; + case ImportType.databaseCSV: + return ['csv']; default: assert(false, 'Unsupported Type $this'); return []; @@ -105,6 +114,7 @@ enum ImportType { bool get allowMultiSelect { switch (this) { case ImportType.historyDocument: + case ImportType.databaseCSV: return true; case ImportType.historyDatabase: case ImportType.markdownOrText: @@ -189,18 +199,28 @@ class _ImportPanelState extends State<_ImportPanel> { case ImportType.historyDocument: final bytes = _documentDataFrom(importType, data); if (bytes != null) { - await ImportBackendService.importHistoryDocument( + await ImportBackendService.importData( bytes, name, parentViewId, + ImportTypePB.HistoryDocument, ); } break; case ImportType.historyDatabase: - await ImportBackendService.importHistoryDatabase( - data, + await ImportBackendService.importData( + utf8.encode(data), name, parentViewId, + ImportTypePB.HistoryDatabase, + ); + break; + case ImportType.databaseCSV: + await ImportBackendService.importData( + utf8.encode(data), + name, + parentViewId, + ImportTypePB.CSV, ); break; default: diff --git a/frontend/rust-lib/flowy-core/src/deps_resolve/folder2_deps.rs b/frontend/rust-lib/flowy-core/src/deps_resolve/folder2_deps.rs index 7591ff25ec..abe6df895e 100644 --- a/frontend/rust-lib/flowy-core/src/deps_resolve/folder2_deps.rs +++ b/frontend/rust-lib/flowy-core/src/deps_resolve/folder2_deps.rs @@ -18,6 +18,7 @@ use flowy_error::FlowyError; use flowy_folder2::deps::{FolderCloudService, FolderUser}; use flowy_folder2::entities::ViewLayoutPB; use flowy_folder2::manager::Folder2Manager; +use flowy_folder2::share::ImportType; use flowy_folder2::view_operation::{ FolderOperationHandler, FolderOperationHandlers, View, WorkspaceViewBuilder, }; @@ -189,6 +190,7 @@ impl FolderOperationHandler for DocumentFolderOperation { &self, view_id: &str, _name: &str, + _import_type: ImportType, bytes: Vec, ) -> FutureResult<(), FlowyError> { let view_id = view_id.to_string(); @@ -315,14 +317,20 @@ impl FolderOperationHandler for DatabaseFolderOperation { &self, view_id: &str, _name: &str, + import_type: ImportType, bytes: Vec, ) -> FutureResult<(), FlowyError> { let database_manager = self.0.clone(); let view_id = view_id.to_string(); + let format = match import_type { + ImportType::CSV => CSVFormat::Original, + ImportType::HistoryDatabase => CSVFormat::META, + _ => CSVFormat::Original, + }; 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) + .import_csv(view_id, content, format) .await?; Ok(()) }) diff --git a/frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs b/frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs index 45d3703f93..705d4b625c 100644 --- a/frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs +++ b/frontend/rust-lib/flowy-database2/src/services/share/csv/import.rs @@ -85,10 +85,7 @@ fn database_from_fields_and_rows( CSVFormat::META => { // match serde_json::from_str(&field_meta) { - Ok(field) => { - // - field - }, + Ok(field) => field, Err(e) => { dbg!(e); default_field(field_meta, index == 0) @@ -197,4 +194,13 @@ mod tests { println!("{:?}", result); } + + #[test] + fn import_empty_csv_data_test() { + let s = r#""#; + let importer = CSVImporter; + let result = + importer.import_csv_from_string(gen_database_view_id(), s.to_string(), CSVFormat::Original); + assert!(result.is_err()); + } } diff --git a/frontend/rust-lib/flowy-folder2/src/entities/import.rs b/frontend/rust-lib/flowy-folder2/src/entities/import.rs index 12d6d9cb3a..0593ee3b65 100644 --- a/frontend/rust-lib/flowy-folder2/src/entities/import.rs +++ b/frontend/rust-lib/flowy-folder2/src/entities/import.rs @@ -9,6 +9,7 @@ use flowy_error::FlowyError; pub enum ImportTypePB { HistoryDocument = 0, HistoryDatabase = 1, + CSV = 2, } impl From for ImportType { @@ -16,6 +17,7 @@ impl From for ImportType { match pb { ImportTypePB::HistoryDocument => ImportType::HistoryDocument, ImportTypePB::HistoryDatabase => ImportType::HistoryDatabase, + ImportTypePB::CSV => ImportType::CSV, } } } diff --git a/frontend/rust-lib/flowy-folder2/src/lib.rs b/frontend/rust-lib/flowy-folder2/src/lib.rs index b7ae39e2b0..65741a4d43 100644 --- a/frontend/rust-lib/flowy-folder2/src/lib.rs +++ b/frontend/rust-lib/flowy-folder2/src/lib.rs @@ -8,7 +8,7 @@ mod user_default; pub mod view_operation; pub mod deps; -mod share; +pub mod share; #[cfg(feature = "test_helper")] mod test_helper; diff --git a/frontend/rust-lib/flowy-folder2/src/manager.rs b/frontend/rust-lib/flowy-folder2/src/manager.rs index 2108f052d5..4f14031c4d 100644 --- a/frontend/rust-lib/flowy-folder2/src/manager.rs +++ b/frontend/rust-lib/flowy-folder2/src/manager.rs @@ -497,7 +497,7 @@ impl Folder2Manager { let view_id = gen_view_id(); if let Some(data) = import_data.data { handler - .import_from_bytes(&view_id, &import_data.name, data) + .import_from_bytes(&view_id, &import_data.name, import_data.import_type, data) .await?; } diff --git a/frontend/rust-lib/flowy-folder2/src/share/import.rs b/frontend/rust-lib/flowy-folder2/src/share/import.rs index 987523241c..6e663b1960 100644 --- a/frontend/rust-lib/flowy-folder2/src/share/import.rs +++ b/frontend/rust-lib/flowy-folder2/src/share/import.rs @@ -4,6 +4,7 @@ use collab_folder::core::ViewLayout; pub enum ImportType { HistoryDocument = 0, HistoryDatabase = 1, + CSV = 2, } #[derive(Clone, Debug)] diff --git a/frontend/rust-lib/flowy-folder2/src/view_operation.rs b/frontend/rust-lib/flowy-folder2/src/view_operation.rs index 2113e30b17..4ed8ca7279 100644 --- a/frontend/rust-lib/flowy-folder2/src/view_operation.rs +++ b/frontend/rust-lib/flowy-folder2/src/view_operation.rs @@ -12,6 +12,7 @@ use lib_infra::future::FutureResult; use lib_infra::util::timestamp; use crate::entities::{CreateViewParams, ViewLayoutPB}; +use crate::share::ImportType; pub type ViewData = Bytes; @@ -204,6 +205,7 @@ pub trait FolderOperationHandler { &self, view_id: &str, name: &str, + import_type: ImportType, bytes: Vec, ) -> FutureResult<(), FlowyError>;