Skip to content

Commit

Permalink
feat: auto-fix table block data when duplicating it (#6185)
Browse files Browse the repository at this point in the history
* fix: remove camera permission and update photo usage description

* feat: optimize the error block style in document

* feat: auto-fix table block when duplicating it
  • Loading branch information
LucasXu0 authored Sep 4, 2024
1 parent 589f5c1 commit 8e34951
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/bl
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/option_action.dart';
import 'package:appflowy/workspace/application/settings/appearance/appearance_cubit.dart';
import 'package:appflowy/workspace/presentation/widgets/pop_up_action.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:appflowy_backend/log.dart';
import 'package:appflowy_editor/appflowy_editor.dart' hide Log;
import 'package:appflowy_popover/appflowy_popover.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
Expand Down Expand Up @@ -64,7 +65,7 @@ class BlockOptionButton extends StatelessWidget {
},
onSelected: (action, controller) {
if (action is OptionActionWrapper) {
_onSelectAction(action.inner);
_onSelectAction(context, action.inner);
controller.close();
}
},
Expand Down Expand Up @@ -121,18 +122,15 @@ class BlockOptionButton extends StatelessWidget {
);
}

void _onSelectAction(OptionAction action) {
void _onSelectAction(BuildContext context, OptionAction action) {
final node = blockComponentContext.node;
final transaction = editorState.transaction;
switch (action) {
case OptionAction.delete:
transaction.deleteNode(node);
break;
case OptionAction.duplicate:
transaction.insertNode(
node.path.next,
node.copyWith(),
);
_duplicateBlock(context, transaction, node);
break;
case OptionAction.turnInto:
break;
Expand All @@ -150,4 +148,102 @@ class BlockOptionButton extends StatelessWidget {
}
editorState.apply(transaction);
}

void _duplicateBlock(
BuildContext context,
Transaction transaction,
Node node,
) {
// 1. verify the node integrity
final type = node.type;
final builder =
context.read<EditorState>().renderer.blockComponentBuilder(type);

if (builder == null) {
Log.error('Block type $type is not supported');
return;
}

final valid = builder.validate(node);
if (!valid) {
Log.error('Block type $type is not valid');
}

// 2. duplicate the node
// the _copyBlock will fix the table block
final newNode = _copyBlock(context, node);

// 3. insert the node to the next of the current node
transaction.insertNode(
node.path.next,
newNode,
);
}

Node _copyBlock(BuildContext context, Node node) {
Node copiedNode = node.copyWith();

final type = node.type;
final builder =
context.read<EditorState>().renderer.blockComponentBuilder(type);

if (builder == null) {
Log.error('Block type $type is not supported');
} else {
final valid = builder.validate(node);
if (!valid) {
Log.error('Block type $type is not valid');
if (node.type == TableBlockKeys.type) {
copiedNode = _fixTableBlock(node);
}
}
}

return copiedNode;
}

Node _fixTableBlock(Node node) {
if (node.type != TableBlockKeys.type) {
return node;
}

// the table node should contains colsLen and rowsLen
final colsLen = node.attributes[TableBlockKeys.colsLen];
final rowsLen = node.attributes[TableBlockKeys.rowsLen];
if (colsLen == null || rowsLen == null) {
return node;
}

final newChildren = <Node>[];
final children = node.children;

// based on the colsLen and rowsLen, iterate the children and fix the data
for (var i = 0; i < rowsLen; i++) {
for (var j = 0; j < colsLen; j++) {
final cell = children
.where(
(n) =>
n.attributes[TableCellBlockKeys.rowPosition] == i &&
n.attributes[TableCellBlockKeys.colPosition] == j,
)
.firstOrNull;
if (cell != null) {
newChildren.add(cell.copyWith());
} else {
newChildren.add(
tableCellNode('', i, j),
);
}
}
}

return node.copyWith(
children: newChildren,
attributes: {
...node.attributes,
TableBlockKeys.colsLen: colsLen,
TableBlockKeys.rowsLen: rowsLen,
},
);
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import 'dart:convert';

import 'package:appflowy/generated/locale_keys.g.dart';
import 'package:appflowy/mobile/presentation/base/animated_gesture.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/actions/mobile_block_action_buttons.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/clipboard_service.dart';
import 'package:appflowy/startup/startup.dart';
import 'package:appflowy/workspace/presentation/home/toast.dart';
import 'package:appflowy/workspace/presentation/widgets/dialogs.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flowy_infra_ui/flowy_infra_ui.dart';
Expand Down Expand Up @@ -59,25 +60,15 @@ class _ErrorBlockComponentWidgetState extends State<ErrorBlockComponentWidget>

@override
Widget build(BuildContext context) {
Widget child = DecoratedBox(
Widget child = Container(
width: double.infinity,
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surfaceContainerHighest,
borderRadius: BorderRadius.circular(4),
),
child: FlowyButton(
onTap: () async {
showSnackBarMessage(
context,
LocaleKeys.document_errorBlock_blockContentHasBeenCopied.tr(),
);
await getIt<ClipboardService>().setData(
ClipboardServiceData(plainText: jsonEncode(node.toJson())),
);
},
text: PlatformExtension.isDesktopOrWeb
? _buildDesktopErrorBlock(context)
: _buildMobileErrorBlock(context),
),
child: PlatformExtension.isDesktopOrWeb
? _buildDesktopErrorBlock(context)
: _buildMobileErrorBlock(context),
);

child = Padding(
Expand Down Expand Up @@ -107,40 +98,61 @@ class _ErrorBlockComponentWidgetState extends State<ErrorBlockComponentWidget>
Widget _buildDesktopErrorBlock(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Wrap(
crossAxisAlignment: WrapCrossAlignment.center,
child: Row(
children: [
const HSpace(4),
const HSpace(12),
FlowyText.regular(
LocaleKeys.document_errorBlock_theBlockIsNotSupported.tr(),
LocaleKeys.document_errorBlock_parseError.tr(args: [node.type]),
),
const HSpace(4),
FlowyText.regular(
'(${LocaleKeys.document_errorBlock_clickToCopyTheBlockContent.tr()})',
color: Theme.of(context).hintColor,
const Spacer(),
OutlinedRoundedButton(
text: LocaleKeys.document_errorBlock_copyBlockContent.tr(),
onTap: _copyBlockContent,
),
const HSpace(12),
],
),
);
}

Widget _buildMobileErrorBlock(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
FlowyText.regular(
LocaleKeys.document_errorBlock_theBlockIsNotSupported.tr(),
),
const VSpace(6),
FlowyText.regular(
'(${LocaleKeys.document_errorBlock_clickToCopyTheBlockContent.tr()})',
color: Theme.of(context).hintColor,
fontSize: 12.0,
),
],
return AnimatedGestureDetector(
onTapUp: _copyBlockContent,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 4.0, right: 24.0),
child: FlowyText.regular(
LocaleKeys.document_errorBlock_parseError.tr(args: [node.type]),
maxLines: 3,
),
),
const VSpace(6),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: FlowyText.regular(
'(${LocaleKeys.document_errorBlock_clickToCopyTheBlockContent.tr()})',
color: Theme.of(context).hintColor,
fontSize: 12.0,
),
),
],
),
),
);
}

