diff --git a/editor/src/components/editor/store/project-server-state.tsx b/editor/src/components/editor/store/project-server-state.tsx index 67dc3284bf46..c2e6626c763a 100644 --- a/editor/src/components/editor/store/project-server-state.tsx +++ b/editor/src/components/editor/store/project-server-state.tsx @@ -6,6 +6,7 @@ import { updateProjectServerState } from '../actions/action-creators' import { checkProjectOwned, projectIsStoredLocally } from '../persistence/persistence-backend' import type { ProjectOwnership } from '../persistence/generic/persistence-types' import { CollaborationEndpoints } from '../collaborative-endpoints' +import { IS_TEST_ENVIRONMENT } from '../../../common/env-vars' export interface ProjectMetadataFromServer { title: string @@ -163,7 +164,7 @@ export interface ProjectServerStateUpdaterProps { } let serverStateWatcherInstance: number | null = null -const baseWatcherIntervalTime: number = 10 * 1000 +const baseWatcherIntervalTime: number = (IS_TEST_ENVIRONMENT ? 100 : 10) * 1000 let currentWatcherIntervalMultiplier: number = 1 function restartServerStateWatcher( diff --git a/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx b/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx index 5a4413891a86..5e6acc80502f 100644 --- a/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx +++ b/editor/src/components/inspector/sections/component-section/component-section.spec.browser2.tsx @@ -8,6 +8,7 @@ import { DataPickerPopupTestId, VariableFromScopeOptionTestId, } from './component-section' +import { ImagePreviewTestId } from './property-control-controls' describe('Set element prop via the data picker', () => { it('can pick from the property data picker', async () => { @@ -320,3 +321,72 @@ registerExternalComponent( ], }, )` + +const projectWithImage = (imageUrl: string) => `import * as React from 'react' +import { + Storyboard, + Scene, + registerInternalComponent, +} from 'utopia-api' + +function Image({ url }) { + return +} + +var Playground = ({ style }) => { + return ( +
+ +
+ ) +} + +export var storyboard = ( + + + + + +) + +registerInternalComponent(Image, { + supportsChildren: false, + properties: { + url: { + control: 'string-input', + }, + }, + variants: [ + { + code: '', + }, + ], +}) + +` diff --git a/editor/src/components/inspector/sections/component-section/property-control-controls.tsx b/editor/src/components/inspector/sections/component-section/property-control-controls.tsx index 3195eddc3cb0..d4077e38a76f 100644 --- a/editor/src/components/inspector/sections/component-section/property-control-controls.tsx +++ b/editor/src/components/inspector/sections/component-section/property-control-controls.tsx @@ -55,6 +55,7 @@ import { normalisePathToUnderlyingTarget, } from '../../../custom-code/code-file' import { useDispatch } from '../../../editor/store/dispatch-context' +import { isImage } from '../../../../core/shared/utils' export interface ControlForPropProps { propPath: PropertyPath @@ -437,6 +438,8 @@ const NumberWithSliderControl = React.memo( }, ) +export const ImagePreviewTestId = 'image-preview' + export const StringInputPropertyControl = React.memo( (props: ControlForPropProps) => { const { propName, propMetadata, controlDescription } = props @@ -444,21 +447,59 @@ export const StringInputPropertyControl = React.memo( const controlId = `${propName}-string-input-property-control` const value = propMetadata.propertyStatus.set ? propMetadata.value : undefined + const safeValue = typeof value === 'string' ? value : '' + return ( - + + + + ) }, ) +interface ImagePreviewProps { + url: string +} +const ImagePreview = React.memo(({ url }: ImagePreviewProps) => { + const [imageCanBeLoaded, setImageCanBeLoaded] = React.useState(isImage(url)) + + // we need to track if the url has changed so we retry loading the image even if it failed before + const urlRef = React.useRef(url) + if (urlRef.current !== url) { + setImageCanBeLoaded(isImage(url)) + urlRef.current = url + } + + // don't render the img when it can not be loaded + const onImageError = React.useCallback(() => { + setImageCanBeLoaded(false) + }, [setImageCanBeLoaded]) + + if (!imageCanBeLoaded) { + return null + } + + return ( + + ) +}) +ImagePreview.displayName = 'ImagePreview' + function keysForVectorOfType(vectorType: 'vector2' | 'vector3' | 'vector4'): Array { switch (vectorType) { case 'vector2':