feat: auto-dismiss collapsed handle on Android if no interaction occurs (#7088)

* feat: support auto-dismiss collapsed handle on Android

* fix: hit test area of collasepd handle is too big

* chore: upgrade appflowy_editor

* fix: simple table issues on mobile

* feat: highlight cell after insertion

* test: text color and cell background color test

* fix: sign_in_page_settings_test
This commit is contained in:
Lucas 2024-12-30 17:56:43 +08:00 committed by GitHub
parent 92722d0922
commit dfe994b341
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 493 additions and 4 deletions

View File

@ -67,6 +67,7 @@ void main() {
// open settings page to check the result
await tester.tapButton(settingsButton);
await tester.pumpAndSettle(const Duration(milliseconds: 250));
// check the server type
expect(

View File

@ -505,6 +505,10 @@ class _AppFlowyEditorPageState extends State<AppFlowyEditorPage>
Position(path: lastNode.path),
);
}
transaction.customSelectionType = SelectionType.inline;
transaction.reason = SelectionUpdateReason.uiEvent;
await editorState.apply(transaction);
}

View File

@ -104,6 +104,18 @@ extension TableMapOperation on Node {
comparator: (iKey, index) => iKey >= index,
);
final rowBoldAttributes = _remapSource(
this.rowBoldAttributes,
index,
comparator: (iKey, index) => iKey >= index,
);
final rowTextColors = _remapSource(
this.rowTextColors,
index,
comparator: (iKey, index) => iKey >= index,
);
return attributes
.mergeValues(
SimpleTableBlockKeys.rowColors,
@ -112,6 +124,14 @@ extension TableMapOperation on Node {
.mergeValues(
SimpleTableBlockKeys.rowAligns,
rowAligns,
)
.mergeValues(
SimpleTableBlockKeys.rowBoldAttributes,
rowBoldAttributes,
)
.mergeValues(
SimpleTableBlockKeys.rowTextColors,
rowTextColors,
);
} catch (e) {
Log.warn('Failed to map row insertion attributes: $e');
@ -167,6 +187,18 @@ extension TableMapOperation on Node {
comparator: (iKey, index) => iKey >= index,
);
final columnBoldAttributes = _remapSource(
this.columnBoldAttributes,
index,
comparator: (iKey, index) => iKey >= index,
);
final columnTextColors = _remapSource(
this.columnTextColors,
index,
comparator: (iKey, index) => iKey >= index,
);
final bool distributeColumnWidthsEvenly =
attributes[SimpleTableBlockKeys.distributeColumnWidthsEvenly] ??
false;
@ -189,6 +221,14 @@ extension TableMapOperation on Node {
.mergeValues(
SimpleTableBlockKeys.columnWidths,
columnWidths,
)
.mergeValues(
SimpleTableBlockKeys.columnBoldAttributes,
columnBoldAttributes,
)
.mergeValues(
SimpleTableBlockKeys.columnTextColors,
columnTextColors,
);
} catch (e) {
Log.warn('Failed to map row insertion attributes: $e');
@ -238,6 +278,18 @@ extension TableMapOperation on Node {
index,
);
final (rowBoldAttributes, duplicatedRowBoldAttribute) =
_findDuplicatedEntryAndRemap(
this.rowBoldAttributes,
index,
);
final (rowTextColors, duplicatedRowTextColor) =
_findDuplicatedEntryAndRemap(
this.rowTextColors,
index,
);
return attributes
.mergeValues(
SimpleTableBlockKeys.rowColors,
@ -248,6 +300,16 @@ extension TableMapOperation on Node {
SimpleTableBlockKeys.rowAligns,
rowAligns,
duplicatedEntry: duplicatedRowAlign,
)
.mergeValues(
SimpleTableBlockKeys.rowBoldAttributes,
rowBoldAttributes,
duplicatedEntry: duplicatedRowBoldAttribute,
)
.mergeValues(
SimpleTableBlockKeys.rowTextColors,
rowTextColors,
duplicatedEntry: duplicatedRowTextColor,
);
} catch (e) {
Log.warn('Failed to map row insertion attributes: $e');
@ -304,6 +366,18 @@ extension TableMapOperation on Node {
index,
);
final (columnBoldAttributes, duplicatedColumnBoldAttribute) =
_findDuplicatedEntryAndRemap(
this.columnBoldAttributes,
index,
);
final (columnTextColors, duplicatedColumnTextColor) =
_findDuplicatedEntryAndRemap(
this.columnTextColors,
index,
);
return attributes
.mergeValues(
SimpleTableBlockKeys.columnColors,
@ -319,6 +393,16 @@ extension TableMapOperation on Node {
SimpleTableBlockKeys.columnWidths,
columnWidths,
duplicatedEntry: duplicatedColumnWidth,
)
.mergeValues(
SimpleTableBlockKeys.columnBoldAttributes,
columnBoldAttributes,
duplicatedEntry: duplicatedColumnBoldAttribute,
)
.mergeValues(
SimpleTableBlockKeys.columnTextColors,
columnTextColors,
duplicatedEntry: duplicatedColumnTextColor,
);
} catch (e) {
Log.warn('Failed to map column duplication attributes: $e');
@ -364,6 +448,7 @@ extension TableMapOperation on Node {
comparator: (iKey, index) => iKey > index,
filterIndex: index,
);
final columnAligns = _remapSource(
this.columnAligns,
index,
@ -371,6 +456,7 @@ extension TableMapOperation on Node {
comparator: (iKey, index) => iKey > index,
filterIndex: index,
);
final columnWidths = _remapSource(
this.columnWidths,
index,
@ -379,6 +465,22 @@ extension TableMapOperation on Node {
filterIndex: index,
);
final columnBoldAttributes = _remapSource(
this.columnBoldAttributes,
index,
increment: false,
comparator: (iKey, index) => iKey > index,
filterIndex: index,
);
final columnTextColors = _remapSource(
this.columnTextColors,
index,
increment: false,
comparator: (iKey, index) => iKey > index,
filterIndex: index,
);
return attributes
.mergeValues(
SimpleTableBlockKeys.columnColors,
@ -391,6 +493,14 @@ extension TableMapOperation on Node {
.mergeValues(
SimpleTableBlockKeys.columnWidths,
columnWidths,
)
.mergeValues(
SimpleTableBlockKeys.columnBoldAttributes,
columnBoldAttributes,
)
.mergeValues(
SimpleTableBlockKeys.columnTextColors,
columnTextColors,
);
} catch (e) {
Log.warn('Failed to map column deletion attributes: $e');
@ -443,6 +553,22 @@ extension TableMapOperation on Node {
filterIndex: index,
);
final rowBoldAttributes = _remapSource(
this.rowBoldAttributes,
index,
increment: false,
comparator: (iKey, index) => iKey > index,
filterIndex: index,
);
final rowTextColors = _remapSource(
this.rowTextColors,
index,
increment: false,
comparator: (iKey, index) => iKey > index,
filterIndex: index,
);
return attributes
.mergeValues(
SimpleTableBlockKeys.rowColors,
@ -451,6 +577,14 @@ extension TableMapOperation on Node {
.mergeValues(
SimpleTableBlockKeys.rowAligns,
rowAligns,
)
.mergeValues(
SimpleTableBlockKeys.rowBoldAttributes,
rowBoldAttributes,
)
.mergeValues(
SimpleTableBlockKeys.rowTextColors,
rowTextColors,
);
} catch (e) {
Log.warn('Failed to map row deletion attributes: $e');
@ -531,6 +665,10 @@ extension TableMapOperation on Node {
final duplicatedColumnColor = this.columnColors[fromIndex.toString()];
final duplicatedColumnAlign = this.columnAligns[fromIndex.toString()];
final duplicatedColumnWidth = this.columnWidths[fromIndex.toString()];
final duplicatedColumnBoldAttribute =
this.columnBoldAttributes[fromIndex.toString()];
final duplicatedColumnTextColor =
this.columnTextColors[fromIndex.toString()];
/// Case 1: fromIndex > toIndex
/// Before:
@ -619,6 +757,34 @@ extension TableMapOperation on Node {
filterIndex: fromIndex,
);
final columnBoldAttributes = _remapSource(
this.columnBoldAttributes,
fromIndex,
increment: fromIndex > toIndex,
comparator: (iKey, index) {
if (fromIndex > toIndex) {
return iKey < fromIndex && iKey >= toIndex;
} else {
return iKey > fromIndex && iKey <= toIndex;
}
},
filterIndex: fromIndex,
);
final columnTextColors = _remapSource(
this.columnTextColors,
fromIndex,
increment: fromIndex > toIndex,
comparator: (iKey, index) {
if (fromIndex > toIndex) {
return iKey < fromIndex && iKey >= toIndex;
} else {
return iKey > fromIndex && iKey <= toIndex;
}
},
filterIndex: fromIndex,
);
return attributes
.mergeValues(
SimpleTableBlockKeys.columnColors,
@ -652,6 +818,28 @@ extension TableMapOperation on Node {
)
: null,
removeNullValue: true,
)
.mergeValues(
SimpleTableBlockKeys.columnBoldAttributes,
columnBoldAttributes,
duplicatedEntry: duplicatedColumnBoldAttribute != null
? MapEntry(
toIndex.toString(),
duplicatedColumnBoldAttribute,
)
: null,
removeNullValue: true,
)
.mergeValues(
SimpleTableBlockKeys.columnTextColors,
columnTextColors,
duplicatedEntry: duplicatedColumnTextColor != null
? MapEntry(
toIndex.toString(),
duplicatedColumnTextColor,
)
: null,
removeNullValue: true,
);
} catch (e) {
Log.warn('Failed to map column deletion attributes: $e');
@ -667,6 +855,9 @@ extension TableMapOperation on Node {
try {
final duplicatedRowColor = this.rowColors[fromIndex.toString()];
final duplicatedRowAlign = this.rowAligns[fromIndex.toString()];
final duplicatedRowBoldAttribute =
this.rowBoldAttributes[fromIndex.toString()];
final duplicatedRowTextColor = this.rowTextColors[fromIndex.toString()];
/// Example:
/// Case 1: fromIndex > toIndex
@ -742,6 +933,34 @@ extension TableMapOperation on Node {
filterIndex: fromIndex,
);
final rowBoldAttributes = _remapSource(
this.rowBoldAttributes,
fromIndex,
increment: fromIndex > toIndex,
comparator: (iKey, index) {
if (fromIndex > toIndex) {
return iKey < fromIndex && iKey >= toIndex;
} else {
return iKey > fromIndex && iKey <= toIndex;
}
},
filterIndex: fromIndex,
);
final rowTextColors = _remapSource(
this.rowTextColors,
fromIndex,
increment: fromIndex > toIndex,
comparator: (iKey, index) {
if (fromIndex > toIndex) {
return iKey < fromIndex && iKey >= toIndex;
} else {
return iKey > fromIndex && iKey <= toIndex;
}
},
filterIndex: fromIndex,
);
return attributes
.mergeValues(
SimpleTableBlockKeys.rowColors,
@ -764,6 +983,28 @@ extension TableMapOperation on Node {
)
: null,
removeNullValue: true,
)
.mergeValues(
SimpleTableBlockKeys.rowBoldAttributes,
rowBoldAttributes,
duplicatedEntry: duplicatedRowBoldAttribute != null
? MapEntry(
toIndex.toString(),
duplicatedRowBoldAttribute,
)
: null,
removeNullValue: true,
)
.mergeValues(
SimpleTableBlockKeys.rowTextColors,
rowTextColors,
duplicatedEntry: duplicatedRowTextColor != null
? MapEntry(
toIndex.toString(),
duplicatedRowTextColor,
)
: null,
removeNullValue: true,
);
} catch (e) {
Log.warn('Failed to map row reordering attributes: $e');

View File

@ -455,6 +455,21 @@ class _SimpleTableMoreActionItemState extends State<SimpleTableMoreActionItem> {
final columnIndex = node.columnIndex;
final editorState = context.read<EditorState>();
editorState.insertColumnInTable(table, columnIndex);
final cell = table.getTableCellNode(
rowIndex: 0,
columnIndex: columnIndex,
);
if (cell == null) {
return;
}
// update selection
editorState.selection = Selection.collapsed(
Position(
path: cell.path.child(0),
),
);
}
void _insertColumnRight() {
@ -466,6 +481,21 @@ class _SimpleTableMoreActionItemState extends State<SimpleTableMoreActionItem> {
final columnIndex = node.columnIndex;
final editorState = context.read<EditorState>();
editorState.insertColumnInTable(table, columnIndex + 1);
final cell = table.getTableCellNode(
rowIndex: 0,
columnIndex: columnIndex + 1,
);
if (cell == null) {
return;
}
// update selection
editorState.selection = Selection.collapsed(
Position(
path: cell.path.child(0),
),
);
}
void _insertRowAbove() {
@ -477,6 +507,18 @@ class _SimpleTableMoreActionItemState extends State<SimpleTableMoreActionItem> {
final rowIndex = node.rowIndex;
final editorState = context.read<EditorState>();
editorState.insertRowInTable(table, rowIndex);
final cell = table.getTableCellNode(rowIndex: rowIndex, columnIndex: 0);
if (cell == null) {
return;
}
// update selection
editorState.selection = Selection.collapsed(
Position(
path: cell.path.child(0),
),
);
}
void _insertRowBelow() {
@ -488,6 +530,18 @@ class _SimpleTableMoreActionItemState extends State<SimpleTableMoreActionItem> {
final rowIndex = node.rowIndex;
final editorState = context.read<EditorState>();
editorState.insertRowInTable(table, rowIndex + 1);
final cell = table.getTableCellNode(rowIndex: rowIndex + 1, columnIndex: 0);
if (cell == null) {
return;
}
// update selection
editorState.selection = Selection.collapsed(
Position(
path: cell.path.child(0),
),
);
}
void _deleteRow() {

View File

@ -166,9 +166,10 @@ class EditorStyleCustomizer {
applyHeightToLastDescent: true,
),
textSpanDecorator: customizeAttributeDecorator,
mobileDragHandleBallSize: const Size.square(12.0),
magnifierSize: const Size(144, 96),
textScaleFactor: textScaleFactor,
mobileDragHandleLeftExtend: 12.0,
mobileDragHandleWidthExtend: 24.0,
);
}

View File

@ -61,8 +61,8 @@ packages:
dependency: "direct main"
description:
path: "."
ref: c68e5f6
resolved-ref: c68e5f6c585205083e27e875b822656425b2853f
ref: cfb8b1b
resolved-ref: cfb8b1b6eb06f73a4fb297b6fd1d54b0ccec2922
url: "https://github.com/AppFlowy-IO/appflowy-editor.git"
source: git
version: "4.0.0"

View File

@ -174,7 +174,7 @@ dependency_overrides:
appflowy_editor:
git:
url: https://github.com/AppFlowy-IO/appflowy-editor.git
ref: "c68e5f6"
ref: "cfb8b1b"
appflowy_editor_plugins:
git:

View File

@ -170,5 +170,67 @@ void main() {
'0': TableAlign.center.key,
});
});
test('delete a column with text color & bold style (1)', () async {
final (editorState, tableNode) = createEditorStateAndTable(
rowCount: 3,
columnCount: 4,
);
// delete the column 1
final tableCellNode =
tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1);
await editorState.updateColumnTextColor(
tableCellNode: tableCellNode!,
color: '0xFF0000FF',
);
await editorState.toggleColumnBoldAttribute(
tableCellNode: tableCellNode,
isBold: true,
);
expect(tableNode.columnTextColors, {
'1': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'1': true,
});
await editorState.deleteColumnInTable(tableNode, 0);
expect(tableNode.columnTextColors, {
'0': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'0': true,
});
expect(tableNode.rowLength, 3);
expect(tableNode.columnLength, 3);
});
test('delete a column with text color & bold style (2)', () async {
final (editorState, tableNode) = createEditorStateAndTable(
rowCount: 3,
columnCount: 4,
);
// delete the column 1
final tableCellNode =
tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1);
await editorState.updateColumnTextColor(
tableCellNode: tableCellNode!,
color: '0xFF0000FF',
);
await editorState.toggleColumnBoldAttribute(
tableCellNode: tableCellNode,
isBold: true,
);
expect(tableNode.columnTextColors, {
'1': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'1': true,
});
await editorState.deleteColumnInTable(tableNode, 1);
expect(tableNode.columnTextColors, {});
expect(tableNode.columnBoldAttributes, {});
expect(tableNode.rowLength, 3);
expect(tableNode.columnLength, 3);
});
});
}

View File

@ -161,5 +161,69 @@ void main() {
'1': TableAlign.center.key,
});
});
test('duplicate a column with text color & bold style (1)', () async {
final (editorState, tableNode) = createEditorStateAndTable(
rowCount: 3,
columnCount: 4,
);
// duplicate the column 1
final tableCellNode =
tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1);
await editorState.updateColumnTextColor(
tableCellNode: tableCellNode!,
color: '0xFF0000FF',
);
await editorState.toggleColumnBoldAttribute(
tableCellNode: tableCellNode,
isBold: true,
);
expect(tableNode.columnTextColors, {
'1': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'1': true,
});
await editorState.duplicateColumnInTable(tableNode, 1);
expect(tableNode.columnTextColors, {
'1': '0xFF0000FF',
'2': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'1': true,
'2': true,
});
});
test('duplicate a column with text color & bold style (2)', () async {
final (editorState, tableNode) = createEditorStateAndTable(
rowCount: 3,
columnCount: 4,
);
// duplicate the column 1
final tableCellNode =
tableNode.getTableCellNode(rowIndex: 0, columnIndex: 1);
await editorState.updateColumnTextColor(
tableCellNode: tableCellNode!,
color: '0xFF0000FF',
);
await editorState.toggleColumnBoldAttribute(
tableCellNode: tableCellNode,
isBold: true,
);
expect(tableNode.columnTextColors, {
'1': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'1': true,
});
await editorState.duplicateColumnInTable(tableNode, 0);
expect(tableNode.columnTextColors, {
'2': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'2': true,
});
});
});
}

View File

@ -190,5 +190,67 @@ void main() {
'0': TableAlign.center.key,
});
});
test('insert a column with text color & bold style (1)', () async {
final (editorState, tableNode) = createEditorStateAndTable(
rowCount: 2,
columnCount: 3,
);
// insert the column at the first position
final tableCellNode =
tableNode.getTableCellNode(rowIndex: 0, columnIndex: 0);
await editorState.updateColumnTextColor(
tableCellNode: tableCellNode!,
color: '0xFF0000FF',
);
await editorState.toggleColumnBoldAttribute(
tableCellNode: tableCellNode,
isBold: true,
);
expect(tableNode.columnTextColors, {
'0': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'0': true,
});
await editorState.insertColumnInTable(tableNode, 0);
expect(tableNode.columnTextColors, {
'1': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'1': true,
});
});
test('insert a column with text color & bold style (2)', () async {
final (editorState, tableNode) = createEditorStateAndTable(
rowCount: 2,
columnCount: 3,
);
// insert the column at the first position
final tableCellNode =
tableNode.getTableCellNode(rowIndex: 0, columnIndex: 0);
await editorState.updateColumnTextColor(
tableCellNode: tableCellNode!,
color: '0xFF0000FF',
);
await editorState.toggleColumnBoldAttribute(
tableCellNode: tableCellNode,
isBold: true,
);
expect(tableNode.columnTextColors, {
'0': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'0': true,
});
await editorState.insertColumnInTable(tableNode, 1);
expect(tableNode.columnTextColors, {
'0': '0xFF0000FF',
});
expect(tableNode.columnBoldAttributes, {
'0': true,
});
});
});
}