mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
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:
parent
5b59800449
commit
e24a8aabeb
@ -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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ class AddButton extends StatelessWidget {
|
||||
switch (type) {
|
||||
case ImportType.historyDocument:
|
||||
case ImportType.historyDatabase:
|
||||
case ImportType.databaseCSV:
|
||||
onSelected(
|
||||
action.pluginBuilder,
|
||||
name,
|
||||
|
@ -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:
|
||||
|
@ -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(())
|
||||
})
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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?;
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@ use collab_folder::core::ViewLayout;
|
||||
pub enum ImportType {
|
||||
HistoryDocument = 0,
|
||||
HistoryDatabase = 1,
|
||||
CSV = 2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -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>;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user