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

View file

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

View file

@ -1,4 +1,5 @@
import type {
IProject,
IProjectConfig,
ISheetObject,
UnknownShorthandCompoundProps,
@ -45,14 +46,37 @@ function equalityCheckWithFunctionsAlwaysEqual(
}
}
export function initialize(config: IProjectConfig) {
if (_projectConfig !== undefined) {
export function initialize(config: IProjectConfig): Promise<void> {
if (_project) {
console.warn(
'Theatric has already been initialized, either through another initialize call, or by calling useControls() before calling initialize().',
)
return
return _project.ready.then(() => {})
}
_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[]> = {}
@ -171,10 +195,8 @@ export function useControls<Config extends ControlsAndButtons>(
[buttons, folder],
)
const sheet = useMemo(
() => getProject('Theatric', _projectConfig ?? undefined).sheet('Panels'),
[],
)
const sheet = useMemo(() => callGetProject().sheet('Panels'), [])
const panel = options.panel ?? 'Default panel'
const allPanelProps = allProps[panel] ?? (allProps[panel] = [])
const allPanelActions = allActions[panel] ?? (allActions[panel] = [])
@ -298,3 +320,11 @@ export const button = (onClick: Button['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 {
// 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
? privateAPI(this).assetStorage.getAssetUrl(asset.id)
: undefined

View file

@ -1,19 +1,32 @@
import type Project from '@theatre/core/projects/Project'
import {val} from '@theatre/dataverse'
import forEachPropDeep from './forEachDeep'
import type {$IntentionalAny} from './types'
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 staticValues = sheets
.flatMap((sheet) => Object.values(sheet?.staticOverrides.byObject ?? {}))
.flatMap((overrides) => Object.values(overrides ?? {}))
const keyframeValues = sheets
.flatMap((sheet) => Object.values(sheet?.sequence?.tracksByObject ?? {}))
.flatMap((tracks) => Object.values(tracks?.trackData ?? {}))
.flatMap((track) => track?.keyframes)
.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
.filter((value) => {
return (

View file

@ -1,5 +1,9 @@
import type {
PropTypeConfig_AllSimples,
PropTypeConfig_Compound,
} from '@theatre/core/propTypes'
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`
@ -24,16 +28,28 @@ import type {$IntentionalAny, SerializableMap} from './types'
* // Also note that `a` is also skippped, because it's not a primitive value.
* ```
*/
export default function forEachDeep<
Primitive extends string | number | boolean,
export default function forEachPropDeep<
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,
startingPath: PathToProp = [],
): void {
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)) {
forEachDeep(value!, fn, [...startingPath, key])
forEachPropDeep(value!, fn, [...startingPath, key])
}
} else if (m === undefined || m === null) {
return
@ -41,3 +57,39 @@ export default function forEachDeep<
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 {Pointer} from '@theatre/dataverse'
import {getPointerParts} from '@theatre/dataverse'
@ -213,7 +213,7 @@ export default class Scrub implements IScrub {
const defaultValueOfProp = root.template.getDefaultsAtPointer(pointer)
forEachDeep(
forEachPropDeep(
defaultValueOfProp,
(val, pathToProp) => {
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 get from 'lodash-es/get'
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 type {SequenceTrackId} from '@theatre/shared/utils/ids'
import {getPointerParts} from '@theatre/dataverse'
@ -233,7 +233,7 @@ export default function createTransactionPrivateApi(
}
if (propConfig.type === 'compound') {
forEachDeep(
forEachPropDeep(
defaultValue,
(v, pathToProp) => {
unsetStaticOrKeyframeProp(v, pathToProp)

View file

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

View file

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

View file

@ -5429,7 +5429,7 @@ __metadata:
languageName: node
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
resolution: "@babel/runtime@npm:7.14.0"
dependencies:
@ -5447,6 +5447,15 @@ __metadata:
languageName: node
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":
version: 7.14.8
resolution: "@babel/runtime@npm:7.14.8"
@ -7613,14 +7622,15 @@ __metadata:
linkType: hard
"@react-three/fiber@npm:^7.0.6":
version: 7.0.6
resolution: "@react-three/fiber@npm:7.0.6"
version: 7.0.29
resolution: "@react-three/fiber@npm:7.0.29"
dependencies:
"@babel/runtime": ^7.13.10
"@types/react-reconciler": ^0.26.2
react-merge-refs: ^1.1.0
react-reconciler: ^0.26.2
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
scheduler: ^0.20.2
use-asset: ^1.0.4
@ -7629,11 +7639,11 @@ __metadata:
peerDependencies:
react: ">=17.0"
react-dom: ">=17.0"
three: ">=0.126"
three: ">=0.133"
peerDependenciesMeta:
react-dom:
optional: true
checksum: f213eef29a600688f03d3ffd2f41ea2ce4da9608326921f3fea15621f86bebbc8154ce5eca071188a78f9684751c7ee824353711e59affb9967f3f7fdeaf2f20
checksum: 79b760b41c4076666b4546c9ee6713cdd815e6cb9e05a90702ba911306180f3968a31cb7ac024cc7683bc6e2af7dd0e55125db26b0dc238647628ad5d9dbd877
languageName: node
linkType: hard
@ -8869,6 +8879,15 @@ __metadata:
languageName: node
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":
version: 17.0.9
resolution: "@types/react@npm:17.0.9"
@ -14174,7 +14193,7 @@ __metadata:
languageName: node
linkType: hard
"debounce@npm:^1.2.0":
"debounce@npm:^1.2.0, debounce@npm:^1.2.1":
version: 1.2.1
resolution: "debounce@npm:1.2.1"
checksum: 682a89506d9e54fb109526f4da255c5546102fbb8e3ae75eef3b04effaf5d4853756aee97475cd4650641869794e44f410eeb20ace2b18ea592287ab2038519e
@ -27998,6 +28017,18 @@ fsevents@^1.2.7:
languageName: node
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":
version: 15.3.8
resolution: "react-use@npm:15.3.8"
@ -28296,6 +28327,13 @@ fsevents@^1.2.7:
languageName: node
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":
version: 0.13.8
resolution: "regenerator-runtime@npm:0.13.8"