Simplified export bookkeeping

This commit is contained in:
Aria Minaei 2021-08-09 21:50:12 +02:00
parent 832542c1c5
commit f08d9bf9c1
9 changed files with 58 additions and 68 deletions

View file

@ -57,6 +57,7 @@ export default class Project {
historic: config.state ?? {
sheetsById: {},
definitionVersion: globals.currentProjectStateDefinitionVersion,
revisionHistory: [],
},
ephemeral: {
loadingState: {

View file

@ -7,7 +7,7 @@ import globals from '@theatre/shared/globals'
/**
* @todo this could be turned into a simple derivation, like:
* editor.isReady: IDerivation<{isReady: true} | {isReady: false, readon: 'conflictBetweenDiskStateAndBrowserState'}>
* editor.isReady: IDerivation<{isReady: true} | {isReady: false, reason: 'conflictBetweenDiskStateAndBrowserState'}>
*/
export default async function initialiseProjectState(
studio: Studio,
@ -42,6 +42,7 @@ export default async function initialiseProjectState(
drafts.historic.coreByProject[projectId] = {
sheetsById: {},
definitionVersion: globals.currentProjectStateDefinitionVersion,
revisionHistory: [],
}
}
@ -81,9 +82,8 @@ export default async function initialiseProjectState(
useBrowserState()
} else {
if (
!browserState.exportBookkeeping ||
browserState.exportBookkeeping.basedOnRevisions.indexOf(
onDiskState.exportBookkeeping.revision,
browserState.revisionHistory.indexOf(
onDiskState.revisionHistory[onDiskState.revisionHistory.length - 1],
) == -1
) {
browserStateIsNotBasedOnDiskState(onDiskState)

View file

@ -1,13 +1,9 @@
import type {StrictRecord} from '@theatre/shared/utils/types'
import type {SheetState_Historic} from './types/SheetState_Historic'
export interface ProjectLoadedState {
type: 'loaded'
}
type ProjectLoadingState =
| {type: 'loading'}
| ProjectLoadedState
| {type: 'loaded'}
| {
type: 'browserStateIsNotBasedOnDiskState'
onDiskState: OnDiskState
@ -34,7 +30,11 @@ export interface ProjectEphemeralState {
*/
export interface ProjectState_Historic {
sheetsById: StrictRecord<string, SheetState_Historic>
exportBookkeeping?: {revision: string; basedOnRevisions: string[]}
/**
* The last 50 revision IDs this state is based on, starting with the most recent one.
* The most recent one is the revision ID of this state
*/
revisionHistory: string[]
definitionVersion: string
}
@ -44,6 +44,4 @@ export interface ProjectState {
ephemeral: ProjectEphemeralState
}
export interface OnDiskState extends ProjectState_Historic {
exportBookkeeping: {revision: string; basedOnRevisions: string[]}
}
export interface OnDiskState extends ProjectState_Historic {}

View file

@ -17,6 +17,7 @@ export async function setupTestSheet(sheetState: SheetState_Historic) {
sheetsById: {
Sheet: sheetState,
},
revisionHistory: [],
}
const project = getProject('Test Project ' + lastProjectN++, {
state: projectState,

View file

@ -17,6 +17,7 @@ import SimpleCache from '@theatre/shared/utils/SimpleCache'
import type {IProject, ISheet} from '@theatre/core'
import PaneManager from './PaneManager'
import type * as _coreExports from '@theatre/core/coreExports'
import type {OnDiskState} from '@theatre/core/projects/store/storeTypes'
export type CoreExports = typeof _coreExports
@ -173,7 +174,7 @@ export class Studio {
this._store.redo()
}
createExportedStateOfProject(projectId: string): string {
createExportedStateOfProject(projectId: string): OnDiskState {
return this._store.createExportedStateOfProject(projectId)
}
}

View file

@ -28,6 +28,7 @@ import {persistStateOfStudio} from './persistStateOfStudio'
import {isSheetObject} from '@theatre/shared/instanceTypes'
import globals from '@theatre/shared/globals'
import {nanoid} from 'nanoid'
import type {OnDiskState} from '@theatre/core/projects/store/storeTypes'
export type Drafts = {
historic: Draft<StudioHistoricState>
@ -271,23 +272,29 @@ export default class StudioStore {
this._reduxStore.dispatch(studioActions.historic.redo())
}
createExportedStateOfProject(projectId: string): string {
createExportedStateOfProject(projectId: string): OnDiskState {
const revision = nanoid(16)
// let's assume projectId is already loaded
this.tempTransaction(({drafts}) => {
const state = drafts.historic.coreByProject[projectId]
const maxNumOfRevisionsToKeep = 50
state.revisionHistory.unshift(revision)
if (state.revisionHistory.length > maxNumOfRevisionsToKeep) {
state.revisionHistory.length = maxNumOfRevisionsToKeep
}
}).commit()
const projectHistoricState =
this._reduxStore.getState().$persistent.historic.innerState.coreByProject[
projectId
]
const revision = nanoid(16)
const s = {
revision,
definitionVersion: globals.currentProjectStateDefinitionVersion,
projectState: projectHistoricState,
const generatedOnDiskState: OnDiskState = {
...projectHistoricState,
}
// pushOnDiskRevisionBrowserStateIsBasedOn.originalReducer(s, revision)
const exportString = JSON.stringify(s, null, 2)
return exportString
return generatedOnDiskState
}
}

View file

@ -1,13 +0,0 @@
import React from 'react'
const BrowserStateIsNotBasedOnDiskStateModal: React.FC<{
projectId: string
}> = (_props) => {
/*
* const projectId = props.projectId
* @todo implement me
*/
return <div>@todo BrowserStateIsNotBasedOnDiskStateModal</div>
}
export default BrowserStateIsNotBasedOnDiskStateModal

View file

@ -1,24 +0,0 @@
import {usePrism} from '@theatre/dataverse-react'
import {val} from '@theatre/dataverse'
import React from 'react'
import BrowserStateIsNotBasedOnDiskStateModal from './BrowserStateIsNotBasedOnDiskStateModal'
import getStudio from '@theatre/studio/getStudio'
const EnsureProjectsDontHaveErrors: React.FC<{}> = ({children}) => {
return usePrism(() => {
const projects = val(getStudio().projectsP)
const projectIds = Object.keys(projects)
for (const projectId of projectIds) {
const project = projects[projectId]
const loadingStateType = val(project.pointers.ephemeral.loadingState.type)
if (loadingStateType === 'browserStateIsNotBasedOnDiskState') {
return <BrowserStateIsNotBasedOnDiskStateModal projectId={projectId} />
}
}
return <div>{children}</div>
}, [children])
}
export default EnsureProjectsDontHaveErrors

View file

@ -1,15 +1,19 @@
import type Project from '@theatre/core/projects/Project'
import getStudio from '@theatre/studio/getStudio'
import React from 'react'
import React, {useCallback, useState} from 'react'
const ProjectDetails: React.FC<{
projects: Project[]
}> = ({projects}) => {
const project = projects[0]
const exportProject = () => {
const str = getStudio().createExportedStateOfProject(
project.address.projectId,
const [downloaded, setDownloaded] = useState(false)
const exportProject = useCallback(() => {
const str = JSON.stringify(
getStudio().createExportedStateOfProject(project.address.projectId),
null,
2,
)
const file = new File([str], 'state.json', {type: 'application/json'})
const objUrl = URL.createObjectURL(file)
@ -17,12 +21,27 @@ const ProjectDetails: React.FC<{
a.href = objUrl
a.target = '_blank'
a.setAttribute('download', 'state.json')
a.rel = 'noopener'
a.click()
}
setDownloaded(true)
setTimeout(() => {
setDownloaded(false)
}, 2000)
setTimeout(() => {
URL.revokeObjectURL(objUrl)
}, 40000)
}, [])
return (
<div>
<button onClick={exportProject}>Export project</button>
<button
onClick={!downloaded ? exportProject : undefined}
disabled={downloaded}
>
Export project {downloaded ? 'Done' : ''}
</button>
</div>
)
}