-
Notifications
You must be signed in to change notification settings - Fork 315
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6784 from LedgerHQ/refactor/medias_nft_gallery
refactor(lld): nfts gallery medias in newArch
- Loading branch information
Showing
16 changed files
with
508 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"ledger-live-desktop": patch | ||
--- | ||
|
||
Moove and refactor medias from nft gallery to newArch. Create a FF to switch between old and new arch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
58 changes: 58 additions & 0 deletions
58
apps/ledger-live-desktop/src/newArch/Collectibles/components/CollectionName.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import React, { memo } from "react"; | ||
import { useNftCollectionMetadata } from "@ledgerhq/live-nft-react"; | ||
import { Account, ProtoNFT } from "@ledgerhq/types-live"; | ||
import NFTCollectionContextMenu from "~/renderer/components/ContextMenu/NFTCollectionContextMenu"; | ||
import { Skeleton } from "LLD/Collectibles/components"; | ||
import styled from "styled-components"; | ||
import { IconsLegacy } from "@ledgerhq/react-ui"; | ||
|
||
const Dots = styled.div` | ||
justify-content: flex-end; | ||
display: flex; | ||
align-items: center; | ||
cursor: pointer; | ||
padding: 5px; | ||
color: ${p => p.theme.colors.palette.text.shade50}; | ||
&:hover { | ||
color: ${p => p.theme.colors.palette.text.shade80}; | ||
} | ||
`; | ||
const Container = styled.div` | ||
display: flex; | ||
column-gap: 10px; | ||
`; | ||
|
||
type Props = { | ||
nft?: ProtoNFT; | ||
fallback?: string; | ||
account?: Account; | ||
showHideMenu?: boolean; | ||
}; // TODO Make me pretty | ||
|
||
const CollectionNameComponent: React.FC<Props> = ({ nft, fallback, account, showHideMenu }) => { | ||
const { status, metadata } = useNftCollectionMetadata(nft?.contract, nft?.currencyId); | ||
const { tokenName } = metadata || {}; | ||
const loading = status === "loading"; | ||
const isComponentReady = account && showHideMenu && nft; | ||
|
||
return ( | ||
<Skeleton width={80} minHeight={24} barHeight={10} show={loading}> | ||
<Container> | ||
{tokenName || fallback || "-"} | ||
{isComponentReady && ( | ||
<NFTCollectionContextMenu | ||
collectionName={tokenName || fallback || "-"} | ||
collectionAddress={nft.contract || ""} | ||
account={account} | ||
leftClick={true} | ||
> | ||
<Dots> | ||
<IconsLegacy.OthersMedium size={20} /> | ||
</Dots> | ||
</NFTCollectionContextMenu> | ||
)} | ||
</Container> | ||
</Skeleton> | ||
); | ||
}; | ||
export const CollectionName = memo<Props>(CollectionNameComponent); |
Empty file.
97 changes: 97 additions & 0 deletions
97
apps/ledger-live-desktop/src/newArch/Collectibles/components/Media/Image.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
import React, { useState } from "react"; | ||
import { ImageProps } from "LLD/Collectibles/types/Media"; | ||
import styled from "styled-components"; | ||
import { Skeleton, Placeholder } from "LLD/Collectibles/components"; | ||
/** | ||
* Nb: This image component can be used for small listings, large gallery rendering, | ||
* and even tokens without an image where it will fallback to a generative image | ||
* based on the token metadata and some hue changes. | ||
* | ||
* The text in the fallback image is only visible if we are in `full` mode, since list | ||
* mode is not large enough for the text to be readable. | ||
*/ | ||
|
||
const Wrapper = styled.div<{ | ||
full?: ImageProps["full"]; | ||
size?: ImageProps["size"]; | ||
loaded: boolean; | ||
square: ImageProps["square"]; | ||
maxHeight?: ImageProps["maxHeight"]; | ||
objectFit?: ImageProps["objectFit"]; | ||
error?: boolean; | ||
}>` | ||
width: ${({ full, size }) => (full ? "100%" : `${size}px`)}; | ||
height: ${({ full }) => full && "100%"}; | ||
aspect-ratio: ${({ square }) => (square ? "1 / 1" : "initial")}; | ||
max-height: ${({ maxHeight }) => maxHeight && `${maxHeight}px`}; | ||
border-radius: 4px; | ||
overflow: hidden; | ||
background-size: contain; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
& > *:nth-child(1) { | ||
display: ${({ loaded, error }) => (loaded || error ? "none" : "block")}; | ||
} | ||
& > img { | ||
display: ${({ loaded, error }) => (loaded || error ? "block" : "none")}; | ||
${({ objectFit }) => | ||
objectFit === "cover" | ||
? `width: 100%; | ||
height: 100%;` | ||
: `max-width: 100%; | ||
max-height: 100%;`} | ||
object-fit: ${p => p.objectFit ?? "cover"}; | ||
border-radius: 4px; | ||
user-select: none; | ||
} | ||
`; | ||
|
||
const ImageComponent: React.FC<ImageProps> = ({ | ||
uri, | ||
metadata, | ||
full = false, | ||
size = 32, | ||
tokenId, | ||
maxHeight, | ||
onClick, | ||
square = true, | ||
objectFit = "cover", | ||
setUseFallback, | ||
isFallback, | ||
}) => { | ||
const [loaded, setLoaded] = useState(false); | ||
const [error, setError] = useState(false); | ||
const isImageReady = uri && !error; | ||
|
||
return ( | ||
<Wrapper | ||
full={full} | ||
size={size} | ||
loaded={loaded} | ||
error={error || !uri} | ||
square={square} | ||
maxHeight={maxHeight} | ||
objectFit={objectFit} | ||
> | ||
<Skeleton full /> | ||
{isImageReady ? ( | ||
<img | ||
// This prevent a bug where the change of props from isFallback | ||
// would not unbind onError and would not trigger it again in case of error | ||
key={isFallback?.toString()} | ||
onClick={onClick} | ||
onLoad={() => setLoaded(true)} | ||
onError={() => (isFallback ? setError(true) : setUseFallback(true))} | ||
src={uri} | ||
/> | ||
) : ( | ||
<Placeholder tokenId={tokenId} metadata={metadata} full={full} /> | ||
)} | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
export const Image = ImageComponent; |
35 changes: 35 additions & 0 deletions
35
apps/ledger-live-desktop/src/newArch/Collectibles/components/Media/Placeholder.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import React, { memo } from "react"; | ||
import { PlaceholderProps } from "LLD/Collectibles/types/Media"; | ||
import styled from "styled-components"; | ||
import { centerEllipsis } from "~/renderer/styles/helpers"; | ||
import Fallback from "~/renderer/images/nftFallback.jpg"; | ||
|
||
const randomHueForTokenId = (tokenId = "") => parseInt(tokenId.substr(-8)) % 360; | ||
|
||
const StyledPlaceholder = styled.div<{ | ||
tokenId?: PlaceholderProps["tokenId"]; | ||
metadata?: PlaceholderProps["metadata"]; | ||
}>` | ||
--hue: ${p => randomHueForTokenId(p.tokenId)}; | ||
background-image: url("${Fallback}"); | ||
background-size: contain; | ||
border-radius: 4px; | ||
width: 100%; | ||
@@ -27,7 +22,6 @@ const StyledPlaceholder = styled.div<{ tokenId?: string; full?: boolean; metadat | ||
aspect-ratio: 1; | ||
&:after { | ||
content: "${p => p?.metadata?.nftName || centerEllipsis(p?.tokenId || "-")}"; | ||
font-size: 16px; | ||
font-size: 1vw; | ||
@@ -42,10 +36,11 @@ const StyledPlaceholder = styled.div<{ tokenId?: string; full?: boolean; metadat | ||
height: 100%; | ||
} | ||
`; | ||
|
||
const PlaceholderComponent = memo<PlaceholderProps>(({ metadata, tokenId }) => ( | ||
<StyledPlaceholder metadata={metadata} tokenId={tokenId} /> | ||
)); | ||
|
||
PlaceholderComponent.displayName = "Placeholder"; | ||
|
||
export const Placeholder = PlaceholderComponent; |
84 changes: 84 additions & 0 deletions
84
apps/ledger-live-desktop/src/newArch/Collectibles/components/Media/Video.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
import React, { useState } from "react"; | ||
import { VideoProps } from "LLD/Collectibles/types/Media"; | ||
import { Skeleton } from "LLD/Collectibles/components"; | ||
import styled from "styled-components"; | ||
|
||
const Wrapper = styled.div<{ | ||
full?: VideoProps["full"]; | ||
size?: VideoProps["size"]; | ||
loaded: boolean; | ||
square: VideoProps["square"]; | ||
maxHeight?: VideoProps["maxHeight"]; | ||
objectFit?: VideoProps["objectFit"]; | ||
error?: boolean; | ||
}>` | ||
width: ${({ full, size }) => (full ? "100%" : `${size}px`)}; | ||
height: ${({ full }) => full && "100%"}; | ||
aspect-ratio: ${({ square, error }) => (square || error ? "1 / 1" : "initial")}; | ||
max-height: ${({ maxHeight }) => maxHeight && `${maxHeight}px`}; | ||
border-radius: 4px; | ||
overflow: hidden; | ||
background-size: contain; | ||
display: flex; | ||
align-items: center; | ||
justify-content: center; | ||
& > *:nth-child(1) { | ||
display: ${({ loaded, error }) => (loaded || error ? "none" : "block")}; | ||
} | ||
& > video { | ||
display: ${({ loaded, error }) => (loaded || error ? "block" : "none")}; | ||
${({ objectFit }) => | ||
objectFit === "cover" | ||
? `width: 100%; | ||
height: 100%;` | ||
: `max-width: 100%; | ||
max-height: 100%;`} | ||
object-fit: ${p => p.objectFit ?? "contain"}; | ||
border-radius: 4px; | ||
user-select: none; | ||
position: relative; | ||
z-index: 10; | ||
} | ||
`; | ||
|
||
const VideoComponent: React.FC<VideoProps> = ({ | ||
uri, | ||
mediaType, | ||
full = false, | ||
size = 32, | ||
maxHeight, | ||
square = true, | ||
objectFit = "contain", | ||
setUseFallback, | ||
}) => { | ||
const [loaded, setLoaded] = useState(false); | ||
|
||
return ( | ||
<Wrapper | ||
full={full} | ||
size={size} | ||
loaded={loaded} | ||
square={square} | ||
maxHeight={maxHeight} | ||
objectFit={objectFit} | ||
> | ||
<Skeleton full /> | ||
<video | ||
onError={() => { | ||
setUseFallback(true); | ||
}} | ||
onLoadedData={() => setLoaded(true)} | ||
autoPlay | ||
loop | ||
controls | ||
disablePictureInPicture | ||
> | ||
<source src={uri} type={mediaType} /> | ||
</video> | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
export const Video = VideoComponent; |
25 changes: 25 additions & 0 deletions
25
apps/ledger-live-desktop/src/newArch/Collectibles/components/Media/index.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import React from "react"; | ||
import { MediaProps } from "LLD/Collectibles/types/Media"; | ||
import useMedia from "LLD/Collectibles/hooks/useMedia"; | ||
import { Placeholder, Image, Video } from "LLD/Collectibles/components"; | ||
|
||
const MediaComponent: React.FC<MediaProps> = props => { | ||
const { useFallback, contentType, uri, mediaType, squareWithDefault, setUseFallback } = | ||
useMedia(props); | ||
const Component = contentType === "video" && !useFallback ? Video : Image; | ||
|
||
return uri ? ( | ||
<Component | ||
{...props} | ||
uri={uri} | ||
mediaType={mediaType} | ||
square={squareWithDefault} | ||
isFallback={useFallback} | ||
setUseFallback={setUseFallback} | ||
/> | ||
) : ( | ||
<Placeholder metadata={props.metadata} tokenId={props.tokenId} /> | ||
); | ||
}; | ||
|
||
export const Media = MediaComponent; |
Oops, something went wrong.