feat: add cover migration for document (#1929)

* feat: add cover migration for document

* fix: should not delete the cover when selecting all

* fix: chinese characters for openai
This commit is contained in:
Lucas.Xu 2023-03-07 09:33:59 +08:00 committed by GitHub
parent 675c833f07
commit 7ff4cecd09
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 81 additions and 35 deletions

View file

@ -1,15 +1,18 @@
import 'dart:convert'; import 'dart:convert';
import 'package:appflowy/plugins/document/presentation/plugins/cover/cover_node_widget.dart';
import 'package:appflowy/plugins/trash/application/trash_service.dart'; import 'package:appflowy/plugins/trash/application/trash_service.dart';
import 'package:appflowy/user/application/user_service.dart'; import 'package:appflowy/user/application/user_service.dart';
import 'package:appflowy/workspace/application/view/view_listener.dart'; import 'package:appflowy/workspace/application/view/view_listener.dart';
import 'package:appflowy/plugins/document/application/doc_service.dart'; import 'package:appflowy/plugins/document/application/doc_service.dart';
import 'package:appflowy_backend/protobuf/flowy-document/entities.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pbserver.dart'; import 'package:appflowy_backend/protobuf/flowy-user/user_profile.pbserver.dart';
import 'package:appflowy_editor/appflowy_editor.dart' import 'package:appflowy_editor/appflowy_editor.dart'
show EditorState, Document, Transaction; show EditorState, Document, Transaction, Node;
import 'package:appflowy_backend/protobuf/flowy-folder/trash.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/trash.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-error/errors.pb.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart'; import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_backend/log.dart'; import 'package:appflowy_backend/log.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:dartz/dartz.dart'; import 'package:dartz/dartz.dart';
@ -78,29 +81,27 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
Future<void> _initial(Initial value, Emitter<DocumentState> emit) async { Future<void> _initial(Initial value, Emitter<DocumentState> emit) async {
final userProfile = await UserBackendService.getCurrentUserProfile(); final userProfile = await UserBackendService.getCurrentUserProfile();
if (userProfile.isRight()) { if (userProfile.isRight()) {
emit( return emit(
state.copyWith( state.copyWith(
loadingState: DocumentLoadingState.finish( loadingState: DocumentLoadingState.finish(
right(userProfile.asRight()), right(userProfile.asRight()),
), ),
), ),
); );
return;
} }
final result = await _documentService.openDocument(view: view); final result = await _documentService.openDocument(view: view);
result.fold( return result.fold(
(documentData) { (documentData) async {
final document = Document.fromJson(jsonDecode(documentData.content)); await _initEditorState(documentData).whenComplete(() {
editorState = EditorState(document: document); emit(
_listenOnDocumentChange(); state.copyWith(
emit( loadingState: DocumentLoadingState.finish(left(unit)),
state.copyWith( userProfilePB: userProfile.asLeft(),
loadingState: DocumentLoadingState.finish(left(unit)), ),
userProfilePB: userProfile.asLeft(), );
), });
);
}, },
(err) { (err) async {
emit( emit(
state.copyWith( state.copyWith(
loadingState: DocumentLoadingState.finish(right(err)), loadingState: DocumentLoadingState.finish(right(err)),
@ -127,8 +128,13 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
); );
} }
void _listenOnDocumentChange() { Future<void> _initEditorState(DocumentDataPB documentData) async {
_subscription = editorState?.transactionStream.listen((transaction) { final document = Document.fromJson(jsonDecode(documentData.content));
final editorState = EditorState(document: document);
this.editorState = editorState;
// listen on document change
_subscription = editorState.transactionStream.listen((transaction) {
final json = jsonEncode(TransactionAdaptor(transaction).toJson()); final json = jsonEncode(TransactionAdaptor(transaction).toJson());
_documentService _documentService
.applyEdit(docId: view.id, operations: json) .applyEdit(docId: view.id, operations: json)
@ -139,6 +145,15 @@ class DocumentBloc extends Bloc<DocumentEvent, DocumentState> {
); );
}); });
}); });
// log
if (kDebugMode) {
editorState.logConfiguration.handler = (log) {
Log.debug(log);
};
}
// migration
final migration = DocumentMigration(editorState: editorState);
await migration.apply();
} }
} }
@ -215,3 +230,33 @@ class TransactionAdaptor {
return json; return json;
} }
} }
class DocumentMigration {
const DocumentMigration({
required this.editorState,
});
final EditorState editorState;
/// Migrate the document to the latest version.
Future<void> apply() async {
final transaction = editorState.transaction;
// A temporary solution to migrate the document to the latest version.
// Once the editor is stable, we can remove this.
// cover plugin
if (editorState.document.nodeAtPath([0])?.type != kCoverType) {
transaction.insertNode(
[0],
Node(type: kCoverType),
);
}
transaction.afterSelection = null;
if (transaction.operations.isNotEmpty) {
editorState.apply(transaction);
}
}
}

