chore: implement import csv ui (#2710)

* chore: implement import csv ui

* feat: support importing CSV

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Nathan.fooo 2023-06-05 18:29:52 +08:00 committed by GitHub
parent 5b59800449
commit e24a8aabeb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 77 additions and 42 deletions

View File

@ -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<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,
static Future<Either<Unit, FlowyError>> importData(
List<int> 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');
}
}
}

View File

@ -87,6 +87,7 @@ class AddButton extends StatelessWidget {
switch (type) {
case ImportType.historyDocument:
case ImportType.historyDatabase:
case ImportType.databaseCSV:
onSelected(
action.pluginBuilder,
name,

View File

@ -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<void> 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<String> 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:

View File

@ -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<u8>,
) -> 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<u8>,
) -> 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(())
})

View File

@ -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());
}
}

View File

@ -9,6 +9,7 @@ use flowy_error::FlowyError;
pub enum ImportTypePB {
HistoryDocument = 0,
HistoryDatabase = 1,
CSV = 2,
}
impl From<ImportTypePB> for ImportType {
@ -16,6 +17,7 @@ impl From<ImportTypePB> for ImportType {
match pb {
ImportTypePB::HistoryDocument => ImportType::HistoryDocument,
ImportTypePB::HistoryDatabase => ImportType::HistoryDatabase,
ImportTypePB::CSV => ImportType::CSV,
}
}
}

View File

@ -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;

View File

@ -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?;
}

View File

@ -4,6 +4,7 @@ use collab_folder::core::ViewLayout;
pub enum ImportType {
HistoryDocument = 0,
HistoryDatabase = 1,
CSV = 2,
}
#[derive(Clone, Debug)]

View File

@ -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<u8>,
) -> FutureResult<(), FlowyError>;