Skip to content

Commit

Permalink
fix(store): ignore own mutations as they arrive from the listener
Browse files Browse the repository at this point in the history
  • Loading branch information
bjoerge committed Jan 10, 2025
1 parent c4f8639 commit 6dee544
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 14 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
"browserslist": "extends @sanity/browserslist-config",
"dependencies": {
"@sanity/client": "^6.24.1",
"@sanity/uuid": "^3.0.2",
"@sanity/diff-match-patch": "^3.1.1",
"hotscript": "^1.0.13",
"lodash": "^4.17.21",
Expand Down
22 changes: 22 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 26 additions & 14 deletions src/store/createOptimisticStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
type TransactionalMutationGroup,
} from './types'
import {createReplayMemoizer} from './utils/createReplayMemoizer'
import {createTransactionId} from './utils/createTransactionId'
import {filterMutationGroupsById} from './utils/filterMutationGroups'

export interface OptimisticStoreBackend {
Expand Down Expand Up @@ -77,17 +78,26 @@ export function createOptimisticStore(
const remote = createDocumentMap()
const memoize = createReplayMemoizer(1000)
let stagedChanges: MutationGroup[] = []
let pendingTransactions: string[] = []

const remoteEvents$ = new Subject<RemoteDocumentEvent>()
const localMutations$ = new Subject<OptimisticDocumentEvent>()

const stage$ = new Subject<void>()

function stage(nextPending: MutationGroup[]) {
function setStaged(nextPending: MutationGroup[]) {
stagedChanges = nextPending
stage$.next()
}

// todo: expose this as subscribable
// technically, changes are _saved_ as long as there's no staged mutations and the submit request didn't fail
// but when we have an empty `pendingTransactions` means that all the mutations we have submitted has been echoed back to us over the listener
// so it can provide an extra safety check
function setPendingTransactions(nextPending: string[]) {
pendingTransactions = pendingTransactions.concat(nextPending)
}

function getLocalEvents(id: string) {
return localMutations$.pipe(filter(event => event.id === id))
}
Expand Down Expand Up @@ -121,6 +131,12 @@ export function createOptimisticStore(
if (event.transactionId === oldRemote?._rev) {
return EMPTY
}
const idx = pendingTransactions.indexOf(event.resultRev)
if (idx > -1) {
// we received our own transaction
pendingTransactions.splice(idx, 1)
return EMPTY
}

const newRemote = applyMutationEventEffects(oldRemote, event)

Expand Down Expand Up @@ -166,7 +182,7 @@ export function createOptimisticStore(
tap(event => {
local.set(event.id, event.after.local)
remote.set(event.id, event.after.remote)
stage(event.rebasedStage)
setStaged(event.rebasedStage)
}),
tap({
next: event => remoteEvents$.next(event),
Expand Down Expand Up @@ -247,21 +263,17 @@ export function createOptimisticStore(
),
),
optimize: () => {
stage(squashMutationGroups(stagedChanges))
setStaged(squashMutationGroups(stagedChanges))
},
submit: () => {
const pending = stagedChanges
stage([])
return lastValueFrom(
backend
.submit(
toTransactions(
// Squashing DMP strings is the last thing we do before submitting
squashDMPStrings(remote, squashMutationGroups(pending)),
),
)
.pipe(toArray()),
setStaged([])
const transactions = toTransactions(
// Squashing DMP strings is the last thing we do before submitting
squashDMPStrings(remote, squashMutationGroups(pending)),
)
setPendingTransactions(transactions.map(t => t.id!))
return lastValueFrom(backend.submit(transactions).pipe(toArray()))
},
}
}
Expand All @@ -271,6 +283,6 @@ function toTransactions(groups: MutationGroup[]): Transaction[] {
if (group.transaction && group.id !== undefined) {
return {id: group.id!, mutations: group.mutations}
}
return {mutations: group.mutations}
return {id: createTransactionId(), mutations: group.mutations}
})
}
5 changes: 5 additions & 0 deletions src/store/utils/createTransactionId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import {uuid} from '@sanity/uuid'

export function createTransactionId() {
return uuid()
}

0 comments on commit 6dee544

Please sign in to comment.