feat: expand share option (#5021)

* feat: expand share option

* chore: optimize the code

---------

Co-authored-by: Lucas.Xu <lucas.xu@appflowy.io>
This commit is contained in:
Vashon 2024-04-11 20:01:36 +08:00 committed by GitHub
parent 1597f7d94c
commit a50918fa6a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 126 additions and 26 deletions

View File

@ -1,6 +1,7 @@
import 'dart:io';
import 'package:appflowy/workspace/application/export/document_exporter.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
@ -11,45 +12,89 @@ import 'package:freezed_annotation/freezed_annotation.dart';
part 'document_share_bloc.freezed.dart';
class DocumentShareBloc extends Bloc<DocumentShareEvent, DocumentShareState> {
DocumentShareBloc({required this.view})
: super(const DocumentShareState.initial()) {
on<ShareMarkdown>(_onShareMarkdown);
DocumentShareBloc({
required this.view,
}) : super(const DocumentShareState.initial()) {
on<DocumentShareEvent>((event, emit) async {
await event.when(
share: (type, path) async {
if (DocumentShareType.unimplemented.contains(type)) {
Log.error('DocumentShareType $type is not implemented');
return;
}
emit(const DocumentShareState.loading());
final exporter = DocumentExporter(view);
final FlowyResult<ExportDataPB, FlowyError> result =
await exporter.export(type.exportType).then((value) {
return value.fold(
(s) {
if (path != null) {
switch (type) {
case DocumentShareType.markdown:
return FlowyResult.success(_saveMarkdownToPath(s, path));
case DocumentShareType.html:
return FlowyResult.success(_saveHTMLToPath(s, path));
default:
break;
}
}
return FlowyResult.failure(FlowyError());
},
(f) => FlowyResult.failure(f),
);
});
emit(DocumentShareState.finish(result));
},
);
});
}
final ViewPB view;
Future<void> _onShareMarkdown(
ShareMarkdown event,
Emitter<DocumentShareState> emit,
) async {
emit(const DocumentShareState.loading());
final documentExporter = DocumentExporter(view);
final result = await documentExporter.export(DocumentExportType.markdown);
emit(
DocumentShareState.finish(
result.fold(
(markdown) =>
FlowyResult.success(_saveMarkdownToPath(markdown, event.path)),
(error) => FlowyResult.failure(error),
),
),
);
}
ExportDataPB _saveMarkdownToPath(String markdown, String path) {
File(path).writeAsStringSync(markdown);
return ExportDataPB()
..data = markdown
..exportType = ExportType.Markdown;
}
ExportDataPB _saveHTMLToPath(String html, String path) {
File(path).writeAsStringSync(html);
return ExportDataPB()
..data = html
..exportType = ExportType.HTML;
}
}
enum DocumentShareType {
markdown,
html,
text,
link;
static List<DocumentShareType> get unimplemented => [text, link];
DocumentExportType get exportType {
switch (this) {
case DocumentShareType.markdown:
return DocumentExportType.markdown;
case DocumentShareType.html:
return DocumentExportType.html;
case DocumentShareType.text:
return DocumentExportType.text;
case DocumentShareType.link:
throw UnsupportedError('DocumentShareType.link is not supported');
}
}
}
@freezed
class DocumentShareEvent with _$DocumentShareEvent {
const factory DocumentShareEvent.shareMarkdown(String path) = ShareMarkdown;
const factory DocumentShareEvent.shareText() = ShareText;
const factory DocumentShareEvent.shareLink() = ShareLink;
const factory DocumentShareEvent.share(DocumentShareType type, String? path) =
Share;
}
@freezed

View File

@ -1,7 +1,9 @@
import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/plugins/document/application/document_share_bloc.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/util/string_extension.dart';
import 'package:appflowy/workspace/application/export/document_exporter.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';
@ -62,6 +64,12 @@ class DocumentShareButton extends StatelessWidget {
case ExportType.Link:
case ExportType.Text:
break;
case ExportType.HTML:
showSnackBarMessage(
context,
LocaleKeys.settings_files_exportFileSuccess.tr(),
);
break;
}
}
@ -125,9 +133,38 @@ class ShareActionListState extends State<ShareActionList> {
fileName: '${name.toFileName()}.md',
);
if (exportPath != null) {
docShareBloc.add(DocumentShareEvent.shareMarkdown(exportPath));
docShareBloc.add(
DocumentShareEvent.share(
DocumentShareType.markdown,
exportPath,
),
);
}
break;
case ShareAction.html:
final exportPath = await getIt<FilePickerService>().saveFile(
dialogTitle: '',
fileName: '${name.toFileName()}.html',
);
if (exportPath != null) {
docShareBloc.add(
DocumentShareEvent.share(
DocumentShareType.html,
exportPath,
),
);
}
break;
case ShareAction.clipboard:
final documentExporter = DocumentExporter(widget.view);
final result =
await documentExporter.export(DocumentExportType.markdown);
result.fold(
(markdown) => getIt<ClipboardService>()
.setData(ClipboardServiceData(plainText: markdown)),
(error) => showMessageToast(error.msg),
);
break;
}
controller.close();
},
@ -146,6 +183,8 @@ class ShareActionListState extends State<ShareActionList> {
enum ShareAction {
markdown,
html,
clipboard,
}
class ShareActionWrapper extends ActionCell {
@ -160,6 +199,10 @@ class ShareActionWrapper extends ActionCell {
switch (inner) {
case ShareAction.markdown:
return LocaleKeys.shareAction_markdown.tr();
case ShareAction.html:
return LocaleKeys.shareAction_html.tr();
case ShareAction.clipboard:
return LocaleKeys.shareAction_clipboard.tr();
}
}
}

View File

@ -21,6 +21,7 @@ enum DocumentExportType {
json,
markdown,
text,
html,
}
class DocumentExporter {
@ -56,6 +57,11 @@ class DocumentExporter {
return FlowyResult.success(markdown);
case DocumentExportType.text:
throw UnimplementedError();
case DocumentExportType.html:
final html = documentToHTML(
document,
);
return FlowyResult.success(html);
}
},
(error) => FlowyResult.failure(error),

View File

@ -92,6 +92,8 @@
"buttonText": "Share",
"workInProgress": "Coming soon",
"markdown": "Markdown",
"html": "HTML",
"clipboard": "Copy to clipboard",
"csv": "CSV",
"copyLink": "Copy Link"
},

View File

@ -70,6 +70,8 @@
"buttonText": "分享",
"workInProgress": "敬请期待",
"markdown": "Markdown",
"html": "HTML",
"clipboard": "拷贝到剪贴板",
"csv": "CSV",
"copyLink": "复制链接"
},

View File

@ -339,6 +339,7 @@ pub enum ExportType {
Text = 0,
Markdown = 1,
Link = 2,
HTML = 3,
}
impl From<i32> for ExportType {
@ -347,6 +348,7 @@ impl From<i32> for ExportType {
0 => ExportType::Text,
1 => ExportType::Markdown,
2 => ExportType::Link,
3 => ExportType::HTML,
_ => {
tracing::error!("🔴Invalid export type: {}", val);
ExportType::Text