void _copyBlockContent() {
showToastNotification(
context,
message: LocaleKeys.document_errorBlock_blockContentHasBeenCopied.tr(),
);

getIt<ClipboardService>().setData(
ClipboardServiceData(plainText: jsonEncode(node.toJson())),
);
}
}
4 changes: 2 additions & 2 deletions frontend/appflowy_flutter/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: da0a56c
resolved-ref: da0a56cf721c55dc2ab944d0769d2821aba509b7
ref: "44989c5"
resolved-ref: "44989c568e71fbf41970ec390cbb62f0db99b6e5"
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "3.2.0"
Expand Down
2 changes: 1 addition & 1 deletion frontend/appflowy_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ dependency_overrides:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: "da0a56c"
ref: "44989c5"

appflowy_editor_plugins:
git:
Expand Down
4 changes: 3 additions & 1 deletion frontend/resources/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -1798,7 +1798,9 @@
"errorBlock": {
"theBlockIsNotSupported": "Unable to parse the block content",
"clickToCopyTheBlockContent": "Click to copy the block content",
"blockContentHasBeenCopied": "The block content has been copied."
"blockContentHasBeenCopied": "The block content has been copied.",
"parseError": "An error occurred while parsing the {} block.",
"copyBlockContent": "Copy block content"
},
"mobilePageSelector": {
"title": "Select page",
Expand Down

0 comments on commit 8e34951

Please sign in to comment.