| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190 |
- // @ts-nocheck
- import { isEqual } from 'lodash'
- import { isPendingAddRow } from '../types'
- import { generateTableChangeKey, rowMatchesIdentifiers } from './queueOperationUtils'
- import { tryParseJson } from '@/lib/helpers'
- import {
- isDeleteRowOperation,
- isEditCellContentOperation,
- NewQueuedOperation,
- QueuedOperation,
- QueuedOperationType,
- type NewDeleteRowOperation,
- type NewEditCellContentOperation,
- } from '@/state/table-editor-operation-queue.types'
- export type DeleteConflictResult =
- | { action: 'skip'; filteredOperations: QueuedOperation[] }
- | { action: 'add'; filteredOperations: QueuedOperation[] }
- export type EditConflictResult =
- | { action: 'reject'; reason: string }
- | { action: 'merge'; updatedOperations: QueuedOperation[] }
- | { action: 'add' }
- export type UpsertResult = {
- operations: QueuedOperation[]
- }
- function editOperationMatchesTempId(operation: QueuedOperation, tempId: string): boolean {
- if (!isEditCellContentOperation(operation)) return false
- return operation.payload.rowIdentifiers.__tempId === tempId
- }
- export function operationMatchesRow(
- operation: QueuedOperation,
- tableId: number,
- rowIdentifiers: Record<string, unknown>
- ): boolean {
- if (operation.tableId !== tableId) return false
- if (
- operation.type === QueuedOperationType.EDIT_CELL_CONTENT ||
- operation.type === QueuedOperationType.DELETE_ROW
- ) {
- return rowMatchesIdentifiers(operation.payload.rowIdentifiers, rowIdentifiers)
- }
- return false
- }
- export function resolveDeleteRowConflicts(
- operations: readonly QueuedOperation[],
- deleteOperation: NewDeleteRowOperation
- ): DeleteConflictResult {
- const rowIdentifiers = deleteOperation.payload.rowIdentifiers
- // Check if this row was newly added (by tempId)
- // If deleting a newly added row, filter out the ADD_ROW operation
- const originalRow = deleteOperation.payload.originalRow
- if (isPendingAddRow(originalRow)) {
- const tempId = originalRow.__tempId
- const addRowKey = generateTableChangeKey({
- type: QueuedOperationType.ADD_ROW,
- tableId: deleteOperation.tableId,
- payload: {
- tempId,
- rowData: originalRow,
- table: deleteOperation.payload.table,
- },
- })
- let filteredOperations = operations
- .filter((op) => op.id !== addRowKey)
- .filter((op) => !editOperationMatchesTempId(op, tempId))
- return { action: 'skip', filteredOperations }
- }
- // For existing rows, remove any pending EDIT_CELL operations for the row being deleted
- const filteredOperations = operations.filter(
- (op) => !operationMatchesRow(op, deleteOperation.tableId, rowIdentifiers)
- )
- return { action: 'add', filteredOperations }
- }
- export function resolveEditCellConflicts(
- operations: readonly QueuedOperation[],
- editOperation: NewEditCellContentOperation
- ): EditConflictResult {
- const rowIdentifiers = editOperation.payload.rowIdentifiers
- // Check if this row is pending deletion
- const isPendingDeletion = operations.filter(isDeleteRowOperation).some((op) => {
- if (op.tableId === editOperation.tableId) {
- return Object.entries(op.payload.rowIdentifiers).every(
- ([key, value]) => rowIdentifiers[key] === value
- )
- }
- return false
- })
- if (isPendingDeletion) {
- return {
- action: 'reject',
- reason:
- 'Cannot edit a cell on a row that is pending deletion. Remove the delete operation first.',
- }
- }
- // Check if this edit is on a newly added row (by tempId)
- const tempId = rowIdentifiers.__tempId
- if (tempId) {
- const addRowIndex = operations.findIndex((op) => {
- if (op.type === QueuedOperationType.ADD_ROW && op.tableId === editOperation.tableId) {
- return op.payload.tempId === tempId
- }
- return false
- })
- if (addRowIndex >= 0) {
- // Merge the edit into the ADD_ROW's rowData
- const updatedOperations = [...operations]
- const addOp = updatedOperations[addRowIndex]
- if (addOp.type === QueuedOperationType.ADD_ROW) {
- const addPayload = { ...addOp.payload }
- addPayload.rowData = {
- ...addPayload.rowData,
- [editOperation.payload.columnName]: editOperation.payload.newValue,
- }
- updatedOperations[addRowIndex] = {
- ...addOp,
- payload: addPayload,
- timestamp: Date.now(),
- }
- }
- return { action: 'merge', updatedOperations }
- }
- }
- return { action: 'add' }
- }
- export function upsertOperation(
- operations: readonly QueuedOperation[],
- newOperation: NewQueuedOperation
- ): UpsertResult {
- const operationKey = generateTableChangeKey(newOperation)
- const existingOpIndex = operations.findIndex((op) => op.id === operationKey)
- const queuedOperation: QueuedOperation = {
- ...newOperation,
- id: operationKey,
- timestamp: Date.now(),
- }
- if (existingOpIndex >= 0) {
- const updatedOperations = [...operations]
- // Keep the old value of the operation that is being overwritten
- // When a user edits the same cell multiple times before saving,
- // we need to preserve the original "before edit" value
- const existingOp = operations[existingOpIndex]
- if (
- queuedOperation.type === QueuedOperationType.EDIT_CELL_CONTENT &&
- existingOp.type === QueuedOperationType.EDIT_CELL_CONTENT
- ) {
- queuedOperation.payload.oldValue = existingOp.payload.oldValue
- const { oldValue, newValue } = queuedOperation.payload
- // [Joshen] These comparisons by data type are because of how the table editor renders the values
- if (
- (typeof oldValue === 'number' && Number(oldValue) === Number(newValue)) ||
- (typeof newValue === 'object' && isEqual(tryParseJson(oldValue), newValue)) ||
- oldValue === newValue
- ) {
- updatedOperations.splice(existingOpIndex, 1)
- return { operations: updatedOperations }
- }
- }
- updatedOperations[existingOpIndex] = queuedOperation
- return { operations: updatedOperations }
- }
- return { operations: [...operations, queuedOperation] }
- }
|