Simplified export bookkeeping
This commit is contained in:
parent
832542c1c5
commit
f08d9bf9c1
9 changed files with 58 additions and 68 deletions
|
@ -57,6 +57,7 @@ export default class Project {
|
|||
historic: config.state ?? {
|
||||
sheetsById: {},
|
||||
definitionVersion: globals.currentProjectStateDefinitionVersion,
|
||||
revisionHistory: [],
|
||||
},
|
||||
ephemeral: {
|
||||
loadingState: {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -17,6 +17,7 @@ export async function setupTestSheet(sheetState: SheetState_Historic) {
|
|||
sheetsById: {
|
||||
Sheet: sheetState,
|
||||
},
|
||||
revisionHistory: [],
|
||||
}
|
||||
const project = getProject('Test Project ' + lastProjectN++, {
|
||||
state: projectState,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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>
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue