Fix the bugs in assetStorage

This commit is contained in:
Aria Minaei 2023-01-25 15:07:53 +01:00
parent 33235e9cd7
commit 2b10e46441
11 changed files with 176 additions and 32 deletions

View file

@ -8,12 +8,12 @@ import React, {useEffect, useState} from 'react'
import {render} from 'react-dom' import {render} from 'react-dom'
import styled from 'styled-components' import styled from 'styled-components'
studio.initialize()
const project = getProject('Image type playground', { const project = getProject('Image type playground', {
assets: { assets: {
baseUrl: 'http://localhost:3000', baseUrl: '/',
}, },
}) })
studio.initialize()
const sheet = project.sheet('Image type') const sheet = project.sheet('Image type')
const Wrapper = styled.div` const Wrapper = styled.div`
@ -35,6 +35,7 @@ const ImageTypeExample: React.FC<{}> = (props) => {
image2: types.image('', { image2: types.image('', {
label: 'another texture', label: 'another texture',
}), }),
// audio: types.__genericAsset(''),
something: 'asdf', something: 'asdf',
color: types.rgba(), color: types.rgba(),
}) })

View file

@ -35,8 +35,8 @@
"@types/react-dom": "^17.0.6", "@types/react-dom": "^17.0.6",
"esbuild": "^0.12.15", "esbuild": "^0.12.15",
"esbuild-register": "^2.5.0", "esbuild-register": "^2.5.0",
"npm-run-all": "^4.1.5",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"npm-run-all": "^4.1.5",
"typescript": "^4.4.2" "typescript": "^4.4.2"
}, },
"dependencies": { "dependencies": {

View file

@ -1,4 +1,5 @@
import type { import type {
IProject,
IProjectConfig, IProjectConfig,
ISheetObject, ISheetObject,
UnknownShorthandCompoundProps, UnknownShorthandCompoundProps,
@ -45,14 +46,37 @@ function equalityCheckWithFunctionsAlwaysEqual(
} }
} }
export function initialize(config: IProjectConfig) { export function initialize(config: IProjectConfig): Promise<void> {
if (_projectConfig !== undefined) { if (_project) {
console.warn( console.warn(
'Theatric has already been initialized, either through another initialize call, or by calling useControls() before calling initialize().', 'Theatric has already been initialized, either through another initialize call, or by calling useControls() before calling initialize().',
) )
return return _project.ready.then(() => {})
} }
_projectConfig = config _projectConfig = config
const project = callGetProject()
return project.ready.then(() => {})
}
export function getAssetUrl(asset: {
type: 'image'
id: string | undefined
}): string | undefined {
if (!_project) {
throw new Error(
'Theatric has not been initialized yet. Please call initialize() before calling getAssetUrl().',
)
}
if (!_project.isReady) {
throw new Error(
'Calling `getAssetUrl()` before `initialize()` is resolved.\n' +
'The best way to solve this is to delay rendering your react app until `project.ready` is resolved, like this: \n\n' +
'```\n' +
'project.ready.then(() => {ReactDom.render(...)})\n' +
'```',
)
}
return _project.getAssetUrl(asset)
} }
const allProps: Record<string, UnknownShorthandCompoundProps[]> = {} const allProps: Record<string, UnknownShorthandCompoundProps[]> = {}
@ -171,10 +195,8 @@ export function useControls<Config extends ControlsAndButtons>(
[buttons, folder], [buttons, folder],
) )
const sheet = useMemo( const sheet = useMemo(() => callGetProject().sheet('Panels'), [])
() => getProject('Theatric', _projectConfig ?? undefined).sheet('Panels'),
[],
)
const panel = options.panel ?? 'Default panel' const panel = options.panel ?? 'Default panel'
const allPanelProps = allProps[panel] ?? (allProps[panel] = []) const allPanelProps = allProps[panel] ?? (allProps[panel] = [])
const allPanelActions = allActions[panel] ?? (allActions[panel] = []) const allPanelActions = allActions[panel] ?? (allActions[panel] = [])
@ -298,3 +320,11 @@ export const button = (onClick: Button['onClick']) => {
onClick, onClick,
} }
} }
let _project: undefined | IProject
function callGetProject() {
if (_project) return _project
_project = getProject('Theatric', _projectConfig ?? undefined)
return _project
}

View file

@ -107,6 +107,15 @@ export default class TheatreProject implements IProject {
} }
getAssetUrl(asset: Asset): string | undefined { getAssetUrl(asset: Asset): string | undefined {
// probably should put this in project.getAssetUrl but this will do for now
if (!this.isReady) {
console.error(
'Calling `project.getAssetUrl()` before `project.ready` is resolved, will always return `undefined`. ' +
'Either use `project.ready.then(() => project.getAssetUrl())` or `await project.ready` before calling `project.getAssetUrl()`.',
)
return undefined
}
return asset.id return asset.id
? privateAPI(this).assetStorage.getAssetUrl(asset.id) ? privateAPI(this).assetStorage.getAssetUrl(asset.id)
: undefined : undefined

View file

@ -1,19 +1,32 @@
import type Project from '@theatre/core/projects/Project' import type Project from '@theatre/core/projects/Project'
import {val} from '@theatre/dataverse' import {val} from '@theatre/dataverse'
import forEachPropDeep from './forEachDeep'
import type {$IntentionalAny} from './types'
export function getAllPossibleAssetIDs(project: Project, type?: string) { export function getAllPossibleAssetIDs(project: Project, type?: string) {
// Apparently the value returned by val() can be undefined. Should fix TS.
const sheets = Object.values(val(project.pointers.historic.sheetsById) ?? {}) const sheets = Object.values(val(project.pointers.historic.sheetsById) ?? {})
const staticValues = sheets const staticValues = sheets
.flatMap((sheet) => Object.values(sheet?.staticOverrides.byObject ?? {})) .flatMap((sheet) => Object.values(sheet?.staticOverrides.byObject ?? {}))
.flatMap((overrides) => Object.values(overrides ?? {})) .flatMap((overrides) => Object.values(overrides ?? {}))
const keyframeValues = sheets const keyframeValues = sheets
.flatMap((sheet) => Object.values(sheet?.sequence?.tracksByObject ?? {})) .flatMap((sheet) => Object.values(sheet?.sequence?.tracksByObject ?? {}))
.flatMap((tracks) => Object.values(tracks?.trackData ?? {})) .flatMap((tracks) => Object.values(tracks?.trackData ?? {}))
.flatMap((track) => track?.keyframes) .flatMap((track) => track?.keyframes)
.map((keyframe) => keyframe?.value) .map((keyframe) => keyframe?.value)
const allAssets = [...staticValues, ...keyframeValues] const allValues = [...keyframeValues]
staticValues.forEach((value) => {
forEachPropDeep(
value,
(v) => {
allValues.push(v as $IntentionalAny)
},
[],
)
})
const allAssets = allValues
// value is Asset of the type provided // value is Asset of the type provided
.filter((value) => { .filter((value) => {
return ( return (

View file

@ -1,5 +1,9 @@
import type {
PropTypeConfig_AllSimples,
PropTypeConfig_Compound,
} from '@theatre/core/propTypes'
import type {PathToProp} from './addresses' import type {PathToProp} from './addresses'
import type {$IntentionalAny, SerializableMap} from './types' import type {$IntentionalAny} from './types'
/** /**
* Iterates recursively over all props of an object (which should be a {@link SerializableMap}) and runs `fn` * Iterates recursively over all props of an object (which should be a {@link SerializableMap}) and runs `fn`
@ -24,16 +28,28 @@ import type {$IntentionalAny, SerializableMap} from './types'
* // Also note that `a` is also skippped, because it's not a primitive value. * // Also note that `a` is also skippped, because it's not a primitive value.
* ``` * ```
*/ */
export default function forEachDeep< export default function forEachPropDeep<
Primitive extends string | number | boolean, Primitive extends
| string
| number
| boolean
| PropTypeConfig_AllSimples['valueType'],
>( >(
m: SerializableMap<Primitive> | Primitive | undefined | unknown, m:
| PropTypeConfig_Compound<$IntentionalAny>['valueType']
| Primitive
| undefined
| unknown,
fn: (value: Primitive, path: PathToProp) => void, fn: (value: Primitive, path: PathToProp) => void,
startingPath: PathToProp = [], startingPath: PathToProp = [],
): void { ): void {
if (typeof m === 'object' && m) { if (typeof m === 'object' && m) {
if (isImage(m) || isRGBA(m)) {
fn(m as $IntentionalAny as Primitive, startingPath)
return
}
for (const [key, value] of Object.entries(m)) { for (const [key, value] of Object.entries(m)) {
forEachDeep(value!, fn, [...startingPath, key]) forEachPropDeep(value!, fn, [...startingPath, key])
} }
} else if (m === undefined || m === null) { } else if (m === undefined || m === null) {
return return
@ -41,3 +57,39 @@ export default function forEachDeep<
fn(m as $IntentionalAny as Primitive, startingPath) fn(m as $IntentionalAny as Primitive, startingPath)
} }
} }
const isImage = (value: unknown): value is {type: 'image'; id: string} => {
return (
typeof value === 'object' &&
value !== null &&
Object.hasOwnProperty.call(value, 'type') &&
// @ts-ignore
value.type === 'image' &&
Object.hasOwnProperty.call(value, 'id') &&
// @ts-ignore
typeof value.id === 'string' &&
// @ts-ignore
value.id !== ''
)
}
const isRGBA = (
value: unknown,
): value is {r: number; g: number; b: number; a: number} => {
return (
typeof value === 'object' &&
value !== null &&
Object.hasOwnProperty.call(value, 'r') &&
Object.hasOwnProperty.call(value, 'g') &&
Object.hasOwnProperty.call(value, 'b') &&
Object.hasOwnProperty.call(value, 'a') &&
// @ts-ignore
typeof value.r === 'number' &&
// @ts-ignore
typeof value.g === 'number' &&
// @ts-ignore
typeof value.b === 'number' &&
// @ts-ignore
typeof value.a === 'number'
)
}

View file

@ -1,4 +1,4 @@
import forEachDeep from '@theatre/shared/utils/forEachDeep' import forEachPropDeep from '@theatre/shared/utils/forEachDeep'
import type {$FixMe} from '@theatre/shared/utils/types' import type {$FixMe} from '@theatre/shared/utils/types'
import type {Pointer} from '@theatre/dataverse' import type {Pointer} from '@theatre/dataverse'
import {getPointerParts} from '@theatre/dataverse' import {getPointerParts} from '@theatre/dataverse'
@ -213,7 +213,7 @@ export default class Scrub implements IScrub {
const defaultValueOfProp = root.template.getDefaultsAtPointer(pointer) const defaultValueOfProp = root.template.getDefaultsAtPointer(pointer)
forEachDeep( forEachPropDeep(
defaultValueOfProp, defaultValueOfProp,
(val, pathToProp) => { (val, pathToProp) => {
stateEditors.studio.ephemeral.projects.stateByProjectId.stateBySheetId.stateByObjectKey.propsBeingScrubbed.flag( stateEditors.studio.ephemeral.projects.stateByProjectId.stateBySheetId.stateByObjectKey.propsBeingScrubbed.flag(

View file

@ -3,7 +3,7 @@ import {isSheetObject} from '@theatre/shared/instanceTypes'
import type {$FixMe, $IntentionalAny} from '@theatre/shared/utils/types' import type {$FixMe, $IntentionalAny} from '@theatre/shared/utils/types'
import get from 'lodash-es/get' import get from 'lodash-es/get'
import type {ITransactionPrivateApi} from './StudioStore' import type {ITransactionPrivateApi} from './StudioStore'
import forEachDeep from '@theatre/shared/utils/forEachDeep' import forEachPropDeep from '@theatre/shared/utils/forEachDeep'
import getDeep from '@theatre/shared/utils/getDeep' import getDeep from '@theatre/shared/utils/getDeep'
import type {SequenceTrackId} from '@theatre/shared/utils/ids' import type {SequenceTrackId} from '@theatre/shared/utils/ids'
import {getPointerParts} from '@theatre/dataverse' import {getPointerParts} from '@theatre/dataverse'
@ -233,7 +233,7 @@ export default function createTransactionPrivateApi(
} }
if (propConfig.type === 'compound') { if (propConfig.type === 'compound') {
forEachDeep( forEachPropDeep(
defaultValue, defaultValue,
(v, pathToProp) => { (v, pathToProp) => {
unsetStaticOrKeyframeProp(v, pathToProp) unsetStaticOrKeyframeProp(v, pathToProp)

View file

@ -60,7 +60,7 @@ const InputLabel = styled.label<{empty: boolean}>`
` `
// file input // file input
const Input = styled.input.attrs({type: 'file', accept: 'image/*'})` const Input = styled.input.attrs({type: 'file'})`
display: none; display: none;
` `
@ -145,7 +145,7 @@ function ImagePropEditor({
<Input <Input
type="file" type="file"
onChange={onChange} onChange={onChange}
accept="image/*" accept="image/*,.hdr"
autoFocus={autoFocus} autoFocus={autoFocus}
/> />
{previewUrl ? <Preview src={previewUrl} /> : <AddImage />} {previewUrl ? <Preview src={previewUrl} /> : <AddImage />}

View file

@ -34,7 +34,7 @@ interface EditingToolsCommon<T> {
permanentlySetValue(v: T): void permanentlySetValue(v: T): void
getAssetUrl: (asset: Asset) => string | undefined getAssetUrl: (asset: Asset) => string | undefined
createAsset(asset: Blob): Promise<string | null> createAsset(asset: File): Promise<string | null>
} }
interface EditingToolsDefault<T> extends EditingToolsCommon<T> { interface EditingToolsDefault<T> extends EditingToolsCommon<T> {
@ -114,7 +114,8 @@ function createPrism<T extends SerializablePrimitive>(
) )
const editAssets = { const editAssets = {
createAsset: obj.sheet.project.assetStorage.createAsset, createAsset: (asset: File): Promise<string | null> =>
obj.sheet.project.assetStorage.createAsset(asset),
getAssetUrl: (asset: Asset) => getAssetUrl: (asset: Asset) =>
asset.id asset.id
? obj.sheet.project.assetStorage.getAssetUrl(asset.id) ? obj.sheet.project.assetStorage.getAssetUrl(asset.id)

View file

@ -5429,7 +5429,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.14.0": "@babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.14.0":
version: 7.14.0 version: 7.14.0
resolution: "@babel/runtime@npm:7.14.0" resolution: "@babel/runtime@npm:7.14.0"
dependencies: dependencies:
@ -5447,6 +5447,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@babel/runtime@npm:^7.13.10":
version: 7.20.13
resolution: "@babel/runtime@npm:7.20.13"
dependencies:
regenerator-runtime: ^0.13.11
checksum: 09b7a97a05c80540db6c9e4ddf8c5d2ebb06cae5caf3a87e33c33f27f8c4d49d9c67a2d72f1570e796045288fad569f98a26ceba0c4f5fad2af84b6ad855c4fb
languageName: node
linkType: hard
"@babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.9.2": "@babel/runtime@npm:^7.14.6, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.7.2, @babel/runtime@npm:^7.9.2":
version: 7.14.8 version: 7.14.8
resolution: "@babel/runtime@npm:7.14.8" resolution: "@babel/runtime@npm:7.14.8"
@ -7613,14 +7622,15 @@ __metadata:
linkType: hard linkType: hard
"@react-three/fiber@npm:^7.0.6": "@react-three/fiber@npm:^7.0.6":
version: 7.0.6 version: 7.0.29
resolution: "@react-three/fiber@npm:7.0.6" resolution: "@react-three/fiber@npm:7.0.29"
dependencies: dependencies:
"@babel/runtime": ^7.13.10 "@babel/runtime": ^7.13.10
"@types/react-reconciler": ^0.26.2
react-merge-refs: ^1.1.0 react-merge-refs: ^1.1.0
react-reconciler: ^0.26.2 react-reconciler: ^0.26.2
react-three-fiber: 0.0.0-deprecated react-three-fiber: 0.0.0-deprecated
react-use-measure: ^2.0.4 react-use-measure: ^2.1.1
resize-observer-polyfill: ^1.5.1 resize-observer-polyfill: ^1.5.1
scheduler: ^0.20.2 scheduler: ^0.20.2
use-asset: ^1.0.4 use-asset: ^1.0.4
@ -7629,11 +7639,11 @@ __metadata:
peerDependencies: peerDependencies:
react: ">=17.0" react: ">=17.0"
react-dom: ">=17.0" react-dom: ">=17.0"
three: ">=0.126" three: ">=0.133"
peerDependenciesMeta: peerDependenciesMeta:
react-dom: react-dom:
optional: true optional: true
checksum: f213eef29a600688f03d3ffd2f41ea2ce4da9608326921f3fea15621f86bebbc8154ce5eca071188a78f9684751c7ee824353711e59affb9967f3f7fdeaf2f20 checksum: 79b760b41c4076666b4546c9ee6713cdd815e6cb9e05a90702ba911306180f3968a31cb7ac024cc7683bc6e2af7dd0e55125db26b0dc238647628ad5d9dbd877
languageName: node languageName: node
linkType: hard linkType: hard
@ -8869,6 +8879,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/react-reconciler@npm:^0.26.2":
version: 0.26.7
resolution: "@types/react-reconciler@npm:0.26.7"
dependencies:
"@types/react": "*"
checksum: 4122d2b08580f775d0aeae9bd10b68248f894096ed14c0ebbc143ef712e21b159e89d0c628bd95dd3329947fc1ee94a0cb1d2d32b32b1d5d225e70030e91e58f
languageName: node
linkType: hard
"@types/react@npm:^17.0.9": "@types/react@npm:^17.0.9":
version: 17.0.9 version: 17.0.9
resolution: "@types/react@npm:17.0.9" resolution: "@types/react@npm:17.0.9"
@ -14174,7 +14193,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"debounce@npm:^1.2.0": "debounce@npm:^1.2.0, debounce@npm:^1.2.1":
version: 1.2.1 version: 1.2.1
resolution: "debounce@npm:1.2.1" resolution: "debounce@npm:1.2.1"
checksum: 682a89506d9e54fb109526f4da255c5546102fbb8e3ae75eef3b04effaf5d4853756aee97475cd4650641869794e44f410eeb20ace2b18ea592287ab2038519e checksum: 682a89506d9e54fb109526f4da255c5546102fbb8e3ae75eef3b04effaf5d4853756aee97475cd4650641869794e44f410eeb20ace2b18ea592287ab2038519e
@ -27998,6 +28017,18 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"react-use-measure@npm:^2.1.1":
version: 2.1.1
resolution: "react-use-measure@npm:2.1.1"
dependencies:
debounce: ^1.2.1
peerDependencies:
react: ">=16.13"
react-dom: ">=16.13"
checksum: b8e8939229d463c3c505f7b617925c0228efae0cd6f651371f463846417b06c9170be57df51293a61027c41770f8a090fdb8a08717c4e36290ccb496e0318f1f
languageName: node
linkType: hard
"react-use@npm:^15.3.3": "react-use@npm:^15.3.3":
version: 15.3.8 version: 15.3.8
resolution: "react-use@npm:15.3.8" resolution: "react-use@npm:15.3.8"
@ -28296,6 +28327,13 @@ fsevents@^1.2.7:
languageName: node languageName: node
linkType: hard linkType: hard
"regenerator-runtime@npm:^0.13.11":
version: 0.13.11
resolution: "regenerator-runtime@npm:0.13.11"
checksum: 27481628d22a1c4e3ff551096a683b424242a216fee44685467307f14d58020af1e19660bf2e26064de946bad7eff28950eae9f8209d55723e2d9351e632bbb4
languageName: node
linkType: hard
"regenerator-runtime@npm:^0.13.4": "regenerator-runtime@npm:^0.13.4":
version: 0.13.8 version: 0.13.8
resolution: "regenerator-runtime@npm:0.13.8" resolution: "regenerator-runtime@npm:0.13.8"