Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement cell-based sync + undo/redo #458

Merged
merged 1 commit into from
Oct 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 172 additions & 0 deletions packages/saaz/src/__snapshots__/rogue.test.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Rogue merging defaults 1`] = `
{
"a": {
"aStep": 1,
"bStep": 1,
"foo": "setBy1",
"obj": {
"objA": "true",
"objB": "true",
},
},
}
`;

exports[`Rogue overriding an existing prop 1`] = `
[
{
"branchName": "base",
"path": [
[
"base",
"a",
],
],
"type": "SetBoxedValue",
"value": 2,
},
]
`;

exports[`Rogue setting a non-existing prop 1`] = `
[
{
"$branches": {
"base": {
"$mapProps": {
"a": {
"$branches": {
"base": {
"$boxedValue": 1,
},
},
"$type": [
"boxed",
"base",
],
},
},
},
},
"$type": [
"map",
"base",
],
},
[
{
"path": [
[
"base",
"a",
],
],
"type": "ChangeType",
"value": [
"boxed",
"base",
],
},
{
"branchName": "base",
"path": [
[
"base",
"a",
],
],
"type": "SetBoxedValue",
"value": 1,
},
],
]
`;

exports[`Rogue setting a non-existing prop to an object 1`] = `
[
{
"$branches": {
"base": {
"$mapProps": {
"a": {
"$branches": {
"base": {
"$mapProps": {
"b": {
"$branches": {
"base": {
"$boxedValue": 1,
},
},
"$type": [
"boxed",
"base",
],
},
},
},
},
"$type": [
"map",
"base",
],
},
},
},
},
"$type": [
"map",
"base",
],
},
[
{
"path": [
[
"base",
"a",
],
],
"type": "ChangeType",
"value": [
"map",
"base",
],
},
{
"path": [
[
"base",
"a",
],
[
"base",
"b",
],
],
"type": "ChangeType",
"value": [
"boxed",
"base",
],
},
{
"branchName": "base",
"path": [
[
"base",
"a",
],
[
"base",
"b",
],
],
"type": "SetBoxedValue",
"value": 1,
},
],
]
`;
24 changes: 14 additions & 10 deletions packages/saaz/src/back/SaazBack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,28 @@ import type {
PeerSubscribeCallback,
AllPeersPresenceState,
PeerPresenceState,
FullSnapshot,
} from '../types'
import {BackStorage} from './BackStorage'
import type {DebouncedFunc} from 'lodash-es'
import {cloneDeep, throttle} from 'lodash-es'
import {ensureStateIsUptodate} from '../shared/utils'
import {ensureStateIsUptodate as ensureOpStateIsUptodate} from '../shared/utils'
import {Atom} from '@theatre/dataverse'
import deepEqual from '@theatre/utils/deepEqual'

export default class SaazBack implements SaazBackInterface {
private _dbName: string
private _storage: BackStorage
private _readyDeferred = defer<void>()
private _dbState: {} = {}
private _dbState: FullSnapshot<$IntentionalAny> = {cell: {}, op: {}}
private _clock: number | null = null
private _peerStates: {
[peerId in string]?: {
lastIncorporatedPeerClock: number
}
} = {}
private _subsribers: Array<PeerSubscribeCallback> = []
private _schema: Schema<$IntentionalAny>
private _schema: Schema<{$schemaVersion: number}>
private _presenceState: Atom<AllPeersPresenceState> = new Atom({})
private _schedulePresenseUpdate: DebouncedFunc<() => void>

Expand Down Expand Up @@ -113,7 +114,10 @@ export default class SaazBack implements SaazBackInterface {
clock: this._clock ?? -1,
lastIncorporatedPeerClock:
this._peerStates[opts.peerId]?.lastIncorporatedPeerClock ?? null,
snapshot: {type: 'Snapshot', value: this._dbState},
snapshot: {
type: 'Snapshot',
value: this._dbState,
},
}
}

Expand Down Expand Up @@ -168,7 +172,7 @@ export default class SaazBack implements SaazBackInterface {
const peerState = this._peerStates[opts.peerId]!

const rebasing = opts.backendClock !== this._clock
let stateSoFar = ensureStateIsUptodate(this._dbState, this._schema)
let snapshotSoFar = ensureOpStateIsUptodate(this._dbState, this._schema)
let lastAcknowledgedClock = peerState.lastIncorporatedPeerClock
let backendClock = this._clock ?? -1
const updatesToIncorporate = []
Expand All @@ -178,20 +182,20 @@ export default class SaazBack implements SaazBackInterface {
continue
}

const before = stateSoFar
const [after] = applyOptimisticUpdateToState(
const snapshotBefore = snapshotSoFar
const [opSnapshotAfter] = applyOptimisticUpdateToState(
update,
before,
snapshotBefore,
this._schema,
true,
)
stateSoFar = after
snapshotSoFar = opSnapshotAfter
lastAcknowledgedClock = update.peerClock
backendClock++
}

if (lastAcknowledgedClock !== peerState.lastIncorporatedPeerClock) {
this._dbState = stateSoFar
this._dbState = snapshotSoFar
peerState.lastIncorporatedPeerClock = lastAcknowledgedClock
this._clock = backendClock
this._callSubscribersForBackendStateUpdate()
Expand Down
Loading
Loading