Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,8 @@
"Enjin",
"poap",
"irss",
"CELO"
"CELO",
"flac"
],
"ignoreRegExpList": ["/@servie/"],
"overrides": [
Expand Down
13 changes: 11 additions & 2 deletions packages/icons/brands/MaskGrey.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createIcon } from '../utils'
import { createPaletteAwareIcon } from '../utils'
import type { SvgIcon } from '@mui/material'

export const MaskGreyIcon: typeof SvgIcon = createIcon(
export const MaskGreyIcon: typeof SvgIcon = createPaletteAwareIcon(
'Mask',
<g>
<path fill="#8796AF" d="M60 120A60 60 0 1060 0a60 60 0 000 120z" />
Expand All @@ -12,6 +12,15 @@ export const MaskGreyIcon: typeof SvgIcon = createIcon(
clipRule="evenodd"
/>
</g>,
<g>
<path d="M18 36c9.941 0 18-8.059 18-18S27.941 0 18 0 0 8.059 0 18s8.059 18 18 18Z" fill="#2F3336" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M28.864 13.575v5.978H9.86c.83 3.627 4.123 6.336 8.058 6.336a8.273 8.273 0 0 0 7.415-4.543h3.531v5.021c0 1.189-.979 2.152-2.186 2.152H9.186C7.98 28.52 7 27.556 7 26.367V13.575h21.864Zm-5.628 7.771a6.46 6.46 0 0 1-5.318 2.764 6.46 6.46 0 0 1-5.317-2.764h10.635Zm-9.98-6.336c-1.714 0-3.131 1.247-3.367 2.87h1.864c.205-.625.8-1.077 1.503-1.077.702 0 1.298.452 1.503 1.076h1.863c-.235-1.622-1.653-2.869-3.366-2.869Zm9.353 0c-1.714 0-3.131 1.247-3.367 2.87h1.864c.205-.625.8-1.077 1.503-1.077.702 0 1.298.452 1.503 1.076h1.863c-.235-1.622-1.653-2.869-3.366-2.869ZM26.678 7c1.207 0 2.186.963 2.186 2.152v2.63H7v-2.63C7 7.963 7.979 7 9.186 7h17.492Z"
fill="#15171A"
/>
</g>,
'0 0 120 120',
[130, 40],
)
16 changes: 16 additions & 0 deletions packages/icons/general/AssetLoading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { createIcon } from '../utils'

export const AssetLoadingIcon = createIcon(
'AssetLoadingIcon',
<g fill="none">
<path d="M18 36c9.941 0 18-8.059 18-18S27.941 0 18 0 0 8.059 0 18s8.059 18 18 18Z" fill="#1C68F3" />
<path
fillRule="evenodd"
clipRule="evenodd"
d="M28.864 13.575v5.978H9.86c.83 3.627 4.123 6.336 8.058 6.336a8.273 8.273 0 0 0 7.415-4.543h3.531v5.021c0 1.189-.979 2.152-2.186 2.152H9.186C7.98 28.52 7 27.556 7 26.367V13.575h21.864Zm-5.628 7.771a6.46 6.46 0 0 1-5.318 2.764 6.46 6.46 0 0 1-5.317-2.764h10.635Zm-9.98-6.336c-1.714 0-3.131 1.247-3.367 2.87h1.864c.205-.625.8-1.077 1.503-1.077.702 0 1.298.452 1.503 1.076h1.863c-.235-1.622-1.653-2.869-3.366-2.869Zm9.353 0c-1.714 0-3.131 1.247-3.367 2.87h1.864c.205-.625.8-1.077 1.503-1.077.702 0 1.298.452 1.503 1.076h1.863c-.235-1.622-1.653-2.869-3.366-2.869ZM26.678 7c1.207 0 2.186.963 2.186 2.152v2.63H7v-2.63C7 7.963 7.979 7 9.186 7h17.492Z"
fill="#fff"
/>
<ellipse cx="18" cy="48" rx="11.5" ry="4" fill="#003EAE" />
</g>,
'0 0 36 52',
)
1 change: 1 addition & 0 deletions packages/icons/general/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,4 @@ export * from './Retweet'
export * from './Drop'
export * from './Right'
export * from './Tutorial'
export * from './AssetLoading'
63 changes: 28 additions & 35 deletions packages/mask/src/plugins/Collectible/SNSAdaptor/ArticleTab.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import type { FC } from 'react'
import { Link } from '@mui/material'
import { makeStyles } from '@masknet/theme'
import { Video } from '../../../components/shared/Video'
import { CollectibleTab } from './CollectibleTab'
import { CollectibleState } from '../hooks/useCollectibleState'
import { AssetPlayer } from '@masknet/shared'
import { useMemo } from 'react'

const useStyles = makeStyles()({
body: {
Expand All @@ -15,44 +14,38 @@ const useStyles = makeStyles()({
maxHeight: '100%',
border: 'none',
},
errorPlaceholder: {
padding: '82px 0',
},
loadingPlaceholder: {
padding: '74px 0',
},
})

interface AssetPlayerProps {
src?: string
alt: string
}

// opensea supports: JPG, PNG, GIF, SVG, MP4, WEBM, MP3, WAV, OGG, GLB, GLTF.
const AssetPlayer: FC<AssetPlayerProps> = ({ src, alt }) => {
const { classes } = useStyles()
if (!src) return null
const isVideo = src.match(/\.(mp4|webm)$/i)
return isVideo ? (
<Video src={src} VideoProps={{ className: classes.player }} />
) : (
<img className={classes.player} src={src} alt={alt} />
)
}

export interface ArticleTabProps {}

export function ArticleTab(props: ArticleTabProps) {
const { classes } = useStyles()
const { asset } = CollectibleState.useContainer()

if (!asset.value) return null
const resourceUrl = asset.value.image_url || asset.value.animation_url
return (
<CollectibleTab>
<div className={classes.body}>
{asset.value.animation_url ? (
<Link href={asset.value.animation_url} target="_blank" rel="noopener noreferrer">
<AssetPlayer src={resourceUrl} alt={asset.value?.name ?? ''} />
</Link>
) : (
<AssetPlayer src={resourceUrl} alt={asset.value?.name ?? ''} />
)}
</div>
</CollectibleTab>
)
return useMemo(() => {
Comment thread
nuanyang233 marked this conversation as resolved.
if (!asset.value) return null
const resourceUrl = asset.value.animation_url || asset.value.image_url
return (
<CollectibleTab>
<div className={classes.body}>
<AssetPlayer
url={resourceUrl}
options={{
playsInline: true,
}}
classes={{
errorPlaceholder: classes.errorPlaceholder,
loadingPlaceholder: classes.loadingPlaceholder,
}}
/>
</div>
</CollectibleTab>
)
}, [asset.value?.animation_url, asset.value?.image_url, classes])
}
3 changes: 3 additions & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@
"anchorme": "^2.1.2",
"bignumber.js": "^9.0.2",
"classnames": "^2.3.1",
"iframe-resizer-react": "^1.1.0",
"lodash-es": "^4.17.21",
"qrcode": "^1.5.0",
"react-feather": "^2.0.9",
"react-use": "^17.3.1",
"urlcat": "^2.0.4",
"use-subscription": "^1.5.1",
"uuid": "^8.3.2"
}
Expand Down
136 changes: 136 additions & 0 deletions packages/shared/src/UI/components/AssetPlayer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import { memo, useRef, useCallback, useState } from 'react'
import IframeResizer, { IFrameComponent } from 'iframe-resizer-react'
import { mediaViewerUrl } from '../../../constants'
import { useTimeoutFn, useUpdateEffect } from 'react-use'
import { makeStyles, useStylesExtends } from '@masknet/theme'
import { Box, SvgIconProps } from '@mui/material'
import { AssetLoadingIcon, MaskGreyIcon } from '@masknet/icons'

interface AssetPlayerProps
extends withClasses<'errorPlaceholder' | 'errorIcon' | 'loadingPlaceholder' | 'loadingIcon'> {
url?: string
type?: string
options?: {
autoPlay?: boolean
controls?: boolean
playsInline?: boolean
loop?: boolean
muted?: boolean
}
iconProps?: SvgIconProps
}
const useStyles = makeStyles()((theme) => ({
errorPlaceholder: {
backgroundColor: theme.palette.background.default,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
borderRadius: 12,
width: '100%',
},
errorIcon: {
width: 36,
height: 36,
},
loadingPlaceholder: {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
},
loadingIcon: {
width: 36,
height: 52,
},
}))

enum AssetPlayerState {
LOADING = 0,
INIT = 1,
NORMAL = 2,
ERROR = 3,
}

const TIMEOUT = 10000

export const AssetPlayer = memo<AssetPlayerProps>(({ url, type, options, iconProps, ...props }) => {
const ref = useRef<IFrameComponent | null>(null)
const classes = useStylesExtends(useStyles(), props)
const [playerState, setPlayerState] = useState(url ? AssetPlayerState.LOADING : AssetPlayerState.ERROR)

//#region If onResized is not triggered within the specified time, set player state to error
const [, cancel, reset] = useTimeoutFn(() => {
if (playerState !== AssetPlayerState.NORMAL) {
setPlayerState(AssetPlayerState.ERROR)
}
}, TIMEOUT)
//#endregion

//#region setup iframe when url and options be changed
const setIframe = useCallback(() => {
// if iframe isn't be init or the load error has been existed
if (!ref.current || playerState === AssetPlayerState.ERROR) return
if (!url) {
setPlayerState(AssetPlayerState.ERROR)
return
}
if (playerState === AssetPlayerState.INIT || playerState === AssetPlayerState.NORMAL) {
reset()
ref.current.iFrameResizer.sendMessage({
url,
type,
...options,
})
return
}
}, [url, type, options, playerState])
//endregion

//#region resource loaded error
const onMessage = useCallback(({ message }: { message: { name: string } }) => {
if (message?.name === 'Error') {
setPlayerState(AssetPlayerState.ERROR)
}
}, [])
//#endregion

useUpdateEffect(() => {
setIframe()
}, [setIframe])

return (
<>
<Box
className={
playerState === AssetPlayerState.ERROR ? classes.errorPlaceholder : classes.loadingPlaceholder
}
style={{ display: playerState === AssetPlayerState.NORMAL ? 'none' : undefined }}>
{playerState === AssetPlayerState.ERROR ? (
<MaskGreyIcon className={classes.errorIcon} viewBox="0 0 36 36" {...iconProps} />
) : (
<AssetLoadingIcon className={classes.loadingIcon} />
)}
</Box>
<IframeResizer
src={mediaViewerUrl}
onInit={(iframe: IFrameComponent) => {
ref.current = iframe
setPlayerState(AssetPlayerState.INIT)
setIframe()
}}
onResized={({ type }) => {
if (type !== 'init') {
cancel()
setPlayerState(AssetPlayerState.NORMAL)
}
}}
style={{ width: playerState !== AssetPlayerState.NORMAL ? 0 : undefined }}
checkOrigin={false}
onMessage={onMessage}
frameBorder="0"
allow="autoplay"
allowFullScreen
/>
</>
)
})
1 change: 1 addition & 0 deletions packages/shared/src/UI/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from './LoadingAnimation'
export * from './MiniNetworkSelector'
export * from './AddressViewer'
export * from './I18NextProviderHMR'
export * from './AssetPlayer'
2 changes: 2 additions & 0 deletions packages/shared/src/constants.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export const SOCIAL_MEDIA_ICON_MAPPING: Record<string, ReactNode> = {
'minds.com': <MindsIcon />,
'instagram.com': <InstagramColoredIcon />,
}

export const mediaViewerUrl = 'https://dimensiondev.github.io/Media-Viewer/index.html'
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.