Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

enhancement: add error toast if media doesnt load #5436

Merged
Merged
Expand Up @@ -29,9 +29,12 @@
Pane,
Text,
TextType,
Alert,
} from 'shared/components'

let modal: Modal
let error: string
let warning: string

const explorerUrl = getOfficialExplorerUrl($activeProfile?.networkProtocol, $activeProfile?.networkType)
const nft: INft = getNftByIdFromAllAccountNfts($selectedAccountIndex, $selectedNftId)
Expand Down Expand Up @@ -122,12 +125,21 @@
<div class="relative w-full h-full flex rounded-2xl overflow-hidden">
<NftMedia
nftId={id}
bind:error
bind:warning
classes="rounded-2xl overflow-hidden flex-1 w-auto h-auto max-w-full max-h-full object-contain absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
autoplay
controls
loop
muted
/>
<div class="absolute right-6 bottom-6 w-auto">
{#if error}
<Alert type="error" message={error} />
{:else if warning}
<Alert type="warning" message={warning} />
{/if}
</div>
</div>
</div>
<Pane classes="flex flex-col p-6 space-y-3 w-full h-full max-w-lg">
Expand Down
26 changes: 15 additions & 11 deletions packages/shared/components/atoms/MediaDisplay.svelte
@@ -1,23 +1,24 @@
<script lang="typescript">
import { MimeType, ParentMimeType } from '@core/nfts'

export let Media: HTMLImageElement | HTMLVideoElement
export let Media: HTMLImageElement | HTMLVideoElement = undefined
export let src: string
export let expectedType: MimeType
export let classes: string = ''
export let alt = ''
export let onError: () => unknown
export let onError: (a?: string) => unknown
export let onWarning: (a?: string) => unknown
export let onLoad: () => unknown
export let autoplay: boolean = false
export let controls: boolean = false
export let muted: boolean = false
export let loop: boolean = false

let type: string
const type: string = convertMimeTypeToHtmlTag(expectedType)
let safeToLoad = false
let isLoaded = false
const MAX_FILE_SIZE_IN_MB = 30000000

$: type = convertMimeTypeToHtmlTag(expectedType)
$: src && void checkContentIsSafeToLoad()
$: isLoaded && muteVideo()

Expand Down Expand Up @@ -47,7 +48,7 @@
case ParentMimeType.Video:
return 'video'
default:
onError()
onWarning('error.nft.unsupportedFileType.')
return undefined
}
}
Expand All @@ -66,18 +67,21 @@

