[fix] Migrations (#159)

* Make room optional

* Improves code for migrations, adds tests

* Update tlstate.spec.ts
pull/160/head
Steve Ruiz 2021-10-16 20:34:34 +01:00 zatwierdzone przez GitHub
rodzic d87263a3d8
commit 273a57209f
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
3 zmienionych plików z 138 dodań i 75 usunięć

Wyświetl plik

@ -35,3 +35,7 @@ A cancel method is expected to revert any changes made to the state since the se
A session may be cancelled using `TLDrawState.complete`. When a session is cancelled, `TLDrawState` calls the session's `complete` method passing in the state as the only parameter. If the `complete` method returns a patch, then that patch is applied to the state; if it returns a `command`, then that command is patched and added to the state's history.
If the `complete` method returns a command, then it is expected that the command's `before` patch will revert any changes made to the state since the session began, including any changes introduced in the command's `after` patch.
## Notes
A session should always be cancelled by pressing escape.

Wyświetl plik

@ -2,6 +2,7 @@
import { TLDrawState } from './tlstate'
import { mockDocument, TLStateUtils } from '~test'
import { ArrowShape, ColorStyle, SessionType, TLDrawShapeType } from '~types'
import * as idb from 'idb-keyval'
import type { SelectTool } from './tool/SelectTool'
describe('TLDrawState', () => {
@ -589,4 +590,44 @@ describe('TLDrawState', () => {
call `replaceDocument`, which does a harder reset of the state's
internal state.
*/
jest.setTimeout(10000)
describe('When changing versions', () => {
it('migrates correctly', (done) => {
const defaultState = TLDrawState.defaultState
const withoutRoom = {
...defaultState,
}
delete withoutRoom.room
TLDrawState.defaultState = withoutRoom
const tlstate = new TLDrawState('migrate_1')
tlstate.createShapes({
id: 'rect1',
type: TLDrawShapeType.Rectangle,
})
setTimeout(() => {
// TODO: Force the version to change and restore room.
TLDrawState.version = 100
TLDrawState.defaultState.room = defaultState.room
const tlstate2 = new TLDrawState('migrate_1')
setTimeout(() => {
try {
expect(tlstate2.getShape('rect1')).toBeTruthy()
done()
} catch (e) {
done(e)
}
}, 100)
}, 100)
})
})
})

Wyświetl plik

@ -1,5 +1,6 @@
/* eslint-disable @typescript-eslint/no-non-null-assertion */
import { StateManager } from 'rko'
import { Vec } from '@tldraw/vec'
import {
TLBoundsEventHandler,
TLBoundsHandleEventHandler,
@ -14,7 +15,6 @@ import {
TLBounds,
Inputs,
} from '@tldraw/core'
import { Vec } from '@tldraw/vec'
import {
FlipType,
TLDrawDocument,
@ -45,70 +45,8 @@ import { sample, USER_COLORS } from './utils'
import { createTools, ToolType } from './tool'
import type { BaseTool } from './tool/BaseTool'
const defaultDocument: TLDrawDocument = {
id: 'doc',
pages: {
page: {
id: 'page',
name: 'Page 1',
childIndex: 1,
shapes: {},
bindings: {},
},
},
pageStates: {
page: {
id: 'page',
selectedIds: [],
camera: {
point: [0, 0],
zoom: 1,
},
},
},
}
const uuid = Utils.uniqueId()
const defaultState: Data = {
settings: {
isPenMode: false,
isDarkMode: false,
isZoomSnap: false,
isFocusMode: false,
isDebugMode: process.env.NODE_ENV === 'development',
isReadonlyMode: false,
nudgeDistanceLarge: 16,
nudgeDistanceSmall: 1,
},
appState: {
activeTool: 'select',
hoveredId: undefined,
currentPageId: 'page',
pages: [{ id: 'page', name: 'page', childIndex: 1 }],
currentStyle: defaultStyle,
selectedStyle: defaultStyle,
isToolLocked: false,
isStyleOpen: false,
isEmptyCanvas: false,
status: TLDrawStatus.Idle,
},
document: defaultDocument,
room: {
id: 'local',
userId: uuid,
users: {
[uuid]: {
id: uuid,
color: sample(USER_COLORS),
point: [100, 100],
selectedIds: [],
activeShapes: [],
},
},
},
}
export class TLDrawState extends StateManager<Data> {
private _onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void
private _onMount?: (tlstate: TLDrawState) => void
@ -153,10 +91,13 @@ export class TLDrawState extends StateManager<Data> {
onChange?: (tlstate: TLDrawState, data: Data, reason: string) => void,
onMount?: (tlstate: TLDrawState) => void
) {
super(defaultState, id, 10, (next, prev) => ({
...next,
document: { ...next.document, ...prev.document },
}))
super(TLDrawState.defaultState, id, TLDrawState.version, (prev, next) => {
console.log('Migrating to a new version.')
return {
...next,
document: { ...next.document, ...prev.document },
}
})
this._onChange = onChange
this._onMount = onMount
@ -167,11 +108,24 @@ export class TLDrawState extends StateManager<Data> {
/* -------------------- Internal -------------------- */
onReady = () => {
this.patchState({
appState: {
status: TLDrawStatus.Idle,
},
})
try {
this.patchState({
appState: {
status: TLDrawStatus.Idle,
},
})
} catch (e) {
console.error('The data appears to be corrupted. Resetting!', e)
this.patchState({
...TLDrawState.defaultState,
appState: {
...TLDrawState.defaultState.appState,
status: TLDrawStatus.Idle,
},
})
}
this._onMount?.(this)
}
@ -582,7 +536,7 @@ export class TLDrawState extends StateManager<Data> {
this.resetHistory()
.clearSelectHistory()
.loadDocument(defaultDocument)
.loadDocument(TLDrawState.defaultDocument)
.patchState(
{
document: {
@ -779,10 +733,10 @@ export class TLDrawState extends StateManager<Data> {
return this.replaceState(
{
...defaultState,
...TLDrawState.defaultState,
document,
appState: {
...defaultState.appState,
...TLDrawState.defaultState.appState,
currentPageId: Object.keys(document.pages)[0],
},
},
@ -2403,4 +2357,68 @@ export class TLDrawState extends StateManager<Data> {
get centerPoint() {
return Vec.round([this.bounds.width / 2, this.bounds.height / 2])
}
static version = 10.1
static defaultDocument: TLDrawDocument = {
id: 'doc',
pages: {
page: {
id: 'page',
name: 'Page 1',
childIndex: 1,
shapes: {},
bindings: {},
},
},
pageStates: {
page: {
id: 'page',
selectedIds: [],
camera: {
point: [0, 0],
zoom: 1,
},
},
},
}
static defaultState: Data = {
settings: {
isPenMode: false,
isDarkMode: false,
isZoomSnap: false,
isFocusMode: false,
isDebugMode: process.env.NODE_ENV === 'development',
isReadonlyMode: false,
nudgeDistanceLarge: 16,
nudgeDistanceSmall: 1,
},
appState: {
activeTool: 'select',
hoveredId: undefined,
currentPageId: 'page',
pages: [{ id: 'page', name: 'page', childIndex: 1 }],
currentStyle: defaultStyle,
selectedStyle: defaultStyle,
isToolLocked: false,
isStyleOpen: false,
isEmptyCanvas: false,
status: TLDrawStatus.Idle,
},
document: TLDrawState.defaultDocument,
room: {
id: 'local',
userId: uuid,
users: {
[uuid]: {
id: uuid,
color: sample(USER_COLORS),
point: [100, 100],
selectedIds: [],
activeShapes: [],
},
},
},
}
}