migrate shapes / assets as a store on putContent
(#2971)
This PR updates the `putContentOntoCurrentPage` so that it migrates shapes / records as a complete store. ### Change Type - [x] `patch` — Bug fix ### Test Plan 1. Copy and paste, ideally between versions.
This commit is contained in:
parent
e6513215b5
commit
1a68857174
1 changed files with 71 additions and 67 deletions
|
@ -1,5 +1,5 @@
|
||||||
import { EMPTY_ARRAY, atom, computed, transact } from '@tldraw/state'
|
import { EMPTY_ARRAY, atom, computed, transact } from '@tldraw/state'
|
||||||
import { ComputedCache, RecordType } from '@tldraw/store'
|
import { ComputedCache, RecordType, StoreSnapshot } from '@tldraw/store'
|
||||||
import {
|
import {
|
||||||
CameraRecordType,
|
CameraRecordType,
|
||||||
InstancePageStateRecordType,
|
InstancePageStateRecordType,
|
||||||
|
@ -26,6 +26,7 @@ import {
|
||||||
TLPage,
|
TLPage,
|
||||||
TLPageId,
|
TLPageId,
|
||||||
TLParentId,
|
TLParentId,
|
||||||
|
TLRecord,
|
||||||
TLShape,
|
TLShape,
|
||||||
TLShapeId,
|
TLShapeId,
|
||||||
TLShapePartial,
|
TLShapePartial,
|
||||||
|
@ -7721,7 +7722,36 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
// decide on a parent for the put shapes; if the parent is among the put shapes(?) then use its parent
|
// decide on a parent for the put shapes; if the parent is among the put shapes(?) then use its parent
|
||||||
|
|
||||||
const currentPageId = this.getCurrentPageId()
|
const currentPageId = this.getCurrentPageId()
|
||||||
const { assets, shapes, rootShapeIds } = content
|
const { rootShapeIds } = content
|
||||||
|
|
||||||
|
// We need to collect the migrated shapes and assets
|
||||||
|
const assets: TLAsset[] = []
|
||||||
|
const shapes: TLShape[] = []
|
||||||
|
|
||||||
|
// Let's treat the content as a store, and then migrate that store.
|
||||||
|
const store: StoreSnapshot<TLRecord> = {
|
||||||
|
store: {
|
||||||
|
...Object.fromEntries(content.assets.map((asset) => [asset.id, asset] as const)),
|
||||||
|
...Object.fromEntries(content.shapes.map((asset) => [asset.id, asset] as const)),
|
||||||
|
},
|
||||||
|
schema: content.schema,
|
||||||
|
}
|
||||||
|
const result = this.store.schema.migrateStoreSnapshot(store)
|
||||||
|
if (result.type === 'error') {
|
||||||
|
throw Error('Could not put content: could not migrate content')
|
||||||
|
}
|
||||||
|
for (const record of Object.values(result.value)) {
|
||||||
|
switch (record.typeName) {
|
||||||
|
case 'asset': {
|
||||||
|
assets.push(record)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'shape': {
|
||||||
|
shapes.push(record)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const idMap = new Map<any, TLShapeId>(shapes.map((shape) => [shape.id, createShapeId()]))
|
const idMap = new Map<any, TLShapeId>(shapes.map((shape) => [shape.id, createShapeId()]))
|
||||||
|
|
||||||
|
@ -7864,76 +7894,50 @@ export class Editor extends EventEmitter<TLEventMap> {
|
||||||
|
|
||||||
let assetsToCreate: TLAsset[] = []
|
let assetsToCreate: TLAsset[] = []
|
||||||
|
|
||||||
if (assets) {
|
const assetsToUpdate: (TLImageAsset | TLVideoAsset)[] = []
|
||||||
for (let i = 0; i < assets.length; i++) {
|
|
||||||
const asset = assets[i]
|
assetsToCreate = assets
|
||||||
const result = this.store.schema.migratePersistedRecord(asset, content.schema)
|
.filter((asset) => !this.store.has(asset.id))
|
||||||
if (result.type === 'success') {
|
.map((asset) => {
|
||||||
assets[i] = result.value as TLAsset
|
if (asset.type === 'image' || asset.type === 'video') {
|
||||||
} else {
|
if (asset.props.src && asset.props.src?.startsWith('data:image')) {
|
||||||
throw Error(
|
assetsToUpdate.push(structuredClone(asset))
|
||||||
`Could not put content:\ncould not migrate content for asset:\n${asset.type}\nreason:${result.reason}`
|
asset.props.src = null
|
||||||
)
|
} else {
|
||||||
|
assetsToUpdate.push(structuredClone(asset))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
const assetsToUpdate: (TLImageAsset | TLVideoAsset)[] = []
|
return asset
|
||||||
|
|
||||||
assetsToCreate = assets
|
|
||||||
.filter((asset) => !this.store.has(asset.id))
|
|
||||||
.map((asset) => {
|
|
||||||
if (asset.type === 'image' || asset.type === 'video') {
|
|
||||||
if (asset.props.src && asset.props.src?.startsWith('data:image')) {
|
|
||||||
assetsToUpdate.push(structuredClone(asset))
|
|
||||||
asset.props.src = null
|
|
||||||
} else {
|
|
||||||
assetsToUpdate.push(structuredClone(asset))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return asset
|
|
||||||
})
|
|
||||||
|
|
||||||
Promise.allSettled(
|
|
||||||
assetsToUpdate.map(async (asset) => {
|
|
||||||
const file = await dataUrlToFile(
|
|
||||||
asset.props.src!,
|
|
||||||
asset.props.name,
|
|
||||||
asset.props.mimeType ?? 'image/png'
|
|
||||||
)
|
|
||||||
|
|
||||||
const newAsset = await this.getAssetForExternalContent({ type: 'file', file })
|
|
||||||
|
|
||||||
if (!newAsset) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return [asset, newAsset] as const
|
|
||||||
})
|
|
||||||
).then((assets) => {
|
|
||||||
this.updateAssets(
|
|
||||||
compact(
|
|
||||||
assets.map((result) =>
|
|
||||||
result.status === 'fulfilled' && result.value
|
|
||||||
? { ...result.value[1], id: result.value[0].id }
|
|
||||||
: undefined
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < newShapes.length; i++) {
|
Promise.allSettled(
|
||||||
const shape = newShapes[i]
|
assetsToUpdate.map(async (asset) => {
|
||||||
const result = this.store.schema.migratePersistedRecord(shape, content.schema)
|
const file = await dataUrlToFile(
|
||||||
if (result.type === 'success') {
|
asset.props.src!,
|
||||||
newShapes[i] = result.value as TLShape
|
asset.props.name,
|
||||||
} else {
|
asset.props.mimeType ?? 'image/png'
|
||||||
throw Error(
|
|
||||||
`Could not put content:\ncould not migrate content for shape:\n${shape.type}\nreason:${result.reason}`
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
}
|
const newAsset = await this.getAssetForExternalContent({ type: 'file', file })
|
||||||
|
|
||||||
|
if (!newAsset) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return [asset, newAsset] as const
|
||||||
|
})
|
||||||
|
).then((assets) => {
|
||||||
|
this.updateAssets(
|
||||||
|
compact(
|
||||||
|
assets.map((result) =>
|
||||||
|
result.status === 'fulfilled' && result.value
|
||||||
|
? { ...result.value[1], id: result.value[0].id }
|
||||||
|
: undefined
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
this.batch(() => {
|
this.batch(() => {
|
||||||
// Create any assets that need to be created
|
// Create any assets that need to be created
|
||||||
|
|
Loading…
Reference in a new issue