import { ActionMoveBase, FiltersMap, withoutIrrelevant } from '@wix/bex-core'; import { cairoDragActionUnsuccessfulUpdateInServer, cairoDragItem, cairoDragItemEndDrop, cairoTryAgainClicked, } from '@wix/bex-core/bi'; import { action } from 'mobx'; import { OptimisticPatch } from '@wix/bex-core/dist/types/state/CollectionOptimisticPatch'; import { DragAndDropCollection, DragAndDropParentState, } from './DragAndDropState'; const withoutDefaults = withoutIrrelevant< | 'currentTab' | 'currentView' | 'currentFilters' | 'currentSortOrder' | 'listSize' | 'filteredListSize' | 'initialItems' >(); export interface CollectionDragAndDropBIParams { state: DragAndDropParentState; collection: DragAndDropCollection; getCollectionSnapshot?: () => { [key: string]: string | number | undefined; }; getBulkSelectCount: () => number; getPendingMovePatchesCount: () => number | null | undefined; } export class CollectionDragAndDropBI { readonly state; readonly collection; readonly getCollectionSnapshot; readonly getBulkSelectCount; readonly getPendingMovePatchesCount; startTime = 0; constructor(params: CollectionDragAndDropBIParams) { this.state = params.state; this.collection = params.collection; this.getCollectionSnapshot = params.getCollectionSnapshot; this.getBulkSelectCount = params.getBulkSelectCount; this.getPendingMovePatchesCount = params.getPendingMovePatchesCount; } get dnd() { return this.state._dnd; } get reportBi() { return this.state.reportBi; } onAttemptDragStart = action((itemId: string) => { if (!this.dnd) { return () => {}; } const { reportBi, dnd, collection, getBulkSelectCount } = this; const { _isHandleActive } = dnd; const { query: { search }, } = collection; const item = collection.getKeyedItem(itemId); const startTime = performance.now(); reportBi( withoutDefaults(cairoDragItem)({ itemId, itemIndex: item?.index, isFromHandle: _isHandleActive, numItemsBefore: getBulkSelectCount(), searchQuery: search.value, isFromSearch: !search.isEmpty, }), ); return action(() => { reportBi( withoutDefaults(cairoDragItemEndDrop)({ isFromHandle: _isHandleActive, itemId, itemIndexBefore: item?.index, duration: Math.round(performance.now() - startTime), endType: 'drag disabled', numItemsBefore: getBulkSelectCount(), searchQuery: search.value, isFromSearch: !search.isEmpty, }), ); }); }); onDragStart() { if (!this.dnd) { return; } const { reportBi, dnd, collection, getBulkSelectCount } = this; const { active, _isHandleActive } = dnd; const { query: { search }, } = collection; if (active == null) { return; } this.startTime = performance.now(); reportBi( withoutDefaults(cairoDragItem)({ itemId: active.key, itemIndex: active.index, isFromHandle: _isHandleActive, numItemsBefore: getBulkSelectCount(), searchQuery: search.value, isFromSearch: !search.isEmpty, dragAndDropID: dnd._moveId, }), ); return (params: { endType?: 'clicked ESC' | 'drop outside' | null }) => { const { startTime } = this; const { overId } = dnd; const activeAfter = collection.getKeyedItem(active.key); reportBi( withoutDefaults(cairoDragItemEndDrop)({ isFromHandle: _isHandleActive, itemId: active.key, duration: Math.round(performance.now() - startTime), endType: params.endType ?? (overId === active.key ? 'drop back' : 'drop succeeded'), numItemsBefore: getBulkSelectCount(), itemIndexBefore: active.index, itemIndexAfter: activeAfter?.index, dragAndDropID: dnd._moveId, }), ); }; } onMoveFailure(params: { reason: 'Update failed' | 'Window closed' | 'Left page'; patches: OptimisticPatch[]; }) { const { reportBi, getBulkSelectCount } = this; const { reason, patches } = params; const errorPatch = patches[0]; if (!errorPatch?.move) { if (process.env.NODE_ENV !== 'production') { throw new Error('Missing move data in patch'); } return; } const { from, over, moveId, isFromHandle } = errorPatch.move; reportBi( withoutDefaults(cairoDragActionUnsuccessfulUpdateInServer)({ ...errorPatch.collectionSnapshot, numItemsBefore: getBulkSelectCount(), numOfWaitingUpdates: patches.length, timeFromEndDrag: Date.now() - errorPatch.createdAt, itemIndexBefore: from.index, itemIndexAfter: over.index, errorType: reason, itemId: from.key, dragAndDropID: moveId, wasFromHandle: isFromHandle, }), ); } onMoveCancelled(move: ActionMoveBase) { const { reportBi, getCollectionSnapshot, getBulkSelectCount, getPendingMovePatchesCount, } = this; const { from, over, moveId, isFromHandle } = move; reportBi( withoutDefaults(cairoDragActionUnsuccessfulUpdateInServer)({ ...(getCollectionSnapshot?.() ?? {}), numItemsBefore: getBulkSelectCount(), numOfWaitingUpdates: getPendingMovePatchesCount() ?? 0, timeFromEndDrag: 0, itemIndexBefore: from.index, itemIndexAfter: over.index, errorType: 'External logic', itemId: from.key, dragAndDropID: moveId, wasFromHandle: isFromHandle, }), ); } onMoveRetry(params: { patches: OptimisticPatch[] }) { const { reportBi, collection } = this; const { patches } = params; const { query: { search }, } = collection; const errorPatch = patches[0]; reportBi( withoutDefaults(cairoTryAgainClicked)({ ...errorPatch.collectionSnapshot, isFromSearch: !search.isEmpty, numOfWaitingUpdates: patches.length, actionName: 'drag', }), ); } }