mirror of
https://github.com/OpenNebula/one.git
synced 2024-12-23 17:33:56 +03:00
parent
532c4b238d
commit
bd821b397e
@ -7,14 +7,14 @@ import ProvisionIcon from '@material-ui/icons/Cloud'
|
||||
import SelectCard from 'client/components/Cards/SelectCard'
|
||||
import Action from 'client/components/Cards/SelectCard/Action'
|
||||
import { StatusBadge } from 'client/components/Status'
|
||||
import Image from 'client/components/Image'
|
||||
import { isExternalURL } from 'client/utils'
|
||||
import * as Types from 'client/types/provision'
|
||||
import {
|
||||
PROVISIONS_STATES,
|
||||
PROVIDER_IMAGES_URL,
|
||||
PROVISION_IMAGES_URL,
|
||||
DEFAULT_IMAGE,
|
||||
IMAGE_FORMATS
|
||||
DEFAULT_IMAGE
|
||||
} from 'client/constants'
|
||||
|
||||
const ProvisionCard = memo(
|
||||
@ -27,34 +27,12 @@ const ProvisionCard = memo(
|
||||
const stateInfo = PROVISIONS_STATES[bodyData?.state]
|
||||
const image = bodyData?.image ?? DEFAULT_IMAGE
|
||||
|
||||
const isExternalImage = isExternalURL(image)
|
||||
const isExternalImage = useMemo(() => isExternalURL(image), [image])
|
||||
|
||||
const mediaProps = useMemo(() => {
|
||||
const src = isExternalImage ? image : `${IMAGES_URL}/${image}`
|
||||
const onError = evt => { evt.target.src = DEFAULT_IMAGE }
|
||||
|
||||
return {
|
||||
component: 'picture',
|
||||
children: (
|
||||
<>
|
||||
{(image && !isExternalImage) && IMAGE_FORMATS.map(format => (
|
||||
<source
|
||||
key={format}
|
||||
srcSet={`${src}.${format}`}
|
||||
type={`image/${format}`}
|
||||
/>
|
||||
))}
|
||||
<img
|
||||
decoding='async'
|
||||
draggable={false}
|
||||
loading='lazy'
|
||||
src={src}
|
||||
onError={onError}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}, [image, isExternalImage])
|
||||
const imageUrl = useMemo(
|
||||
() => isExternalImage ? image : `${IMAGES_URL}/${image}`,
|
||||
[isExternalImage]
|
||||
)
|
||||
|
||||
return (
|
||||
<SelectCard
|
||||
@ -73,7 +51,10 @@ const ProvisionCard = memo(
|
||||
)
|
||||
}
|
||||
isSelected={isSelected}
|
||||
mediaProps={mediaProps}
|
||||
mediaProps={{
|
||||
component: 'div',
|
||||
children: <Image src={imageUrl} withSources={image && !isExternalImage} />
|
||||
}}
|
||||
subheader={`#${ID}`}
|
||||
title={NAME}
|
||||
disableFilterImage={isExternalImage}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import * as React from 'react'
|
||||
import React, { memo, useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import * as Types from 'client/types/provision'
|
||||
@ -6,48 +6,23 @@ import * as Types from 'client/types/provision'
|
||||
import ProvidersIcon from '@material-ui/icons/Public'
|
||||
import SelectCard from 'client/components/Cards/SelectCard'
|
||||
|
||||
import Image from 'client/components/Image'
|
||||
import { isExternalURL } from 'client/utils'
|
||||
import {
|
||||
PROVIDER_IMAGES_URL,
|
||||
PROVISION_IMAGES_URL,
|
||||
DEFAULT_IMAGE,
|
||||
IMAGE_FORMATS
|
||||
} from 'client/constants'
|
||||
import { PROVIDER_IMAGES_URL, PROVISION_IMAGES_URL } from 'client/constants'
|
||||
|
||||
const ProvisionTemplateCard = React.memo(
|
||||
const ProvisionTemplateCard = memo(
|
||||
({ value, isProvider, isSelected, isValid, handleClick }) => {
|
||||
const { description, name, plain = {} } = value
|
||||
const { image = '' } = isProvider ? plain : value
|
||||
|
||||
const isExternalImage = isExternalURL(image)
|
||||
const IMAGES_URL = isProvider ? PROVIDER_IMAGES_URL : PROVISION_IMAGES_URL
|
||||
|
||||
const mediaProps = React.useMemo(() => {
|
||||
const IMAGES_URL = isProvider ? PROVIDER_IMAGES_URL : PROVISION_IMAGES_URL
|
||||
const src = isExternalImage ? image : `${IMAGES_URL}/${image}`
|
||||
const onError = evt => { evt.target.src = DEFAULT_IMAGE }
|
||||
const isExternalImage = useMemo(() => isExternalURL(image), [image])
|
||||
|
||||
return {
|
||||
component: 'picture',
|
||||
children: (
|
||||
<>
|
||||
{(image && !isExternalImage) && IMAGE_FORMATS.map(format => (
|
||||
<source
|
||||
key={format}
|
||||
srcSet={`${IMAGES_URL}/${image}.${format}`}
|
||||
type={`image/${format}`}
|
||||
/>
|
||||
))}
|
||||
<img
|
||||
decoding='async'
|
||||
draggable={false}
|
||||
loading='lazy'
|
||||
src={src}
|
||||
onError={onError}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}, [image, isProvider])
|
||||
const imageUrl = useMemo(
|
||||
() => isExternalImage ? image : `${IMAGES_URL}/${image}`,
|
||||
[isExternalImage]
|
||||
)
|
||||
|
||||
return (
|
||||
<SelectCard
|
||||
@ -57,7 +32,10 @@ const ProvisionTemplateCard = React.memo(
|
||||
icon={<ProvidersIcon />}
|
||||
cardActionAreaProps={{ disabled: !isValid }}
|
||||
isSelected={isSelected}
|
||||
mediaProps={mediaProps}
|
||||
mediaProps={{
|
||||
component: 'div',
|
||||
children: <Image src={imageUrl} withSources={image && !isExternalImage} />
|
||||
}}
|
||||
subheader={description}
|
||||
title={name}
|
||||
/>
|
||||
|
63
src/fireedge/src/client/components/Image/index.js
Normal file
63
src/fireedge/src/client/components/Image/index.js
Normal file
@ -0,0 +1,63 @@
|
||||
import React, { useState, memo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import { DEFAULT_IMAGE, IMAGE_FORMATS } from 'client/constants'
|
||||
|
||||
const MAX_RETRIES = 2
|
||||
const INITIAL_STATE = { fail: false, retries: 0 }
|
||||
|
||||
const Image = memo(({ src = DEFAULT_IMAGE, withSources, imgProps }) => {
|
||||
const [error, setError] = useState(INITIAL_STATE)
|
||||
|
||||
const addRetry = () => {
|
||||
setError(prev => ({ ...prev, retries: prev.retries + 1 }))
|
||||
}
|
||||
|
||||
const onImageFail = () => {
|
||||
setError(prev => ({ fail: true, retries: prev.retries + 1 }))
|
||||
}
|
||||
|
||||
if (error.retries >= MAX_RETRIES) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (error.fail) {
|
||||
return <img src={DEFAULT_IMAGE} draggable={false} onError={addRetry} />
|
||||
}
|
||||
|
||||
return (
|
||||
<picture>
|
||||
{withSources && IMAGE_FORMATS.map(format => (
|
||||
<source key={format}
|
||||
srcSet={`${src}.${format}`}
|
||||
type={`image/${format}`}
|
||||
/>
|
||||
))}
|
||||
<img {...imgProps} src={src} onError={onImageFail} />
|
||||
</picture>
|
||||
)
|
||||
}, (prev, next) => prev.src === next.src)
|
||||
|
||||
Image.propTypes = {
|
||||
src: PropTypes.string,
|
||||
withSources: PropTypes.bool,
|
||||
imgProps: PropTypes.shape({
|
||||
decoding: PropTypes.oneOf(['sync', 'async', 'auto']),
|
||||
draggable: PropTypes.bool,
|
||||
loading: PropTypes.oneOf(['eager', 'lazy'])
|
||||
})
|
||||
}
|
||||
|
||||
Image.defaultProps = {
|
||||
src: undefined,
|
||||
withSources: false,
|
||||
imgProps: {
|
||||
decoding: 'async',
|
||||
draggable: false,
|
||||
loading: 'lazy'
|
||||
}
|
||||
}
|
||||
|
||||
Image.displayName = 'Image'
|
||||
|
||||
export default Image
|
Loading…
Reference in New Issue
Block a user