View file

@ -91,7 +91,13 @@ class HttpOpenAIRepository implements OpenAIRepository {
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
return Right(TextCompletionResponse.fromJson(json.decode(response.body))); return Right(
TextCompletionResponse.fromJson(
json.decode(
utf8.decode(response.bodyBytes),
),
),
);
} else { } else {
return Left(OpenAIError.fromJson(json.decode(response.body)['error'])); return Left(OpenAIError.fromJson(json.decode(response.body)['error']));
} }
@ -119,7 +125,13 @@ class HttpOpenAIRepository implements OpenAIRepository {
); );
if (response.statusCode == 200) { if (response.statusCode == 200) {
return Right(TextEditResponse.fromJson(json.decode(response.body))); return Right(
TextEditResponse.fromJson(
json.decode(
utf8.decode(response.bodyBytes),
),
),
);
} else { } else {
return Left(OpenAIError.fromJson(json.decode(response.body)['error'])); return Left(OpenAIError.fromJson(json.decode(response.body)['error']));
} }

View file

@ -1,10 +1,9 @@
import 'package:appflowy/plugins/document/document.dart'; import 'package:appflowy/plugins/document/document.dart';
import 'package:appflowy/plugins/document/presentation/plugins/cover/cover_node_widget.dart';
import 'package:appflowy/startup/plugin/plugin.dart'; import 'package:appflowy/startup/plugin/plugin.dart';
import 'package:appflowy/startup/startup.dart'; import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_panel.dart'; import 'package:appflowy/workspace/presentation/home/menu/app/header/import/import_panel.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart'; import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_editor/appflowy_editor.dart' show Document, Node; import 'package:appflowy_editor/appflowy_editor.dart' show Document;
import 'package:appflowy_popover/appflowy_popover.dart'; import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:flowy_infra/image.dart'; import 'package:flowy_infra/image.dart';
import 'package:flowy_infra_ui/style_widget/icon_button.dart'; import 'package:flowy_infra_ui/style_widget/icon_button.dart';
@ -61,12 +60,7 @@ class AddButton extends StatelessWidget {
}, },
onSelected: (action, controller) { onSelected: (action, controller) {
if (action is AddButtonActionWrapper) { if (action is AddButtonActionWrapper) {
Document? document; onSelected(action.pluginBuilder, null);
if (action.pluginType == PluginType.editor) {
// initialize the document if needed.
document = buildInitialDocument();
}
onSelected(action.pluginBuilder, document);
} }
if (action is ImportActionWrapper) { if (action is ImportActionWrapper) {
showImportPanel(context, (document) { showImportPanel(context, (document) {
@ -80,12 +74,6 @@ class AddButton extends StatelessWidget {
}, },
); );
} }
Document buildInitialDocument() {
final document = Document.empty();
document.insert([0], [Node(type: kCoverType)]);
return document;
}
} }
class AddButtonActionWrapper extends ActionCell { class AddButtonActionWrapper extends ActionCell {

View file

@ -12,8 +12,9 @@ ShortcutEventHandler backspaceEventHandler = (editorState, event) {
nodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false); nodes = selection.isBackward ? nodes : nodes.reversed.toList(growable: false);
selection = selection.isBackward ? selection : selection.reversed; selection = selection.isBackward ? selection : selection.reversed;
final textNodes = nodes.whereType<TextNode>().toList(); final textNodes = nodes.whereType<TextNode>().toList();
final List<Node> nonTextNodes = final List<Node> nonTextNodes = nodes
nodes.where((node) => node is! TextNode).toList(growable: false); .where((node) => node is! TextNode && node.selectable != null)
.toList(growable: false);
final transaction = editorState.transaction; final transaction = editorState.transaction;
List<int>? cancelNumberListPath; List<int>? cancelNumberListPath;