mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: share database via csv (#3285)
* chore: share database via csv * fix: flutter test
This commit is contained in:
parent
1205f0ebf7
commit
0806436c19
@ -0,0 +1,66 @@
|
||||
import 'dart:io';
|
||||
import 'package:appflowy/workspace/application/settings/share/export_service.dart';
|
||||
import 'package:appflowy_backend/log.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-document2/entities.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:dartz/dartz.dart';
|
||||
part 'share_bloc.freezed.dart';
|
||||
|
||||
class DatabaseShareBloc extends Bloc<DatabaseShareEvent, DatabaseShareState> {
|
||||
DatabaseShareBloc({
|
||||
required this.view,
|
||||
}) : super(const DatabaseShareState.initial()) {
|
||||
on<ShareCSV>(_onShareCSV);
|
||||
}
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
Future<void> _onShareCSV(
|
||||
ShareCSV event,
|
||||
Emitter<DatabaseShareState> emit,
|
||||
) async {
|
||||
emit(const DatabaseShareState.loading());
|
||||
|
||||
final result = await BackendExportService.exportDatabaseAsCSV(view.id);
|
||||
result.fold(
|
||||
(l) => _saveCSVToPath(l.data, event.path),
|
||||
(r) => Log.error(r),
|
||||
);
|
||||
|
||||
emit(
|
||||
DatabaseShareState.finish(
|
||||
result.fold(
|
||||
(l) {
|
||||
_saveCSVToPath(l.data, event.path);
|
||||
return left(unit);
|
||||
},
|
||||
(r) => right(r),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ExportDataPB _saveCSVToPath(String markdown, String path) {
|
||||
File(path).writeAsStringSync(markdown);
|
||||
return ExportDataPB()
|
||||
..data = markdown
|
||||
..exportType = ExportType.Markdown;
|
||||
}
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DatabaseShareEvent with _$DatabaseShareEvent {
|
||||
const factory DatabaseShareEvent.shareCSV(String path) = ShareCSV;
|
||||
}
|
||||
|
||||
@freezed
|
||||
class DatabaseShareState with _$DatabaseShareState {
|
||||
const factory DatabaseShareState.initial() = _Initial;
|
||||
const factory DatabaseShareState.loading() = _Loading;
|
||||
const factory DatabaseShareState.finish(
|
||||
Either<Unit, FlowyError> successOrFail,
|
||||
) = _Finish;
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import 'package:appflowy/plugins/database_view/application/tar_bar_bloc.dart';
|
||||
import 'package:appflowy/plugins/database_view/widgets/share_button.dart';
|
||||
import 'package:appflowy/plugins/util.dart';
|
||||
import 'package:appflowy/startup/plugin/plugin.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/home_stack.dart';
|
||||
@ -212,4 +213,16 @@ class DatabasePluginWidgetBuilder extends PluginWidgetBuilder {
|
||||
|
||||
@override
|
||||
List<NavigationItem> get navigationItems => [this];
|
||||
|
||||
@override
|
||||
Widget? get rightBarItem {
|
||||
return Row(
|
||||
children: [
|
||||
DatabaseShareButton(
|
||||
key: ValueKey(notifier.view.id),
|
||||
view: notifier.view,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,154 @@
|
||||
import 'package:appflowy/generated/locale_keys.g.dart';
|
||||
import 'package:appflowy/plugins/database_view/application/share_bloc.dart';
|
||||
import 'package:appflowy/startup/startup.dart';
|
||||
import 'package:appflowy/workspace/application/view/view_listener.dart';
|
||||
import 'package:appflowy/workspace/presentation/home/toast.dart';
|
||||
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
|
||||
import 'package:appflowy_popover/appflowy_popover.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flowy_infra/file_picker/file_picker_service.dart';
|
||||
import 'package:flowy_infra_ui/widget/rounded_button.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
|
||||
import 'package:appflowy_backend/protobuf/flowy-folder2/view.pb.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
|
||||
class DatabaseShareButton extends StatelessWidget {
|
||||
const DatabaseShareButton({
|
||||
super.key,
|
||||
required this.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => DatabaseShareBloc(view: view),
|
||||
child: BlocListener<DatabaseShareBloc, DatabaseShareState>(
|
||||
listener: (context, state) {
|
||||
state.mapOrNull(
|
||||
finish: (state) {
|
||||
state.successOrFail.fold(
|
||||
(data) => _handleExportData(context),
|
||||
_handleExportError,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: BlocBuilder<DatabaseShareBloc, DatabaseShareState>(
|
||||
builder: (context, state) => ConstrainedBox(
|
||||
constraints: const BoxConstraints.expand(
|
||||
height: 30,
|
||||
width: 100,
|
||||
),
|
||||
child: DatabaseShareActionList(view: view),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleExportData(BuildContext context) {
|
||||
showSnackBarMessage(
|
||||
context,
|
||||
LocaleKeys.settings_files_exportFileSuccess.tr(),
|
||||
);
|
||||
}
|
||||
|
||||
void _handleExportError(FlowyError error) {
|
||||
showMessageToast(error.msg);
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseShareActionList extends StatefulWidget {
|
||||
const DatabaseShareActionList({
|
||||
super.key,
|
||||
required this.view,
|
||||
});
|
||||
|
||||
final ViewPB view;
|
||||
|
||||
@override
|
||||
State<DatabaseShareActionList> createState() =>
|
||||
DatabaseShareActionListState();
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
class DatabaseShareActionListState extends State<DatabaseShareActionList> {
|
||||
late String name;
|
||||
late final ViewListener viewListener = ViewListener(viewId: widget.view.id);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
listenOnViewUpdated();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
viewListener.stop();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final databaseShareBloc = context.read<DatabaseShareBloc>();
|
||||
return PopoverActionList<ShareActionWrapper>(
|
||||
direction: PopoverDirection.bottomWithCenterAligned,
|
||||
offset: const Offset(0, 8),
|
||||
actions: ShareAction.values
|
||||
.map((action) => ShareActionWrapper(action))
|
||||
.toList(),
|
||||
buildChild: (controller) {
|
||||
return RoundedTextButton(
|
||||
title: LocaleKeys.shareAction_buttonText.tr(),
|
||||
onPressed: () => controller.show(),
|
||||
);
|
||||
},
|
||||
onSelected: (action, controller) async {
|
||||
switch (action.inner) {
|
||||
case ShareAction.csv:
|
||||
final exportPath = await getIt<FilePickerService>().saveFile(
|
||||
dialogTitle: '',
|
||||
fileName: '${Uri.encodeComponent(name)}.csv',
|
||||
);
|
||||
if (exportPath != null) {
|
||||
databaseShareBloc.add(DatabaseShareEvent.shareCSV(exportPath));
|
||||
}
|
||||
break;
|
||||
}
|
||||
controller.close();
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void listenOnViewUpdated() {
|
||||
name = widget.view.name;
|
||||
viewListener.start(
|
||||
onViewUpdated: (view) {
|
||||
name = view.name;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum ShareAction {
|
||||
csv,
|
||||
}
|
||||
|
||||
class ShareActionWrapper extends ActionCell {
|
||||
final ShareAction inner;
|
||||
|
||||
ShareActionWrapper(this.inner);
|
||||
|
||||
Widget? icon(Color iconColor) => null;
|
||||
|
||||
@override
|
||||
String get name {
|
||||
switch (inner) {
|
||||
case ShareAction.csv:
|
||||
return LocaleKeys.shareAction_csv.tr();
|
||||
}
|
||||
}
|
||||
}
|
66
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
66
frontend/appflowy_tauri/src-tauri/Cargo.lock
generated
@ -140,7 +140,7 @@ checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||
[[package]]
|
||||
name = "appflowy-integrate"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -728,7 +728,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@ -746,7 +746,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-client-ws"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab-sync",
|
||||
@ -764,7 +764,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-database"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -781,6 +781,8 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"strum",
|
||||
"strum_macros 0.25.2",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
@ -791,7 +793,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-define"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
@ -799,7 +801,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-derive"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -811,7 +813,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-document"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -830,7 +832,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-folder"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
@ -850,7 +852,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-persistence"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
"chrono",
|
||||
@ -870,7 +872,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-plugins"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@ -899,7 +901,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-sync"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"collab",
|
||||
@ -921,7 +923,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "collab-user"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=cff1b9#cff1b99f4ed51f65dab73492eac4da8e7907f079"
|
||||
source = "git+https://github.com/AppFlowy-IO/AppFlowy-Collab?rev=1b297c#1b297c2ed75aa33b964f0da546d771b00805be62"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"collab",
|
||||
@ -1544,7 +1546,7 @@ dependencies = [
|
||||
"flowy-sqlite",
|
||||
"lib-dispatch",
|
||||
"protobuf",
|
||||
"strum_macros",
|
||||
"strum_macros 0.21.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1629,7 +1631,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"strum_macros 0.25.2",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
@ -1683,7 +1685,7 @@ dependencies = [
|
||||
"protobuf",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum_macros",
|
||||
"strum_macros 0.21.1",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
@ -1756,7 +1758,7 @@ dependencies = [
|
||||
"nanoid",
|
||||
"parking_lot 0.12.1",
|
||||
"protobuf",
|
||||
"strum_macros",
|
||||
"strum_macros 0.21.1",
|
||||
"tokio",
|
||||
"tokio-stream",
|
||||
"tracing",
|
||||
@ -1868,11 +1870,13 @@ dependencies = [
|
||||
name = "flowy-user"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"appflowy-integrate",
|
||||
"base64 0.21.2",
|
||||
"bytes",
|
||||
"chrono",
|
||||
"collab",
|
||||
"collab-database",
|
||||
"collab-document",
|
||||
"collab-folder",
|
||||
"collab-user",
|
||||
@ -1883,6 +1887,7 @@ dependencies = [
|
||||
"flowy-derive",
|
||||
"flowy-encrypt",
|
||||
"flowy-error",
|
||||
"flowy-folder-deps",
|
||||
"flowy-notification",
|
||||
"flowy-server-config",
|
||||
"flowy-sqlite",
|
||||
@ -1897,7 +1902,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_repr",
|
||||
"strum_macros",
|
||||
"strum_macros 0.21.1",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"unicode-segmentation",
|
||||
@ -3464,6 +3469,15 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-src"
|
||||
version = "111.27.0+1.1.1v"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06e8f197c82d7511c5b014030c9b1efeda40d7d5f99d23b4ceed3524a5e63f02"
|
||||
dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.90"
|
||||
@ -3472,6 +3486,7 @@ checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"openssl-src",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
@ -5041,9 +5056,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.21.0"
|
||||
version = "0.25.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
|
||||
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
@ -5057,6 +5072,19 @@ dependencies = [
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.25.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad8d03b598d3d0fff69bf533ee3ef19b8eeb342729596df84bcc7e1f96ec4059"
|
||||
dependencies = [
|
||||
"heck 0.4.1",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rustversion",
|
||||
"syn 2.0.22",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.5.0"
|
||||
|
@ -55,6 +55,7 @@
|
||||
"buttonText": "Share",
|
||||
"workInProgress": "Coming soon",
|
||||
"markdown": "Markdown",
|
||||
"csv": "CSV",
|
||||
"copyLink": "Copy Link"
|
||||
},
|
||||
"moreAction": {
|
||||
|
@ -64,15 +64,14 @@ impl DocumentManager {
|
||||
data: Option<DocumentData>,
|
||||
) -> FlowyResult<Arc<MutexDocument>> {
|
||||
tracing::trace!("create a document: {:?}", doc_id);
|
||||
let collab = self.collab_for_document(uid, doc_id, vec![])?;
|
||||
|
||||
match self.get_document(doc_id).await {
|
||||
Ok(document) => Ok(document),
|
||||
Err(_) => {
|
||||
let data = data.unwrap_or_else(default_document_data);
|
||||
let document = Arc::new(MutexDocument::create_with_data(collab, data)?);
|
||||
Ok(document)
|
||||
},
|
||||
if self.is_doc_exist(doc_id).unwrap_or(false) {
|
||||
self.get_document(doc_id).await
|
||||
} else {
|
||||
let collab = self.collab_for_document(uid, doc_id, vec![])?;
|
||||
let data = data.unwrap_or_else(default_document_data);
|
||||
let document = Arc::new(MutexDocument::create_with_data(collab, data)?);
|
||||
Ok(document)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user