Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
F
frontend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
vicotor
frontend
Commits
0d3890df
Commit
0d3890df
authored
Mar 05, 2024
by
tom
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add fallback for NFT animation media
parent
ac9bcfe9
Changes
9
Show whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
183 additions
and
19 deletions
+183
-19
media-type.ts
pages/api/media-type.ts
+4
-2
NFTItem.tsx
ui/address/tokens/NFTItem.tsx
+2
-1
NftMedia.pw.tsx
ui/shared/nft/NftMedia.pw.tsx
+52
-5
NftMedia.tsx
ui/shared/nft/NftMedia.tsx
+23
-9
NftMedia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png
...ia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png
+0
-0
NftMedia.pw.tsx_default_no-url-with-fallback-1.png
...hots__/NftMedia.pw.tsx_default_no-url-with-fallback-1.png
+0
-0
useNftMediaInfo.tsx
ui/shared/nft/useNftMediaInfo.tsx
+98
-0
TokenInventoryItem.tsx
ui/token/TokenInventoryItem.tsx
+2
-1
TokenInstanceDetails.tsx
ui/tokenInstance/TokenInstanceDetails.tsx
+2
-1
No files found.
pages/api/media-type.ts
View file @
0d3890df
...
...
@@ -22,11 +22,13 @@ export default async function mediaTypeHandler(req: NextApiRequest, res: NextApi
return
'
video
'
;
}
if
(
contentType
?.
startsWith
(
'
image
'
))
{
return
'
image
'
;
}
if
(
contentType
?.
startsWith
(
'
text/html
'
))
{
return
'
html
'
;
}
return
'
image
'
;
})();
res
.
status
(
200
).
json
({
type
:
mediaType
});
}
catch
(
error
)
{
...
...
ui/address/tokens/NFTItem.tsx
View file @
0d3890df
...
...
@@ -26,7 +26,8 @@ const NFTItem = ({ token, value, isLoading, withTokenLink, ...tokenInstance }: P
<
Link
href=
{
isLoading
?
undefined
:
tokenInstanceLink
}
>
<
NftMedia
mb=
"18px"
url=
{
tokenInstance
?.
animation_url
||
tokenInstance
?.
image_url
||
null
}
animationUrl=
{
tokenInstance
?.
animation_url
??
null
}
imageUrl=
{
tokenInstance
?.
image_url
??
null
}
isLoading=
{
isLoading
}
/>
</
Link
>
...
...
ui/shared/nft/NftMedia.pw.tsx
View file @
0d3890df
...
...
@@ -10,7 +10,54 @@ test.describe('no url', () => {
test
(
'
preview +@dark-mode
'
,
async
({
mount
})
=>
{
const
component
=
await
mount
(
<
TestApp
>
<
NftMedia
url=
{
null
}
/>
<
NftMedia
animationUrl=
{
null
}
imageUrl=
{
null
}
/>
</
TestApp
>,
);
await
expect
(
component
).
toHaveScreenshot
();
});
test
(
'
with fallback
'
,
async
({
mount
,
page
})
=>
{
const
IMAGE_URL
=
'
https://localhost:3000/my-image.jpg
'
;
await
page
.
route
(
IMAGE_URL
,
(
route
)
=>
{
return
route
.
fulfill
({
status
:
200
,
path
:
'
./playwright/mocks/image_long.jpg
'
,
});
});
const
component
=
await
mount
(
<
TestApp
>
<
NftMedia
animationUrl=
{
null
}
imageUrl=
{
IMAGE_URL
}
/>
</
TestApp
>,
);
await
expect
(
component
).
toHaveScreenshot
();
});
test
(
'
non-media url and fallback
'
,
async
({
mount
,
page
})
=>
{
const
ANIMATION_URL
=
'
https://localhost:3000/my-animation.m3u8
'
;
const
ANIMATION_MEDIA_TYPE_API_URL
=
`/node-api/media-type?url=
${
encodeURIComponent
(
ANIMATION_URL
)
}
`
;
const
IMAGE_URL
=
'
https://localhost:3000/my-image.jpg
'
;
await
page
.
route
(
ANIMATION_MEDIA_TYPE_API_URL
,
(
route
)
=>
{
return
route
.
fulfill
({
status
:
200
,
body
:
JSON
.
stringify
({
type
:
undefined
}),
});
});
await
page
.
route
(
IMAGE_URL
,
(
route
)
=>
{
return
route
.
fulfill
({
status
:
200
,
path
:
'
./playwright/mocks/image_long.jpg
'
,
});
});
const
component
=
await
mount
(
<
TestApp
>
<
NftMedia
animationUrl=
{
ANIMATION_URL
}
imageUrl=
{
IMAGE_URL
}
/>
</
TestApp
>,
);
...
...
@@ -35,7 +82,7 @@ test.describe('image', () => {
test
(
'
preview +@dark-mode
'
,
async
({
mount
})
=>
{
const
component
=
await
mount
(
<
TestApp
>
<
NftMedia
url=
{
MEDIA_URL
}
/>
<
NftMedia
animationUrl=
{
MEDIA_URL
}
imageUrl=
{
null
}
/>
</
TestApp
>,
);
...
...
@@ -55,7 +102,7 @@ test('image preview hover', async({ mount, page }) => {
const
component
=
await
mount
(
<
TestApp
>
<
NftMedia
url=
{
MEDIA_URL
}
w=
"250px"
/>
<
NftMedia
animationUrl=
{
MEDIA_URL
}
imageUrl=
{
null
}
w=
"250px"
/>
</
TestApp
>,
);
...
...
@@ -75,7 +122,7 @@ test('image fullscreen +@dark-mode +@mobile', async({ mount, page }) => {
});
const
component
=
await
mount
(
<
TestApp
>
<
NftMedia
url=
{
MEDIA_URL
}
withFullscreen
w=
"250px"
/>
<
NftMedia
animationUrl=
{
MEDIA_URL
}
imageUrl=
{
null
}
withFullscreen
w=
"250px"
/>
</
TestApp
>,
);
...
...
@@ -107,7 +154,7 @@ test.describe('page', () => {
test
(
'
preview +@dark-mode
'
,
async
({
mount
})
=>
{
const
component
=
await
mount
(
<
TestApp
>
<
NftMedia
url=
{
MEDIA_URL
}
/>
<
NftMedia
animationUrl=
{
MEDIA_URL
}
imageUrl=
{
null
}
/>
</
TestApp
>,
);
...
...
ui/shared/nft/NftMedia.tsx
View file @
0d3890df
...
...
@@ -9,29 +9,31 @@ import NftImage from './NftImage';
import
NftImageFullscreen
from
'
./NftImageFullscreen
'
;
import
NftVideo
from
'
./NftVideo
'
;
import
NftVideoFullscreen
from
'
./NftVideoFullscreen
'
;
import
useNftMedia
Type
from
'
./useNftMediaType
'
;
import
useNftMedia
Info
from
'
./useNftMediaInfo
'
;
import
{
mediaStyleProps
}
from
'
./utils
'
;
interface
Props
{
url
:
string
|
null
;
imageUrl
:
string
|
null
;
animationUrl
:
string
|
null
;
className
?:
string
;
isLoading
?:
boolean
;
withFullscreen
?:
boolean
;
}
const
NftMedia
=
({
u
rl
,
className
,
isLoading
,
withFullscreen
}:
Props
)
=>
{
const
NftMedia
=
({
imageUrl
,
animationU
rl
,
className
,
isLoading
,
withFullscreen
}:
Props
)
=>
{
const
[
isMediaLoading
,
setIsMediaLoading
]
=
React
.
useState
(
true
);
const
[
isLoadingError
,
setIsLoadingError
]
=
React
.
useState
(
false
);
const
{
ref
,
inView
}
=
useInView
({
triggerOnce
:
true
});
const
type
=
useNftMediaType
(
url
,
!
isLoading
&&
inView
);
const
mediaInfo
=
useNftMediaInfo
({
imageUrl
,
animationUrl
,
isEnabled
:
!
isLoading
&&
inView
}
);
React
.
useEffect
(()
=>
{
if
(
!
isLoading
)
{
setIsMediaLoading
(
Boolean
(
url
));
if
(
!
isLoading
&&
!
mediaInfo
)
{
setIsMediaLoading
(
false
);
setIsLoadingError
(
true
);
}
},
[
isLoading
,
url
]);
},
[
isLoading
,
mediaInfo
]);
const
handleMediaLoaded
=
React
.
useCallback
(()
=>
{
setIsMediaLoading
(
false
);
...
...
@@ -45,11 +47,17 @@ const NftMedia = ({ url, className, isLoading, withFullscreen }: Props) => {
const
{
isOpen
,
onOpen
,
onClose
}
=
useDisclosure
();
const
content
=
(()
=>
{
if
(
!
url
||
isLoadingError
)
{
if
(
!
mediaInfo
||
isLoadingError
)
{
const
styleProps
=
withFullscreen
?
{}
:
mediaStyleProps
;
return
<
NftFallback
{
...
styleProps
}
/>;
}
const
{
type
,
url
}
=
mediaInfo
;
if
(
!
url
)
{
return
null
;
}
const
props
=
{
src
:
url
,
onLoad
:
handleMediaLoaded
,
...
...
@@ -70,7 +78,13 @@ const NftMedia = ({ url, className, isLoading, withFullscreen }: Props) => {
})();
const
modal
=
(()
=>
{
if
(
!
url
||
!
withFullscreen
)
{
if
(
!
mediaInfo
||
!
withFullscreen
)
{
return
null
;
}
const
{
type
,
url
}
=
mediaInfo
;
if
(
!
url
)
{
return
null
;
}
...
...
ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-non-media-url-and-fallback-1.png
0 → 100644
View file @
0d3890df
21 KB
ui/shared/nft/__screenshots__/NftMedia.pw.tsx_default_no-url-with-fallback-1.png
0 → 100644
View file @
0d3890df
21 KB
ui/shared/nft/useNftMedia
Type
.tsx
→
ui/shared/nft/useNftMedia
Info
.tsx
View file @
0d3890df
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
React
from
'
react
'
;
import
type
{
StaticRoute
}
from
'
nextjs-routes
'
;
import
{
route
}
from
'
nextjs-routes
'
;
...
...
@@ -9,15 +10,57 @@ import useFetch from 'lib/hooks/useFetch';
import
type
{
MediaType
}
from
'
./utils
'
;
import
{
getPreliminaryMediaType
}
from
'
./utils
'
;
export
default
function
useNftMediaType
(
url
:
string
|
null
,
isEnabled
:
boolean
)
{
interface
Params
{
imageUrl
:
string
|
null
;
animationUrl
:
string
|
null
;
isEnabled
:
boolean
;
}
interface
ReturnType
{
type
:
MediaType
|
undefined
;
url
:
string
|
null
;
}
export
default
function
useNftMediaInfo
({
imageUrl
,
animationUrl
,
isEnabled
}:
Params
):
ReturnType
|
null
{
const
primaryQuery
=
useNftMediaTypeQuery
(
animationUrl
,
isEnabled
);
const
secondaryQuery
=
useNftMediaTypeQuery
(
imageUrl
,
!
primaryQuery
.
isPending
&&
!
primaryQuery
.
data
);
return
React
.
useMemo
(()
=>
{
if
(
primaryQuery
.
isPending
)
{
return
{
type
:
undefined
,
url
:
animationUrl
,
};
}
if
(
primaryQuery
.
data
)
{
return
primaryQuery
.
data
;
}
if
(
secondaryQuery
.
isPending
)
{
return
{
type
:
undefined
,
url
:
imageUrl
,
};
}
if
(
secondaryQuery
.
data
)
{
return
secondaryQuery
.
data
;
}
return
null
;
},
[
animationUrl
,
imageUrl
,
primaryQuery
.
data
,
primaryQuery
.
isPending
,
secondaryQuery
.
data
,
secondaryQuery
.
isPending
]);
}
function
useNftMediaTypeQuery
(
url
:
string
|
null
,
enabled
:
boolean
)
{
const
fetch
=
useFetch
();
const
{
data
}
=
useQuery
<
unknown
,
ResourceError
<
unknown
>
,
MediaType
>
({
return
useQuery
<
unknown
,
ResourceError
<
unknown
>
,
ReturnType
|
null
>
({
queryKey
:
[
'
nft-media-type
'
,
url
],
queryFn
:
async
()
=>
{
if
(
!
url
)
{
return
'
image
'
;
return
null
;
}
// media could be either image, gif, video or html-page
...
...
@@ -29,21 +72,27 @@ export default function useNftMediaType(url: string | null, isEnabled: boolean)
const
preliminaryType
=
getPreliminaryMediaType
(
url
);
if
(
preliminaryType
)
{
return
preliminaryType
;
return
{
type
:
preliminaryType
,
url
}
;
}
const
type
=
await
(
async
()
=>
{
try
{
const
mediaTypeResourceUrl
=
route
({
pathname
:
'
/node-api/media-type
'
as
StaticRoute
<
'
/api/media-type
'
>
[
'
pathname
'
],
query
:
{
url
}
});
const
response
=
await
fetch
<
{
type
:
MediaType
|
undefined
},
ResourceError
>
(
mediaTypeResourceUrl
,
undefined
,
{
resource
:
'
media-type
'
});
return
'
type
'
in
response
?
response
.
type
??
'
image
'
:
'
image
'
;
return
'
type
'
in
response
?
response
.
type
:
undefined
;
}
catch
(
error
)
{
return
'
image
'
;
return
;
}
})();
if
(
!
type
)
{
return
null
;
}
return
{
type
,
url
};
},
enabled
:
isEnabled
&&
Boolean
(
url
)
,
enabled
,
staleTime
:
Infinity
,
});
return
data
;
}
ui/token/TokenInventoryItem.tsx
View file @
0d3890df
...
...
@@ -20,7 +20,8 @@ const TokenInventoryItem = ({ item, token, isLoading }: Props) => {
const
mediaElement
=
(
<
NftMedia
mb=
"18px"
url=
{
item
.
animation_url
||
item
.
image_url
}
animationUrl=
{
item
.
animation_url
}
imageUrl=
{
item
.
image_url
}
isLoading=
{
isLoading
}
/>
);
...
...
ui/tokenInstance/TokenInstanceDetails.tsx
View file @
0d3890df
...
...
@@ -74,7 +74,8 @@ const TokenInstanceDetails = ({ data, token, scrollRef, isLoading }: Props) => {
<
TokenNftMarketplaces
isLoading=
{
isLoading
}
hash=
{
token
.
address
}
id=
{
data
.
id
}
/>
</
Grid
>
<
NftMedia
url=
{
data
.
animation_url
||
data
.
image_url
}
animationUrl=
{
data
.
animation_url
}
imageUrl=
{
data
.
image_url
}
w=
"250px"
flexShrink=
{
0
}
alignSelf=
{
{
base
:
'
center
'
,
lg
:
'
flex-start
'
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment