Skip to content

Commit

Permalink
馃帀 CRT post release fixes (Joystream#6218)
Browse files Browse the repository at this point in the history
* Introduce validation for min revenue share duration

* Fix inflation info on channel view

* Token holders table

* Marketplace table header change

* Fix iOS layout error

* Add runtime error handling

* Add warning for revenue share start modal
  • Loading branch information
WRadoslaw committed Apr 25, 2024
1 parent 0d90657 commit e1d4e80
Show file tree
Hide file tree
Showing 13 changed files with 289 additions and 87 deletions.
47 changes: 25 additions & 22 deletions packages/atlas/src/components/CrtPreviewLayout/CrtPreviewLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,39 +38,42 @@ type CrtPreviewViewProps = {
}

export const getTokenDetails = (token: FullCreatorTokenFragment, cumulativeRevenue?: string) => {
const details = []
if (cumulativeRevenue)
details.push({
caption: 'TOTAL REV.',
content: new BN(cumulativeRevenue),
icon: <JoyTokenIcon size={16} variant="silver" />,
tooltipText: 'Total cumulative revenue of this channel on Joystream to date.',
withDenomination: true,
})

if (token.revenueShareRatioPermill)
details.push({
const details: {
caption: string
content: string | BN | number
tooltipText: string
withToken?: boolean
withDenomination?: boolean
customTicker?: string
icon?: ReactElement
}[] = [
{
caption: 'REV. SHARE',
content: `${permillToPercentage(token.revenueShareRatioPermill)}%`,
tooltipText: `Percentage of the future revenue that channel shares with token holders. Each token holder can claim amount of revenue proportionate to their ownership of the channel tokens supply.`,
})

if (token.annualCreatorRewardPermill)
details.push({
},
{
caption: 'INFLATION',
content: `${permillToPercentage(token.annualCreatorRewardPermill)}%`,
tooltipText:
'This percentage of the token supply gets minted every year and paid to creator for channel management.',
})

if (token.totalSupply)
details.push({
},
{
caption: 'TOTAL SUPPLY',
content: +token.totalSupply,
tooltipText: `Total amount of tokens owned by all holders.`,
withToken: true,
customTicker: `$${token.symbol}`,
},
]

if (cumulativeRevenue)
details.push({
caption: 'TOTAL REV.',
content: new BN(cumulativeRevenue),
icon: <JoyTokenIcon size={16} variant="silver" />,
tooltipText: 'Total cumulative revenue of this channel on Joystream to date.',
withDenomination: true,
})

return details
}

Expand Down
52 changes: 27 additions & 25 deletions packages/atlas/src/components/TablePagination/TablePagination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const PER_PAGE_ITEMS: SelectItem[] = [
]

export type TablePaginationProps = {
setPerPage: (perPage: number) => void
setPerPage?: (perPage: number) => void
onChangePage: (page: number) => void
className?: string
itemsPerPage: number
Expand Down Expand Up @@ -61,7 +61,7 @@ export const TablePagination: FC<TablePaginationProps> = ({
const handlePerPageChange = useCallback(
(value?: string | null) => {
if (value) {
setPerPage(+value)
setPerPage?.(+value)
onChangePage(0)
}
},
Expand All @@ -70,29 +70,31 @@ export const TablePagination: FC<TablePaginationProps> = ({

return (
<Container className={className}>
<HorizontalContainer>
<div>
<Select
icon={
<Text as="p" variant={smMatch ? 't200' : 't100'} color="colorText">
{smMatch ? 'Rows per page:' : 'Rows:'}
</Text>
}
value={String(itemsPerPage)}
onChange={handlePerPageChange}
items={PER_PAGE_ITEMS}
/>
</div>
<PageInfo as="p" variant={smMatch ? 't200' : 't100'} color="colorTextStrong">
{!totalCount
? 0
: `${1 + itemsPerPage * page}-${Math.min(
itemsPerPage + itemsPerPage * page,
totalCount
)} of ${totalCount}`}{' '}
items
</PageInfo>
</HorizontalContainer>
{setPerPage ? (
<HorizontalContainer>
<div>
<Select
icon={
<Text as="p" variant={smMatch ? 't200' : 't100'} color="colorText">
{smMatch ? 'Rows per page:' : 'Rows:'}
</Text>
}
value={String(itemsPerPage)}
onChange={handlePerPageChange}
items={PER_PAGE_ITEMS}
/>
</div>
<PageInfo as="p" variant={smMatch ? 't200' : 't100'} color="colorTextStrong">
{!totalCount
? 0
: `${1 + itemsPerPage * page}-${Math.min(
itemsPerPage + itemsPerPage * page,
totalCount
)} of ${totalCount}`}{' '}
items
</PageInfo>
</HorizontalContainer>
) : null}

<HorizontalContainer>
<FlexBox gap={4} width="auto" alignItems="center">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ const COLUMNS: TableProps['columns'] = [
width: 9,
},
{
Header: () => <RightAlignedHeader>SALES VOLUME</RightAlignedHeader>,
Header: () => <RightAlignedHeader>REVENUE VOLUME</RightAlignedHeader>,
accessor: 'salesVolume',
width: 4,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export const Wrapper = styled.div`
padding: ${sizes(4)};
background-color: ${cVar('colorBackgroundMuted')};
display: grid;
grid-template-rows: auto 1fr;
${media.md} {
grid-template-rows: auto 1fr;
padding: ${sizes(6)};
}
`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@ export const tableLoadingData = Array.from({ length: 5 }, () => ({
</ColumnBox>
),
total: <SkeletonLoader height={20} width="70%" />,
vested: <SkeletonLoader height={20} width="100%" />,
transferable: <SkeletonLoader height={20} width="100%" />,
}))

const COLUMNS: TableProps['columns'] = [
{ Header: 'Member', accessor: 'member', width: 4 },
{ Header: 'Total', accessor: 'total', width: 2 },
{ Header: 'Unlocked', accessor: 'transferable', width: 3 },
{ Header: 'Unlocked', accessor: 'transferable', width: 2 },
]

type CrtHolder = {
Expand Down Expand Up @@ -79,12 +79,7 @@ export const CrtHoldersTable = ({
</FlexBox>
),
transferable: (
<StyledTransferableBalance
variant="t200"
memberId={row.memberId}
tokenId={row.tokenId}
ticker={`${row.tokenSymbol}`}
/>
<StyledTransferableBalance variant="t200" format="short" memberId={row.memberId} tokenId={row.tokenId} />
),
})),
[data, ownerId]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import {
} from '@/assets/icons'
import { Avatar } from '@/components/Avatar'
import { FlexBox } from '@/components/FlexBox'
import { NumberFormat } from '@/components/NumberFormat'
import { NumberFormat, NumberFormatProps } from '@/components/NumberFormat'
import { Table, TableProps } from '@/components/Table'
import { ColumnBox } from '@/components/Table/Table.styles'
import { Text, TextVariant } from '@/components/Text'
import { Text } from '@/components/Text'
import { Button } from '@/components/_buttons/Button'
import { BuyMarketTokenModal } from '@/components/_crt/BuyMarketTokenModal'
import { SellTokenModal } from '@/components/_crt/SellTokenModal'
Expand Down Expand Up @@ -265,24 +265,20 @@ export const TransferableBalance = ({
memberId,
tokenId,
ticker,
className,
variant,
...rest
}: {
memberId: string
tokenId: string
ticker?: string
className?: string
variant?: TextVariant
}) => {
} & Pick<NumberFormatProps, 'variant' | 'className' | 'format'>) => {
const { tokenBalance } = useGetTokenBalance(tokenId, memberId)
return (
<NumberFormat
className={className}
{...rest}
value={tokenBalance}
variant={variant}
as="p"
withToken
customTicker={`$${ticker}`}
withToken={!!ticker}
customTicker={ticker ? `$${ticker}` : undefined}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export type HoldersWidgetProps = {
totalHolders: number
}

const TILES_PER_PAGE = 5
const TILES_PER_PAGE = 10

export const HoldersWidget = ({ tokenId, ownerId, totalSupply, totalHolders, tokenSymbol }: HoldersWidgetProps) => {
const [showModal, setShowModal] = useState(false)
Expand All @@ -29,8 +29,6 @@ export const HoldersWidget = ({ tokenId, ownerId, totalSupply, totalHolders, tok
totalCount,
currentPage,
setCurrentPage,
setPerPage,
perPage,
} = useHoldersPagination(tokenId, { initialPageSize: TILES_PER_PAGE })

const holders = useMemo(
Expand Down Expand Up @@ -60,11 +58,10 @@ export const HoldersWidget = ({ tokenId, ownerId, totalSupply, totalHolders, tok
isLoading={isLoading}
show={showModal}
onExitClick={() => setShowModal(false)}
pageSize={perPage}
pageSize={TILES_PER_PAGE}
pagination={{
setPerPage,
totalCount,
itemsPerPage: perPage,
itemsPerPage: TILES_PER_PAGE,
page: currentPage,
onChangePage: setCurrentPage,
}}
Expand Down Expand Up @@ -115,7 +112,12 @@ type CrtHoldersTableModalProps = {
const CrtHoldersTableModal = ({ data, onExitClick, show, pagination, isLoading }: CrtHoldersTableModalProps) => {
return (
<DialogModal onExitClick={onExitClick} show={show} noContentPadding title="Holders">
<StyledHoldersTable data={data} isLoading={isLoading} pagination={pagination} />
<StyledHoldersTable
data={data}
isLoading={isLoading}
pageSize={pagination?.itemsPerPage}
pagination={pagination}
/>
</DialogModal>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,13 @@ import { Controller, useForm } from 'react-hook-form'

import { useGetFullCreatorTokenLazyQuery } from '@/api/queries/__generated__/creatorTokens.generated'
import { FullCreatorTokenFragment } from '@/api/queries/__generated__/fragments.generated'
import { SvgActionArrowRight, SvgActionClock, SvgActionCreatorToken, SvgActionLinkUrl } from '@/assets/icons'
import {
SvgActionArrowRight,
SvgActionClock,
SvgActionCreatorToken,
SvgActionLinkUrl,
SvgAlertsWarning24,
} from '@/assets/icons'
import { FlexBox } from '@/components/FlexBox/FlexBox'
import { NumberFormat } from '@/components/NumberFormat'
import { Text } from '@/components/Text'
Expand All @@ -25,6 +31,7 @@ import { useNetworkUtils } from '@/providers/networkUtils/networkUtils.hooks'
import { useSnackbar } from '@/providers/snackbars'
import { useTransaction } from '@/providers/transactions/transactions.hooks'
import { useUser } from '@/providers/user/user.hooks'
import { sizes } from '@/styles'
import { SentryLogger } from '@/utils/logs'
import { pluralizeNoun } from '@/utils/misc'
import { permillToPercentage } from '@/utils/number'
Expand All @@ -51,13 +58,13 @@ export const StartRevenueShare = ({ token, onClose, show }: StartRevenueSharePro
const [openClaimShareModal, setOpenClaimShareModal] = useState(false)
const [showSuccessModal, setShowSuccessModal] = useState(false)

const { joystream, proxyCallback } = useJoystream()
const { joystream, proxyCallback, chainState } = useJoystream()
const { refetchCreatorTokenData, refetchChannelPayments } = useNetworkUtils()
const { memberId, channelId, activeChannel } = useUser()
const { displaySnackbar } = useSnackbar()
const handleTransaction = useTransaction()
const { copyToClipboard } = useClipboard()
const { convertMsTimestampToBlock } = useBlockTimeEstimation()
const { convertMsTimestampToBlock, convertBlocksToDuration } = useBlockTimeEstimation()
const { tokenBalance } = useGetTokenBalance(token.id, memberId ?? '-1')
const [refetchToken, { data: localTokenData }] = useGetFullCreatorTokenLazyQuery({
variables: {
Expand Down Expand Up @@ -86,7 +93,7 @@ export const StartRevenueShare = ({ token, onClose, show }: StartRevenueSharePro
})
const [startDate, endDate] = watch(['startDate', 'endDate'])
const endDateTimestamp = useMemo(() => {
// we need to create new date object to aviod modifying it in addDaystoDate
// we need to create new date object to avoid modifying it in addDaysToDate
const rawStartDate = startDate?.type === 'date' ? new Date(startDate.date.getTime()) : new Date()
return endDate?.type === 'date'
? endDate.date
Expand Down Expand Up @@ -320,13 +327,13 @@ export const StartRevenueShare = ({ token, onClose, show }: StartRevenueSharePro
}}
>
<FlexBox flow="column" gap={8}>
<FlexBox equalChildren alignItems="end" gap={6}>
<FlexBox equalChildren alignItems="start" gap={6}>
<Controller
name="startDate"
control={control}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormField error={error?.message} disableErrorAnimation label="Starts">
<OuterBox>
<OuterBox marginTop={2}>
<InnerBox>
<AuctionDatePicker
error={!!error}
Expand Down Expand Up @@ -358,6 +365,29 @@ export const StartRevenueShare = ({ token, onClose, show }: StartRevenueSharePro
<Controller
name="endDate"
control={control}
rules={{
validate: (value, formValues) => {
const rawStartDate =
formValues.startDate?.type === 'date' ? new Date(formValues.startDate.date.getTime()) : new Date()

const valueTimestamp =
value?.type === 'date'
? value.date
: value?.durationDays
? addDaysToDate(value.durationDays, new Date(rawStartDate))
: new Date()

const minDurationMs = convertBlocksToDuration(chainState.minRevenueSplitDuration)

if (valueTimestamp.getTime() - Date.now() < minDurationMs) {
return `Revenue share must end after ${formatDateTimeAt(
new Date(rawStartDate.getTime() + minDurationMs)
)}`
}

return true
},
}}
render={({ field: { onChange, value }, fieldState: { error } }) => (
<FormField
error={error?.message}
Expand Down Expand Up @@ -396,16 +426,23 @@ export const StartRevenueShare = ({ token, onClose, show }: StartRevenueSharePro
{row.content}
</FlexBox>
))}
<FlexBox marginTop={3} gap={3} alignItems="start">
<SvgAlertsWarning24 />
<Text variant="t200" as="span" color="colorTextCaution">
Active revenue share will pause all open market transactions for its entire duration.
</Text>
</FlexBox>
</FlexBox>
</FlexBox>
</DialogModal>
</>
)
}

const OuterBox = styled.div`
const OuterBox = styled.div<{ marginTop?: number }>`
position: relative;
height: 50px;
margin-top: ${(props) => sizes(props.marginTop ?? 0)};
`

const InnerBox = styled.div`
Expand Down

0 comments on commit e1d4e80

Please sign in to comment.