async function checkContentIsSafeToLoad() {
try {
if (src && typeof src === 'string') {
if (type && src && typeof src === 'string') {
const response = await fetch(src, { method: 'HEAD', cache: 'force-cache' })
if (response.headers.get('Content-Type') === expectedType) {
safeToLoad = true
} else {
if (response.headers.get('Content-Type') !== expectedType) {
safeToLoad = false
onError('error.nft.notMatchingFileTypes.')
} else if (Number(response.headers.get('Content-Length')) > MAX_FILE_SIZE_IN_MB) {
safeToLoad = false
onError()
onWarning('error.nft.tooLargeFile.')
} else {
safeToLoad = true
}
}
} catch (error) {
safeToLoad = false
onError()
onError('error.nft.unsafeToLoad.')
}
}
</script>
Expand Down
31 changes: 14 additions & 17 deletions packages/shared/components/atoms/buttons/TooltipIcon.svelte
@@ -1,7 +1,6 @@
<script lang="typescript">
import { Icon, Text, Tooltip, FontWeight, TextType } from 'shared/components'
import { Icon, Text, Tooltip, FontWeight, TextType, Position } from 'shared/components'
import { Icon as IconEnum } from '@lib/auxiliary/icon'
import { Position } from 'shared/components/enums'

export let title: string = ''
export let text: string = ''
Expand All @@ -11,6 +10,8 @@
export let classes: string = ''
export let iconClasses: string = ''
export let position: Position = Position.Right
export let primaryColor: string = undefined
export let secondaryColor: string = undefined

let tooltipAnchor: HTMLElement
let isTooltipVisible = false
Expand All @@ -27,24 +28,20 @@
bind:this={tooltipAnchor}
class="text-gray-600"
>
<Icon {width} {height} {icon} classes={iconClasses} />
<Icon {width} {height} {icon} classes={iconClasses} {primaryColor} {secondaryColor} />
</icon-container>
{#if isTooltipVisible}
<Tooltip anchor={tooltipAnchor} {position} {...$$restProps}>
{#if title}
<Text
bigger
type={TextType.h5}
fontWeight={FontWeight.medium}
classes="text-left mb-2"
color="gray-900"
>
{title}
</Text>
{/if}
{#if text}
<Text smaller classes="text-left" color="gray-700" lineHeight="leading-140">{text}</Text>
{/if}
<div class="flex flex-col space-y-2">
{#if title}
<Text bigger type={TextType.h5} fontWeight={FontWeight.medium} classes="text-left" color="gray-900">
{title}
</Text>
{/if}
{#if text}
<Text smaller classes="text-left" color="gray-700" lineHeight="leading-140">{text}</Text>
{/if}
</div>
<slot />
</Tooltip>
{/if}
Expand Down
38 changes: 36 additions & 2 deletions packages/shared/components/molecules/NftGalleryItem.svelte
@@ -1,32 +1,66 @@
<script lang="typescript">
import { INft } from '@core/nfts'
import { Text, FontWeight, NftMedia } from 'shared/components'
import { Text, FontWeight, NftMedia, TooltipIcon, Position } from 'shared/components'
import { CollectiblesRoute, collectiblesRouter } from '@core/router'
import { selectedNftId } from '@core/nfts/stores'

export let nft: INft

let nftWrapperClientWidth: number
let error: string
let warning: string

function openCollectiblesDetailsView(): void {
$selectedNftId = nft.id
$collectiblesRouter.goTo(CollectiblesRoute.Details)
}

let tooltipContent
$: if (error) {
tooltipContent = {
icon: 'error-filled',
iconClasses: 'fill-current text-red-700',
text: error,
}
} else if (warning) {
tooltipContent = {
icon: 'exclamation-filled',
iconClasses: 'fill-current text-yellow-700',
text: warning,
}
}
</script>

<button on:click={openCollectiblesDetailsView} class="flex flex-col items-center justify-center">
<div class="w-full rounded-2xl overflow-hidden flex flex-col shadow-elevation-1">
<div
class="w-full overflow-hidden flex"
class="w-full flex relative"
bind:clientWidth={nftWrapperClientWidth}
style="height: {nftWrapperClientWidth}px; "
>
<NftMedia
nftId={nft.id}
bind:error
bind:warning
classes="bg-gray-200 dark:bg-gray-700 min-w-full min-h-full object-cover"
translationSuffix="short"
loop
muted
/>
{#if error || warning}
<div class="absolute right-3 top-3">
<TooltipIcon
height={24}
width={24}
icon={tooltipContent.icon}
iconClasses={tooltipContent.iconClasses}
text={tooltipContent.text}
size="small"
primaryColor="white"
position={Position.Left}
/>
</div>
{/if}
</div>
<div class="w-full flex flex-col justify-center p-3.5 bg-white dark:bg-gray-800">
<Text fontWeight={FontWeight.semibold} fontSize="12" classes="text-left truncate">{nft.name}</Text>
Expand Down
15 changes: 14 additions & 1 deletion packages/shared/components/molecules/NftMedia.svelte
@@ -1,5 +1,6 @@
<script lang="typescript">
import { selectedAccountIndex } from '@core/account'
import { localize } from '@core/i18n'
import { getNftByIdFromAllAccountNfts, rewriteIpfsUri } from '@core/nfts'
import { MediaDisplay } from 'shared/components'
import MediaPlaceholder from './MediaPlaceholder.svelte'
Expand All @@ -10,6 +11,9 @@
export let loop: boolean = false
export let muted: boolean = false
export let classes: string = ''
export let error: string = ''
export let warning: string = ''
export let translationSuffix: string = 'long'

const bgColor = 'gray-200'
const darkBgColor = 'gray-700'
Expand Down Expand Up @@ -46,6 +50,7 @@
newUrl = rewriteIpfsUri(targetUrl)
break
default:
error = localize('error.nft.unsupportedUrl.' + translationSuffix)
return undefined
}

Expand All @@ -56,8 +61,14 @@
}
}

function handleLoadingError(): void {
function handleLoadingError(err): void {
hasError = true
error = localize(err + translationSuffix)
}

function handleWarning(warn): void {
hasError = true
warning = localize(warn + translationSuffix)
}

function handleOnLoad(): void {
Expand All @@ -77,6 +88,7 @@
classes="hidden {classes}"
onLoad={handleOnLoad}
onError={handleLoadingError}
onWarning={handleWarning}
/>

{#if !isLoaded}
Expand All @@ -94,6 +106,7 @@
{muted}
{classes}
onError={handleLoadingError}
onWarning={handleWarning}
/>
{/if}
{/if}
30 changes: 30 additions & 0 deletions packages/shared/locales/en.json
Expand Up @@ -1809,6 +1809,36 @@
"eventId": {
"doesNotStartWith0x": "Event ID should start with '0x'",
"insufficientLength": "Event ID should be 66 characters long"
},
"nft": {
"unsupportedUrl": {
"short": "Url not supported",
"long": "Url schema not supported"
},
"unsupportedFileType": {
"short": "Unsupported media type",
"long": "The media type is not currently supported"
},
"notMatchingFileTypes": {
"short": "Loading blocked",
"long": "NFT file type does not match the expected media type"
},
"tooLargeFile": {
"short": "File is too large",
"long": "Loading NFT blocked because the file is too large"
},
"corsError": {
"short": "Loading blocked",
"long": "Loading NFT blocked by CORS policy"
},
"unsafeToLoad": {
"short": "Unsafe to load",
"long": "An error occurred while checking if the NFT was safe to load"
},
"loadingError": {
"short": "Unable to load",
"long": "An error occurred while loading the NFT"
}
}
},
"warning": {
Expand Down