diff --git a/packages/atlas/src/components/AssetImage/AssetImage.tsx b/packages/atlas/src/components/AssetImage/AssetImage.tsx index 9ba7d415cc..e89bb7c50d 100644 --- a/packages/atlas/src/components/AssetImage/AssetImage.tsx +++ b/packages/atlas/src/components/AssetImage/AssetImage.tsx @@ -27,10 +27,12 @@ export const AssetImage: FC = ({ resolvedUrls, isLoading, type, > {loading ? ( - ) : imagePlaceholder && !url ? ( + ) : url ? ( + + ) : imagePlaceholder ? ( <>{imagePlaceholder} ) : ( - + )} diff --git a/packages/atlas/src/components/CrtPreviewLayout/CrtPreviewLayout.tsx b/packages/atlas/src/components/CrtPreviewLayout/CrtPreviewLayout.tsx index d3244bd3ca..fa7f953347 100644 --- a/packages/atlas/src/components/CrtPreviewLayout/CrtPreviewLayout.tsx +++ b/packages/atlas/src/components/CrtPreviewLayout/CrtPreviewLayout.tsx @@ -1,3 +1,4 @@ +import styled from '@emotion/styled' import BN from 'bn.js' import { ReactElement, useMemo } from 'react' import { useNavigate } from 'react-router' @@ -14,8 +15,10 @@ import { HoldersWidget } from '@/components/_crt/HoldersWidget' import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader' import { absoluteRoutes } from '@/config/routes' import { useMediaMatch } from '@/hooks/useMediaMatch' +import { hapiBnToTokenNumber } from '@/joystream-lib/utils' import { useConfirmationModal } from '@/providers/confirmationModal' import { useUser } from '@/providers/user/user.hooks' +import { media, square } from '@/styles' import { permillToPercentage } from '@/utils/number' import { @@ -59,9 +62,14 @@ export const getTokenDetails = (token: FullCreatorTokenFragment, cumulativeReven 'This percentage of the token supply gets minted every year and paid to creator for channel management.', }, { - caption: 'TOTAL SUPPLY', - content: +token.totalSupply, - tooltipText: `Total amount of tokens owned by all holders.`, + caption: 'MARKET CAP', + content: + token.lastPrice && token.totalSupply + ? hapiBnToTokenNumber(new BN(token.lastPrice).muln(+token.totalSupply)) + : 0, + tooltipText: `The total market value of a token's supply.`, + icon: , + withDenomination: true, }, ] @@ -69,7 +77,7 @@ export const getTokenDetails = (token: FullCreatorTokenFragment, cumulativeReven details.push({ caption: 'TOTAL REV.', content: new BN(cumulativeRevenue), - icon: , + icon: , tooltipText: 'Total cumulative revenue of this channel on Joystream to date.', withDenomination: true, }) @@ -161,3 +169,11 @@ export const CrtPreviewLayout = ({ ) } + +export const StyledJoyTokenIcon = styled(JoyTokenIcon)` + ${square(16)} + + ${media.sm} { + ${square(24)} + } +` diff --git a/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/MarketplaceCarouselCard.styles.ts b/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/MarketplaceCarouselCard.styles.ts index 79e97adb97..0a6b8f0bcf 100644 --- a/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/MarketplaceCarouselCard.styles.ts +++ b/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/MarketplaceCarouselCard.styles.ts @@ -1,7 +1,7 @@ import styled from '@emotion/styled' import { Link } from 'react-router-dom' -import { cVar, media, sizes } from '@/styles' +import { cVar, media, sizes, zIndex } from '@/styles' export const InformationContainer = styled.div<{ isPaused: boolean }>` width: 100%; @@ -35,6 +35,12 @@ export const Container = styled.div` } ` +export const LinkArea = styled.div` + position: absolute; + inset: 0; + z-index: ${zIndex.nearOverlay}; +` + export const VideoContainer = styled.div` position: relative; width: 100%; diff --git a/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/CrtCarouselDetails.tsx b/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/CrtCarouselDetails.tsx index 25e0694eae..31fcf641cb 100644 --- a/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/CrtCarouselDetails.tsx +++ b/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/CrtCarouselDetails.tsx @@ -14,6 +14,7 @@ import { VideoCardWrapper } from '@/components/NftCarousel/components/Marketplac import { Text } from '@/components/Text' import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader' import { DetailsContent, DetailsContentProps } from '@/components/_nft/NftTile' +import { absoluteRoutes } from '@/config/routes' import { useMediaMatch } from '@/hooks/useMediaMatch' import { hapiBnToTokenNumber } from '@/joystream-lib/utils' import { pluralizeNoun } from '@/utils/misc' @@ -131,10 +132,11 @@ export const CrtCarouselDetails = ({ goNextSlide={slideNext} mediaUrls={mediaUrls} thumbnailUrls={thumbnailUrls} + to={absoluteRoutes.viewer.channel(channel?.channel.id ?? '', { tab: 'Token' })} details={ - + diff --git a/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/NftCarouselDetails.tsx b/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/NftCarouselDetails.tsx index 3117bb08c0..e51002081f 100644 --- a/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/NftCarouselDetails.tsx +++ b/packages/atlas/src/components/NftCarousel/components/MarketplaceCarouselCard/types/NftCarouselDetails.tsx @@ -1,6 +1,7 @@ import BN from 'bn.js' import { ReactNode, useMemo, useState } from 'react' import { useNavigate } from 'react-router' +import { Link } from 'react-router-dom' import { CSSTransition } from 'react-transition-group' import { getNftStatus } from '@/api/hooks/nfts' @@ -12,6 +13,7 @@ import { Container, DetailsContainer, InformationContainer, + LinkArea, StatsContainer, StyledLink, VideoContainer, @@ -224,6 +226,7 @@ type VideoCardWrapperProps = { mediaUrls: string[] thumbnailUrls: string[] goNextSlide: () => void + to?: string details: ReactNode } @@ -233,28 +236,33 @@ export const VideoCardWrapper = ({ thumbnailUrls, mediaUrls, videoId, + to, details, }: VideoCardWrapperProps) => { const [isPaused, setIsPaused] = useState(!active) - const debouncedActive = useDebounceValue(active, 500) + const debouncedActive = useDebounceValue(active, 800, true) return ( + + + {active ? ( setIsPaused(true)} onPlay={() => setIsPaused(false)} preload="auto" src={mediaUrls ?? undefined} poster={thumbnailUrls ?? undefined} - handleActions={active ? debouncedActive : active} + handleActions={active ? !!debouncedActive : active} videoPlaytime={30} onEnded={goNextSlide} + customLink={to} /> ) : ( diff --git a/packages/atlas/src/components/Table/Table.styles.ts b/packages/atlas/src/components/Table/Table.styles.ts index c87377ba1a..3398f0aa84 100644 --- a/packages/atlas/src/components/Table/Table.styles.ts +++ b/packages/atlas/src/components/Table/Table.styles.ts @@ -1,11 +1,12 @@ import { css } from '@emotion/react' import styled from '@emotion/styled' +import { Link } from 'react-router-dom' import { Pagination } from '@/components/Pagination' import { Text } from '@/components/Text' import { cVar, sizes } from '@/styles' -export const Wrapper = styled.div` +export const Wrapper = styled.div<{ interactive?: boolean }>` background-color: ${cVar('colorBackgroundMuted')}; overflow: auto; @@ -14,6 +15,25 @@ export const Wrapper = styled.div` cursor: pointer; } } + + ${(props) => + props.interactive + ? css` + .table-row { + transition: background-color 300ms ease-in-out; + border-radius: ${cVar('radiusMedium')}; + + :hover { + cursor: pointer; + background: ${cVar('colorBackgroundMutedAlpha')}; + } + } + ` + : ''} +` + +export const AnchorRow = styled(Link)` + text-decoration: none; ` export const TableBase = styled.table` diff --git a/packages/atlas/src/components/Table/Table.tsx b/packages/atlas/src/components/Table/Table.tsx index c4a2d21d49..8e6692af2b 100644 --- a/packages/atlas/src/components/Table/Table.tsx +++ b/packages/atlas/src/components/Table/Table.tsx @@ -7,6 +7,7 @@ import { Text } from '@/components/Text' import { useMediaMatch } from '@/hooks/useMediaMatch' import { + AnchorRow, EmptyTableContainer, EmptyTableDescription, EmptyTableHeader, @@ -22,6 +23,7 @@ import { export type TableProps = { columns: Column[] onRowClick?: (rowIdx: number) => void + getRowTo?: (rowIdx: number) => string data: T[] title?: string pageSize?: number @@ -31,6 +33,7 @@ export type TableProps = { description: string icon: ReactElement } + interactive?: boolean className?: string pagination?: TablePaginationProps minWidth?: number @@ -46,6 +49,8 @@ export const Table = ({ onRowClick, className, pagination, + interactive, + getRowTo, minWidth = 300, }: TableProps) => { const scrollRef = useRef(null) @@ -74,7 +79,7 @@ export const Table = ({ const mdMatch = useMediaMatch('md') return ( - + {title && ( ({ {subpage.map((row, idx) => { prepareRow(row) + const rowTo = getRowTo ? getRowTo(idx) : undefined + const mappedCells = row.cells.map((cell) => ( + + {cell.render('Cell')} + + )) + + if (rowTo) { + return ( + onRowClick?.(idx)} + key={row.getRowProps().key} + to={rowTo} + > + {mappedCells} + + ) + } + return ( ({ onClick={() => onRowClick?.(idx)} key={row.getRowProps().key} > - {row.cells.map((cell) => ( - - {cell.render('Cell')} - - ))} + {mappedCells} ) })} diff --git a/packages/atlas/src/components/TopEarningChannels/TopEarningChannels.tsx b/packages/atlas/src/components/TopEarningChannels/TopEarningChannels.tsx index 5978b1668e..110f663486 100644 --- a/packages/atlas/src/components/TopEarningChannels/TopEarningChannels.tsx +++ b/packages/atlas/src/components/TopEarningChannels/TopEarningChannels.tsx @@ -38,7 +38,7 @@ const COLUMNS: TableProps['columns'] = [ width: 9, }, { - Header: () => REVENUE VOLUME, + Header: () => REVENUE, accessor: 'salesVolume', width: 4, }, @@ -61,7 +61,7 @@ export const TopEarningChannels = ({ withCrtOnly }: { withCrtOnly?: boolean }) = }) const lgMatch = useMediaMatch('lg') - const mappedData: TableProps['data'] = useMemo(() => { + const mappedData = useMemo(() => { return loading ? Array.from({ length: 10 }, () => ({ index: null, @@ -72,6 +72,7 @@ export const TopEarningChannels = ({ withCrtOnly }: { withCrtOnly?: boolean }) = ), salesVolume: , + channelId: null, })) : channels?.map((data, index) => ({ index: ( @@ -94,6 +95,7 @@ export const TopEarningChannels = ({ withCrtOnly }: { withCrtOnly?: boolean }) = ), channel: , + channelId: data.id, })) ?? [] }, [channels, loading]) @@ -124,6 +126,10 @@ export const TopEarningChannels = ({ withCrtOnly }: { withCrtOnly?: boolean }) = columns={COLUMNS} data={mappedData} doubleColumn={lgMatch} + getRowTo={(idx) => + absoluteRoutes.viewer.channel(mappedData[idx].channelId ?? '', { tab: withCrtOnly ? 'Token' : 'Videos' }) + } + interactive />, ], }} diff --git a/packages/atlas/src/components/TopSellingChannelsTable/TopSellingChannelsTable.tsx b/packages/atlas/src/components/TopSellingChannelsTable/TopSellingChannelsTable.tsx index dd593b2a51..4b6c1a6d2d 100644 --- a/packages/atlas/src/components/TopSellingChannelsTable/TopSellingChannelsTable.tsx +++ b/packages/atlas/src/components/TopSellingChannelsTable/TopSellingChannelsTable.tsx @@ -23,7 +23,6 @@ import { NftSoldText, SenderItemIconsWrapper, SkeletonChannelContainer, - StyledLink, StyledListItem, StyledTable, } from './TopSellingChannelsTable.styles' @@ -77,7 +76,7 @@ export const TopSellingChannelsTable = ({ withCrtOnly }: { withCrtOnly?: boolean }) const lgMatch = useMediaMatch('lg') - const mappedData: TableProps['data'] = useMemo(() => { + const mappedData = useMemo(() => { return loading ? Array.from({ length: 10 }, () => ({ index: null, @@ -89,6 +88,7 @@ export const TopSellingChannelsTable = ({ withCrtOnly }: { withCrtOnly?: boolean ), nftsSold: , salesVolume: , + channelId: null, })) : data?.[sort].map((data, index) => ({ index: ( @@ -116,6 +116,7 @@ export const TopSellingChannelsTable = ({ withCrtOnly }: { withCrtOnly?: boolean ), channel: , + channelId: data.channel.id, })) ?? [] }, [data, loading, sort]) @@ -188,6 +189,10 @@ export const TopSellingChannelsTable = ({ withCrtOnly }: { withCrtOnly?: boolean emptyState={tableEmptyState} columns={withCrtOnly ? COLUMNS.filter((col) => col.accessor !== 'nftsSold') : COLUMNS} data={mappedData} + getRowTo={(idx) => + absoluteRoutes.viewer.channel(mappedData[idx].channelId ?? '', { tab: withCrtOnly ? 'Token' : 'Videos' }) + } + interactive doubleColumn={lgMatch} />, ], @@ -201,26 +206,24 @@ const Channel = ({ channel }: { channel: BasicChannelFieldsFragment }) => { // todo to be implemented const verified = false return ( - - } - label={channel.title} - isInteractive={false} - nodeEnd={ - - {hasCreatorToken && ( - - - - )} - {verified && ( - - - - )} - - } - /> - + } + label={channel.title} + isInteractive={false} + nodeEnd={ + + {hasCreatorToken && ( + + + + )} + {verified && ( + + + + )} + + } + /> ) } diff --git a/packages/atlas/src/components/_crt/AllTokensSection/AllTokensSection.tsx b/packages/atlas/src/components/_crt/AllTokensSection/AllTokensSection.tsx index 274a73e122..22bc60329f 100644 --- a/packages/atlas/src/components/_crt/AllTokensSection/AllTokensSection.tsx +++ b/packages/atlas/src/components/_crt/AllTokensSection/AllTokensSection.tsx @@ -6,6 +6,7 @@ import { NumberFormat } from '@/components/NumberFormat' import { Section } from '@/components/Section/Section' import { Button } from '@/components/_buttons/Button' import { MarketplaceCrtTable } from '@/components/_crt/MarketplaceCrtTable' +import { absoluteRoutes } from '@/config/routes' import { SORTING_FILTERS, useCrtSectionFilters } from '@/hooks/useCrtSectionFilters' import { useMediaMatch } from '@/hooks/useMediaMatch' import { useTokensPagination } from '@/hooks/useTokensPagniation' @@ -71,6 +72,8 @@ export const AllTokensSection = () => { absoluteRoutes.viewer.channel(tableData[idx]?.channelId ?? '', { tab: 'Token' })} + interactive isLoading={isLoading} pageSize={perPage} pagination={{ diff --git a/packages/atlas/src/components/_crt/BuyMarketTokenModal/BuyMarketTokenModal.tsx b/packages/atlas/src/components/_crt/BuyMarketTokenModal/BuyMarketTokenModal.tsx index 7bef2e6c08..c8daaf9e54 100644 --- a/packages/atlas/src/components/_crt/BuyMarketTokenModal/BuyMarketTokenModal.tsx +++ b/packages/atlas/src/components/_crt/BuyMarketTokenModal/BuyMarketTokenModal.tsx @@ -20,6 +20,7 @@ import { useMediaMatch } from '@/hooks/useMediaMatch' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { hapiBnToTokenNumber, tokenNumberToHapiBn } from '@/joystream-lib/utils' import { useFee, useJoystream, useSubscribeAccountBalance } from '@/providers/joystream' +import { useJoystreamStore } from '@/providers/joystream/joystream.store' import { useNetworkUtils } from '@/providers/networkUtils/networkUtils.hooks' import { useSnackbar } from '@/providers/snackbars' import { useTransaction } from '@/providers/transactions/transactions.hooks' @@ -54,6 +55,7 @@ export const BuyMarketTokenModal = ({ tokenId, onClose: _onClose, show }: BuySal const amountRef = useRef(null) const { control, watch, handleSubmit, reset, formState } = useForm<{ tokenAmount: number }>() const tokenAmount = watch('tokenAmount') || 0 + const currentBlockRef = useRef(useJoystreamStore((store) => store.currentBlock)) const { fullFee } = useFee('purchaseTokenOnMarketTx', ['1', '1', String(tokenAmount ?? 0), '1000000']) const { data, loading } = useGetFullCreatorTokenQuery({ variables: { @@ -63,7 +65,8 @@ export const BuyMarketTokenModal = ({ tokenId, onClose: _onClose, show }: BuySal SentryLogger.error('Error while fetching creator token', 'BuyMarketTokenModal', error) }, }) - const hasActiveRevenueShare = data?.creatorTokenById?.revenueShares.some((rS) => !rS.finalized) + const activeRevenueShare = data?.creatorTokenById?.revenueShares.find((rS) => !rS.finalized) + const hasActiveRevenueShare = (activeRevenueShare?.endsAt ?? 0) > currentBlockRef.current const { data: memberTokenAccount } = useGetCreatorTokenHoldersQuery({ variables: { where: { diff --git a/packages/atlas/src/components/_crt/CrtCard/CrtCard.tsx b/packages/atlas/src/components/_crt/CrtCard/CrtCard.tsx index 6c6eaedbf2..abf514a048 100644 --- a/packages/atlas/src/components/_crt/CrtCard/CrtCard.tsx +++ b/packages/atlas/src/components/_crt/CrtCard/CrtCard.tsx @@ -74,18 +74,19 @@ export const CrtCard = ({ }) baseDetails.push({ - caption: 'Channel revenue', + caption: 'Revenue', content: channelRevenue ?? 0, icon: , }) baseDetails.push({ - caption: 'Transaction vol.', + caption: 'Volume', content: 0, icon: , }) } + // todo: crt sale if (status?.type === 'sale') { baseDetails.push({ caption: 'Sale', @@ -129,13 +130,13 @@ export const CrtCard = ({ }) baseDetails.push({ - caption: 'Channel revenue', + caption: 'Revenue', content: channelRevenue ?? 0, icon: , }) baseDetails.push({ - caption: 'Transaction vol.', + caption: 'Volume', content: status.transactionVolume, icon: , }) diff --git a/packages/atlas/src/components/_crt/CrtPortfolioTable/CrtPortfolioTable.tsx b/packages/atlas/src/components/_crt/CrtPortfolioTable/CrtPortfolioTable.tsx index 1ea2a80ad8..a0b572ad5f 100644 --- a/packages/atlas/src/components/_crt/CrtPortfolioTable/CrtPortfolioTable.tsx +++ b/packages/atlas/src/components/_crt/CrtPortfolioTable/CrtPortfolioTable.tsx @@ -1,6 +1,5 @@ import styled from '@emotion/styled' import { ReactElement, useMemo, useState } from 'react' -import { useNavigate } from 'react-router' import { useBasicChannel } from '@/api/hooks/channel' import { TokenStatus } from '@/api/queries/__generated__/baseTypes.generated' @@ -81,6 +80,7 @@ export const CrtPortfolioTable = ({ data, emptyState, isLoading }: CrtPortfolioT const mappingData = useMemo(() => { return [...data].slice(currentPage * perPage, currentPage * perPage + perPage).map((row) => ({ + channelId: row.channelId, token: , status: , transferable: ( @@ -130,6 +130,8 @@ export const CrtPortfolioTable = ({ data, emptyState, isLoading }: CrtPortfolioT pageSize={data.length || undefined} columns={COLUMNS} data={isLoading ? tableLoadingData : mappingData} + getRowTo={(idx) => absoluteRoutes.viewer.channel(mappingData[idx]?.channelId ?? '', { tab: 'Token' })} + interactive={!isLoading} emptyState={emptyState} pagination={{ setPerPage, @@ -152,21 +154,10 @@ export const TokenInfo = ({ customAvatar?: ReactElement }) => { const { channel } = useBasicChannel(channelId ?? '') - const navigate = useNavigate() return ( - {customAvatar ?? ( - (channelId ? navigate(absoluteRoutes.viewer.channel(channelId, { tab: 'Token' })) : undefined)} - /> - )} - - (channelId ? navigate(absoluteRoutes.viewer.channel(channelId, { tab: 'Token' })) : undefined)} - className="pointer" - alignItems="center" - > + {customAvatar ?? } + {tokenTitle} diff --git a/packages/atlas/src/components/_crt/CrtStatusWidget/CrtStatusWidget.tsx b/packages/atlas/src/components/_crt/CrtStatusWidget/CrtStatusWidget.tsx index 4389280cfe..7877a915aa 100644 --- a/packages/atlas/src/components/_crt/CrtStatusWidget/CrtStatusWidget.tsx +++ b/packages/atlas/src/components/_crt/CrtStatusWidget/CrtStatusWidget.tsx @@ -71,14 +71,8 @@ export const CrtStatusWidget: FC = ({ token }) => { } - withDenomination + caption="Total supply" + content={+token.totalSupply} /> +} & Pick -export const HoldersTable = ({ data, currentMemberId, symbol, pagination, pageSize, isLoading }: HoldersTableProps) => { +export const HoldersTable = ({ data, currentMemberId, symbol, isLoading, ...tableProps }: HoldersTableProps) => { const mappedData = useMemo( () => data.map((row) => ({ @@ -62,13 +62,7 @@ export const HoldersTable = ({ data, currentMemberId, symbol, pagination, pageSi [currentMemberId, data, symbol] ) return ( - + ) } diff --git a/packages/atlas/src/components/_crt/MarketplaceCrtTable/MarketplaceCrtTable.tsx b/packages/atlas/src/components/_crt/MarketplaceCrtTable/MarketplaceCrtTable.tsx index 3bb7491088..c09cc6ceb2 100644 --- a/packages/atlas/src/components/_crt/MarketplaceCrtTable/MarketplaceCrtTable.tsx +++ b/packages/atlas/src/components/_crt/MarketplaceCrtTable/MarketplaceCrtTable.tsx @@ -54,15 +54,9 @@ export type MarketplaceCrtTableProps = { data: MarketplaceToken[] isLoading: boolean emptyState?: TableProps['emptyState'] -} & Pick +} & Pick -export const MarketplaceCrtTable = ({ - data, - emptyState, - isLoading, - pagination, - pageSize, -}: MarketplaceCrtTableProps) => { +export const MarketplaceCrtTable = ({ data, emptyState, isLoading, ...tableProps }: MarketplaceCrtTableProps) => { const mappingData = useMemo(() => { return data.map((row) => ({ token: , @@ -106,8 +100,7 @@ export const MarketplaceCrtTable = ({ columns={COLUMNS} data={isLoading ? tableLoadingData : mappingData} emptyState={emptyState} - pageSize={pageSize} - pagination={pagination} + {...tableProps} /> ) diff --git a/packages/atlas/src/components/_crt/RevenueShareStakersTable/RevenueShareStakersTable.tsx b/packages/atlas/src/components/_crt/RevenueShareStakersTable/RevenueShareStakersTable.tsx index eaeb74df4c..b31ddb18f8 100644 --- a/packages/atlas/src/components/_crt/RevenueShareStakersTable/RevenueShareStakersTable.tsx +++ b/packages/atlas/src/components/_crt/RevenueShareStakersTable/RevenueShareStakersTable.tsx @@ -5,6 +5,7 @@ import { useMemo } from 'react' import { NumberFormat } from '@/components/NumberFormat' import { Table, TableProps } from '@/components/Table' import { DateBlockCell, LoadingMemberRow, TokenAmount } from '@/components/Table/Table.cells' +import { absoluteRoutes } from '@/config/routes' const COLUMNS: TableProps['columns'] = [ { Header: 'Date', accessor: 'stakedAt' }, @@ -29,13 +30,23 @@ export const RevenueShareStakersTable = ({ data, tokenSymbol = 'N/A' }: RevenueS return { stakedAt: , member: , + memberId: row.memberId, staked: , earnings: , } }) }, [data, tokenSymbol]) - return + return ( + absoluteRoutes.viewer.memberById(mappedData[idx]?.memberId ?? '')} + interactive + /> + ) } const StyledTable = styled(Table)` diff --git a/packages/atlas/src/components/_crt/StartRevenueShareModal/StartRevenueShareModal.tsx b/packages/atlas/src/components/_crt/StartRevenueShareModal/StartRevenueShareModal.tsx index 989388c422..3c84e94929 100644 --- a/packages/atlas/src/components/_crt/StartRevenueShareModal/StartRevenueShareModal.tsx +++ b/packages/atlas/src/components/_crt/StartRevenueShareModal/StartRevenueShareModal.tsx @@ -52,7 +52,7 @@ const datePickerItemsFactory = (days: number[]) => }, })) -const endDateItems = datePickerItemsFactory([7, 14, 30]) +const endDateItems = datePickerItemsFactory([7]) export const StartRevenueShare = ({ token, onClose, show }: StartRevenueShareProps) => { const [openClaimShareModal, setOpenClaimShareModal] = useState(false) diff --git a/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.styles.ts b/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.styles.ts index 8c3384fc00..42fa1e7b64 100644 --- a/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.styles.ts +++ b/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.styles.ts @@ -1,7 +1,6 @@ import isPropValid from '@emotion/is-prop-valid' import { css } from '@emotion/react' import styled from '@emotion/styled' -import { Link } from 'react-router-dom' import { AssetImage } from '@/components/AssetImage' import { AssetVideo } from '@/components/AssetVideo/AssetVideo' @@ -62,7 +61,7 @@ export const ButtonBox = styled.div` ` // on Firefox there is a gap between fades, negative margin fixes that -export const StyledLink = styled(Link, { shouldForwardProp: isPropValid })<{ withFade?: boolean }>` +export const StyledFade = styled('div', { shouldForwardProp: isPropValid })<{ withFade?: boolean }>` ::after { content: ''; position: absolute; diff --git a/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.tsx b/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.tsx index 034dae5343..2da75b69b7 100644 --- a/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.tsx +++ b/packages/atlas/src/components/_video/BackgroundVideoPlayer/BackgroundVideoPlayer.tsx @@ -4,11 +4,10 @@ import { CSSTransition } from 'react-transition-group' import { SvgActionPause, SvgActionPlay, SvgActionSoundOff, SvgActionSoundOn } from '@/assets/icons' import { Button } from '@/components/_buttons/Button' import { VideoProgress } from '@/components/_video/BackgroundVideoPlayer/VideoProgress' -import { absoluteRoutes } from '@/config/routes' import { transitions } from '@/styles' import { ConsoleLogger } from '@/utils/logs' -import { ButtonBox, StyledLink, StyledVideo, VideoPoster, VideoWrapper } from './BackgroundVideoPlayer.styles' +import { ButtonBox, StyledFade, StyledVideo, VideoPoster, VideoWrapper } from './BackgroundVideoPlayer.styles' type BackgroundVideoPlayerProps = { className?: string @@ -19,6 +18,7 @@ type BackgroundVideoPlayerProps = { withFade?: boolean src: string[] poster: string[] + customLink?: string } & Omit, 'src' | 'poster'> export const BackgroundVideoPlayer: FC = ({ @@ -33,6 +33,7 @@ export const BackgroundVideoPlayer: FC = ({ videoPlaytime, videoId, withFade, + customLink, ...props }) => { const videoRef = useRef(null) @@ -118,7 +119,7 @@ export const BackgroundVideoPlayer: FC = ({ )} {playing && } - + = ({ )} - + ) } diff --git a/packages/atlas/src/hooks/useDebounceValue.ts b/packages/atlas/src/hooks/useDebounceValue.ts index f69f28e3cd..81f405176e 100644 --- a/packages/atlas/src/hooks/useDebounceValue.ts +++ b/packages/atlas/src/hooks/useDebounceValue.ts @@ -1,15 +1,24 @@ import { useEffect, useState } from 'react' -export function useDebounceValue(value: T, delay?: number): T { - const [debouncedValue, setDebouncedValue] = useState(value) +export function useDebounceValue( + value: T, + delay?: number, + emptyInitialValue?: R +): R extends true ? T | null : T { + type ReturnType = R extends true ? T | null : T + // Initialize the state with appropriate type + const [debouncedValue, setDebouncedValue] = useState( + emptyInitialValue ? (null as ReturnType) : (value as ReturnType) + ) useEffect(() => { - const timer = setTimeout(() => setDebouncedValue(value), delay ?? 500) + const timer = setTimeout(() => setDebouncedValue(value as ReturnType), delay ?? 500) return () => { clearTimeout(timer) } }, [value, delay]) + // TypeScript will infer the return type based on the value of emptyInitialValue return debouncedValue } diff --git a/packages/atlas/src/views/studio/CrtDashboard/tabs/CrtHoldersTab.tsx b/packages/atlas/src/views/studio/CrtDashboard/tabs/CrtHoldersTab.tsx index 57d191277d..8f850d195e 100644 --- a/packages/atlas/src/views/studio/CrtDashboard/tabs/CrtHoldersTab.tsx +++ b/packages/atlas/src/views/studio/CrtDashboard/tabs/CrtHoldersTab.tsx @@ -2,6 +2,7 @@ import { useMemo } from 'react' import { FullCreatorTokenFragment } from '@/api/queries/__generated__/fragments.generated' import { HoldersTable } from '@/components/_crt/HoldersTable/HoldersTable' +import { absoluteRoutes } from '@/config/routes' import { useHoldersPagination } from '@/hooks/useHoldersPagination' import { useUser } from '@/providers/user/user.hooks' @@ -37,6 +38,8 @@ export const CrtHoldersTab = ({ token }: CrtHoldersTabProps) => { return ( absoluteRoutes.viewer.memberById(mappedData[idx]?.member.id ?? '')} + interactive isLoading={isLoading} currentMemberId={memberId ?? ''} symbol={token.symbol ?? 'N/A'} diff --git a/packages/atlas/src/views/viewer/MarketplaceView/MarketplaceView.tsx b/packages/atlas/src/views/viewer/MarketplaceView/MarketplaceView.tsx index 31d57ed63e..c53b44298f 100644 --- a/packages/atlas/src/views/viewer/MarketplaceView/MarketplaceView.tsx +++ b/packages/atlas/src/views/viewer/MarketplaceView/MarketplaceView.tsx @@ -11,16 +11,16 @@ import { MarketplaceCrtTab } from './tabs/MarketplaceCrtTab' import { MarketplaceNftTab } from './tabs/MarketplaceNftTab' const TABS = [ - { - name: 'Video NFTs', - description: 'Explore offers of non-fungible tokens for popular videos', - icon: , - }, { name: 'Creator Tokens', description: 'Discover channels you can invest in', icon: , }, + { + name: 'Video NFTs', + description: 'Explore offers of non-fungible tokens for popular videos', + icon: , + }, ] as const type TabsNames = (typeof TABS)[number]['name'] @@ -54,8 +54,8 @@ export const MarketplaceView: FC = () => { /> - {currentTab === 0 && } - {currentTab === 1 && } + {currentTab === 0 && } + {currentTab === 1 && } )