fix: simple table issues and locale issues (#7138)

* fix: can't make changes on row or column of table

* fix: fallback to en-US if the locale is invalid

* chore: remove unused code

* fix: simple table issues
This commit is contained in:
Lucas 2025-01-05 19:55:15 +08:00 committed by Lucas.Xu
parent 0c1eb7306a
commit 430e21aec2
15 changed files with 163 additions and 56 deletions

View file

@ -85,7 +85,11 @@ class DocumentCollaboratorsBloc
final ids = <dynamic>{};
final sorted = states.value.values.toList()
..sort((a, b) => b.timestamp.compareTo(a.timestamp))
..retainWhere((e) => ids.add(e.user.uid.toString() + e.user.deviceId));
// filter the duplicate users
..retainWhere((e) => ids.add(e.user.uid.toString() + e.user.deviceId))
// only keep version 1 and metadata is not empty
..retainWhere((e) => e.version == 1)
..retainWhere((e) => e.metadata.isNotEmpty);
for (final state in sorted) {
if (state.version != 1) {
continue;

View file

@ -399,10 +399,11 @@ ParagraphBlockComponentBuilder _buildParagraphBlockComponentBuilder(
return ParagraphBlockComponentBuilder(
configuration: configuration.copyWith(
placeholderText: placeholderText,
textStyle: (node) => _buildTextStyleInTableCell(
textStyle: (node, {TextSpan? textSpan}) => _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
),
textAlign: (node) => _buildTextAlignInTableCell(
context,
@ -421,10 +422,11 @@ TodoListBlockComponentBuilder _buildTodoListBlockComponentBuilder(
return TodoListBlockComponentBuilder(
configuration: configuration.copyWith(
placeholderText: (_) => LocaleKeys.blockPlaceholders_todoList.tr(),
textStyle: (node) => _buildTextStyleInTableCell(
textStyle: (node, {TextSpan? textSpan}) => _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
),
textAlign: (node) => _buildTextAlignInTableCell(
context,
@ -451,10 +453,11 @@ BulletedListBlockComponentBuilder _buildBulletedListBlockComponentBuilder(
return BulletedListBlockComponentBuilder(
configuration: configuration.copyWith(
placeholderText: (_) => LocaleKeys.blockPlaceholders_bulletList.tr(),
textStyle: (node) => _buildTextStyleInTableCell(
textStyle: (node, {TextSpan? textSpan}) => _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
),
textAlign: (node) => _buildTextAlignInTableCell(
context,
@ -473,10 +476,11 @@ NumberedListBlockComponentBuilder _buildNumberedListBlockComponentBuilder(
return NumberedListBlockComponentBuilder(
configuration: configuration.copyWith(
placeholderText: (_) => LocaleKeys.blockPlaceholders_numberList.tr(),
textStyle: (node) => _buildTextStyleInTableCell(
textStyle: (node, {TextSpan? textSpan}) => _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
),
textAlign: (node) => _buildTextAlignInTableCell(
context,
@ -507,10 +511,11 @@ QuoteBlockComponentBuilder _buildQuoteBlockComponentBuilder(
return QuoteBlockComponentBuilder(
configuration: configuration.copyWith(
placeholderText: (_) => LocaleKeys.blockPlaceholders_quote.tr(),
textStyle: (node) => _buildTextStyleInTableCell(
textStyle: (node, {TextSpan? textSpan}) => _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
),
textAlign: (node) => _buildTextAlignInTableCell(
context,
@ -529,10 +534,11 @@ HeadingBlockComponentBuilder _buildHeadingBlockComponentBuilder(
) {
return HeadingBlockComponentBuilder(
configuration: configuration.copyWith(
textStyle: (node) => _buildTextStyleInTableCell(
textStyle: (node, {TextSpan? textSpan}) => _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
),
padding: (node) {
if (customHeadingPadding != null) {
@ -698,10 +704,11 @@ CalloutBlockComponentBuilder _buildCalloutBlockComponentBuilder(
node: node,
configuration: configuration,
),
textStyle: (node) => _buildTextStyleInTableCell(
textStyle: (node, {TextSpan? textSpan}) => _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
),
),
inlinePadding: const EdgeInsets.symmetric(vertical: 8.0),
@ -789,11 +796,12 @@ ToggleListBlockComponentBuilder _buildToggleListBlockComponentBuilder(
return const EdgeInsets.only(top: 12.0, bottom: 4.0);
},
textStyle: (node) {
textStyle: (node, {TextSpan? textSpan}) {
final textStyle = _buildTextStyleInTableCell(
context,
node: node,
configuration: configuration,
textSpan: textSpan,
);
final level = node.attributes[ToggleListBlockKeys.level] as int?;
if (level == null) {
@ -828,9 +836,14 @@ OutlineBlockComponentBuilder _buildOutlineBlockComponentBuilder(
) {
return OutlineBlockComponentBuilder(
configuration: configuration.copyWith(
placeholderTextStyle: (_) =>
placeholderTextStyle: (node, {TextSpan? textSpan}) =>
styleCustomizer.outlineBlockPlaceholderStyleBuilder(),
padding: (_) => const EdgeInsets.only(top: 12.0, bottom: 4.0),
padding: (node) {
if (UniversalPlatform.isMobile) {
return configuration.padding(node);
}
return const EdgeInsets.only(top: 12.0, bottom: 4.0);
},
),
);
}
@ -877,7 +890,8 @@ SubPageBlockComponentBuilder _buildSubPageBlockComponentBuilder(
}) {
return SubPageBlockComponentBuilder(
configuration: configuration.copyWith(
textStyle: (node) => styleCustomizer.subPageBlockTextStyleBuilder(),
textStyle: (node, {TextSpan? textSpan}) =>
styleCustomizer.subPageBlockTextStyleBuilder(),
padding: (node) {
if (UniversalPlatform.isMobile) {
return const EdgeInsets.symmetric(horizontal: 18);
@ -892,8 +906,9 @@ TextStyle _buildTextStyleInTableCell(
BuildContext context, {
required Node node,
required BlockComponentConfiguration configuration,
required TextSpan? textSpan,
}) {
TextStyle textStyle = configuration.textStyle(node);
TextStyle textStyle = configuration.textStyle(node, textSpan: textSpan);
if (node.isInHeaderColumn ||
node.isInHeaderRow ||
@ -906,6 +921,11 @@ TextStyle _buildTextStyleInTableCell(
final cellTextColor = node.textColorInColumn ?? node.textColorInRow;
// enable it if we need to support the text color of the text span
// final isTextSpanColorNull = textSpan?.style?.color == null;
// final isTextSpanChildrenColorNull =
// textSpan?.children?.every((e) => e.style?.color == null) ?? true;
if (cellTextColor != null) {
textStyle = textStyle.copyWith(
color: buildEditorCustomizedColor(

View file

@ -1,14 +1,14 @@
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:flutter/material.dart';
import 'package:appflowy/plugins/document/presentation/editor_drop_manager.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_file.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/copy_and_paste/paste_from_image.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/mention/mention_page_block.dart';
import 'package:appflowy/plugins/document/presentation/editor_plugins/plugins.dart';
import 'package:appflowy/shared/patterns/file_type_patterns.dart';
import 'package:appflowy/workspace/presentation/widgets/draggable_item/draggable_item.dart';
import 'package:appflowy_backend/protobuf/flowy-folder/view.pb.dart';
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
const _excludeFromDropTarget = [
@ -16,6 +16,9 @@ const _excludeFromDropTarget = [
CustomImageBlockKeys.type,
MultiImageBlockKeys.type,
FileBlockKeys.type,
SimpleTableBlockKeys.type,
SimpleTableCellBlockKeys.type,
SimpleTableRowBlockKeys.type,
];
class EditorDropHandler extends StatelessWidget {
@ -38,8 +41,13 @@ class EditorDropHandler extends StatelessWidget {
Widget build(BuildContext context) {
final childWidget = Consumer<EditorDropManagerState>(
builder: (context, dropState, _) => DragTarget<ViewPB>(
onLeave: (_) => editorState.selectionService.removeDropTarget(),
onLeave: (_) {
editorState.selectionService.removeDropTarget();
disableAutoScrollWhenDragging = false;
},
onMove: (details) {
disableAutoScrollWhenDragging = true;
if (details.data.id == viewId) {
return;
}

View file

@ -140,7 +140,9 @@ bool shouldIgnoreDragTarget({
}
final targetNode = editorState.getNodeAtPath(targetPath);
if (targetNode != null && targetNode.isInTable) {
if (targetNode != null &&
targetNode.isInTable &&
targetNode.type != SimpleTableBlockKeys.type) {
return true;
}

View file

@ -16,6 +16,7 @@ class VisualDragArea extends StatelessWidget {
final DragAreaBuilderData data;
final Node dragNode;
final EditorState editorState;
@override
Widget build(BuildContext context) {
final targetNode = data.targetNode;

View file

@ -258,10 +258,10 @@ class _CalloutBlockComponentWidgetState
placeholderText: placeholderText,
textAlign: alignment?.toTextAlign ?? textAlign,
textSpanDecorator: (textSpan) => textSpan.updateTextStyle(
textStyle,
textStyleWithTextSpan(textSpan: textSpan),
),
placeholderTextSpanDecorator: (textSpan) => textSpan.updateTextStyle(
placeholderTextStyle,
placeholderTextStyleWithTextSpan(textSpan: textSpan),
),
textDirection: textDirection,
cursorColor: editorState.editorStyle.cursorColor,

View file

@ -31,12 +31,16 @@ class _SimpleTableMoreActionPopupState
RenderBox? get renderBox => context.findRenderObject() as RenderBox?;
late final simpleTableContext = context.read<SimpleTableContext>();
Node? tableNode;
Node? tableCellNode;
@override
void initState() {
super.initState();
final tableCellNode =
context.read<SimpleTableContext>().hoveringTableCell.value;
tableCellNode = context.read<SimpleTableContext>().hoveringTableCell.value;
tableNode = tableCellNode?.parentTableNode;
gestureInterceptor = SelectionGestureInterceptor(
key: 'simple_table_more_action_popup_interceptor_${tableCellNode?.id}',
canTap: (details) => !_isTapInBounds(details.globalPosition),
@ -59,10 +63,6 @@ class _SimpleTableMoreActionPopupState
@override
Widget build(BuildContext context) {
final simpleTableContext = context.read<SimpleTableContext>();
final tableCellNode = simpleTableContext.hoveringTableCell.value;
final tableNode = tableCellNode?.parentTableNode;
if (tableNode == null) {
return const SizedBox.shrink();
}
@ -70,6 +70,9 @@ class _SimpleTableMoreActionPopupState
return AppFlowyPopover(
onOpen: () => _onOpen(tableCellNode: tableCellNode),
onClose: () => _onClose(),
canClose: () async {
return true;
},
direction: widget.type == SimpleTableMoreActionType.row
? PopoverDirection.bottomWithCenterAligned
: PopoverDirection.bottomWithLeftAligned,
@ -81,7 +84,7 @@ class _SimpleTableMoreActionPopupState
child: SimpleTableDraggableReorderButton(
editorState: editorState,
simpleTableContext: simpleTableContext,
node: tableNode,
node: tableNode!,
index: widget.index,
isShowingMenu: widget.isShowingMenu,
type: widget.type,

View file

@ -200,6 +200,8 @@ class SubPageBlockComponentState extends State<SubPageBlockComponent>
return const SizedBox.shrink();
}
final textStyle = textStyleWithTextSpan();
Widget child = Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: MouseRegion(

View file

@ -312,7 +312,9 @@ class _ToggleListBlockComponentWidgetState
placeholderText: placeholderText,
lineHeight: 1.5,
textSpanDecorator: (textSpan) {
var result = textSpan.updateTextStyle(textStyle);
var result = textSpan.updateTextStyle(
textStyleWithTextSpan(textSpan: textSpan),
);
if (level != null) {
result = result.updateTextStyle(
widget.textStyleBuilder?.call(level),
@ -321,13 +323,17 @@ class _ToggleListBlockComponentWidgetState
return result;
},
placeholderTextSpanDecorator: (textSpan) {
var result = textSpan.updateTextStyle(textStyle);
var result = textSpan.updateTextStyle(
textStyleWithTextSpan(textSpan: textSpan),
);
if (level != null && widget.textStyleBuilder != null) {
result = result.updateTextStyle(
widget.textStyleBuilder?.call(level),
);
}
return result.updateTextStyle(placeholderTextStyle);
return result.updateTextStyle(
placeholderTextStyleWithTextSpan(textSpan: textSpan),
);
},
textDirection: textDirection,
textAlign: alignment?.toTextAlign ?? textAlign,

View file

@ -138,22 +138,36 @@ class DateReferenceService extends InlineActionsDelegate {
final tomorrow = today.add(const Duration(days: 1));
final yesterday = today.subtract(const Duration(days: 1));
_allOptions = [
_itemFromDate(
late InlineActionsMenuItem todayItem;
late InlineActionsMenuItem tomorrowItem;
late InlineActionsMenuItem yesterdayItem;
try {
todayItem = _itemFromDate(
today,
LocaleKeys.relativeDates_today.tr(),
[DateFormat.yMd(_locale).format(today)],
),
_itemFromDate(
);
tomorrowItem = _itemFromDate(
tomorrow,
LocaleKeys.relativeDates_tomorrow.tr(),
[DateFormat.yMd(_locale).format(tomorrow)],
),
_itemFromDate(
);
yesterdayItem = _itemFromDate(
yesterday,
LocaleKeys.relativeDates_yesterday.tr(),
[DateFormat.yMd(_locale).format(yesterday)],
),
);
} catch (e) {
todayItem = _itemFromDate(today);
tomorrowItem = _itemFromDate(tomorrow);
yesterdayItem = _itemFromDate(yesterday);
}
_allOptions = [
todayItem,
tomorrowItem,
yesterdayItem,
];
}
@ -173,7 +187,17 @@ class DateReferenceService extends InlineActionsDelegate {
String? label,
List<String>? keywords,
]) {
final labelStr = label ?? DateFormat.yMd(_locale).format(date);
late String labelStr;
if (label != null) {
labelStr = label;
} else {
try {
labelStr = DateFormat.yMd(_locale).format(date);
} catch (e) {
// fallback to en-US
labelStr = DateFormat.yMd('en-US').format(date);
}
}
return InlineActionsMenuItem(
label: labelStr.capitalize(),

View file

@ -170,17 +170,32 @@ class ReminderReferenceService extends InlineActionsDelegate {
final tomorrow = today.add(const Duration(days: 1));
final oneWeek = today.add(const Duration(days: 7));
_allOptions = [
_itemFromDate(
late InlineActionsMenuItem todayItem;
late InlineActionsMenuItem oneWeekItem;
try {
todayItem = _itemFromDate(
tomorrow,
LocaleKeys.relativeDates_tomorrow.tr(),
[DateFormat.yMd(_locale).format(tomorrow)],
),
_itemFromDate(
);
} catch (e) {
todayItem = _itemFromDate(today);
}
try {
oneWeekItem = _itemFromDate(
oneWeek,
LocaleKeys.relativeDates_oneWeek.tr(),
[DateFormat.yMd(_locale).format(oneWeek)],
),
);
} catch (e) {
oneWeekItem = _itemFromDate(oneWeek);
}
_allOptions = [
todayItem,
oneWeekItem,
];
}
@ -200,7 +215,17 @@ class ReminderReferenceService extends InlineActionsDelegate {
String? label,
List<String>? keywords,
]) {
final labelStr = label ?? DateFormat.yMd(_locale).format(date);
late String labelStr;
if (label != null) {
labelStr = label;
} else {
try {
labelStr = DateFormat.yMd(_locale).format(date);
} catch (e) {
// fallback to en-US
labelStr = DateFormat.yMd('en-US').format(date);
}
}
return InlineActionsMenuItem(
label: labelStr.capitalize(),

View file

@ -8,7 +8,14 @@ const _friendlyFmt = 'MMM dd, y';
const _dmyFmt = 'dd/MM/y';
extension DateFormatter on UserDateFormatPB {
DateFormat get toFormat => DateFormat(_toFormat[this] ?? _friendlyFmt);
DateFormat get toFormat {
try {
return DateFormat(_toFormat[this] ?? _friendlyFmt);
} catch (_) {
// fallback to en-US
return DateFormat(_toFormat[this] ?? _friendlyFmt, 'en-US');
}
}
String formatDate(
DateTime date,

View file

@ -1,6 +1,11 @@
import 'package:flutter/material.dart';
import 'package:universal_platform/universal_platform.dart';
/// This value is used to disable the auto scroll when dragging.
///
/// It is used to prevent the auto scroll when dragging a view item to a document.
bool disableAutoScrollWhenDragging = false;
class DraggableItem<T extends Object> extends StatefulWidget {
const DraggableItem({
super.key,
@ -67,7 +72,7 @@ class _DraggableItemState<T extends Object> extends State<DraggableItem<T>> {
childWhenDragging: widget.childWhenDragging ?? widget.child,
child: widget.child,
onDragUpdate: (details) {
if (widget.enableAutoScroll) {
if (widget.enableAutoScroll && !disableAutoScrollWhenDragging) {
dragTarget = details.globalPosition & widget.hitTestSize;
autoScroller?.startAutoScrollIfNecessary(dragTarget!);
}
@ -88,7 +93,7 @@ class _DraggableItemState<T extends Object> extends State<DraggableItem<T>> {
}
void initAutoScrollerIfNeeded(BuildContext context) {
if (!widget.enableAutoScroll) {
if (!widget.enableAutoScroll || disableAutoScrollWhenDragging) {
return;
}
@ -104,7 +109,7 @@ class _DraggableItemState<T extends Object> extends State<DraggableItem<T>> {
autoScroller = EdgeDraggingAutoScroller(
scrollable!,
onScrollViewScrolled: () {
if (dragTarget != null) {
if (dragTarget != null && !disableAutoScrollWhenDragging) {
autoScroller!.startAutoScrollIfNecessary(dragTarget!);
}
},

View file

@ -61,8 +61,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: "9f6a299"
resolved-ref: "9f6a29968ecbb61678b8e0e8c9d90bcba44a24e3"
ref: "1208cc6"
resolved-ref: "1208cc60ebaf2ef942a6d391be2aeda9621bb66b"
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "4.0.0"
@ -70,8 +70,8 @@ packages:
dependency: "direct main"
description:
path: "packages/appflowy_editor_plugins"
ref: "8047c21"
resolved-ref: "8047c21868273d544684522eb61e4ac2d2041409"
ref: ca8289099e40e0d6ad0605fbbe01fde3091538bb
resolved-ref: ca8289099e40e0d6ad0605fbbe01fde3091538bb
url: "https://github.com/AppFlowy-IO/AppFlowy-plugins.git"
source: git
version: "0.0.6"

View file

@ -174,13 +174,13 @@ dependency_overrides:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: "9f6a299"
ref: "1208cc6"
appflowy_editor_plugins:
git:
url: https://github.com/AppFlowy-IO/AppFlowy-plugins.git
path: "packages/appflowy_editor_plugins"
ref: "8047c21"
ref: "ca8289099e40e0d6ad0605fbbe01fde3091538bb"
sheet:
git: