mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-17 22:37:04 +08:00
fix(editor): show added or deleted rows immediately in grouped table and Kanban views (#12731)
https://github.com/user-attachments/assets/214fbe4f-b667-44b7-85a3-77ef4cfa8cca This PR fixes a bug where adding or deleting rows in a grouped table view did not visually update the UI until the user manually refreshed the page or navigated away and back. The issue gave the impression that the action had not completed. Same issue for Kanban cards. The result now is: Users now see new rows or deleted rows reflected in real-time without needing to reload or navigate away. This applies to both grouped table views and Kanban cards. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Bug Fixes** - Ensured the UI updates immediately after adding, deleting, or moving cards and rows in Kanban and Table views on both mobile and desktop. - Fixed issues where UI changes were not reflected after certain actions, such as ungrouping, deleting, or inserting items. - Improved row locking behavior during add and delete operations to prevent UI inconsistencies. - **Tests** - Added comprehensive tests for row operations and menu interactions to verify UI updates and correct method calls in data views. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: zzj3720 <zuozijian1994@gmail.com>
This commit is contained in:
@@ -112,7 +112,10 @@ export class GroupTrait {
|
||||
return;
|
||||
}
|
||||
const { staticMap, groupInfo } = staticInfo;
|
||||
const groupMap: Record<string, Group> = { ...staticMap };
|
||||
const groupMap: Record<string, Group> = {};
|
||||
Object.entries(staticMap).forEach(([key, group]) => {
|
||||
groupMap[key] = new Group(key, group.value, groupInfo, this);
|
||||
});
|
||||
this.view.rows$.value.forEach(row => {
|
||||
const value = this.view.cellGetOrCreate(row.rowId, groupInfo.property.id)
|
||||
.jsonValue$.value;
|
||||
@@ -182,6 +185,7 @@ export class GroupTrait {
|
||||
) {}
|
||||
|
||||
addToGroup(rowId: string, key: string) {
|
||||
this.view.lockRows(false);
|
||||
const groupMap = this.groupDataMap$.value;
|
||||
const groupInfo = this.groupInfo$.value;
|
||||
if (!groupMap || !groupInfo) {
|
||||
@@ -254,6 +258,7 @@ export class GroupTrait {
|
||||
toGroupKey: string,
|
||||
position: InsertToPosition
|
||||
) {
|
||||
this.view.lockRows(false);
|
||||
const groupMap = this.groupDataMap$.value;
|
||||
if (!groupMap) {
|
||||
return;
|
||||
@@ -290,6 +295,7 @@ export class GroupTrait {
|
||||
}
|
||||
|
||||
moveGroupTo(groupKey: string, position: InsertToPosition) {
|
||||
this.view.lockRows(false);
|
||||
const groups = this.groupsDataList$.value;
|
||||
if (!groups) {
|
||||
return;
|
||||
@@ -305,6 +311,7 @@ export class GroupTrait {
|
||||
}
|
||||
|
||||
removeFromGroup(rowId: string, key: string) {
|
||||
this.view.lockRows(false);
|
||||
const groupMap = this.groupDataMap$.value;
|
||||
if (!groupMap) {
|
||||
return;
|
||||
@@ -323,6 +330,7 @@ export class GroupTrait {
|
||||
}
|
||||
|
||||
updateValue(rows: string[], value: unknown) {
|
||||
this.view.lockRows(false);
|
||||
const propertyId = this.property$.value?.id;
|
||||
if (!propertyId) {
|
||||
return;
|
||||
|
||||
@@ -128,6 +128,7 @@ export abstract class SingleViewBase<
|
||||
);
|
||||
|
||||
rowsDelete(rows: string[]): void {
|
||||
this.lockRows(false);
|
||||
this.dataSource.rowDelete(rows);
|
||||
}
|
||||
|
||||
@@ -258,6 +259,7 @@ export abstract class SingleViewBase<
|
||||
abstract propertyGetOrCreate(propertyId: string): Property;
|
||||
|
||||
rowAdd(insertPosition: InsertToPosition | number): string {
|
||||
this.lockRows(false);
|
||||
return this.dataSource.rowAdd(insertPosition);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,10 +61,12 @@ export class MobileKanbanGroup extends SignalWatcher(
|
||||
|
||||
private readonly clickAddCard = () => {
|
||||
this.view.addCard('end', this.group.key);
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
private readonly clickAddCardInStart = () => {
|
||||
this.view.addCard('start', this.group.key);
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
private readonly clickGroupOptions = (e: MouseEvent) => {
|
||||
@@ -79,12 +81,14 @@ export class MobileKanbanGroup extends SignalWatcher(
|
||||
this.group.rows.forEach(row => {
|
||||
this.group.manager.removeFromGroup(row.rowId, this.group.key);
|
||||
});
|
||||
this.requestUpdate();
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowsDelete(this.group.rows.map(row => row.rowId));
|
||||
this.requestUpdate();
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -66,7 +66,9 @@ export class MobileKanbanViewUILogic extends DataViewUILogicBase<
|
||||
|
||||
addRow = (position: InsertToPosition) => {
|
||||
if (this.readonly) return;
|
||||
return this.view.rowAdd(position);
|
||||
const id = this.view.rowAdd(position);
|
||||
this.ui$.value?.requestUpdate();
|
||||
return id;
|
||||
};
|
||||
|
||||
focusFirstCell = () => {};
|
||||
|
||||
@@ -83,6 +83,7 @@ export const popCardMenu = (
|
||||
{ before: true, id: cardId },
|
||||
groupKey
|
||||
);
|
||||
kanbanViewLogic.ui$.value?.requestUpdate();
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
@@ -97,6 +98,7 @@ export const popCardMenu = (
|
||||
{ before: false, id: cardId },
|
||||
groupKey
|
||||
);
|
||||
kanbanViewLogic.ui$.value?.requestUpdate();
|
||||
},
|
||||
}),
|
||||
],
|
||||
@@ -111,6 +113,7 @@ export const popCardMenu = (
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
kanbanViewLogic.view.rowsDelete([cardId]);
|
||||
kanbanViewLogic.ui$.value?.requestUpdate();
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -128,6 +128,7 @@ export class KanbanSelectionController implements ReactiveController {
|
||||
if (selection.selectionType === 'card') {
|
||||
this.view.rowsDelete(selection.cards.map(v => v.cardId));
|
||||
this.selection = undefined;
|
||||
this.logic.ui$.value?.requestUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,7 @@ export class KanbanGroup extends SignalWatcher(
|
||||
isEditing: true,
|
||||
};
|
||||
});
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
private readonly clickAddCardInStart = () => {
|
||||
@@ -127,6 +128,7 @@ export class KanbanGroup extends SignalWatcher(
|
||||
isEditing: true,
|
||||
};
|
||||
});
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
private readonly clickGroupOptions = (e: MouseEvent) => {
|
||||
@@ -139,12 +141,14 @@ export class KanbanGroup extends SignalWatcher(
|
||||
this.group.rows.forEach(row => {
|
||||
this.group.manager.removeFromGroup(row.rowId, this.group.key);
|
||||
});
|
||||
this.requestUpdate();
|
||||
},
|
||||
}),
|
||||
menu.action({
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowsDelete(this.group.rows.map(row => row.rowId));
|
||||
this.requestUpdate();
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -73,6 +73,7 @@ export class KanbanViewUILogic extends DataViewUILogicBase<
|
||||
rowId,
|
||||
});
|
||||
}
|
||||
this.ui$.value?.requestUpdate();
|
||||
return rowId;
|
||||
};
|
||||
|
||||
|
||||
@@ -51,10 +51,12 @@ export class MobileTableGroup extends SignalWatcher(
|
||||
|
||||
private readonly clickAddRow = () => {
|
||||
this.view.rowAdd('end', this.group?.key);
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
private readonly clickAddRowInStart = () => {
|
||||
this.view.rowAdd('start', this.group?.key);
|
||||
this.requestUpdate();
|
||||
};
|
||||
|
||||
private readonly clickGroupOptions = (e: MouseEvent) => {
|
||||
@@ -77,6 +79,7 @@ export class MobileTableGroup extends SignalWatcher(
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowsDelete(group.rows.map(row => row.rowId));
|
||||
this.requestUpdate();
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -38,6 +38,7 @@ export const popMobileRowMenu = (
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
view.rowsDelete([rowId]);
|
||||
tableViewLogic.ui$.value?.requestUpdate();
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -44,6 +44,7 @@ export class TableClipboardController implements ReactiveController {
|
||||
}
|
||||
if (deleteRows.length) {
|
||||
this.logic.view.rowsDelete(deleteRows);
|
||||
this.logic.ui$.value?.requestUpdate();
|
||||
}
|
||||
}
|
||||
this.clipboard
|
||||
|
||||
@@ -30,6 +30,7 @@ export class TableHotkeysController implements ReactiveController {
|
||||
const rows = TableViewRowSelection.rowsIds(selection);
|
||||
this.selectionController.selection = undefined;
|
||||
this.logic.view.rowsDelete(rows);
|
||||
this.logic.ui$.value?.requestUpdate();
|
||||
return;
|
||||
}
|
||||
const {
|
||||
|
||||
@@ -376,6 +376,7 @@ export class TableSelectionController implements ReactiveController {
|
||||
deleteRow(rowId: string) {
|
||||
this.view.rowsDelete([rowId]);
|
||||
this.focusToCell('up');
|
||||
this.logic.ui$.value?.requestUpdate();
|
||||
}
|
||||
|
||||
focusFirstCell() {
|
||||
|
||||
@@ -45,6 +45,7 @@ export class TableGroupFooter extends WithDisposable(ShadowlessElement) {
|
||||
private readonly clickAddRow = () => {
|
||||
const group = this.group$.value;
|
||||
const rowId = this.tableViewManager.rowAdd('end', group?.key);
|
||||
this.requestUpdate();
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
const rowIndex = this.selectionController.getRow(group?.key, rowId)
|
||||
|
||||
@@ -58,6 +58,7 @@ export class TableGroupHeader extends SignalWatcher(
|
||||
return;
|
||||
}
|
||||
this.tableViewManager.rowAdd('start', group.key);
|
||||
this.requestUpdate();
|
||||
const selectionController = this.selectionController;
|
||||
selectionController.selection = undefined;
|
||||
requestAnimationFrame(() => {
|
||||
@@ -95,6 +96,7 @@ export class TableGroupHeader extends SignalWatcher(
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.tableViewManager.rowsDelete(group.rows.map(row => row.rowId));
|
||||
this.requestUpdate();
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -71,6 +71,7 @@ export const popRowMenu = (
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
selectionController.view.rowsDelete(rows);
|
||||
selectionController.logic.ui$.value?.requestUpdate();
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
@@ -43,6 +43,7 @@ export class TableClipboardController implements ReactiveController {
|
||||
}
|
||||
if (deleteRows.length) {
|
||||
this.logic.view.rowsDelete(deleteRows);
|
||||
this.logic.ui$.value?.requestUpdate();
|
||||
}
|
||||
}
|
||||
this.clipboard
|
||||
|
||||
@@ -28,6 +28,7 @@ export class TableHotkeysController implements ReactiveController {
|
||||
const rows = TableViewRowSelection.rowsIds(selection);
|
||||
this.selectionController.selection = undefined;
|
||||
this.logic.view.rowsDelete(rows);
|
||||
this.logic.ui$.value?.requestUpdate();
|
||||
return;
|
||||
}
|
||||
const {
|
||||
|
||||
@@ -351,6 +351,7 @@ export class TableSelectionController implements ReactiveController {
|
||||
deleteRow(rowId: string) {
|
||||
this.view.rowsDelete([rowId]);
|
||||
this.focusToCell('up');
|
||||
this.logic.ui$.value?.requestUpdate();
|
||||
}
|
||||
|
||||
focusFirstCell() {
|
||||
|
||||
@@ -83,6 +83,7 @@ export class TableGroup extends SignalWatcher(
|
||||
},
|
||||
isEditing: true,
|
||||
});
|
||||
this.requestUpdate();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -102,6 +103,7 @@ export class TableGroup extends SignalWatcher(
|
||||
},
|
||||
isEditing: true,
|
||||
});
|
||||
this.requestUpdate();
|
||||
});
|
||||
};
|
||||
|
||||
@@ -125,6 +127,7 @@ export class TableGroup extends SignalWatcher(
|
||||
name: 'Delete Cards',
|
||||
select: () => {
|
||||
this.view.rowsDelete(group.rows.map(row => row.rowId));
|
||||
this.requestUpdate();
|
||||
},
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -71,6 +71,7 @@ export const popRowMenu = (
|
||||
prefix: DeleteIcon(),
|
||||
select: () => {
|
||||
selectionController.view.rowsDelete(rows);
|
||||
selectionController.logic.ui$.value?.requestUpdate();
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user