-
Notifications
You must be signed in to change notification settings - Fork 426
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
da3a1e6
commit f638790
Showing
18 changed files
with
614 additions
and
299 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
158 changes: 31 additions & 127 deletions
158
packages/sanity/src/core/store/_legacy/history/history/chunker.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,160 +1,64 @@ | ||
/* eslint-disable no-nested-ternary */ | ||
import {type MendozaEffectPair, type MendozaPatch} from '@sanity/types' | ||
|
||
import {type Chunk, type ChunkType} from '../../../../field' | ||
import {type Chunk} from '../../../../field' | ||
import {getEventFromTransaction} from '../../../events/getDocumentEvents' | ||
import {type EditDocumentVersionEvent} from '../../../events/types' | ||
import {type Transaction} from './types' | ||
|
||
function canMergeEdit(type: ChunkType) { | ||
return type === 'create' || type === 'editDraft' | ||
} | ||
|
||
const CHUNK_WINDOW = 5 * 60 * 1000 // 5 minutes | ||
|
||
function isWithinMergeWindow(a: string, b: string) { | ||
return Date.parse(b) - Date.parse(a) < CHUNK_WINDOW | ||
} | ||
|
||
export function mergeChunk(left: Chunk, right: Chunk): Chunk | [Chunk, Chunk] { | ||
if (left.end !== right.start) throw new Error('chunks are not next to each other') | ||
|
||
// TODO: How to detect first squash/create | ||
|
||
const draftState = combineState(left.draftState, right.draftState) | ||
const publishedState = combineState(left.publishedState, right.publishedState) | ||
|
||
if (left.type === 'delete' && right.type === 'editDraft') { | ||
return [left, {...right, type: 'create', draftState, publishedState}] | ||
} | ||
|
||
// Convert deletes into either discardDraft or unpublish depending on what's been deleted. | ||
if (right.type === 'delete') { | ||
if (draftState === 'missing' && publishedState === 'present') { | ||
return [left, {...right, type: 'discardDraft', draftState, publishedState}] | ||
} | ||
|
||
if (draftState === 'present' && publishedState === 'missing') { | ||
return [left, {...right, type: 'unpublish', draftState, publishedState}] | ||
} | ||
const addMergedEvents = ( | ||
leftEvent: EditDocumentVersionEvent, | ||
rightEvent: EditDocumentVersionEvent, | ||
): EditDocumentVersionEvent => { | ||
const mergedEvents = leftEvent.mergedEvents || [] | ||
delete leftEvent.mergedEvents | ||
return { | ||
...rightEvent, | ||
mergedEvents: [...mergedEvents, leftEvent], | ||
} | ||
} | ||
|
||
export function mergeChunk(left: Chunk, right: Chunk): Chunk | [Chunk, Chunk] { | ||
if (left.end !== right.start) throw new Error('chunks are not next to each other') | ||
if ( | ||
canMergeEdit(left.type) && | ||
right.type === 'editDraft' && | ||
isWithinMergeWindow(left.endTimestamp, right.startTimestamp) | ||
left.event.type === 'document.editVersion' && | ||
right.event.type === 'document.editVersion' && | ||
isWithinMergeWindow(left.endTimestamp, right.startTimestamp) && | ||
// TODO: confirm we don't want to merge if the author is different | ||
left.event.author === right.event.author | ||
) { | ||
const authors = new Set<string>() | ||
for (const author of left.authors) authors.add(author) | ||
for (const author of right.authors) authors.add(author) | ||
|
||
return { | ||
index: 0, | ||
id: right.id, | ||
type: left.type, | ||
start: left.start, | ||
end: right.end, | ||
event: addMergedEvents(left.event, right.event), | ||
startTimestamp: left.startTimestamp, | ||
endTimestamp: right.endTimestamp, | ||
authors, | ||
draftState, | ||
publishedState, | ||
} | ||
} | ||
|
||
return [left, {...right, draftState, publishedState}] | ||
} | ||
|
||
type ChunkState = 'unedited' | 'deleted' | 'upsert' | ||
function getChunkState(effect?: MendozaEffectPair): ChunkState { | ||
const modified = Boolean(effect) | ||
const deleted = effect && isDeletePatch(effect?.apply) | ||
|
||
if (deleted) { | ||
return 'deleted' | ||
} | ||
|
||
if (modified) { | ||
return 'upsert' | ||
} | ||
|
||
return 'unedited' | ||
return [left, right] | ||
} | ||
|
||
/* | ||
* getChunkType tries to determine what effect the given transaction had on the document | ||
* More information about the logic can be found here https://github.com/sanity-io/sanity/pull/2633#issuecomment-886461812 | ||
* | ||
* | | draft unedited | draft deleted | draft upsert | | ||
* |--------------------|----------------|---------------|--------------| | ||
* | published unedited | X | delete | editDraft | | ||
* | published deleted | delete | delete | delete | | ||
* | published upsert | liveEdit | publish | liveEdit | | ||
*/ | ||
function getChunkType(transaction: Transaction): ChunkType { | ||
const draftState = getChunkState(transaction.draftEffect) | ||
const publishedState = getChunkState(transaction.publishedEffect) | ||
|
||
if (publishedState === 'unedited') { | ||
if (draftState === 'deleted') { | ||
return 'delete' | ||
} | ||
|
||
if (draftState === 'upsert') { | ||
return 'editDraft' | ||
} | ||
} | ||
|
||
if (publishedState === 'deleted') { | ||
return 'delete' | ||
} | ||
|
||
if (publishedState === 'upsert') { | ||
if (draftState === 'unedited') { | ||
return 'editLive' | ||
} | ||
|
||
if (draftState === 'deleted') { | ||
return 'publish' | ||
} | ||
|
||
if (draftState === 'upsert') { | ||
return 'editLive' | ||
} | ||
} | ||
|
||
return 'editLive' | ||
} | ||
|
||
export function chunkFromTransaction(transaction: Transaction): Chunk { | ||
const modifiedDraft = Boolean(transaction.draftEffect) | ||
const modifiedPublished = Boolean(transaction.publishedEffect) | ||
|
||
const draftDeleted = transaction.draftEffect && isDeletePatch(transaction.draftEffect.apply) | ||
const publishedDeleted = | ||
transaction.publishedEffect && isDeletePatch(transaction.publishedEffect.apply) | ||
|
||
const type = getChunkType(transaction) | ||
|
||
export function chunkFromTransaction( | ||
publishedId: string, | ||
transaction: Transaction, | ||
transactions: Transaction[], | ||
): Chunk { | ||
// TODO; get the previous transactions, they need to account for the index of this transaction, we need all the previous ones. | ||
const previousTransactions = transactions.filter((tx) => tx.index < transaction.index).reverse() | ||
console.log(transaction.id, {transaction, previousTransactions}) | ||
return { | ||
index: 0, | ||
id: transaction.id, | ||
type, | ||
start: transaction.index, | ||
end: transaction.index + 1, | ||
startTimestamp: transaction.timestamp, | ||
endTimestamp: transaction.timestamp, | ||
authors: new Set([transaction.author]), | ||
draftState: modifiedDraft ? (draftDeleted ? 'missing' : 'present') : 'unknown', | ||
publishedState: modifiedPublished ? (publishedDeleted ? 'missing' : 'present') : 'unknown', | ||
event: getEventFromTransaction(publishedId, transaction, previousTransactions), | ||
} | ||
} | ||
|
||
function combineState( | ||
left: 'present' | 'missing' | 'unknown', | ||
right: 'present' | 'missing' | 'unknown', | ||
) { | ||
return right === 'unknown' ? left : right | ||
} | ||
|
||
export function isDeletePatch(patch: MendozaPatch): boolean { | ||
return patch[0] === 0 && patch[1] === null | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.