From 8e1a4f5e787ed0ff858f9bdb311b9f152ba695bb Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Wed, 27 Sep 2023 21:42:23 +0200 Subject: [PATCH 01/18] Initial layout setup --- packages/atlas/atlas.config.yml | 10 +- packages/atlas/src/.env | 2 +- .../atlas/src/components/FlexBox/FlexBox.ts | 35 ++ .../atlas/src/components/FlexBox/index.ts | 1 + .../src/components/LayoutGrid/LayoutGrid.tsx | 21 + .../_buttons/Button/Button.styles.ts | 4 + .../_ypp/BenefitCard/BenefitCard.stories.tsx | 41 +- .../_ypp/BenefitCard/BenefitCard.styles.ts | 68 +-- .../_ypp/BenefitCard/BenefitCard.tsx | 236 ++-------- .../YppLandingView/YppRewardSection.tsx | 417 +++++++++--------- .../atlas/src/views/studio/StudioLayout.tsx | 2 +- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 180 ++++---- .../tabs/YppDashboardTabs.styles.ts | 39 ++ 13 files changed, 466 insertions(+), 590 deletions(-) create mode 100644 packages/atlas/src/components/FlexBox/FlexBox.ts create mode 100644 packages/atlas/src/components/FlexBox/index.ts diff --git a/packages/atlas/atlas.config.yml b/packages/atlas/atlas.config.yml index 0f0da3f5ec..17f1be811c 100644 --- a/packages/atlas/atlas.config.yml +++ b/packages/atlas/atlas.config.yml @@ -101,11 +101,11 @@ features: linkText: Go to Notion # Used only on YPP Dashboard - if empty defaults to "Go to {title}" label: Notion # Used for YPP Dashboard to inform user which vendor given feature uses - if empty defaults to title icon: info # Optional icon to be displayed. Possible icons: message, info, tokenStack - - title: Payments - link: /studio/payments - linkText: Go to Payments - label: Studio - icon: tokenStack +# - title: Payments +# link: /studio/payments +# linkText: Go to Payments +# label: Studio +# icon: tokenStack - title: Support link: https://discord.com/channels/811216481340751934/1053294778529353788 linkText: Go to Discord diff --git a/packages/atlas/src/.env b/packages/atlas/src/.env index a02dc10c94..7422a14c3e 100644 --- a/packages/atlas/src/.env +++ b/packages/atlas/src/.env @@ -43,7 +43,7 @@ VITE_DEVELOPMENT_ORION_URL=https://atlas-dev.joystream.org/orion-api/graphql VITE_DEVELOPMENT_QUERY_NODE_SUBSCRIPTION_URL=wss://atlas-dev.joystream.org/orion-v2/graphql VITE_DEVELOPMENT_NODE_URL=wss://atlas-dev.joystream.org/ws-rpc VITE_DEVELOPMENT_FAUCET_URL=https://atlas-dev.joystream.org/member-faucet/register -VITE_DEVELOPMENT_YPP_FAUCET_URL=https://52.204.147.11.nip.io/membership +VITE_DEVELOPMENT_YPP_FAUCET_URL=https://50.19.175.219.nip.io/membershiphttps/ # Experimental env URLs VITE_NEXT_ORION_AUTH_URL= diff --git a/packages/atlas/src/components/FlexBox/FlexBox.ts b/packages/atlas/src/components/FlexBox/FlexBox.ts new file mode 100644 index 0000000000..9b63be9c27 --- /dev/null +++ b/packages/atlas/src/components/FlexBox/FlexBox.ts @@ -0,0 +1,35 @@ +import { css } from '@emotion/react' +import styled from '@emotion/styled' + +import { sizes } from '@/styles' + +type FlexBoxProps = { + gap?: number + flow?: 'row' | 'column' | 'column-reverse' | 'row-reverse' + alignItems?: string + justifyContent?: string + equalChildren?: boolean + width?: string | number + marginTop?: number +} + +export const FlexBox = styled.div` + display: flex; + ${(props) => css` + gap: ${sizes(props.gap ?? 1)}; + flex-direction: ${props.flow ?? 'row'}; + align-items: ${props.alignItems ?? 'start'}; + justify-content: ${props.justifyContent ?? 'start'}; + width: ${props.width ?? '100%'}; + margin-top: ${props.marginTop ? sizes(props.marginTop) : 'none'}; + `} + ${(props) => + props.equalChildren + ? css` + > * { + flex: 1; + min-width: 0; + } + ` + : ''} +` diff --git a/packages/atlas/src/components/FlexBox/index.ts b/packages/atlas/src/components/FlexBox/index.ts new file mode 100644 index 0000000000..3e7ca541b0 --- /dev/null +++ b/packages/atlas/src/components/FlexBox/index.ts @@ -0,0 +1 @@ +export * from './FlexBox' diff --git a/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx b/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx index cdcc3f7bb4..d3afbd022a 100644 --- a/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx +++ b/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx @@ -1,6 +1,7 @@ import { css } from '@emotion/react' import styled from '@emotion/styled' +import { FlexBox } from '@/components/FlexBox' import { media } from '@/styles' type ReponsivenessObject = Partial> @@ -63,3 +64,23 @@ export const GridItem = styled('div', { ${createBreakpointGridItemRules('xl')} ${createBreakpointGridItemRules('xxl')} ` + +export const FlexGridItem = styled(FlexBox, { + shouldForwardProp: (prop) => !filteredProps.includes(prop as string), +})` + min-width: 0; + + ${({ colStart }) => !isResponsivenessObject(colStart) && colStart && `grid-column-start: ${colStart};`} + ${({ colSpan }) => !isResponsivenessObject(colSpan) && colSpan && `grid-column-end: span ${colSpan};`} + ${({ rowStart }) => !isResponsivenessObject(rowStart) && rowStart && `grid-row-start: ${rowStart};`} + ${({ rowSpan }) => !isResponsivenessObject(rowSpan) && rowSpan && `grid-row-end: span ${rowSpan};`} + + ${createBreakpointGridItemRules('base')} + ${createBreakpointGridItemRules('xxs')} + ${createBreakpointGridItemRules('xs')} + ${createBreakpointGridItemRules('sm')} + ${createBreakpointGridItemRules('md')} + ${createBreakpointGridItemRules('lg')} + ${createBreakpointGridItemRules('xl')} + ${createBreakpointGridItemRules('xxl')} +` diff --git a/packages/atlas/src/components/_buttons/Button/Button.styles.ts b/packages/atlas/src/components/_buttons/Button/Button.styles.ts index 079dd162e8..0a10e69e2e 100644 --- a/packages/atlas/src/components/_buttons/Button/Button.styles.ts +++ b/packages/atlas/src/components/_buttons/Button/Button.styles.ts @@ -426,6 +426,10 @@ export const ButtonBase = styled('button', { shouldForwardProp: isPropValid }) (rounded ? '999px' : cVar('radiusSmall'))}; + span { + white-space: nowrap; + } + &:focus ${BorderWrapper} { visibility: visible; } diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.stories.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.stories.tsx index c1f4076d56..1dc65a6b95 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.stories.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.stories.tsx @@ -1,7 +1,5 @@ import styled from '@emotion/styled' -import { Meta, Story } from '@storybook/react' - -import { Text } from '@/components/Text' +import { Meta, StoryFn } from '@storybook/react' import { BenefitCard, BenefitCardProps } from './BenefitCard' @@ -14,44 +12,11 @@ export default { args: { title: 'Share Atlas video on YouTube', description: 'To share Atlas video you need to first upload your own video to the platform.', - steps: [ - <> - Click{' '} - - “Publish new video” - {' '} - button and proceed to video workspace - , - <> - While publishing your new video make sure that all assets like thumbnail, video file, title, description are the{' '} - - same as used previously on YouTube - - , - <> - After completing the upload -{' '} - - contact your collabolator on discord - - , - 'Place the link in the video description', - ], - variant: 'full', - actionButton: { - text: 'Publish new video', - }, - dollarAmount: { - type: 'number', - amount: 2.56, - }, - joyAmount: { - type: 'number', - amount: 12356, - }, + dollarAmount: 69, }, } as Meta -const Template: Story = (args) => ( +const Template: StoryFn = (args) => ( {' '} diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts index 8617390099..771a209ee4 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts @@ -4,9 +4,9 @@ import pattern1 from '@/assets/illustrations/svgs/other-benefit-card-pattern-1.s import pattern2 from '@/assets/illustrations/svgs/other-benefit-card-pattern-2.svg' import pattern3 from '@/assets/illustrations/svgs/other-benefit-card-pattern-3.svg' import { JoyTokenIcon } from '@/components/JoyTokenIcon' -import { Text } from '@/components/Text' +import { LayoutGrid } from '@/components/LayoutGrid' import { Button } from '@/components/_buttons/Button' -import { cVar, media, sizes, square } from '@/styles' +import { cVar, media, sizes } from '@/styles' export type Variant = 'compact' | 'full' @@ -46,9 +46,10 @@ export const Pattern = styled.div` export const Wrapper = styled.div<{ variant: Variant }>` background-color: ${({ variant }) => cVar(variant === 'full' ? 'colorBackgroundMuted' : 'colorBackground')}; - + width: 100%; + display: grid; ${media.sm} { - display: flex; + grid-template-columns: auto 1fr; } ${Pattern} { @@ -93,12 +94,6 @@ export const Content = styled.div<{ isCompact: boolean }>` } ` -export const TitleWrapper = styled.div` - display: flex; - justify-content: space-between; - gap: ${sizes(6)}; -` - export const StyledList = styled.ul` padding-left: 0; counter-reset: list-number; @@ -108,28 +103,6 @@ export const StyledList = styled.ul` margin-bottom: 0; ` -export const ListElement = styled(Text)` - list-style: none; - display: grid; - grid-template-columns: auto 1fr; - align-items: flex-start; - gap: inherit; - - ::before { - ${square('20px')}; - - counter-increment: list-number; - content: counter(list-number); - display: inline-flex; - align-items: center; - justify-content: center; - font: ${cVar('typographyDesktopT100')}; - background-color: ${cVar('colorBackgroundStrong')}; - border-radius: 50%; - color: ${cVar('colorTextStrong')}; - } -` - export const StyledButton = styled(Button)` ${media.sm} { align-self: flex-end; @@ -137,33 +110,10 @@ export const StyledButton = styled(Button)` } ` -export const ActionWrapper = styled.div` - display: flex; - flex-direction: column; - margin-top: ${sizes(4)}; - - ${media.sm} { - margin-top: 0; - text-align: right; - } -` - -export const RewardWrapper = styled.div<{ isCompact: boolean }>` - display: ${({ isCompact }) => (isCompact ? 'flex' : 'block')}; - text-align: ${({ isCompact }) => (!isCompact ? 'right' : 'left')}; - - ${media.sm} { - display: block; - text-align: right; - } -` - -export const TokenRewardWrapper = styled.div` - display: flex; - align-items: center; - justify-content: flex-end; -` - export const StyledJoyTokenIcon = styled(JoyTokenIcon)` margin-right: ${({ size }) => sizes(size === 16 ? 1 : 2)}; ` + +export const ContenBox = styled(LayoutGrid)` + padding: ${sizes(6)}; +` diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx index 116cf6e104..92151191ac 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx @@ -1,224 +1,54 @@ -import BN from 'bn.js' import { FC, ReactNode } from 'react' -import { NumberFormat } from '@/components/NumberFormat' +import { FlexBox } from '@/components/FlexBox' +import { Information } from '@/components/Information' +import { FlexGridItem } from '@/components/LayoutGrid' import { Text } from '@/components/Text' -import { atlasConfig } from '@/config' import { useMediaMatch } from '@/hooks/useMediaMatch' -import { - ActionWrapper, - Content, - ListElement, - Pattern, - RewardWrapper, - StyledButton, - StyledJoyTokenIcon, - StyledList, - TitleWrapper, - TokenRewardWrapper, - Variant, - Wrapper, -} from './BenefitCard.styles' - -type JoyAmountRange = { - type: 'range' - max: BN | number - min: BN | number | null -} -type JoyAmountNumber = { - type: 'number' - amount: BN | number -} - -type DollarAmountRange = { - type: 'range' - max: number - min: number | null -} -type DollarAmountNumber = { - type: 'number' - amount: number -} +import { ContenBox, Pattern, Wrapper } from './BenefitCard.styles' export type BenefitCardProps = { - variant?: Variant title: string description?: string - steps?: ReactNode[] - actionButton?: { - text: string - onClick?: () => void - to?: string - } - joyAmount: JoyAmountNumber | JoyAmountRange | null - dollarAmount?: DollarAmountNumber | DollarAmountRange | null + dollarAmount?: number className?: string + actionNode?: ReactNode } -export const BenefitCard: FC = ({ - variant = 'full', - title, - description, - steps, - actionButton, - joyAmount, - dollarAmount, - className, -}) => { +export const BenefitCard: FC = ({ title, description, dollarAmount, className, actionNode }) => { const smMatch = useMediaMatch('sm') - const isFullVariant = variant === 'full' - - const RewardAmount = () => { - const isJoyTokenIconVisible = - !(dollarAmount && !joyAmount) && - ((!smMatch && !isFullVariant && dollarAmount) || (!isFullVariant && dollarAmount) || isFullVariant) - return ( - - - {!!dollarAmount && !isFullVariant && ( - - + - - )} - {!!dollarAmount && dollarAmount.type === 'number' && ( - - )} - {!!dollarAmount && dollarAmount.type === 'range' && dollarAmount.min && ( - <> - - - - - - - - )} - {!!dollarAmount && dollarAmount.type === 'range' && !dollarAmount.min && ( - <> - - up to - - - - )} - {!!dollarAmount && !joyAmount && ( - - USD - - )} - - {joyAmount && ( - <> - - {isJoyTokenIconVisible ? ( - - ) : ( - - + - - )} - {joyAmount.type === 'number' && ( - - )} - {joyAmount.type === 'range' && joyAmount.min && ( - <> - - - - - - - - )} - {(!dollarAmount && !isFullVariant) || - (dollarAmount && !joyAmount && ( - - {dollarAmount ? 'USD' : atlasConfig.joystream.tokenTicker} - - ))} - - - )} - - ) - } + const lgMatch = useMediaMatch('lg') return ( - + - -
- -
- - {title} + + + + {title} + + + {description} + + + + {dollarAmount && ( + + + +{dollarAmount} USD - - {description} - -
- {!smMatch && isFullVariant && } -
- {steps && isFullVariant && ( - - {steps?.map((step, idx) => ( - - {step} - - ))} - - )} -
- - {(smMatch || !isFullVariant) && } - {actionButton && isFullVariant && ( - - {actionButton.text} - + + )} - -
+ {actionNode} + +
) } diff --git a/packages/atlas/src/views/global/YppLandingView/YppRewardSection.tsx b/packages/atlas/src/views/global/YppLandingView/YppRewardSection.tsx index 7117057d76..5da636b924 100644 --- a/packages/atlas/src/views/global/YppLandingView/YppRewardSection.tsx +++ b/packages/atlas/src/views/global/YppLandingView/YppRewardSection.tsx @@ -1,216 +1,215 @@ -import { FC, useRef, useState } from 'react' +// import { useRef, useState } from 'react' -import { Information } from '@/components/Information' -import { LayoutGrid } from '@/components/LayoutGrid' -import { NumberFormat } from '@/components/NumberFormat' -import { Text } from '@/components/Text' -import { TooltipText } from '@/components/Tooltip/Tooltip.styles' -import { BenefitCard } from '@/components/_ypp/BenefitCard' -import { atlasConfig } from '@/config' -import { useMediaMatch } from '@/hooks/useMediaMatch' +// +// import { Information } from '@/components/Information' +// import { LayoutGrid } from '@/components/LayoutGrid' +// import { NumberFormat } from '@/components/NumberFormat' +// import { Text } from '@/components/Text' +// import { TooltipText } from '@/components/Tooltip/Tooltip.styles' +// import { BenefitCard } from '@/components/_ypp/BenefitCard' +// import { atlasConfig } from '@/config' +// import { useMediaMatch } from '@/hooks/useMediaMatch' -import { - BackgroundContainer, - CenteredLayoutGrid, - HeaderGridItem, - StyledLimitedWidthContainer, -} from './YppLandingView.styles' -import { - BenefitsCardButton, - BenefitsCardsButtonsGroup, - BenefitsCardsContainerGridItem, - ColorAnchor, - RewardsSubtitleGridItem, - RewardsSubtitleWrapper, -} from './YppRewardSection.styles' +// import { +// BackgroundContainer, +// CenteredLayoutGrid, +// HeaderGridItem, +// StyledLimitedWidthContainer, +// } from './YppLandingView.styles' +// import { +// BenefitsCardButton, +// BenefitsCardsButtonsGroup, +// BenefitsCardsContainerGridItem, +// ColorAnchor, +// RewardsSubtitleGridItem, +// RewardsSubtitleWrapper, +// } from './YppRewardSection.styles' -export const calculateReward = ( - amount: number | number[] | { min: number | null; max: number } | null, - multiplier: number | number[], - tier: number -) => { - if (amount === null) { - return null - } else if (typeof amount === 'number') { - return { - type: 'number' as const, - amount: amount * (typeof multiplier === 'number' ? multiplier : multiplier[tier]), - } - } else if (Array.isArray(amount)) { - return { - type: 'number' as const, - amount: amount[tier], - } - } else { - return { type: 'range' as const, min: amount.min, max: amount.max } - } -} - -export const YppRewardSection: FC = () => { - const mdMatch = useMediaMatch('md') - const tiers = atlasConfig.features.ypp.tiersDefinition?.tiers - const rewards = atlasConfig.features.ypp.rewards - const [activeTier, setActiveTier] = useState((tiers && tiers.length - 1) || 0) - const ref = useRef(null) - - if (!rewards?.length) { - return null - } +// export const calculateReward = ( +// amount: number | number[] | { min: number | null; max: number } | null, +// multiplier: number | number[], +// tier: number +// ) => { +// if (amount === null) { +// return null +// } else if (typeof amount === 'number') { +// return { +// type: 'number' as const, +// amount: amount * (typeof multiplier === 'number' ? multiplier : multiplier[tier]), +// } +// } else if (Array.isArray(amount)) { +// return { +// type: 'number' as const, +// amount: amount[tier], +// } +// } else { +// return { type: 'range' as const, min: amount.min, max: amount.max } +// } +// } - return ( - - - - - - Rewards overview - - - Guaranteed payouts for sign up, referrals and quality content. The more subscribers on your YouTube - channel, the higher are the rewards to reap! - - - +export const YppRewardSection = () => { + // const mdMatch = useMediaMatch('md') + // const tiers = atlasConfig.features.ypp.tiersDefinition?.tiers + // const rewards = atlasConfig.features.ypp.rewards + // const [activeTier, setActiveTier] = useState((tiers && tiers.length - 1) || 0) + // const ref = useRef(null) - {tiers && ( - - {tiers.map((tier, idx, tierArray) => { - const isFirstTier = idx === 0 - const isLastTier = idx === tierArray.length - 1 - const isActiveTier = idx === activeTier - if (isFirstTier) { - return ( - setActiveTier(idx)} - > - < - {' '} - - ) - } - if (isLastTier) { - return ( - setActiveTier(idx)} - > - > - {' '} - - ) - } - return ( - setActiveTier(idx)} - > - - - - {' '} - - ) - })} - - )} - - - {rewards.map((reward) => { - const customMultiplier = reward.customMultiplier && reward.customMultiplier[activeTier] - const currentMultiplier = tiers ? tiers[activeTier].multiplier : 1 - const rewardAmount = calculateReward(reward.baseAmount, customMultiplier || currentMultiplier, activeTier) - const rewardAmountUsd = calculateReward( - reward.baseUsdAmount, - customMultiplier || currentMultiplier, - activeTier - ) + return null - return ( - - ) - })} - - - - - Payments are made in {atlasConfig.joystream.tokenTicker} tokens - - - {atlasConfig.joystream.tokenTicker} token is a native crypto asset of Joystream blockchain. It is - used for platform governance, purchasing NFTs, trading creator tokens, and covering blockchain - processing fees. They are listed on{' '} - - MEXC - {' '} - exchange under "JOYSTREAM" ticker. - - } - multiline - reference={ref.current} - delay={1000} - /> - - - - - - ) + // return ( + // + // + // + // + // + // Rewards overview + // + // + // Guaranteed payouts for sign up, referrals and quality content. The more subscribers on your YouTube + // channel, the higher are the rewards to reap! + // + // + // + // + // {tiers && ( + // + // {tiers.map((tier, idx, tierArray) => { + // const isFirstTier = idx === 0 + // const isLastTier = idx === tierArray.length - 1 + // const isActiveTier = idx === activeTier + // if (isFirstTier) { + // return ( + // setActiveTier(idx)} + // > + // < + // {' '} + // + // ) + // } + // if (isLastTier) { + // return ( + // setActiveTier(idx)} + // > + // > + // {' '} + // + // ) + // } + // return ( + // setActiveTier(idx)} + // > + // + // - + // {' '} + // + // ) + // })} + // + // )} + // + // + // {rewards.map((reward) => { + // const customMultiplier = reward.customMultiplier && reward.customMultiplier[activeTier] + // const currentMultiplier = tiers ? tiers[activeTier].multiplier : 1 + // const rewardAmount = calculateReward(reward.baseAmount, customMultiplier || currentMultiplier, activeTier) + // const rewardAmountUsd = calculateReward( + // reward.baseUsdAmount, + // customMultiplier || currentMultiplier, + // activeTier + // ) + // + // return ( + // + // ) + // })} + // + // + // + // + // Payments are made in {atlasConfig.joystream.tokenTicker} tokens + // + // + // {atlasConfig.joystream.tokenTicker} token is a native crypto asset of Joystream blockchain. It is + // used for platform governance, purchasing NFTs, trading creator tokens, and covering blockchain + // processing fees. They are listed on{' '} + // + // MEXC + // {' '} + // exchange under "JOYSTREAM" ticker. + // + // } + // multiline + // reference={ref.current} + // delay={1000} + // /> + // + // + // + // + // + // ) } diff --git a/packages/atlas/src/views/studio/StudioLayout.tsx b/packages/atlas/src/views/studio/StudioLayout.tsx index 99bae6fac2..c9ce2795b5 100644 --- a/packages/atlas/src/views/studio/StudioLayout.tsx +++ b/packages/atlas/src/views/studio/StudioLayout.tsx @@ -250,7 +250,7 @@ const _StudioLayout = () => { } - isAuth={channelSet && isYppSigned} + isAuth={channelSet} redirectTo={yppRedirect()} /> } diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 24bdcea48f..1c9f28c953 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -1,23 +1,23 @@ import { FC } from 'react' import { SvgActionNewTab, SvgAlertsError24, SvgAlertsInformative24 } from '@/assets/icons' -import { Banner } from '@/components/Banner' +import { FlexBox } from '@/components/FlexBox' +import { Information } from '@/components/Information' +import { GridItem, LayoutGrid } from '@/components/LayoutGrid' import { NumberFormat } from '@/components/NumberFormat' import { Text } from '@/components/Text' import { WidgetTile } from '@/components/WidgetTile' -import { Button } from '@/components/_buttons/Button' +import { Button, TextButton } from '@/components/_buttons/Button' import { BenefitCard } from '@/components/_ypp/BenefitCard' import { atlasConfig } from '@/config' -import { useClipboard } from '@/hooks/useClipboard' -import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' -import { useUser } from '@/providers/user/user.hooks' +// import { useClipboard } from '@/hooks/useClipboard' +import { useMediaMatch } from '@/hooks/useMediaMatch' +// import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' +// import { useUser } from '@/providers/user/user.hooks' import { configYppIconMapper } from '@/views/global/YppLandingView/YppFooter' -import { calculateReward } from '@/views/global/YppLandingView/YppRewardSection' import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' -import { RewardsWrapper, StyledBanner, StyledSvgAlertsInformative24, WidgetsWrapper } from './YppDashboardTabs.styles' - -import { REWARDS } from '../YppDashboard.config' +import { StatusDot, StyledBanner, StyledSvgAlertsInformative24, YppSyncStatus } from './YppDashboardTabs.styles' const APP_NAME = atlasConfig.general.appName const tiers = atlasConfig.features.ypp.tiersDefinition?.tiers @@ -25,16 +25,23 @@ const tiers = atlasConfig.features.ypp.tiersDefinition?.tiers type YppDashboardMainTabProps = { currentTier?: number } - +const syncOn = true export const YppDashboardMainTab: FC = ({ currentTier = 0 }) => { - const { copyToClipboard } = useClipboard() - const { channelId } = useUser() + // const { copyToClipboard } = useClipboard() + // const { channelId } = useUser() + const mdMatch = useMediaMatch('md') + const lgMatch = useMediaMatch('lg') const { currentChannel } = useGetYppSyncedChannels() - const { trackReferralLinkGenerated } = useSegmentAnalytics() + // const { trackReferralLinkGenerated } = useSegmentAnalytics() const multiplier = tiers ? tiers[currentTier].multiplier : 1 return ( <> + } + title="Have more than one YouTube channel?" + description={`You can apply to the YouTube Partner Program with as many YouTube & ${APP_NAME} channels as you want. Each YouTube channel can be assigned to only one ${APP_NAME} channel. \nYou can create a new channel from the top right menu.`} + /> {currentChannel?.yppStatus === 'Suspended' && ( = ({ currentTier } /> )} - {atlasConfig.features.ypp.widgets && ( - - {atlasConfig.features.ypp.widgets.map((widget) => ( - , - to: widget.link, - iconPlacement: 'right', - }} - /> + + +
tier
+
+ + +
service status
+
+ + + + 22 sep 2023 + + } iconPlacement="right"> + Go to Airtable + + + } + /> + + {atlasConfig.features.ypp.widgets && + atlasConfig.features.ypp.widgets.map((widget) => ( + + + + {widget.icon ? configYppIconMapper[widget.icon] : null} + + {widget.title} + + + } iconPlacement="right"> + {widget.linkText ?? `Go to ${widget.title}`} + + + } + /> + ))} -
- )} - - {REWARDS?.map((reward) => { - const customMultiplier = reward.customMultiplier?.[currentTier] - const rewardAmount = calculateReward(reward.joyAmount, customMultiplier || multiplier, currentTier) - const rewardAmountUsd = calculateReward(reward.usdAmount, customMultiplier || multiplier, currentTier) - return ( - { - if ( - reward.actionButton && - 'copyReferral' in reward.actionButton && - reward.actionButton.copyReferral - ) { - trackReferralLinkGenerated(channelId) - copyToClipboard( - `${window.location.host}/ypp?referrerId=${channelId}`, - 'Referral link copied to clipboard' - ) - } - }, - } - : undefined - } - joyAmount={rewardAmount} - dollarAmount={rewardAmountUsd} - /> - ) - })} - - } - title="Have more than one YouTube channel?" - description={`You can apply to the YouTube Partner Program with as many YouTube & ${APP_NAME} channels as you want. Each YouTube channel can be assigned to only one ${APP_NAME} channel. \nYou can create a new channel from the top right menu.`} - /> + + + + + + + + Autosync: On + + + ) : ( + + + Suspended + + + + ) + } + /> + + + Copy referral link} + /> + + ) } diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts index 502fac3bef..29e4807b2f 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts @@ -1,3 +1,4 @@ +import { keyframes } from '@emotion/react' import styled from '@emotion/styled' import { SvgActionArrowRight, SvgAlertsInformative24, SvgAlertsWarning32 } from '@/assets/icons' @@ -73,3 +74,41 @@ export const StyledActionBar = styled(ActionBar)` export const FallbackContainer = styled.div` margin-top: 128px; ` +const dotPulse = keyframes` + 0% { + box-shadow: none; + } + + 10% { + box-shadow: 0 0 0 3px #0c984680; + } + + 20%, 100% { + box-shadow: none; + } + + +` +export const YppSyncStatus = styled.div` + display: flex; + align-items: center; + justify-content: center; + gap: ${sizes(2)}; + background-color: ${cVar('colorCoreNeutral800Lighten')}; + padding: ${sizes(2)} ${sizes(4)}; + border-radius: 99px; + width: fit-content; + + p { + white-space: nowrap; + } +` + +export const StatusDot = styled.div` + width: 10px; + height: 10px; + border-radius: 50%; + background: linear-gradient(#0ebe57, #096c34); + box-shadow: 0 0 0 5px #0c984680; + animation: 10s ease-out ${dotPulse} infinite; +` From 49d98a41554bee2de3b75bff4ea9ab85df423a14 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Thu, 28 Sep 2023 08:55:17 +0200 Subject: [PATCH 02/18] Ypp status widget --- .../ServiceStatusWidget.tsx | 88 +++++++++++++++++++ ...l.stories.tsx => YppStatusDot.stories.tsx} | 6 +- ...sPill.styles.ts => YppStatusDot.styles.ts} | 22 ++--- .../{YppStatusPill.tsx => YppStatusDot.tsx} | 38 ++------ .../components/_ypp/YppStatusPill/index.ts | 2 +- .../studio/YppDashboard/YppDashboard.tsx | 2 - .../YppDashboard/tabs/YppDashboardMainTab.tsx | 5 +- 7 files changed, 112 insertions(+), 51 deletions(-) create mode 100644 packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx rename packages/atlas/src/components/_ypp/YppStatusPill/{YppStatusPill.stories.tsx => YppStatusDot.stories.tsx} (50%) rename packages/atlas/src/components/_ypp/YppStatusPill/{YppStatusPill.styles.ts => YppStatusDot.styles.ts} (78%) rename packages/atlas/src/components/_ypp/YppStatusPill/{YppStatusPill.tsx => YppStatusDot.tsx} (51%) diff --git a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx new file mode 100644 index 0000000000..feefbc7b7b --- /dev/null +++ b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx @@ -0,0 +1,88 @@ +import styled from '@emotion/styled' +import { useMemo } from 'react' +import { useQuery } from 'react-query' + +import { axiosInstance } from '@/api/axios' +import { FlexBox } from '@/components/FlexBox' +import { Information } from '@/components/Information' +import { FlexGridItem, LayoutGrid } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { YppStatusDot } from '@/components/_ypp/YppStatusPill' +import { atlasConfig } from '@/config' +import { cVar, sizes } from '@/styles' +import { ConsoleLogger } from '@/utils/logs' + +type YppStatusDto = { + version: string + syncStatus: string + syncBacklog: number +} + +const YOUTUBE_BACKEND_URL = atlasConfig.features.ypp.youtubeSyncApiUrl +const YPP_DELAY_THRESHOLD = atlasConfig.features.ypp.yppDelayThreshold ?? 500 + +export const ServiceStatusWidget = () => { + const { data } = useQuery('ypp-status', () => + axiosInstance(`${YOUTUBE_BACKEND_URL}/status`).catch(() => + ConsoleLogger.warn('Failed to fetch YPP status') + ) + ) + + const details = useMemo(() => { + if (!data) return [] + const output: [number | string, string, string][] = [] // [value, title, tooltip] + output.push([data.data.syncBacklog, 'VIDEO IN QUEUE', "It's pretty long"]) + output.push([ + data.data.syncBacklog * 2, // no info bout it + 'PLACE IN THE QUEUE', + "We don' really know your place in the queue", + ]) + output.push([ + `In ${Math.round(data.data.syncBacklog / YPP_DELAY_THRESHOLD)} days`, + 'ETA TO FULL SYNC', + "Well we don't really know this either", + ]) + return output + }, [data]) + return ( + + + + YPP SYNC SERVICE STATUS + {data ? ( + YPP_DELAY_THRESHOLD + ? 'delayed' + : data.data.syncStatus === 'enabled' + ? 'operational' + : 'stopped' + } + /> + ) : null} + + + New YT video uploads are checked once every 24 hours + + + {details.map(([value, title, tooltip], idx) => ( + + + {value} + + + + {title} + + + + + ))} + + ) +} + +const LayoutBox = styled(LayoutGrid)` + background-color: ${cVar('colorBackgroundMuted')}; + padding: ${sizes(6)}; +` diff --git a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.stories.tsx b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.stories.tsx similarity index 50% rename from packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.stories.tsx rename to packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.stories.tsx index 35d85ab60c..7c5f38fe71 100644 --- a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.stories.tsx +++ b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.stories.tsx @@ -1,12 +1,12 @@ import { Meta, StoryFn } from '@storybook/react' -import { YppStatusPill } from '@/components/_ypp/YppStatusPill/YppStatusPill' +import { YppStatusDot } from '@/components/_ypp/YppStatusPill/YppStatusDot' export default { title: 'components/YppStatusPill', - component: YppStatusPill, + component: YppStatusDot, } as Meta -const Template: StoryFn = () => +const Template: StoryFn = () => export const Default = Template.bind({}) diff --git a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.styles.ts b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.styles.ts similarity index 78% rename from packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.styles.ts rename to packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.styles.ts index 13d723328c..f5d1e4e75e 100644 --- a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.styles.ts +++ b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.styles.ts @@ -1,18 +1,18 @@ import { css, keyframes } from '@emotion/react' import styled from '@emotion/styled' -import { cVar, sizes } from '@/styles' +import { sizes } from '@/styles' -export const Container = styled.div` - display: flex; - align-items: center; - justify-content: center; - gap: ${sizes(2)}; - background-color: ${cVar('colorCoreNeutral800Lighten')}; - padding: ${sizes(2)} ${sizes(4)}; - border-radius: 99px; - width: fit-content; -` +// export const Container = styled.div` +// display: flex; +// align-items: center; +// justify-content: center; +// gap: ${sizes(2)}; +// background-color: ${cVar('colorCoreNeutral800Lighten')}; +// padding: ${sizes(2)} ${sizes(4)}; +// border-radius: 99px; +// width: fit-content; +// ` type DotProps = { status: 'operational' | 'delayed' | 'stopped' diff --git a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.tsx b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.tsx similarity index 51% rename from packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.tsx rename to packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.tsx index 99e670c0b3..de2642815b 100644 --- a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.tsx +++ b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.tsx @@ -1,13 +1,9 @@ import { useRef } from 'react' -import { useQuery } from 'react-query' -import { axiosInstance } from '@/api/axios' import { Text } from '@/components/Text' import { Tooltip } from '@/components/Tooltip' -import { atlasConfig } from '@/config' -import { ConsoleLogger } from '@/utils/logs' -import { Container, StatusDot, TooltipBox } from './YppStatusPill.styles' +import { StatusDot, TooltipBox } from './YppStatusDot.styles' export type YppStatusType = 'operational' | 'delayed' | 'stopped' @@ -18,43 +14,21 @@ const getTooltipText = (status: YppStatusType) => { case 'operational': return ['FULLY OPERATIONAL', 'Everything works as expected!'] case 'stopped': + default: return ['ON HOLD', 'The sync network experienced a major outage - we are currently working on fixing the issue.'] } } -const YOUTUBE_BACKEND_URL = atlasConfig.features.ypp.youtubeSyncApiUrl -const YPP_DELAY_THRESHOLD = atlasConfig.features.ypp.yppDelayThreshold ?? 500 - -type YppStatusDto = { - version: string - syncStatus: string - syncBacklog: number +export type YppStatusDotProps = { + status: YppStatusType } - -export const YppStatusPill = () => { +export const YppStatusDot = ({ status }: YppStatusDotProps) => { const statusRef = useRef(null) - const { data, isLoading } = useQuery('ypp-status', () => - axiosInstance(`${YOUTUBE_BACKEND_URL}/status`).catch(() => - ConsoleLogger.warn('Failed to fetch YPP status') - ) - ) - - if (!data || isLoading) { - return null - } - - const isDelayed = data.data.syncBacklog > YPP_DELAY_THRESHOLD - const status: YppStatusType = isDelayed ? 'delayed' : data.data.syncStatus === 'enabled' ? 'operational' : 'stopped' const [tooltipTitle, tooltipText] = getTooltipText(status) return ( <> - - - YPP Sync Status: - - - + { YouTube Partner Program - {TIERS.length && !isLoading && ( {TIERS[currentTier].icon} diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 1c9f28c953..291b76b710 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -9,6 +9,7 @@ import { Text } from '@/components/Text' import { WidgetTile } from '@/components/WidgetTile' import { Button, TextButton } from '@/components/_buttons/Button' import { BenefitCard } from '@/components/_ypp/BenefitCard' +import { ServiceStatusWidget } from '@/components/_ypp/ServiceStatusWidget/ServiceStatusWidget' import { atlasConfig } from '@/config' // import { useClipboard } from '@/hooks/useClipboard' import { useMediaMatch } from '@/hooks/useMediaMatch' @@ -82,11 +83,11 @@ export const YppDashboardMainTab: FC = ({ currentTier )} -
tier
+
tier
-
service status
+
Date: Thu, 28 Sep 2023 13:06:15 +0200 Subject: [PATCH 03/18] Further work on YppDashboardTier.tsx --- packages/atlas/src/.env | 4 +- packages/atlas/src/assets/icons/index.ts | 4 + .../ServiceStatusWidget.tsx | 25 ++- .../YppDashboardTier.styles.ts | 162 ++++++++++++++++++ .../YppDashboardTier/YppDashboardTier.tsx | 137 +++++++++++++++ .../components/_ypp/YppDashboardTier/index.ts | 1 + .../YppStatusPill/YppStatusDot.stories.tsx | 2 +- packages/atlas/src/utils/misc.ts | 9 + packages/atlas/src/utils/time.ts | 8 + .../studio/MyVideosView/MyVideosView.tsx | 2 - .../YppDashboard/tabs/YppDashboardMainTab.tsx | 13 +- 11 files changed, 348 insertions(+), 19 deletions(-) create mode 100644 packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts create mode 100644 packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx create mode 100644 packages/atlas/src/components/_ypp/YppDashboardTier/index.ts diff --git a/packages/atlas/src/.env b/packages/atlas/src/.env index 7422a14c3e..70e8dd4f58 100644 --- a/packages/atlas/src/.env +++ b/packages/atlas/src/.env @@ -19,7 +19,7 @@ VITE_HCAPTCHA_SITE_KEY=41cae189-7676-4f6b-aa56-635be26d3ceb # YPP configuration VITE_GOOGLE_CONSOLE_CLIENT_ID=246331758613-rc1psegmsr9l4e33nqu8rre3gno5dsca.apps.googleusercontent.com -VITE_YOUTUBE_SYNC_API_URL=https://52.204.147.11.nip.io +VITE_YOUTUBE_SYNC_API_URL=https://50.19.175.219.nip.io VITE_YOUTUBE_COLLABORATOR_MEMBER_ID=18 # Analytics tools @@ -43,7 +43,7 @@ VITE_DEVELOPMENT_ORION_URL=https://atlas-dev.joystream.org/orion-api/graphql VITE_DEVELOPMENT_QUERY_NODE_SUBSCRIPTION_URL=wss://atlas-dev.joystream.org/orion-v2/graphql VITE_DEVELOPMENT_NODE_URL=wss://atlas-dev.joystream.org/ws-rpc VITE_DEVELOPMENT_FAUCET_URL=https://atlas-dev.joystream.org/member-faucet/register -VITE_DEVELOPMENT_YPP_FAUCET_URL=https://50.19.175.219.nip.io/membershiphttps/ +VITE_DEVELOPMENT_YPP_FAUCET_URL=https://50.19.175.219.nip.io/memberships # Experimental env URLs VITE_NEXT_ORION_AUTH_URL= diff --git a/packages/atlas/src/assets/icons/index.ts b/packages/atlas/src/assets/icons/index.ts index e2c5e444f8..aa669e10df 100644 --- a/packages/atlas/src/assets/icons/index.ts +++ b/packages/atlas/src/assets/icons/index.ts @@ -177,6 +177,10 @@ export * from './ControlsVideoModeCompactView' export * from './ControlsVideo' export * from './ControlsZoomIn' export * from './ControlsZoomOut' +export * from './IconRankBronze' +export * from './IconRankDiamond' +export * from './IconRankGold' +export * from './IconRankSilver' export * from './IllustrativeEdit' export * from './IllustrativeFileFailed' export * from './IllustrativeFileSelected' diff --git a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx index feefbc7b7b..6e344918ce 100644 --- a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx +++ b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx @@ -21,7 +21,11 @@ type YppStatusDto = { const YOUTUBE_BACKEND_URL = atlasConfig.features.ypp.youtubeSyncApiUrl const YPP_DELAY_THRESHOLD = atlasConfig.features.ypp.yppDelayThreshold ?? 500 -export const ServiceStatusWidget = () => { +export type ServiceStatusWidgetProps = { + isOptedIn: boolean +} + +export const ServiceStatusWidget = ({ isOptedIn }: ServiceStatusWidgetProps) => { const { data } = useQuery('ypp-status', () => axiosInstance(`${YOUTUBE_BACKEND_URL}/status`).catch(() => ConsoleLogger.warn('Failed to fetch YPP status') @@ -31,24 +35,26 @@ export const ServiceStatusWidget = () => { const details = useMemo(() => { if (!data) return [] const output: [number | string, string, string][] = [] // [value, title, tooltip] - output.push([data.data.syncBacklog, 'VIDEO IN QUEUE', "It's pretty long"]) + output.push([isOptedIn ? data.data.syncBacklog : '-', 'VIDEO IN QUEUE', "It's pretty long"]) output.push([ - data.data.syncBacklog * 2, // no info bout it + isOptedIn ? data.data.syncBacklog * 2 : '-', // no info bout it 'PLACE IN THE QUEUE', "We don' really know your place in the queue", ]) output.push([ - `In ${Math.round(data.data.syncBacklog / YPP_DELAY_THRESHOLD)} days`, + isOptedIn ? `In ${Math.round(data.data.syncBacklog / YPP_DELAY_THRESHOLD)} days` : '-', 'ETA TO FULL SYNC', "Well we don't really know this either", ]) return output - }, [data]) + }, [data, isOptedIn]) return ( - - YPP SYNC SERVICE STATUS + + + YPP SYNC SERVICE STATUS + {data ? ( { } /> ) : null} - + New YT video uploads are checked once every 24 hours @@ -84,5 +90,6 @@ export const ServiceStatusWidget = () => { const LayoutBox = styled(LayoutGrid)` background-color: ${cVar('colorBackgroundMuted')}; - padding: ${sizes(6)}; + padding: ${sizes(4)}; + height: 100%; ` diff --git a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts new file mode 100644 index 0000000000..301e8d6b9e --- /dev/null +++ b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts @@ -0,0 +1,162 @@ +import { css, keyframes } from '@emotion/react' +import styled from '@emotion/styled' + +import { FlexBox } from '@/components/FlexBox' +import { cVar, sizes, square } from '@/styles' + +type YppChannelTierTypes = 'Verified::Bronze' | 'Verified::Silver' | 'Verified::Gold' | 'Verified::Diamond' + +export const SignInWrapper = styled.div` + background-color: ${cVar('colorBackgroundAlpha')}; + border: 1px solid ${cVar('colorBorderAlpha')}; + display: grid; + place-items: center; + gap: ${sizes(1)}; + padding: ${sizes(3.5)}; + width: 100%; + border-radius: ${cVar('radiusMedium')}; + + svg { + ${square(32)}; + + path { + fill: ${cVar('colorTextMuted')}; + } + } + + :hover { + cursor: pointer; + background-color: ${cVar('colorBackgroundStrongAlpha')}; + + svg { + ${square(32)}; + + path { + fill: ${cVar('colorText')}; + } + } + } +` + +export const VerificationWrapper = styled.div` + background-color: ${cVar('colorBackgroundMutedAlpha')}; + padding: ${sizes(6)}; + width: 100%; + border-radius: ${cVar('radiusMedium')}; + + svg { + ${square(32)}; + + path { + fill: ${cVar('colorTextMuted')}; + } + } +` + +export const Wrapper = styled(FlexBox)` + padding: ${sizes(6)}; + height: 100%; + background-color: ${cVar('colorBackgroundMuted')}; +` +export const shine = keyframes` + 0% { + transform: translateX(0) rotate(21.56deg); + } + + 16% { + transform: translateX(1000px) rotate(21.56deg); + } + + 100% { + transform: translateX(1000px) rotate(21.56deg); + } +` + +export const TierWrapper = styled(FlexBox)<{ tier: YppChannelTierTypes }>` + border-radius: ${cVar('radiusMedium')}; + padding: ${sizes(4)}; + position: relative; + overflow: hidden; + + > div { + svg { + ${square(42)}; + } + + .pill { + background-color: rgb(10 28 41 / 0.52); + padding: ${sizes(0.5)} ${sizes(1.5)}; + border-radius: ${cVar('radiusMedium')}; + } + + z-index: 10; + } + + background: ${(props) => { + switch (props.tier) { + case 'Verified::Diamond': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #0FD4B9 0%, #49C9B9 67.19%, #2CAD9F 100%)' + case 'Verified::Gold': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #C68F00 0%, #DCAA00 67.19%, #EEC117 100%)' + case 'Verified::Silver': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #C5C7C3 0%, #817D78 67.19%, #615D6A 100%)' + default: + case 'Verified::Bronze': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #C77D6E 0%, #834C3F 67.19%, #603930 100%)' + } + }}; + + ::before { + content: ' '; + position: absolute; + inset: 0; + opacity: 0.3; + background: ${(props) => { + switch (props.tier) { + case 'Verified::Diamond': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #0FD4B9 0%, #49C9B9 67.19%, #2CAD9F 100%)' + case 'Verified::Gold': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #C68F00 0%, #DCAA00 67.19%, #EEC117 100%)' + case 'Verified::Silver': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #C5C7C3 0%, #817D78 67.19%, #615D6A 100%)' + default: + case 'Verified::Bronze': + return 'radial-gradient(128.74% 56.2% at 50% 97.53%, #C77D6E 0%, #834C3F 67.19%, #603930 100%)' + } + }}; + filter: url(#noise); + } + + ${(props) => + props.tier === 'Verified::Diamond' && + css` + ::after { + content: ' '; + width: 48px; + height: 150px; + top: -42px; + left: -150px; + filter: blur(2px); + transform: rotate(21.56deg); + position: absolute; + background: rgb(39 233 206 / 0.5); + animation: ${shine} 8.5s ease-out infinite; + } + `} +` + +export const SuspendedWrapper = styled.div` + background-color: rgb(254 71 71 / 0.15); + padding: ${sizes(6)}; + border: 1px dashed ${cVar('colorTextError')}; + width: 100%; + border-radius: ${cVar('radiusMedium')}; + + svg { + ${square(32)}; + + path { + fill: ${cVar('colorTextError')}; + } + } +` diff --git a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx new file mode 100644 index 0000000000..33f164140a --- /dev/null +++ b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx @@ -0,0 +1,137 @@ +import { + SvgActionLoader, + SvgActionNewChannel, + SvgAlertsError32, + SvgIconRankBronze, + SvgIconRankDiamond, + SvgIconRankGold, + SvgIconRankSilver, +} from '@/assets/icons' +import { FlexBox } from '@/components/FlexBox' +import { Information } from '@/components/Information' +import { Text } from '@/components/Text' +import { convertUpperCamelToSentence } from '@/utils/misc' + +import { SignInWrapper, SuspendedWrapper, TierWrapper, VerificationWrapper, Wrapper } from './YppDashboardTier.styles' + +type YppChannelTierTypes = 'Verified::Bronze' | 'Verified::Silver' | 'Verified::Gold' | 'Verified::Diamond' + +type YppChannelSuspendedTypes = + | 'Suspended::SubparQuality' + | 'Suspended::DuplicateContent' + | 'Suspended::UnsupportedTopic' + | 'Suspended::ProgramTermsExploit' + +type YppChannelStatus = YppChannelTierTypes | YppChannelSuspendedTypes | 'Unverified' | 'OptedOut' + +export type YppDashboardTierProps = { + status?: YppChannelStatus +} + +export const YppDashboardTier = ({ status }: YppDashboardTierProps) => { + const content = () => { + switch (true) { + case status?.startsWith('Verified'): + return + case status?.startsWith('Suspended'): + return + case status === 'Unverified': + return + default: + case !status: + return + } + } + return ( + + + + CHANNEL YPP STATUS + + + + {content()} + + ) +} + +const SignInContent = () => { + return ( + + + + Sign up to participate + + + ) +} + +const VerificationBox = () => { + return ( + + + + + + Verification pending + + + May take up to 48 hours + + + + + ) +} + +const getTierIcon = (tier: YppChannelTierTypes) => { + switch (tier) { + case 'Verified::Diamond': + return + case 'Verified::Gold': + return + case 'Verified::Silver': + return + case 'Verified::Bronze': + default: + return + } +} + +const TierBox = ({ tier }: { tier: YppChannelTierTypes }) => { + return ( + + + {getTierIcon(tier)} + + + Verified + +
+ + {tier.split('::')[1]} tier + +
+
+
+
+ ) +} + +const SuspendedBox = ({ status }: { status: YppChannelSuspendedTypes }) => { + return ( + + + + + + Suspended + + + Reason: {convertUpperCamelToSentence(status.split('::')[1])} + + + + + ) +} diff --git a/packages/atlas/src/components/_ypp/YppDashboardTier/index.ts b/packages/atlas/src/components/_ypp/YppDashboardTier/index.ts new file mode 100644 index 0000000000..44de06ca8b --- /dev/null +++ b/packages/atlas/src/components/_ypp/YppDashboardTier/index.ts @@ -0,0 +1 @@ +export * from './YppDashboardTier' diff --git a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.stories.tsx b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.stories.tsx index 7c5f38fe71..4cc2fb3459 100644 --- a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.stories.tsx +++ b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.stories.tsx @@ -7,6 +7,6 @@ export default { component: YppStatusDot, } as Meta -const Template: StoryFn = () => +const Template: StoryFn = () => export const Default = Template.bind({}) diff --git a/packages/atlas/src/utils/misc.ts b/packages/atlas/src/utils/misc.ts index 1fd5fc9283..77e3a3539b 100644 --- a/packages/atlas/src/utils/misc.ts +++ b/packages/atlas/src/utils/misc.ts @@ -80,3 +80,12 @@ export const retryWalletPromise = ( .catch((error) => reject(error)) }) } + +export function convertUpperCamelToSentence(input: string) { + const words = input.split(/(?=[A-Z])/) + return words + .map((word, index) => { + return index === 0 ? word.charAt(0).toUpperCase() + word.slice(1) : word.toLowerCase() + }) + .join(' ') +} diff --git a/packages/atlas/src/utils/time.ts b/packages/atlas/src/utils/time.ts index 5c7a081e86..261c37de36 100644 --- a/packages/atlas/src/utils/time.ts +++ b/packages/atlas/src/utils/time.ts @@ -40,3 +40,11 @@ export const getTimeDiffInSeconds = (time: Date) => Math.max(0, Math.round((time export const convertDateFormat = (timestamp: Date | string) => { return timestamp instanceof Date ? timestamp : parseISO(timestamp) } + +export const getNextFriday = (date = new Date()) => { + const nextDate = new Date(date) + const currentDayOfWeek = nextDate.getDay() + const daysToAdd = currentDayOfWeek <= 5 ? 5 - currentDayOfWeek : 7 - currentDayOfWeek + 5 + nextDate.setDate(nextDate.getDate() + daysToAdd) + return nextDate +} diff --git a/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx b/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx index 739a9c93a0..89c6a17d65 100644 --- a/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx +++ b/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx @@ -17,7 +17,6 @@ import { MintNftFirstTimeModal } from '@/components/_overlays/MintNftFirstTimeMo import { MintNftModal } from '@/components/_overlays/MintNftModal' import { VideoTileDraft } from '@/components/_video/VideoTileDraft' import { VideoTilePublisher } from '@/components/_video/VideoTilePublisher' -import { YppStatusPill } from '@/components/_ypp/YppStatusPill' import { atlasConfig } from '@/config' import { cancelledVideoFilter } from '@/config/contentFilter' import { absoluteRoutes } from '@/config/routes' @@ -362,7 +361,6 @@ export const MyVideosView = () => { My videos - {currentChannel && } {!smMatch && sortVisibleAndUploadButtonVisible && ( } fullWidth {...uploadVideoButtonProps}> diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 291b76b710..090ce95e86 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -10,9 +10,11 @@ import { WidgetTile } from '@/components/WidgetTile' import { Button, TextButton } from '@/components/_buttons/Button' import { BenefitCard } from '@/components/_ypp/BenefitCard' import { ServiceStatusWidget } from '@/components/_ypp/ServiceStatusWidget/ServiceStatusWidget' +import { YppDashboardTier } from '@/components/_ypp/YppDashboardTier' import { atlasConfig } from '@/config' // import { useClipboard } from '@/hooks/useClipboard' import { useMediaMatch } from '@/hooks/useMediaMatch' +import { formatDate, getNextFriday } from '@/utils/time' // import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' // import { useUser } from '@/providers/user/user.hooks' import { configYppIconMapper } from '@/views/global/YppLandingView/YppFooter' @@ -35,7 +37,8 @@ export const YppDashboardMainTab: FC = ({ currentTier const { currentChannel } = useGetYppSyncedChannels() // const { trackReferralLinkGenerated } = useSegmentAnalytics() const multiplier = tiers ? tiers[currentTier].multiplier : 1 - + const nextPayoutDate = getNextFriday() + console.log(currentChannel, 'xd') return ( <> = ({ currentTier )} -
tier
+
- + = ({ currentTier customNode={ - 22 sep 2023 + {formatDate(nextPayoutDate)} } iconPlacement="right"> Go to Airtable @@ -131,7 +134,7 @@ export const YppDashboardMainTab: FC = ({ currentTier From 4445573cb5f86992ae27328b4ff4b4f765812b45 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Thu, 28 Sep 2023 13:56:01 +0200 Subject: [PATCH 04/18] Configure tiers --- packages/atlas/atlas.config.yml | 46 ++++-- .../YppReferralTable/YppReferralTable.tsx | 9 +- .../_ypp/BenefitCard/BenefitCard.tsx | 30 +++- .../ServiceStatusWidget.tsx | 16 +- .../YppDashboardTier.styles.ts | 3 +- .../YppDashboardTier/YppDashboardTier.tsx | 24 ++- packages/atlas/src/config/configSchema.ts | 16 +- .../src/hooks/useYppAuthorizeHandler.tsx | 73 +++++++++ .../global/YppLandingView/YppLandingView.tsx | 66 +------- .../YppLandingView/YppLandingView.types.ts | 11 +- .../YppDashboard/YppDashboard.config.tsx | 53 +------ .../studio/YppDashboard/YppDashboard.tsx | 51 +----- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 150 +++++++++++------- .../YppDashboardReferralsTab.tsx | 19 ++- 14 files changed, 284 insertions(+), 283 deletions(-) create mode 100644 packages/atlas/src/hooks/useYppAuthorizeHandler.tsx diff --git a/packages/atlas/atlas.config.yml b/packages/atlas/atlas.config.yml index 17f1be811c..639f36757f 100644 --- a/packages/atlas/atlas.config.yml +++ b/packages/atlas/atlas.config.yml @@ -51,20 +51,38 @@ features: enrollmentUsdReward: 5 # Amount for successful enrollment in YPP in USD. referralBaseReward: 2.5 # Self-explanatory, it should match `baseUsdAmount.min` value in last `rewards` entry tiersDefinition: # Tiers for YouTube partner program rewards. You can provide maximum three tiers. Each tier should has own multiplier. - tiers: - - minimumSubscribers: 0 - multiplier: 1 - - minimumSubscribers: 1000 - multiplier: 2 - - minimumSubscribers: 5000 - multiplier: 4 - - minimumSubscribers: 25000 - multiplier: 6 - - minimumSubscribers: 50000 - multiplier: 8 - - minimumSubscribers: 100000 - multiplier: 10 - tiersTooltip: The more subscribers you have on YouTube, the higher the payouts in the program. + - tier: 'bronze' + reqs: + - Lower effort production. + - Growing subscriber count base of channel supporters. + rewards: + - 0 # Reward for signup in USD + - 0 # Reward for synced video + - 0 # Reward for referral + - tier: 'silver' + reqs: + - Original good quality of content. + - Sizeable subscriber base with high ratio of views for videos. + rewards: + - 10 + - 1 + - 10 + - tier: 'gold' + reqs: + - Great quality of content. + - Large subscriber base of fans active in the comments section. + rewards: + - 30 + - 3 + - 20 + - tier: 'diamond' + reqs: + - Top tier professional quality. + - Recognized influencer & large follower audience + rewards: + - 100 + - 5 + - 50 rewards: - title: Sign Up to YouTube Partner Program showInDashboard: false # Optional. If false the reward will be shown only on YouTube partner program landing page. Default true diff --git a/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx b/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx index a44ce8e08d..bb7ef48009 100644 --- a/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx +++ b/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx @@ -12,7 +12,6 @@ import { useBlockTimeEstimation } from '@/hooks/useBlockTimeEstimation' import { SentryLogger } from '@/utils/logs' import { formatNumber } from '@/utils/number' import { formatDateTime } from '@/utils/time' -import { TIERS } from '@/views/studio/YppDashboard/YppDashboard.config' import { TierDescription, TierWrapper } from '@/views/studio/YppDashboard/YppDashboard.styles' import { COLUMNS, tableLoadingData } from './YppReferralTable.utils' @@ -80,15 +79,13 @@ const Channel = ({ channel }: { channel: YppReferral['channel'] }) => { const Tier = ({ tier }: { tier: number }) => { return ( - {TIERS[tier].icon} + {/*{TIERS[tier].icon}*/}
- Tier {tier + 1}{' '} + Tier - - {TIERS[tier].rules} - +
diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx index 92151191ac..6c069be123 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx @@ -12,11 +12,19 @@ export type BenefitCardProps = { title: string description?: string dollarAmount?: number + isRangeAmount?: boolean className?: string actionNode?: ReactNode } -export const BenefitCard: FC = ({ title, description, dollarAmount, className, actionNode }) => { +export const BenefitCard: FC = ({ + title, + description, + dollarAmount, + className, + actionNode, + isRangeAmount, +}) => { const smMatch = useMediaMatch('sm') const lgMatch = useMediaMatch('lg') @@ -38,11 +46,23 @@ export const BenefitCard: FC = ({ title, description, dollarAm alignItems={smMatch ? 'center' : 'start'} flow={smMatch ? (lgMatch ? 'row' : 'row-reverse') : 'column'} > - {dollarAmount && ( + {typeof dollarAmount === 'number' && ( - - +{dollarAmount} USD - + {isRangeAmount ? ( + + + Up to +{dollarAmount} USD + + + Depending on tier + + + ) : ( + + {dollarAmount > 0 ? `+${dollarAmount} USD` : 'Not paid'} + + )} + )} diff --git a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx index 6e344918ce..d5b91e1ef7 100644 --- a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx +++ b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx @@ -11,6 +11,7 @@ import { YppStatusDot } from '@/components/_ypp/YppStatusPill' import { atlasConfig } from '@/config' import { cVar, sizes } from '@/styles' import { ConsoleLogger } from '@/utils/logs' +import { YppChannelStatus } from '@/views/global/YppLandingView/YppLandingView.types' type YppStatusDto = { version: string @@ -22,10 +23,10 @@ const YOUTUBE_BACKEND_URL = atlasConfig.features.ypp.youtubeSyncApiUrl const YPP_DELAY_THRESHOLD = atlasConfig.features.ypp.yppDelayThreshold ?? 500 export type ServiceStatusWidgetProps = { - isOptedIn: boolean + status?: YppChannelStatus } -export const ServiceStatusWidget = ({ isOptedIn }: ServiceStatusWidgetProps) => { +export const ServiceStatusWidget = ({ status }: ServiceStatusWidgetProps) => { const { data } = useQuery('ypp-status', () => axiosInstance(`${YOUTUBE_BACKEND_URL}/status`).catch(() => ConsoleLogger.warn('Failed to fetch YPP status') @@ -34,20 +35,23 @@ export const ServiceStatusWidget = ({ isOptedIn }: ServiceStatusWidgetProps) => const details = useMemo(() => { if (!data) return [] + const hideData = !status || !status.startsWith('Verified') const output: [number | string, string, string][] = [] // [value, title, tooltip] - output.push([isOptedIn ? data.data.syncBacklog : '-', 'VIDEO IN QUEUE', "It's pretty long"]) + output.push([hideData ? '-' : data.data.syncBacklog, 'VIDEOS IN QUEUE', "It's pretty long"]) output.push([ - isOptedIn ? data.data.syncBacklog * 2 : '-', // no info bout it + hideData ? '-' : data.data.syncBacklog * 2, // no info bout it 'PLACE IN THE QUEUE', "We don' really know your place in the queue", ]) output.push([ - isOptedIn ? `In ${Math.round(data.data.syncBacklog / YPP_DELAY_THRESHOLD)} days` : '-', + // isOptedIn ? `In ${Math.round(data.data.syncBacklog / YPP_DELAY_THRESHOLD)} days` : '-', + '-', 'ETA TO FULL SYNC', "Well we don't really know this either", ]) return output - }, [data, isOptedIn]) + }, [data, status]) + return ( diff --git a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts index 301e8d6b9e..dfa75c6c0e 100644 --- a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts +++ b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.styles.ts @@ -3,8 +3,7 @@ import styled from '@emotion/styled' import { FlexBox } from '@/components/FlexBox' import { cVar, sizes, square } from '@/styles' - -type YppChannelTierTypes = 'Verified::Bronze' | 'Verified::Silver' | 'Verified::Gold' | 'Verified::Diamond' +import { YppChannelTierTypes } from '@/views/global/YppLandingView/YppLandingView.types' export const SignInWrapper = styled.div` background-color: ${cVar('colorBackgroundAlpha')}; diff --git a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx index 33f164140a..cb1cd1b3e0 100644 --- a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx +++ b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx @@ -11,24 +11,20 @@ import { FlexBox } from '@/components/FlexBox' import { Information } from '@/components/Information' import { Text } from '@/components/Text' import { convertUpperCamelToSentence } from '@/utils/misc' +import { + YppChannelStatus, + YppChannelSuspendedTypes, + YppChannelTierTypes, +} from '@/views/global/YppLandingView/YppLandingView.types' import { SignInWrapper, SuspendedWrapper, TierWrapper, VerificationWrapper, Wrapper } from './YppDashboardTier.styles' -type YppChannelTierTypes = 'Verified::Bronze' | 'Verified::Silver' | 'Verified::Gold' | 'Verified::Diamond' - -type YppChannelSuspendedTypes = - | 'Suspended::SubparQuality' - | 'Suspended::DuplicateContent' - | 'Suspended::UnsupportedTopic' - | 'Suspended::ProgramTermsExploit' - -type YppChannelStatus = YppChannelTierTypes | YppChannelSuspendedTypes | 'Unverified' | 'OptedOut' - export type YppDashboardTierProps = { status?: YppChannelStatus + onSignUp?: () => void } -export const YppDashboardTier = ({ status }: YppDashboardTierProps) => { +export const YppDashboardTier = ({ status, onSignUp }: YppDashboardTierProps) => { const content = () => { switch (true) { case status?.startsWith('Verified'): @@ -39,7 +35,7 @@ export const YppDashboardTier = ({ status }: YppDashboardTierProps) => { return default: case !status: - return + return } } return ( @@ -55,9 +51,9 @@ export const YppDashboardTier = ({ status }: YppDashboardTierProps) => { ) } -const SignInContent = () => { +const SignInContent = ({ onSignUp }: { onSignUp?: () => void }) => { return ( - + Sign up to participate diff --git a/packages/atlas/src/config/configSchema.ts b/packages/atlas/src/config/configSchema.ts index c0023748a4..7b46f952d6 100644 --- a/packages/atlas/src/config/configSchema.ts +++ b/packages/atlas/src/config/configSchema.ts @@ -1,7 +1,7 @@ import { z } from 'zod' // keep config schema in separate file so it can be imported without relying on YAML plugin - +const YppTierEnum = z.enum(['bronze', 'silver', 'gold', 'diamond']) const YppWidgetIconEnum = z.enum(['info', 'message', 'tokenStack']) export const configSchema = z.object({ @@ -49,15 +49,13 @@ export const configSchema = z.object({ enrollmentReward: z.number().nullable(), enrollmentUsdReward: z.number().nullable(), referralBaseReward: z.number().nullable(), - tiersDefinition: z - .object({ - tiersTooltip: z.string().nullable(), - tiers: z - .array(z.object({ minimumSubscribers: z.number(), multiplier: z.number().default(1) })) - .max(6) - .optional(), + tiersDefinition: z.array( + z.object({ + tier: YppTierEnum, + reqs: z.array(z.string()), + rewards: z.array(z.number()).max(3), }) - .optional(), + ), rewards: z .array( z.object({ diff --git a/packages/atlas/src/hooks/useYppAuthorizeHandler.tsx b/packages/atlas/src/hooks/useYppAuthorizeHandler.tsx new file mode 100644 index 0000000000..4919dba742 --- /dev/null +++ b/packages/atlas/src/hooks/useYppAuthorizeHandler.tsx @@ -0,0 +1,73 @@ +import { useCallback } from 'react' +import { useQuery } from 'react-query' +import { useNavigate, useSearchParams } from 'react-router-dom' + +import { axiosInstance } from '@/api/axios' +import { atlasConfig } from '@/config' +import { absoluteRoutes } from '@/config/routes' +import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' +import { useSnackbar } from '@/providers/snackbars' +import { useYppStore } from '@/providers/ypp/ypp.store' +import { SentryLogger } from '@/utils/logs' +import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' + +const SINGUP_DAILY_QUOTA = 500 // 2% of the total daily quota + +export const useYppAuthorizeHandler = () => { + const { displaySnackbar } = useSnackbar() + const [searchParams] = useSearchParams() + + const navigate = useNavigate() + const { trackYppSignInButtonClick } = useSegmentAnalytics() + const yppModalOpenName = useYppStore((state) => state.yppModalOpenName) + const setYppModalOpen = useYppStore((state) => state.actions.setYppModalOpenName) + const { data } = useQuery('ypp-quota-fetch', () => + axiosInstance + .get<{ signupQuotaUsed: number }>(`${atlasConfig.features.ypp.youtubeSyncApiUrl}/youtube/quota-usage/today`) + .then((res) => res.data) + .catch((e) => SentryLogger.error('Quota fetch failed', 'YppLandingView', e)) + ) + const isTodaysQuotaReached = data ? data.signupQuotaUsed > SINGUP_DAILY_QUOTA : false + const [referrer, utmSource, utmCampaign] = [ + searchParams.get('referrerId'), + searchParams.get('utm_source'), + searchParams.get('utm_campaign'), + ] + const { currentChannel } = useGetYppSyncedChannels() + + const isYppSigned = !!currentChannel + + return useCallback(async () => { + if (isTodaysQuotaReached) { + displaySnackbar({ + title: 'Something went wrong', + description: + "Due to high demand, we've reached the quota on the daily new sign ups. Please try again tomorrow.", + iconType: 'error', + }) + return + } + + if (isYppSigned) { + navigate(absoluteRoutes.studio.ypp()) + return + } + + if (!yppModalOpenName) { + trackYppSignInButtonClick(referrer, utmSource, utmCampaign) + setYppModalOpen('ypp-requirements') + return + } + }, [ + isTodaysQuotaReached, + isYppSigned, + yppModalOpenName, + displaySnackbar, + navigate, + trackYppSignInButtonClick, + referrer, + utmSource, + utmCampaign, + setYppModalOpen, + ]) +} diff --git a/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx b/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx index e43557698d..a8021fda5e 100644 --- a/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx +++ b/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx @@ -1,20 +1,13 @@ import AOS from 'aos' import 'aos/dist/aos.css' -import { FC, useCallback, useEffect, useState } from 'react' -import { useQuery } from 'react-query' -import { useNavigate, useSearchParams } from 'react-router-dom' +import { FC, useEffect, useState } from 'react' import { ParallaxProvider } from 'react-scroll-parallax' -import { axiosInstance } from '@/api/axios' import { YppReferralBanner } from '@/components/_ypp/YppReferralBanner' -import { atlasConfig } from '@/config' -import { absoluteRoutes } from '@/config/routes' import { useHeadTags } from '@/hooks/useHeadTags' -import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' -import { useSnackbar } from '@/providers/snackbars' +import { useYppAuthorizeHandler } from '@/hooks/useYppAuthorizeHandler' import { useUser } from '@/providers/user/user.hooks' import { useYppStore } from '@/providers/ypp/ypp.store' -import { SentryLogger } from '@/utils/logs' import { YppAuthorizationModal } from './YppAuthorizationModal' import { YppCardsSections } from './YppCardsSections' @@ -24,39 +17,22 @@ import { Wrapper } from './YppLandingView.styles' import { YppRewardSection } from './YppRewardSection' import { useGetYppSyncedChannels } from './useGetYppSyncedChannels' -const SINGUP_DAILY_QUOTA = 500 // 2% of the total daily quota - export const YppLandingView: FC = () => { const headTags = useHeadTags('YouTube Partner Program') - const yppModalOpenName = useYppStore((state) => state.yppModalOpenName) const setYppModalOpen = useYppStore((state) => state.actions.setYppModalOpenName) const { activeMembership, channelId } = useUser() - const [searchParams] = useSearchParams() const { setSelectedChannelId, setShouldContinueYppFlowAfterCreatingChannel } = useYppStore((store) => store.actions) - const { displaySnackbar } = useSnackbar() - const navigate = useNavigate() - const { trackYppSignInButtonClick } = useSegmentAnalytics() + const selectedChannelTitle = activeMembership?.channels.find((channel) => channel.id === channelId)?.title - const { data } = useQuery('ypp-quota-fetch', () => - axiosInstance - .get<{ signupQuotaUsed: number }>(`${atlasConfig.features.ypp.youtubeSyncApiUrl}/youtube/quota-usage/today`) - .then((res) => res.data) - .catch((e) => SentryLogger.error('Quota fetch failed', 'YppLandingView', e)) - ) const [wasSignInTriggered, setWasSignInTriggered] = useState(false) - const isTodaysQuotaReached = data ? data.signupQuotaUsed > SINGUP_DAILY_QUOTA : false const shouldContinueYppFlowAfterCreatingChannel = useYppStore( (store) => store.shouldContinueYppFlowAfterCreatingChannel ) - const [referrer, utmSource, utmCampaign] = [ - searchParams.get('referrerId'), - searchParams.get('utm_source'), - searchParams.get('utm_campaign'), - ] const { unsyncedChannels, isLoading, currentChannel } = useGetYppSyncedChannels() const isYppSigned = !!currentChannel const hasAnotherUnsyncedChannel = isYppSigned && !!unsyncedChannels?.length + const handleYppSignUpClick = useYppAuthorizeHandler() useEffect(() => { AOS.init({ @@ -65,40 +41,6 @@ export const YppLandingView: FC = () => { }) }, []) - const handleYppSignUpClick = useCallback(async () => { - if (isTodaysQuotaReached) { - displaySnackbar({ - title: 'Something went wrong', - description: - "Due to high demand, we've reached the quota on the daily new sign ups. Please try again tomorrow.", - iconType: 'error', - }) - return - } - - if (isYppSigned) { - navigate(absoluteRoutes.studio.ypp()) - return - } - - if (!yppModalOpenName) { - trackYppSignInButtonClick(referrer, utmSource, utmCampaign) - setYppModalOpen('ypp-requirements') - return - } - }, [ - isTodaysQuotaReached, - isYppSigned, - yppModalOpenName, - displaySnackbar, - navigate, - trackYppSignInButtonClick, - referrer, - utmSource, - utmCampaign, - setYppModalOpen, - ]) - useEffect(() => { // rerun handleYppSignUpClick after sign in flow if (wasSignInTriggered) { diff --git a/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts b/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts index 2101c81f25..d0592c5db0 100644 --- a/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts +++ b/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts @@ -1,11 +1,18 @@ -type YppStatus = 'Unverified' | 'Verified' | 'Suspended' | 'OptedOut' +export type YppChannelTierTypes = 'Verified::Bronze' | 'Verified::Silver' | 'Verified::Gold' | 'Verified::Diamond' +export type YppChannelSuspendedTypes = + | 'Suspended::SubparQuality' + | 'Suspended::DuplicateContent' + | 'Suspended::UnsupportedTopic' + | 'Suspended::ProgramTermsExploit' + +export type YppChannelStatus = YppChannelTierTypes | YppChannelSuspendedTypes | 'Unverified' | 'OptedOut' export type YppSyncedChannel = { title: string description: string aggregatedStats: number shouldBeIngested: boolean - yppStatus: YppStatus + yppStatus: YppChannelStatus joystreamChannelId: number videoCategoryId: string thumbnails: { diff --git a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.config.tsx b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.config.tsx index 37a1fda982..0b90a7b2e7 100644 --- a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.config.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.config.tsx @@ -1,54 +1,13 @@ -import { SvgTierIcon1, SvgTierIcon2, SvgTierIcon3, SvgTierIcon4, SvgTierIcon5, SvgTierIcon6 } from '@/assets/icons' -import { NumberFormat } from '@/components/NumberFormat' import { atlasConfig } from '@/config' -const configTiers = atlasConfig.features.ypp.tiersDefinition?.tiers - -const tiersIcons = [ - , - , - , - , - , - , -] - -export const TIERS = configTiers - ? configTiers.map((tier, index) => ({ - rules: ( -
- {index === 0 && '< '} - {index === configTiers.length - 1 && '> '} - - {index !== 0 && index !== configTiers.length - 1 && ( - <> - {' '} - -{' '} - - - )} -
- ), - icon: tiersIcons[index], - subscribers: tier.minimumSubscribers, - multiplier: tier.multiplier, - })) - : [] - +const configTiers = atlasConfig.features.ypp.tiersDefinition const configRewards = atlasConfig.features.ypp.rewards +export const getTierRewards = (tier: string): number[] | undefined => { + // [signup, video sync, referral] + return configTiers.find((configTier) => configTier.tier === tier)?.rewards +} + type RewardWithDescription = NonNullable[number] & { stepsDescription: string actionButtonText: string diff --git a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx index 6bc0fb3659..d0834c0092 100644 --- a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx @@ -1,10 +1,8 @@ import { FC, useEffect, useMemo, useState } from 'react' -import { Information } from '@/components/Information' import { LimitedWidthContainer } from '@/components/LimitedWidthContainer' import { Tabs } from '@/components/Tabs' import { Text } from '@/components/Text' -import { atlasConfig } from '@/config' import { useHeadTags } from '@/hooks/useHeadTags' import { useMediaMatch } from '@/hooks/useMediaMatch' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' @@ -12,16 +10,7 @@ import { useUploadsStore } from '@/providers/uploads/uploads.store' import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' import { YppDashboardReferralsTab } from '@/views/studio/YppDashboard/tabs/YppDashboardReferralsTab/YppDashboardReferralsTab' -import { TIERS } from './YppDashboard.config' -import { - Divider, - Header, - HeaderContentBox, - TabsWrapper, - TierCount, - TierDescription, - TierWrapper, -} from './YppDashboard.styles' +import { Divider, Header, TabsWrapper } from './YppDashboard.styles' import { YppDashboardMainTab, YppDashboardSettingsTab } from './tabs' const TABS = ['Dashboard', 'Referrals', 'Settings'] as const @@ -35,13 +24,6 @@ export const YppDashboard: FC = () => { const { processingAssets, uploads } = useUploadsStore() const subscribersCount = currentChannel?.subscribersCount || 0 - const currentTier = TIERS.reduce((prev, current, idx) => { - if (subscribersCount >= (current?.subscribers || 0)) { - return idx - } else { - return prev - } - }, 0) useEffect(() => { // if user avatar is currently processing membership will be refetched when it's uploaded, @@ -53,20 +35,18 @@ export const YppDashboard: FC = () => { trackPageView('YPP Dashboard', { tab: TABS[currentVideosTab] }) }, [currentVideosTab, processingAssets, trackPageView, uploads]) - const tiersTooltip = atlasConfig.features.ypp.tiersDefinition?.tiersTooltip - const mappedTabs = TABS.map((tab) => ({ name: tab })) const content = useMemo(() => { switch (TABS[currentVideosTab]) { case 'Dashboard': - return + return case 'Referrals': return case 'Settings': return } - }, [currentVideosTab, currentTier]) + }, [currentVideosTab]) return ( <> @@ -76,29 +56,8 @@ export const YppDashboard: FC = () => { YouTube Partner Program - - {TIERS.length && !isLoading && ( - - {TIERS[currentTier].icon} - -
- - - Tier {currentTier + 1}{' '} - - - out of {TIERS.length} - - - - {TIERS[currentTier].rules} - -
- {tiersTooltip ? : null} -
-
- )} -
+ {/**/} + {/**/} diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 090ce95e86..5b06034792 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -1,10 +1,9 @@ import { FC } from 'react' -import { SvgActionNewTab, SvgAlertsError24, SvgAlertsInformative24 } from '@/assets/icons' +import { SvgActionNewChannel, SvgActionNewTab } from '@/assets/icons' import { FlexBox } from '@/components/FlexBox' import { Information } from '@/components/Information' import { GridItem, LayoutGrid } from '@/components/LayoutGrid' -import { NumberFormat } from '@/components/NumberFormat' import { Text } from '@/components/Text' import { WidgetTile } from '@/components/WidgetTile' import { Button, TextButton } from '@/components/_buttons/Button' @@ -12,85 +11,85 @@ import { BenefitCard } from '@/components/_ypp/BenefitCard' import { ServiceStatusWidget } from '@/components/_ypp/ServiceStatusWidget/ServiceStatusWidget' import { YppDashboardTier } from '@/components/_ypp/YppDashboardTier' import { atlasConfig } from '@/config' -// import { useClipboard } from '@/hooks/useClipboard' import { useMediaMatch } from '@/hooks/useMediaMatch' +import { useYppAuthorizeHandler } from '@/hooks/useYppAuthorizeHandler' import { formatDate, getNextFriday } from '@/utils/time' -// import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' -// import { useUser } from '@/providers/user/user.hooks' +import { YppAuthorizationModal } from '@/views/global/YppLandingView/YppAuthorizationModal' import { configYppIconMapper } from '@/views/global/YppLandingView/YppFooter' import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' +import { getTierRewards } from '@/views/studio/YppDashboard/YppDashboard.config' import { StatusDot, StyledBanner, StyledSvgAlertsInformative24, YppSyncStatus } from './YppDashboardTabs.styles' const APP_NAME = atlasConfig.general.appName -const tiers = atlasConfig.features.ypp.tiersDefinition?.tiers -type YppDashboardMainTabProps = { - currentTier?: number -} -const syncOn = true -export const YppDashboardMainTab: FC = ({ currentTier = 0 }) => { +export const YppDashboardMainTab: FC = () => { // const { copyToClipboard } = useClipboard() // const { channelId } = useUser() + const mdMatch = useMediaMatch('md') const lgMatch = useMediaMatch('lg') - const { currentChannel } = useGetYppSyncedChannels() + const handleYppSignUpClick = useYppAuthorizeHandler() + + const { unsyncedChannels } = useGetYppSyncedChannels() // const { trackReferralLinkGenerated } = useSegmentAnalytics() - const multiplier = tiers ? tiers[currentTier].multiplier : 1 const nextPayoutDate = getNextFriday() - console.log(currentChannel, 'xd') + const currentChannel = { yppStatus: 'Verified::Diamond' } + return ( <> + + } title="Have more than one YouTube channel?" description={`You can apply to the YouTube Partner Program with as many YouTube & ${APP_NAME} channels as you want. Each YouTube channel can be assigned to only one ${APP_NAME} channel. \nYou can create a new channel from the top right menu.`} /> - {currentChannel?.yppStatus === 'Suspended' && ( - } - description={ - - You will not be rewarded while this channel is suspended. Your channel did not pass the verification due - to{' '} - - . - - } - /> - )} - {currentChannel?.yppStatus === 'Unverified' && ( - } - description={ - - Your channel needs to get verified before content syncing starts. It normally takes 12-48 hours for - channels to get verified. -
- Once verified, you will qualify for the rewards. Payouts are made on a weekly basis, every Friday, for the - previous calendar week. Your first payment will involve the reward for the sign up of{' '} - {' '} - USD paid out in ${atlasConfig.joystream.tokenTicker} tokens based on the market rate. -
- } - /> - )} + {/*{currentChannel?.yppStatus === 'Suspended' && (*/} + {/* }*/} + {/* description={*/} + {/* */} + {/* You will not be rewarded while this channel is suspended. Your channel did not pass the verification due*/} + {/* to{' '}*/} + {/* */} + {/* .*/} + {/* */} + {/* }*/} + {/* />*/} + {/*)}*/} + {/*{currentChannel?.yppStatus === 'Unverified' && (*/} + {/* }*/} + {/* description={*/} + {/* */} + {/* Your channel needs to get verified before content syncing starts. It normally takes 12-48 hours for*/} + {/* channels to get verified.*/} + {/*
*/} + {/* Once verified, you will qualify for the rewards. Payouts are made on a weekly basis, every Friday, for the*/} + {/* previous calendar week. Your first payment will involve the reward for the sign up of{' '}*/} + {/* {' '}*/} + {/* USD paid out in ${atlasConfig.joystream.tokenTicker} tokens based on the market rate.*/} + {/*
*/} + {/* }*/} + {/* />*/} + {/*)}*/} - + - + = ({ currentTier ))} } + disabled={!!currentChannel} + iconPlacement="right" + onClick={handleYppSignUpClick} + > + Sign up + + ) : undefined + } /> @@ -166,7 +195,8 @@ export const YppDashboardMainTab: FC = ({ currentTier Copy referral link} /> diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardReferralsTab/YppDashboardReferralsTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardReferralsTab/YppDashboardReferralsTab.tsx index 58cc02b6ab..05128f00ad 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardReferralsTab/YppDashboardReferralsTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardReferralsTab/YppDashboardReferralsTab.tsx @@ -9,7 +9,6 @@ import { useClipboard } from '@/hooks/useClipboard' import { useUser } from '@/providers/user/user.hooks' import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' -import { TIERS } from '../../YppDashboard.config' import { FallbackContainer } from '../YppDashboardTabs.styles' const BASE_REFERRAL_REWARD = atlasConfig.features.ypp.referralBaseReward ?? 0 @@ -21,19 +20,19 @@ export const YppDashboardReferralsTab = () => { const mappedData: YppReferral[] = useMemo( () => currentChannel?.referredChannels?.map((channelData) => { - const tier = TIERS.reduce((prev, current, idx) => { - if (channelData.subscribersCount >= (current?.subscribers || 0)) { - return idx - } else { - return prev - } - }, 0) + // const tier = TIERS.reduce((prev, current, idx) => { + // if (channelData.subscribersCount >= (current?.subscribers || 0)) { + // return idx + // } else { + // return prev + // } + // }, 0) return { date: new Date(channelData.createdAt), channel: String(channelData.joystreamChannelId), - rewardUsd: TIERS[tier].multiplier * BASE_REFERRAL_REWARD, + rewardUsd: 1 * BASE_REFERRAL_REWARD, status: channelData.yppStatus, - tier, + tier: 1, } }) ?? [], [currentChannel?.referredChannels] From 5a7c035fbf0f73f6d6a3829d3c8eed6726a31256 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Thu, 28 Sep 2023 14:07:54 +0200 Subject: [PATCH 05/18] Add tooltips --- .../components/_ypp/BenefitCard/BenefitCard.tsx | 4 +++- .../ServiceStatusWidget/ServiceStatusWidget.tsx | 14 ++++++++++---- .../_ypp/YppDashboardTier/YppDashboardTier.tsx | 2 +- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 10 +++++++--- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx index 6c069be123..d38ac0617b 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx @@ -12,6 +12,7 @@ export type BenefitCardProps = { title: string description?: string dollarAmount?: number + amountTooltip?: string isRangeAmount?: boolean className?: string actionNode?: ReactNode @@ -24,6 +25,7 @@ export const BenefitCard: FC = ({ className, actionNode, isRangeAmount, + amountTooltip, }) => { const smMatch = useMediaMatch('sm') const lgMatch = useMediaMatch('lg') @@ -63,7 +65,7 @@ export const BenefitCard: FC = ({
)} - +
)} {actionNode} diff --git a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx index d5b91e1ef7..845b4b6260 100644 --- a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx +++ b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx @@ -37,17 +37,23 @@ export const ServiceStatusWidget = ({ status }: ServiceStatusWidgetProps) => { if (!data) return [] const hideData = !status || !status.startsWith('Verified') const output: [number | string, string, string][] = [] // [value, title, tooltip] - output.push([hideData ? '-' : data.data.syncBacklog, 'VIDEOS IN QUEUE', "It's pretty long"]) + + // todo: all this data needs to be user scoped, rn backend does not support it + output.push([ + hideData ? '-' : data.data.syncBacklog, + 'VIDEOS IN QUEUE', + 'This is the total amount of your YouTube videos that are waiting to be synced', + ]) output.push([ - hideData ? '-' : data.data.syncBacklog * 2, // no info bout it + hideData ? '-' : data.data.syncBacklog * 2 === 0 ? 'Syncing...' : data.data.syncBacklog * 2, // no info bout it 'PLACE IN THE QUEUE', - "We don' really know your place in the queue", + 'Sync system is based on queue as we sync channels one at a time. When you reach place 1 in queue your sync will start.', ]) output.push([ // isOptedIn ? `In ${Math.round(data.data.syncBacklog / YPP_DELAY_THRESHOLD)} days` : '-', '-', 'ETA TO FULL SYNC', - "Well we don't really know this either", + 'Estimated time of full sync of your videos may change based on YPP service status or service overload.', ]) return output }, [data, status]) diff --git a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx index cb1cd1b3e0..cc3182dab0 100644 --- a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx +++ b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx @@ -44,7 +44,7 @@ export const YppDashboardTier = ({ status, onSignUp }: YppDashboardTierProps) => CHANNEL YPP STATUS - + {content()}
diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 5b06034792..fc0110f0c3 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -34,7 +34,7 @@ export const YppDashboardMainTab: FC = () => { const { unsyncedChannels } = useGetYppSyncedChannels() // const { trackReferralLinkGenerated } = useSegmentAnalytics() const nextPayoutDate = getNextFriday() - const currentChannel = { yppStatus: 'Verified::Diamond' } + const currentChannel = { yppStatus: 'Suspended::DuplicateContent' } return ( <> @@ -95,7 +95,7 @@ export const YppDashboardMainTab: FC = () => { @@ -148,6 +148,7 @@ export const YppDashboardMainTab: FC = () => { : getTierRewards(currentChannel.yppStatus.split('::')[1].toLowerCase())?.[0] } isRangeAmount={!currentChannel || !currentChannel.yppStatus.startsWith('Verified')} + amountTooltip="Ranks are assigned at discretion of Joystream team based on such factors as content quality and channel popularity" actionNode={ !currentChannel || !currentChannel.yppStatus.startsWith('Verified') ? ( - ) : undefined - } - /> - + {!hasDismissedSignupMessage && ( + + } + disabled={!!currentChannel} + iconPlacement="right" + onClick={handleYppSignUpClick} + > + Sign up + + ) : ( + updateDismissedMessages(getMessageIdForChannel(channelId as string))} + icon={smMatch && } + > + {!smMatch ? 'Close' : ''} + + ) + } + /> + + )} { description="Get rewarded for every new creator who signs up to YPP program using your referral link. Referrals rewards depends on the tier assigned to the invited channel." dollarAmount={getTierRewards('diamond')?.[2]} isRangeAmount - actionNode={} + actionNode={} /> diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts index 29e4807b2f..7b60b53b4b 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts @@ -4,6 +4,7 @@ import styled from '@emotion/styled' import { SvgActionArrowRight, SvgAlertsInformative24, SvgAlertsWarning32 } from '@/assets/icons' import { ActionBar } from '@/components/ActionBar' import { Banner } from '@/components/Banner' +import { Button } from '@/components/_buttons/Button' import { cVar, media, sizes, zIndex } from '@/styles' export { Divider } from '../YppDashboard.styles' @@ -95,9 +96,13 @@ export const YppSyncStatus = styled.div` justify-content: center; gap: ${sizes(2)}; background-color: ${cVar('colorCoreNeutral800Lighten')}; - padding: ${sizes(2)} ${sizes(4)}; + padding: ${sizes(2.5)} ${sizes(4)}; border-radius: 99px; - width: fit-content; + width: 100%; + + ${media.sm} { + width: fit-content; + } p { white-space: nowrap; @@ -112,3 +117,17 @@ export const StatusDot = styled.div` box-shadow: 0 0 0 5px #0c984680; animation: 10s ease-out ${dotPulse} infinite; ` + +export const StyledCloseButton = styled(Button)` + position: static; + + ${media.sm} { + position: absolute; + top: ${sizes(6)}; + right: ${sizes(6)}; + } + + ${media.lg} { + position: static; + } +` From 386835f0eb54beecea1b5a679c442c99e4e6a555 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Thu, 28 Sep 2023 14:47:45 +0200 Subject: [PATCH 07/18] Add copy logic --- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 36 +++++++++++++------ 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 7cacc8af29..029255000e 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -11,7 +11,9 @@ import { BenefitCard } from '@/components/_ypp/BenefitCard' import { ServiceStatusWidget } from '@/components/_ypp/ServiceStatusWidget/ServiceStatusWidget' import { YppDashboardTier } from '@/components/_ypp/YppDashboardTier' import { atlasConfig } from '@/config' +import { useClipboard } from '@/hooks/useClipboard' import { useMediaMatch } from '@/hooks/useMediaMatch' +import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { useYppAuthorizeHandler } from '@/hooks/useYppAuthorizeHandler' import { usePersonalDataStore } from '@/providers/personalData' import { useUser } from '@/providers/user/user.hooks' @@ -30,22 +32,21 @@ const getMessageIdForChannel = (channelId: string) => { } export const YppDashboardMainTab: FC = () => { + const { copyToClipboard } = useClipboard() + const { trackReferralLinkGenerated } = useSegmentAnalytics() const { channelId } = useUser() - - const mdMatch = useMediaMatch('md') - const smMatch = useMediaMatch('sm') - const lgMatch = useMediaMatch('lg') const handleYppSignUpClick = useYppAuthorizeHandler() const hasDismissedSignupMessage = usePersonalDataStore((state) => state.dismissedMessages.some((message) => message.id === getMessageIdForChannel(channelId as string)) ) const updateDismissedMessages = usePersonalDataStore((state) => state.actions.updateDismissedMessages) + const { unsyncedChannels, currentChannel } = useGetYppSyncedChannels() - const { unsyncedChannels } = useGetYppSyncedChannels() - // const { trackReferralLinkGenerated } = useSegmentAnalytics() + const mdMatch = useMediaMatch('md') + const smMatch = useMediaMatch('sm') + const lgMatch = useMediaMatch('lg') const nextPayoutDate = getNextFriday() - const currentChannel = { yppStatus: 'Verified::Diamond' } - console.log(hasDismissedSignupMessage) + return ( <> @@ -190,7 +191,7 @@ export const YppDashboardMainTab: FC = () => { description="Get paid for every new video published on YouTube after the date of sign up. Minimum video duration has to be 5 minutes. Max videos rewarded are 3 per week." dollarAmount={ !currentChannel || !currentChannel.yppStatus.startsWith('Verified') - ? currentChannel.yppStatus.startsWith('Suspended') + ? currentChannel?.yppStatus.startsWith('Suspended') ? undefined : 5 : getTierRewards(currentChannel.yppStatus.split('::')[1].toLowerCase())?.[1] @@ -198,7 +199,7 @@ export const YppDashboardMainTab: FC = () => { isRangeAmount={!currentChannel || !currentChannel.yppStatus.startsWith('Verified')} amountTooltip="Your YouTube channel is being automatically synced with your Gleev channel. You will be rewarded every time a new video gets synced." actionNode={ - !currentChannel.yppStatus.startsWith('Suspended') ? ( + !currentChannel?.yppStatus.startsWith('Suspended') ? ( @@ -222,7 +223,20 @@ export const YppDashboardMainTab: FC = () => { description="Get rewarded for every new creator who signs up to YPP program using your referral link. Referrals rewards depends on the tier assigned to the invited channel." dollarAmount={getTierRewards('diamond')?.[2]} isRangeAmount - actionNode={} + actionNode={ + + } /> From 270eb3d717294f6a0467b52cfcaf8586f707a82a Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Thu, 28 Sep 2023 16:47:35 +0200 Subject: [PATCH 08/18] Redirect user on sign up click --- packages/atlas/src/hooks/useYppAuthorizeHandler.tsx | 10 +++++----- .../src/views/studio/MyVideosView/MyVideosView.tsx | 4 ++-- .../src/views/studio/YppDashboard/YppDashboard.tsx | 6 ------ .../studio/YppDashboard/tabs/YppDashboardMainTab.tsx | 12 ++++++++++-- 4 files changed, 17 insertions(+), 15 deletions(-) diff --git a/packages/atlas/src/hooks/useYppAuthorizeHandler.tsx b/packages/atlas/src/hooks/useYppAuthorizeHandler.tsx index 4919dba742..6eeccd437b 100644 --- a/packages/atlas/src/hooks/useYppAuthorizeHandler.tsx +++ b/packages/atlas/src/hooks/useYppAuthorizeHandler.tsx @@ -11,7 +11,7 @@ import { useYppStore } from '@/providers/ypp/ypp.store' import { SentryLogger } from '@/utils/logs' import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' -const SINGUP_DAILY_QUOTA = 500 // 2% of the total daily quota +export const SINGUP_DAILY_QUOTA = 500 // 2% of the total daily quota export const useYppAuthorizeHandler = () => { const { displaySnackbar } = useSnackbar() @@ -37,7 +37,7 @@ export const useYppAuthorizeHandler = () => { const isYppSigned = !!currentChannel - return useCallback(async () => { + return useCallback(() => { if (isTodaysQuotaReached) { displaySnackbar({ title: 'Something went wrong', @@ -45,18 +45,18 @@ export const useYppAuthorizeHandler = () => { "Due to high demand, we've reached the quota on the daily new sign ups. Please try again tomorrow.", iconType: 'error', }) - return + return false } if (isYppSigned) { navigate(absoluteRoutes.studio.ypp()) - return + return false } if (!yppModalOpenName) { trackYppSignInButtonClick(referrer, utmSource, utmCampaign) setYppModalOpen('ypp-requirements') - return + return true } }, [ isTodaysQuotaReached, diff --git a/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx b/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx index 89c6a17d65..f5c8df4b16 100644 --- a/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx +++ b/packages/atlas/src/views/studio/MyVideosView/MyVideosView.tsx @@ -74,7 +74,7 @@ export const MyVideosView = () => { const mdMatch = useMediaMatch('md') const { setNftToMint } = useNftActions() const [shouldHideMintModal, setShouldHideMintModal] = useState(false) - const [showMintModal, setShowMintModal] = useState(currentChannel?.yppStatus === 'Verified') + const [showMintModal, setShowMintModal] = useState(!!currentChannel?.yppStatus.startsWith('Verified')) const mintConfirmationDismissed = usePersonalDataStore((state) => state.dismissedMessages.some((message) => message.id === MINTING_CONFIRMATION_ID) @@ -345,7 +345,7 @@ export const MyVideosView = () => { { if (shouldHideMintModal) { updateMintConfirmationDismiss(MINTING_CONFIRMATION_ID, true) diff --git a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx index d0834c0092..e2ea7bd115 100644 --- a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx @@ -7,7 +7,6 @@ import { useHeadTags } from '@/hooks/useHeadTags' import { useMediaMatch } from '@/hooks/useMediaMatch' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { useUploadsStore } from '@/providers/uploads/uploads.store' -import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' import { YppDashboardReferralsTab } from '@/views/studio/YppDashboard/tabs/YppDashboardReferralsTab/YppDashboardReferralsTab' import { Divider, Header, TabsWrapper } from './YppDashboard.styles' @@ -19,12 +18,9 @@ export const YppDashboard: FC = () => { const headTags = useHeadTags('YouTube Partner Program') const mdMatch = useMediaMatch('md') const [currentVideosTab, setCurrentVideosTab] = useState(0) - const { currentChannel, isLoading } = useGetYppSyncedChannels() const { trackPageView } = useSegmentAnalytics() const { processingAssets, uploads } = useUploadsStore() - const subscribersCount = currentChannel?.subscribersCount || 0 - useEffect(() => { // if user avatar is currently processing membership will be refetched when it's uploaded, // which will trigger page view event @@ -56,8 +52,6 @@ export const YppDashboard: FC = () => { YouTube Partner Program - {/**/} - {/**/} diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 029255000e..4df7045f59 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -1,4 +1,5 @@ import { FC } from 'react' +import { useNavigate } from 'react-router-dom' import { SvgActionClose, SvgActionNewChannel, SvgActionNewTab } from '@/assets/icons' import { FlexBox } from '@/components/FlexBox' @@ -11,6 +12,7 @@ import { BenefitCard } from '@/components/_ypp/BenefitCard' import { ServiceStatusWidget } from '@/components/_ypp/ServiceStatusWidget/ServiceStatusWidget' import { YppDashboardTier } from '@/components/_ypp/YppDashboardTier' import { atlasConfig } from '@/config' +import { absoluteRoutes } from '@/config/routes' import { useClipboard } from '@/hooks/useClipboard' import { useMediaMatch } from '@/hooks/useMediaMatch' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' @@ -35,7 +37,8 @@ export const YppDashboardMainTab: FC = () => { const { copyToClipboard } = useClipboard() const { trackReferralLinkGenerated } = useSegmentAnalytics() const { channelId } = useUser() - const handleYppSignUpClick = useYppAuthorizeHandler() + const navigate = useNavigate() + const _handleYppSignUpClick = useYppAuthorizeHandler() const hasDismissedSignupMessage = usePersonalDataStore((state) => state.dismissedMessages.some((message) => message.id === getMessageIdForChannel(channelId as string)) ) @@ -46,7 +49,12 @@ export const YppDashboardMainTab: FC = () => { const smMatch = useMediaMatch('sm') const lgMatch = useMediaMatch('lg') const nextPayoutDate = getNextFriday() - + const handleYppSignUpClick = () => { + const success = _handleYppSignUpClick() + if (success) { + navigate(absoluteRoutes.viewer.ypp()) + } + } return ( <> From e80df8257e42dd3449ccf09dba7e43821f81f245 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Thu, 28 Sep 2023 16:59:27 +0200 Subject: [PATCH 09/18] Remap routes on studio to go into dashboard only --- .../SidenavStudio/SidenavStudio.tsx | 2 +- .../atlas/src/views/studio/StudioLayout.tsx | 53 +++++-------------- .../studio/YppDashboard/YppDashboard.tsx | 4 +- 3 files changed, 17 insertions(+), 42 deletions(-) diff --git a/packages/atlas/src/components/_navigation/SidenavStudio/SidenavStudio.tsx b/packages/atlas/src/components/_navigation/SidenavStudio/SidenavStudio.tsx index ba1dd8697c..7d8de70bd3 100644 --- a/packages/atlas/src/components/_navigation/SidenavStudio/SidenavStudio.tsx +++ b/packages/atlas/src/components/_navigation/SidenavStudio/SidenavStudio.tsx @@ -58,7 +58,7 @@ const studioNavbarItems: NavItemType[] = [ icon: , name: 'YPP', expandedName: 'YouTube Partner Program', - to: absoluteRoutes.studio.ypp(), + to: absoluteRoutes.studio.yppDashboard(), matchPattern: { path: absoluteRoutes.studio.ypp() + '/*', }, diff --git a/packages/atlas/src/views/studio/StudioLayout.tsx b/packages/atlas/src/views/studio/StudioLayout.tsx index c9ce2795b5..dcb85a5c72 100644 --- a/packages/atlas/src/views/studio/StudioLayout.tsx +++ b/packages/atlas/src/views/studio/StudioLayout.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled' import { ErrorBoundary } from '@sentry/react' -import { FC, Suspense, lazy, useCallback, useEffect, useLayoutEffect, useState } from 'react' +import { FC, Suspense, lazy, useEffect, useLayoutEffect, useState } from 'react' import { Route, Routes } from 'react-router' import { useLocation, useNavigate } from 'react-router-dom' import { CSSTransition } from 'react-transition-group' @@ -57,10 +57,6 @@ const StudioWelcomeView = lazy(() => const VideoWorkspace = lazy(() => import('./VideoWorkspace').then((module) => ({ default: module.VideoWorkspace }))) const YppDashboard = lazy(() => import('./YppDashboard').then((module) => ({ default: module.YppDashboard }))) -const YppLandingView = lazy(() => - import('../global/YppLandingView').then((module) => ({ default: module.YppLandingView })) -) - const NotFoundView = lazy(() => import('../viewer/NotFoundView').then((module) => ({ default: module.NotFoundView }))) const ENTRY_POINT_ROUTE = absoluteRoutes.studio.index() @@ -96,9 +92,8 @@ const _StudioLayout = () => { const hasMembership = !!memberships?.length const channelSet = !!(channelId && activeMembership?.channels.find((channel) => channel.id === channelId)) - const { currentChannel, isLoading } = useGetYppSyncedChannels() + const { isLoading } = useGetYppSyncedChannels() const isLoadingYPPData = isLoading || membershipsLoading || isAuthenticating - const isYppSigned = !!currentChannel useLayoutEffect(() => { if (!isAllowedBrowser()) { @@ -134,15 +129,6 @@ const _StudioLayout = () => { return () => clearTimeout(trackRequestTimeout) }, [location.pathname, trackPageView]) - const yppRedirect = useCallback(() => { - if (!channelSet) { - return ENTRY_POINT_ROUTE - } - if (!isYppSigned) { - return absoluteRoutes.studio.ypp() - } - }, [channelSet, isYppSigned]) - return ( <> @@ -232,30 +218,17 @@ const _StudioLayout = () => { } /> {atlasConfig.features.ypp.googleConsoleClientId && ( - <> - } - isAuth={channelSet && !isYppSigned} - redirectTo={absoluteRoutes.studio.yppDashboard()} - /> - } - /> - } - isAuth={channelSet} - redirectTo={yppRedirect()} - /> - } - /> - + } + isAuth={channelSet} + redirectTo={ENTRY_POINT_ROUTE} + /> + } + /> )} } /> diff --git a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx index e2ea7bd115..0f8b6ea556 100644 --- a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx @@ -7,6 +7,7 @@ import { useHeadTags } from '@/hooks/useHeadTags' import { useMediaMatch } from '@/hooks/useMediaMatch' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { useUploadsStore } from '@/providers/uploads/uploads.store' +import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' import { YppDashboardReferralsTab } from '@/views/studio/YppDashboard/tabs/YppDashboardReferralsTab/YppDashboardReferralsTab' import { Divider, Header, TabsWrapper } from './YppDashboard.styles' @@ -20,6 +21,7 @@ export const YppDashboard: FC = () => { const [currentVideosTab, setCurrentVideosTab] = useState(0) const { trackPageView } = useSegmentAnalytics() const { processingAssets, uploads } = useUploadsStore() + const { currentChannel } = useGetYppSyncedChannels() useEffect(() => { // if user avatar is currently processing membership will be refetched when it's uploaded, @@ -31,7 +33,7 @@ export const YppDashboard: FC = () => { trackPageView('YPP Dashboard', { tab: TABS[currentVideosTab] }) }, [currentVideosTab, processingAssets, trackPageView, uploads]) - const mappedTabs = TABS.map((tab) => ({ name: tab })) + const mappedTabs = TABS.filter((tab) => (currentChannel ? true : tab !== 'Settings')).map((tab) => ({ name: tab })) const content = useMemo(() => { switch (TABS[currentVideosTab]) { From adc9c654d0d52fa8ce3048e283481374753a88e1 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Thu, 28 Sep 2023 17:10:50 +0200 Subject: [PATCH 10/18] Add missing assets --- .../atlas/src/assets/icons/IconRankBronze.tsx | 39 +++++++++++++ .../src/assets/icons/IconRankDiamond.tsx | 56 +++++++++++++++++++ .../atlas/src/assets/icons/IconRankGold.tsx | 38 +++++++++++++ .../atlas/src/assets/icons/IconRankSilver.tsx | 33 +++++++++++ .../assets/icons/svgs/icon-rank-bronze.svg | 18 ++++++ .../assets/icons/svgs/icon-rank-diamond.svg | 44 +++++++++++++++ .../src/assets/icons/svgs/icon-rank-gold.svg | 18 ++++++ .../assets/icons/svgs/icon-rank-silver.svg | 26 +++++++++ 8 files changed, 272 insertions(+) create mode 100644 packages/atlas/src/assets/icons/IconRankBronze.tsx create mode 100644 packages/atlas/src/assets/icons/IconRankDiamond.tsx create mode 100644 packages/atlas/src/assets/icons/IconRankGold.tsx create mode 100644 packages/atlas/src/assets/icons/IconRankSilver.tsx create mode 100644 packages/atlas/src/assets/icons/svgs/icon-rank-bronze.svg create mode 100644 packages/atlas/src/assets/icons/svgs/icon-rank-diamond.svg create mode 100644 packages/atlas/src/assets/icons/svgs/icon-rank-gold.svg create mode 100644 packages/atlas/src/assets/icons/svgs/icon-rank-silver.svg diff --git a/packages/atlas/src/assets/icons/IconRankBronze.tsx b/packages/atlas/src/assets/icons/IconRankBronze.tsx new file mode 100644 index 0000000000..86a4034bc6 --- /dev/null +++ b/packages/atlas/src/assets/icons/IconRankBronze.tsx @@ -0,0 +1,39 @@ +// THIS FILE WAS AUTOGENERATED BY SVGR. DO NOT MODIFY IT MANUALLY; +import { Ref, SVGProps, forwardRef, memo } from 'react' + +const SvgIconRankBronze = forwardRef((props: SVGProps, ref: Ref) => ( + + + + + + + + + + + + + + + + + + +)) +SvgIconRankBronze.displayName = 'SvgIconRankBronze' +const Memo = memo(SvgIconRankBronze) +export { Memo as SvgIconRankBronze } diff --git a/packages/atlas/src/assets/icons/IconRankDiamond.tsx b/packages/atlas/src/assets/icons/IconRankDiamond.tsx new file mode 100644 index 0000000000..85c71a5709 --- /dev/null +++ b/packages/atlas/src/assets/icons/IconRankDiamond.tsx @@ -0,0 +1,56 @@ +// THIS FILE WAS AUTOGENERATED BY SVGR. DO NOT MODIFY IT MANUALLY; +import { Ref, SVGProps, forwardRef, memo } from 'react' + +const SvgIconRankDiamond = forwardRef((props: SVGProps, ref: Ref) => ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)) +SvgIconRankDiamond.displayName = 'SvgIconRankDiamond' +const Memo = memo(SvgIconRankDiamond) +export { Memo as SvgIconRankDiamond } diff --git a/packages/atlas/src/assets/icons/IconRankGold.tsx b/packages/atlas/src/assets/icons/IconRankGold.tsx new file mode 100644 index 0000000000..b8f62aeae0 --- /dev/null +++ b/packages/atlas/src/assets/icons/IconRankGold.tsx @@ -0,0 +1,38 @@ +// THIS FILE WAS AUTOGENERATED BY SVGR. DO NOT MODIFY IT MANUALLY; +import { Ref, SVGProps, forwardRef, memo } from 'react' + +const SvgIconRankGold = forwardRef((props: SVGProps, ref: Ref) => ( + + + + + + + + + + + + + + + + + +)) +SvgIconRankGold.displayName = 'SvgIconRankGold' +const Memo = memo(SvgIconRankGold) +export { Memo as SvgIconRankGold } diff --git a/packages/atlas/src/assets/icons/IconRankSilver.tsx b/packages/atlas/src/assets/icons/IconRankSilver.tsx new file mode 100644 index 0000000000..491f80c2d8 --- /dev/null +++ b/packages/atlas/src/assets/icons/IconRankSilver.tsx @@ -0,0 +1,33 @@ +// THIS FILE WAS AUTOGENERATED BY SVGR. DO NOT MODIFY IT MANUALLY; +import { Ref, SVGProps, forwardRef, memo } from 'react' + +const SvgIconRankSilver = forwardRef((props: SVGProps, ref: Ref) => ( + + + + + + + + + + + + + + + + + +)) +SvgIconRankSilver.displayName = 'SvgIconRankSilver' +const Memo = memo(SvgIconRankSilver) +export { Memo as SvgIconRankSilver } diff --git a/packages/atlas/src/assets/icons/svgs/icon-rank-bronze.svg b/packages/atlas/src/assets/icons/svgs/icon-rank-bronze.svg new file mode 100644 index 0000000000..b86291bc17 --- /dev/null +++ b/packages/atlas/src/assets/icons/svgs/icon-rank-bronze.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/atlas/src/assets/icons/svgs/icon-rank-diamond.svg b/packages/atlas/src/assets/icons/svgs/icon-rank-diamond.svg new file mode 100644 index 0000000000..819821db5d --- /dev/null +++ b/packages/atlas/src/assets/icons/svgs/icon-rank-diamond.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/atlas/src/assets/icons/svgs/icon-rank-gold.svg b/packages/atlas/src/assets/icons/svgs/icon-rank-gold.svg new file mode 100644 index 0000000000..e63c56eef8 --- /dev/null +++ b/packages/atlas/src/assets/icons/svgs/icon-rank-gold.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/atlas/src/assets/icons/svgs/icon-rank-silver.svg b/packages/atlas/src/assets/icons/svgs/icon-rank-silver.svg new file mode 100644 index 0000000000..745207f563 --- /dev/null +++ b/packages/atlas/src/assets/icons/svgs/icon-rank-silver.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + From a65351f585ee1bed6f97b030815220cade66b8e9 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Tue, 3 Oct 2023 07:25:00 +0200 Subject: [PATCH 11/18] Correct payments link --- .../views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 4df7045f59..16fbb63db6 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -121,7 +121,11 @@ export const YppDashboardMainTab: FC = () => { {formatDate(nextPayoutDate)} - } iconPlacement="right"> + } + iconPlacement="right" + > Go to Airtable From c6b4fe22521d110e0ed92bffde54a70b01042664 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Tue, 3 Oct 2023 07:25:44 +0200 Subject: [PATCH 12/18] Adjust sync details information to follow BE data --- .../ServiceStatusWidget.tsx | 24 ++++++++++++------- .../YppLandingView/YppLandingView.types.ts | 5 ++++ 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx index 845b4b6260..26b1d595d8 100644 --- a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx +++ b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx @@ -11,7 +11,8 @@ import { YppStatusDot } from '@/components/_ypp/YppStatusPill' import { atlasConfig } from '@/config' import { cVar, sizes } from '@/styles' import { ConsoleLogger } from '@/utils/logs' -import { YppChannelStatus } from '@/views/global/YppLandingView/YppLandingView.types' +import { formatDurationShort } from '@/utils/time' +import { YppChannelStatus, YppSyncedChannel } from '@/views/global/YppLandingView/YppLandingView.types' type YppStatusDto = { version: string @@ -24,9 +25,10 @@ const YPP_DELAY_THRESHOLD = atlasConfig.features.ypp.yppDelayThreshold ?? 500 export type ServiceStatusWidgetProps = { status?: YppChannelStatus + syncStatus?: YppSyncedChannel['syncStatus'] } -export const ServiceStatusWidget = ({ status }: ServiceStatusWidgetProps) => { +export const ServiceStatusWidget = ({ status, syncStatus }: ServiceStatusWidgetProps) => { const { data } = useQuery('ypp-status', () => axiosInstance(`${YOUTUBE_BACKEND_URL}/status`).catch(() => ConsoleLogger.warn('Failed to fetch YPP status') @@ -34,29 +36,33 @@ export const ServiceStatusWidget = ({ status }: ServiceStatusWidgetProps) => { ) const details = useMemo(() => { - if (!data) return [] + if (!syncStatus) return [] const hideData = !status || !status.startsWith('Verified') const output: [number | string, string, string][] = [] // [value, title, tooltip] - // todo: all this data needs to be user scoped, rn backend does not support it output.push([ - hideData ? '-' : data.data.syncBacklog, + hideData ? '-' : syncStatus.backlogCount || 'All synced', 'VIDEOS IN QUEUE', 'This is the total amount of your YouTube videos that are waiting to be synced', ]) output.push([ - hideData ? '-' : data.data.syncBacklog * 2 === 0 ? 'Syncing...' : data.data.syncBacklog * 2, // no info bout it + hideData + ? '-' + : syncStatus.placeInSyncQueue === 0 + ? syncStatus.backlogCount > 0 + ? 'Syncing...' + : '-' + : syncStatus.placeInSyncQueue, // no info bout it 'PLACE IN THE QUEUE', 'Sync system is based on queue as we sync channels one at a time. When you reach place 1 in queue your sync will start.', ]) output.push([ - // isOptedIn ? `In ${Math.round(data.data.syncBacklog / YPP_DELAY_THRESHOLD)} days` : '-', - '-', + hideData ? '-' : syncStatus.backlogCount > 0 ? `In ${formatDurationShort(syncStatus.fullSyncEta)} hours` : '-', 'ETA TO FULL SYNC', 'Estimated time of full sync of your videos may change based on YPP service status or service overload.', ]) return output - }, [data, status]) + }, [status, syncStatus]) return ( diff --git a/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts b/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts index d0592c5db0..e617518f1f 100644 --- a/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts +++ b/packages/atlas/src/views/global/YppLandingView/YppLandingView.types.ts @@ -31,4 +31,9 @@ export type YppSyncedChannel = { yppStatus: 'Unverified' | 'Suspended' | 'Verified' }[] referrerChannelId: string + syncStatus: { + backlogCount: number + fullSyncEta: number + placeInSyncQueue: number + } } From 33daa615a0f33fc7a7ca6f78f7c5c8f25a181b58 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Tue, 3 Oct 2023 10:28:55 +0200 Subject: [PATCH 13/18] Modify eslint config --- .stylelintrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/.stylelintrc.js b/.stylelintrc.js index 01c9727f2c..bb3850bd90 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -3,6 +3,7 @@ module.exports = { defaultSeverity: 'warning', customSyntax: '@stylelint/postcss-css-in-js', rules: { + 'function-url-quotes': null, 'no-empty-source': null, 'function-name-case': null, 'custom-property-empty-line-before': null, From 7b6cdc6d729d8c2c0a11097f40a3b06aaa1b513a Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Tue, 3 Oct 2023 11:13:52 +0200 Subject: [PATCH 14/18] Fix linter --- packages/atlas/atlas.config.yml | 10 +++++----- .../components/YppReferralTable/YppReferralTable.tsx | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/atlas/atlas.config.yml b/packages/atlas/atlas.config.yml index 639f36757f..9ed1885645 100644 --- a/packages/atlas/atlas.config.yml +++ b/packages/atlas/atlas.config.yml @@ -119,11 +119,11 @@ features: linkText: Go to Notion # Used only on YPP Dashboard - if empty defaults to "Go to {title}" label: Notion # Used for YPP Dashboard to inform user which vendor given feature uses - if empty defaults to title icon: info # Optional icon to be displayed. Possible icons: message, info, tokenStack -# - title: Payments -# link: /studio/payments -# linkText: Go to Payments -# label: Studio -# icon: tokenStack + # - title: Payments + # link: /studio/payments + # linkText: Go to Payments + # label: Studio + # icon: tokenStack - title: Support link: https://discord.com/channels/811216481340751934/1053294778529353788 linkText: Go to Discord diff --git a/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx b/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx index bb7ef48009..0d2bf69cd1 100644 --- a/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx +++ b/packages/atlas/src/components/YppReferralTable/YppReferralTable.tsx @@ -76,7 +76,7 @@ const Channel = ({ channel }: { channel: YppReferral['channel'] }) => { ) } -const Tier = ({ tier }: { tier: number }) => { +const Tier = (_: { tier: number }) => { return ( {/*{TIERS[tier].icon}*/} From 9691c4ebee41d7f04c419c7617bd61337fe04bcf Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Wed, 4 Oct 2023 11:13:36 +0200 Subject: [PATCH 15/18] Overall fixes --- .../src/components/CopyButton/CopyButton.tsx | 50 +++++++++ .../_ypp/BenefitCard/BenefitCard.tsx | 2 +- .../ServiceStatusWidget.tsx | 7 +- .../YppDashboardTier/YppDashboardTier.tsx | 5 +- .../_ypp/YppStatusPill/YppStatusDot.styles.ts | 9 +- .../_ypp/YppStatusPill/YppStatusDot.tsx | 6 +- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 101 ++++++------------ .../tabs/YppDashboardTabs.styles.ts | 32 ++---- 8 files changed, 108 insertions(+), 104 deletions(-) create mode 100644 packages/atlas/src/components/CopyButton/CopyButton.tsx diff --git a/packages/atlas/src/components/CopyButton/CopyButton.tsx b/packages/atlas/src/components/CopyButton/CopyButton.tsx new file mode 100644 index 0000000000..c4c2039f43 --- /dev/null +++ b/packages/atlas/src/components/CopyButton/CopyButton.tsx @@ -0,0 +1,50 @@ +import styled from '@emotion/styled' +import { useRef, useState } from 'react' + +import { Text } from '@/components/Text' +import { Button, ButtonProps } from '@/components/_buttons/Button' +import { Popover, PopoverImperativeHandle } from '@/components/_overlays/Popover' +import { useClipboard } from '@/hooks/useClipboard' +import { cVar, sizes } from '@/styles' + +export type CopyButtonProps = { + textToCopy: string + copySuccessText?: string +} & Omit +export const CopyButton = ({ textToCopy, copySuccessText = 'Copied', ...buttonProps }: CopyButtonProps) => { + const { copyToClipboard } = useClipboard() + const popoverRef = useRef(null) + const [copyButtonClicked, setCopyButtonClicked] = useState(false) + const handleCopy = () => { + if (!textToCopy || copyButtonClicked) { + return + } + copyToClipboard(textToCopy) + setCopyButtonClicked(true) + popoverRef.current?.show() + setTimeout(() => { + setCopyButtonClicked(false) + popoverRef.current?.hide() + }, 3_000) + } + + return ( +
+ }> + + + {copySuccessText} + + + + +
+ ) +} + +const PopoverContent = styled.div` + background-color: ${cVar('colorBackgroundElevated')}; + border-radius: ${cVar('radiusSmall')}; + padding: ${sizes(2)}; +` diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx index d38ac0617b..55aa0ecc9d 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx @@ -65,7 +65,7 @@ export const BenefitCard: FC = ({
)} - + {amountTooltip && } )} {actionNode} diff --git a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx index 26b1d595d8..c11e9759b4 100644 --- a/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx +++ b/packages/atlas/src/components/_ypp/ServiceStatusWidget/ServiceStatusWidget.tsx @@ -36,8 +36,7 @@ export const ServiceStatusWidget = ({ status, syncStatus }: ServiceStatusWidgetP ) const details = useMemo(() => { - if (!syncStatus) return [] - const hideData = !status || !status.startsWith('Verified') + const hideData = !status || !syncStatus || !status.startsWith('Verified') const output: [number | string, string, string][] = [] // [value, title, tooltip] output.push([ @@ -67,7 +66,7 @@ export const ServiceStatusWidget = ({ status, syncStatus }: ServiceStatusWidgetP return ( - + YPP SYNC SERVICE STATUS @@ -96,7 +95,7 @@ export const ServiceStatusWidget = ({ status, syncStatus }: ServiceStatusWidgetP {title} - + ))} diff --git a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx index cc3182dab0..66ef73d61a 100644 --- a/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx +++ b/packages/atlas/src/components/_ypp/YppDashboardTier/YppDashboardTier.tsx @@ -44,7 +44,10 @@ export const YppDashboardTier = ({ status, onSignUp }: YppDashboardTierProps) => CHANNEL YPP STATUS - + {content()}
diff --git a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.styles.ts b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.styles.ts index f5d1e4e75e..7aeb32b1e7 100644 --- a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.styles.ts +++ b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.styles.ts @@ -1,7 +1,7 @@ import { css, keyframes } from '@emotion/react' import styled from '@emotion/styled' -import { sizes } from '@/styles' +import { sizes, square } from '@/styles' // export const Container = styled.div` // display: flex; @@ -63,6 +63,13 @@ export const StatusDot = styled.div` animation: 10s ease-out ${getDotAnimation} infinite; ` +export const StatusDotWrapper = styled.div` + ${square(32)}; + + display: grid; + place-items: center; +` + export const TooltipBox = styled.div` padding: ${sizes(1)}; display: grid; diff --git a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.tsx b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.tsx index de2642815b..df468980d3 100644 --- a/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.tsx +++ b/packages/atlas/src/components/_ypp/YppStatusPill/YppStatusDot.tsx @@ -3,7 +3,7 @@ import { useRef } from 'react' import { Text } from '@/components/Text' import { Tooltip } from '@/components/Tooltip' -import { StatusDot, TooltipBox } from './YppStatusDot.styles' +import { StatusDot, StatusDotWrapper, TooltipBox } from './YppStatusDot.styles' export type YppStatusType = 'operational' | 'delayed' | 'stopped' @@ -28,7 +28,9 @@ export const YppStatusDot = ({ status }: YppStatusDotProps) => { return ( <> - + + + { } export const YppDashboardMainTab: FC = () => { - const { copyToClipboard } = useClipboard() const { trackReferralLinkGenerated } = useSegmentAnalytics() const { channelId } = useUser() const navigate = useNavigate() @@ -43,8 +43,11 @@ export const YppDashboardMainTab: FC = () => { state.dismissedMessages.some((message) => message.id === getMessageIdForChannel(channelId as string)) ) const updateDismissedMessages = usePersonalDataStore((state) => state.actions.updateDismissedMessages) - const { unsyncedChannels, currentChannel } = useGetYppSyncedChannels() - + const { unsyncedChannels } = useGetYppSyncedChannels() + const currentChannel = { + yppStatus: 'Verified::Bronze', + shouldBeIngested: true, + } const mdMatch = useMediaMatch('md') const smMatch = useMediaMatch('sm') const lgMatch = useMediaMatch('lg') @@ -58,50 +61,6 @@ export const YppDashboardMainTab: FC = () => { return ( <> - - {/*}*/} - {/* title="Have more than one YouTube channel?"*/} - {/* description={`You can apply to the YouTube Partner Program with as many YouTube & ${APP_NAME} channels as you want. Each YouTube channel can be assigned to only one ${APP_NAME} channel. \nYou can create a new channel from the top right menu.`}*/} - {/*/>*/} - {/*{currentChannel?.yppStatus === 'Suspended' && (*/} - {/* }*/} - {/* description={*/} - {/* */} - {/* You will not be rewarded while this channel is suspended. Your channel did not pass the verification due*/} - {/* to{' '}*/} - {/* */} - {/* .*/} - {/* */} - {/* }*/} - {/* />*/} - {/*)}*/} - {/*{currentChannel?.yppStatus === 'Unverified' && (*/} - {/* }*/} - {/* description={*/} - {/* */} - {/* Your channel needs to get verified before content syncing starts. It normally takes 12-48 hours for*/} - {/* channels to get verified.*/} - {/*
*/} - {/* Once verified, you will qualify for the rewards. Payouts are made on a weekly basis, every Friday, for the*/} - {/* previous calendar week. Your first payment will involve the reward for the sign up of{' '}*/} - {/* {' '}*/} - {/* USD paid out in ${atlasConfig.joystream.tokenTicker} tokens based on the market rate.*/} - {/*
*/} - {/* }*/} - {/* />*/} - {/*)}*/} @@ -115,6 +74,7 @@ export const YppDashboardMainTab: FC = () => { title="Next payments round" tooltip={{ text: 'All of the payments are processed every Friday. The hour of payouts may vary.', + placement: 'top-start', }} customNode={ @@ -211,14 +171,20 @@ export const YppDashboardMainTab: FC = () => { isRangeAmount={!currentChannel || !currentChannel.yppStatus.startsWith('Verified')} amountTooltip="Your YouTube channel is being automatically synced with your Gleev channel. You will be rewarded every time a new video gets synced." actionNode={ - !currentChannel?.yppStatus.startsWith('Suspended') ? ( - - - - Autosync: On - - - ) : ( + currentChannel?.yppStatus.startsWith('Verified') ? ( + currentChannel?.shouldBeIngested && ( + + + + + + + + Autosync: {currentChannel.shouldBeIngested ? 'On' : 'Off'} + + + ) + ) : !currentChannel?.yppStatus.startsWith('Suspended') ? null : ( Suspended @@ -236,18 +202,15 @@ export const YppDashboardMainTab: FC = () => { dollarAmount={getTierRewards('diamond')?.[2]} isRangeAmount actionNode={ - + trackReferralLinkGenerated(channelId)}> + + Copy referral link + + } /> diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts index 7b60b53b4b..aa2d5c17fe 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts @@ -5,35 +5,10 @@ import { SvgActionArrowRight, SvgAlertsInformative24, SvgAlertsWarning32 } from import { ActionBar } from '@/components/ActionBar' import { Banner } from '@/components/Banner' import { Button } from '@/components/_buttons/Button' -import { cVar, media, sizes, zIndex } from '@/styles' +import { cVar, media, sizes, square, zIndex } from '@/styles' export { Divider } from '../YppDashboard.styles' -export const RewardsWrapper = styled.div` - display: grid; - gap: ${sizes(4)}; - margin-bottom: ${sizes(4)}; - - ${media.md} { - gap: ${sizes(6)}; - margin-bottom: ${sizes(6)}; - } -` - -export const WidgetsWrapper = styled.section` - display: grid; - gap: ${sizes(4)}; - margin-bottom: ${sizes(4)}; - - ${media.sm} { - grid-template-columns: repeat(3, 1fr); - } - - ${media.md} { - margin-bottom: ${sizes(6)}; - } -` - export const StyledSvgAlertsInformative24 = styled(SvgAlertsInformative24)` path { fill: ${cVar('colorTextStrong')}; @@ -108,7 +83,12 @@ export const YppSyncStatus = styled.div` white-space: nowrap; } ` +export const StatusDotWrapper = styled.div` + ${square(20)}; + display: grid; + place-items: center; +` export const StatusDot = styled.div` width: 10px; height: 10px; From 36b24b1c6c18913936ca94f7ba42584ab05229b4 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Wed, 4 Oct 2023 11:37:34 +0200 Subject: [PATCH 16/18] Css fixes --- .../src/components/CopyButton/CopyButton.tsx | 17 +++++++--- .../_ypp/BenefitCard/BenefitCard.styles.ts | 6 +++- .../_ypp/BenefitCard/BenefitCard.tsx | 7 ++-- .../studio/YppDashboard/YppDashboard.tsx | 3 +- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 34 ++++++++++--------- .../tabs/YppDashboardTabs.styles.ts | 9 +++++ 6 files changed, 51 insertions(+), 25 deletions(-) diff --git a/packages/atlas/src/components/CopyButton/CopyButton.tsx b/packages/atlas/src/components/CopyButton/CopyButton.tsx index c4c2039f43..3a6b2fc9ce 100644 --- a/packages/atlas/src/components/CopyButton/CopyButton.tsx +++ b/packages/atlas/src/components/CopyButton/CopyButton.tsx @@ -10,8 +10,16 @@ import { cVar, sizes } from '@/styles' export type CopyButtonProps = { textToCopy: string copySuccessText?: string -} & Omit -export const CopyButton = ({ textToCopy, copySuccessText = 'Copied', ...buttonProps }: CopyButtonProps) => { + className?: string + onClick?: () => void +} & Omit +export const CopyButton = ({ + textToCopy, + copySuccessText = 'Copied', + className, + onClick, + ...buttonProps +}: CopyButtonProps) => { const { copyToClipboard } = useClipboard() const popoverRef = useRef(null) const [copyButtonClicked, setCopyButtonClicked] = useState(false) @@ -21,6 +29,7 @@ export const CopyButton = ({ textToCopy, copySuccessText = 'Copied', ...buttonPr } copyToClipboard(textToCopy) setCopyButtonClicked(true) + onClick?.() popoverRef.current?.show() setTimeout(() => { setCopyButtonClicked(false) @@ -29,7 +38,7 @@ export const CopyButton = ({ textToCopy, copySuccessText = 'Copied', ...buttonPr } return ( -
+ }> @@ -39,7 +48,7 @@ export const CopyButton = ({ textToCopy, copySuccessText = 'Copied', ...buttonPr
+ ) } diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts index ad68edce38..ef9215774e 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.styles.ts @@ -116,5 +116,9 @@ export const StyledJoyTokenIcon = styled(JoyTokenIcon)` ` export const ContenBox = styled(LayoutGrid)` - padding: ${sizes(6)}; + padding: ${sizes(4)}; + + ${media.sm} { + padding: ${sizes(6)}; + } ` diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx index 55aa0ecc9d..2c5324be0d 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx @@ -28,6 +28,7 @@ export const BenefitCard: FC = ({ amountTooltip, }) => { const smMatch = useMediaMatch('sm') + const mdMatch = useMediaMatch('md') const lgMatch = useMediaMatch('lg') return ( @@ -35,7 +36,7 @@ export const BenefitCard: FC = ({ - + {title} @@ -44,14 +45,14 @@ export const BenefitCard: FC = ({ {typeof dollarAmount === 'number' && ( {isRangeAmount ? ( - + Up to +{dollarAmount} USD diff --git a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx index 0f8b6ea556..f50470db0d 100644 --- a/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/YppDashboard.tsx @@ -18,6 +18,7 @@ const TABS = ['Dashboard', 'Referrals', 'Settings'] as const export const YppDashboard: FC = () => { const headTags = useHeadTags('YouTube Partner Program') const mdMatch = useMediaMatch('md') + const xsMatch = useMediaMatch('xs') const [currentVideosTab, setCurrentVideosTab] = useState(0) const { trackPageView } = useSegmentAnalytics() const { processingAssets, uploads } = useUploadsStore() @@ -51,7 +52,7 @@ export const YppDashboard: FC = () => { {headTags}
- + YouTube Partner Program
diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index 1700f7a111..ff0bb506d9 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -2,7 +2,6 @@ import { FC } from 'react' import { useNavigate } from 'react-router-dom' import { SvgActionClose, SvgActionNewChannel, SvgActionNewTab } from '@/assets/icons' -import { CopyButton } from '@/components/CopyButton/CopyButton' import { FlexBox } from '@/components/FlexBox' import { Information } from '@/components/Information' import { GridItem, LayoutGrid } from '@/components/LayoutGrid' @@ -26,7 +25,13 @@ import { configYppIconMapper } from '@/views/global/YppLandingView/YppFooter' import { useGetYppSyncedChannels } from '@/views/global/YppLandingView/useGetYppSyncedChannels' import { getTierRewards } from '@/views/studio/YppDashboard/YppDashboard.config' -import { StatusDot, StatusDotWrapper, StyledCloseButton, YppSyncStatus } from './YppDashboardTabs.styles' +import { + StatusDot, + StatusDotWrapper, + StyledCloseButton, + StyledCopyButton, + YppSyncStatus, +} from './YppDashboardTabs.styles' const SIGNUP_MESSAGE = 'YPP_SIGNUP_MESSAGE-' @@ -43,11 +48,8 @@ export const YppDashboardMainTab: FC = () => { state.dismissedMessages.some((message) => message.id === getMessageIdForChannel(channelId as string)) ) const updateDismissedMessages = usePersonalDataStore((state) => state.actions.updateDismissedMessages) - const { unsyncedChannels } = useGetYppSyncedChannels() - const currentChannel = { - yppStatus: 'Verified::Bronze', - shouldBeIngested: true, - } + const { unsyncedChannels, currentChannel } = useGetYppSyncedChannels() + const mdMatch = useMediaMatch('md') const smMatch = useMediaMatch('sm') const lgMatch = useMediaMatch('lg') @@ -139,6 +141,7 @@ export const YppDashboardMainTab: FC = () => { icon={} disabled={!!currentChannel} iconPlacement="right" + fullWidth={!smMatch} onClick={handleYppSignUpClick} > Sign up @@ -202,15 +205,14 @@ export const YppDashboardMainTab: FC = () => { dollarAmount={getTierRewards('diamond')?.[2]} isRangeAmount actionNode={ - trackReferralLinkGenerated(channelId)}> - - Copy referral link - - + trackReferralLinkGenerated(channelId)} + > + Copy referral link + } /> diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts index aa2d5c17fe..832e9ec2ce 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts @@ -4,6 +4,7 @@ import styled from '@emotion/styled' import { SvgActionArrowRight, SvgAlertsInformative24, SvgAlertsWarning32 } from '@/assets/icons' import { ActionBar } from '@/components/ActionBar' import { Banner } from '@/components/Banner' +import { CopyButton } from '@/components/CopyButton/CopyButton' import { Button } from '@/components/_buttons/Button' import { cVar, media, sizes, square, zIndex } from '@/styles' @@ -111,3 +112,11 @@ export const StyledCloseButton = styled(Button)` position: static; } ` + +export const StyledCopyButton = styled(CopyButton)` + width: 100%; + + ${media.sm} { + width: auto; + } +` From 5d435da128007b63c34d5cefc320f3526487f4d7 Mon Sep 17 00:00:00 2001 From: WRadoslaw Date: Wed, 4 Oct 2023 11:58:42 +0200 Subject: [PATCH 17/18] BenefitCard layout rework --- .../_ypp/BenefitCard/BenefitCard.tsx | 71 +++++++++++-------- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 7 +- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx index 2c5324be0d..27634cc659 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx @@ -28,9 +28,30 @@ export const BenefitCard: FC = ({ amountTooltip, }) => { const smMatch = useMediaMatch('sm') - const mdMatch = useMediaMatch('md') const lgMatch = useMediaMatch('lg') + const rewardContent = + typeof dollarAmount === 'number' ? ( + + {isRangeAmount ? ( + + + Up to +{dollarAmount} USD + + + Depending on tier + + + ) : ( + + {dollarAmount > 0 ? `+${dollarAmount} USD` : 'Not paid'} + + )} + + {amountTooltip && } + + ) : null + return ( @@ -43,34 +64,26 @@ export const BenefitCard: FC = ({ {description}
- - {typeof dollarAmount === 'number' && ( - - {isRangeAmount ? ( - - - Up to +{dollarAmount} USD - - - Depending on tier - - - ) : ( - - {dollarAmount > 0 ? `+${dollarAmount} USD` : 'Not paid'} - - )} - - {amountTooltip && } - - )} - {actionNode} - + {lgMatch ? ( + <> + + {rewardContent} + + + {actionNode} + + + ) : ( + + {rewardContent} + {actionNode} + + )}
) diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index ff0bb506d9..a58461721a 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -172,7 +172,11 @@ export const YppDashboardMainTab: FC = () => { : getTierRewards(currentChannel.yppStatus.split('::')[1].toLowerCase())?.[1] } isRangeAmount={!currentChannel || !currentChannel.yppStatus.startsWith('Verified')} - amountTooltip="Your YouTube channel is being automatically synced with your Gleev channel. You will be rewarded every time a new video gets synced." + amountTooltip={ + !currentChannel?.yppStatus.startsWith('Verified') + ? 'Ranks are assigned at discretion of Joystream team based on such factors as content quality and channel popularity' + : 'Your YouTube channel is being automatically synced with your Gleev channel. You will be rewarded every time a new video gets synced.' + } actionNode={ currentChannel?.yppStatus.startsWith('Verified') ? ( currentChannel?.shouldBeIngested && ( @@ -203,6 +207,7 @@ export const YppDashboardMainTab: FC = () => { title="Refer another YouTube creator" description="Get rewarded for every new creator who signs up to YPP program using your referral link. Referrals rewards depends on the tier assigned to the invited channel." dollarAmount={getTierRewards('diamond')?.[2]} + amountTooltip="Ranks are assigned at discretion of Joystream team based on such factors as content quality and channel popularity" isRangeAmount actionNode={ Date: Wed, 4 Oct 2023 13:40:11 +0200 Subject: [PATCH 18/18] Add off sync and tooltips --- .../_ypp/BenefitCard/BenefitCard.tsx | 14 +++-- .../YppDashboard/tabs/YppDashboardMainTab.tsx | 60 +++++++++++++------ .../tabs/YppDashboardTabs.styles.ts | 12 ++-- 3 files changed, 58 insertions(+), 28 deletions(-) diff --git a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx index 27634cc659..37190c2373 100644 --- a/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx +++ b/packages/atlas/src/components/_ypp/BenefitCard/BenefitCard.tsx @@ -66,10 +66,16 @@ export const BenefitCard: FC = ({ {lgMatch ? ( <> - - {rewardContent} - - + {typeof dollarAmount === 'number' && ( + + {rewardContent} + + )} + {actionNode} diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx index a58461721a..f22b75a6cb 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardMainTab.tsx @@ -60,6 +60,27 @@ export const YppDashboardMainTab: FC = () => { navigate(absoluteRoutes.viewer.ypp()) } } + + const syncStatusContent = ( + + + + + + + + Autosync: {currentChannel?.shouldBeIngested ? 'On' : 'Off'} + + + ) + return ( <> @@ -115,7 +136,7 @@ export const YppDashboardMainTab: FC = () => { /> ))} - {!hasDismissedSignupMessage && ( + {!hasDismissedSignupMessage && !currentChannel?.yppStatus.startsWith('Suspended') && ( { } actionNode={ currentChannel?.yppStatus.startsWith('Verified') ? ( - currentChannel?.shouldBeIngested && ( - - - - - - - - Autosync: {currentChannel.shouldBeIngested ? 'On' : 'Off'} - - - ) + syncStatusContent ) : !currentChannel?.yppStatus.startsWith('Suspended') ? null : ( - - - Suspended - - + + + + Suspended + + + + {syncStatusContent} ) } diff --git a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts index 832e9ec2ce..de0da4bb41 100644 --- a/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts +++ b/packages/atlas/src/views/studio/YppDashboard/tabs/YppDashboardTabs.styles.ts @@ -51,13 +51,13 @@ export const StyledActionBar = styled(ActionBar)` export const FallbackContainer = styled.div` margin-top: 128px; ` -const dotPulse = keyframes` +const dotPulse = ({ isOn }: { isOn: boolean }) => keyframes` 0% { box-shadow: none; } 10% { - box-shadow: 0 0 0 3px #0c984680; + box-shadow: 0 0 0 3px ${isOn ? '#0c984680' : '#ff695f80'}; } 20%, 100% { @@ -90,13 +90,13 @@ export const StatusDotWrapper = styled.div` display: grid; place-items: center; ` -export const StatusDot = styled.div` +export const StatusDot = styled.div<{ isOn: boolean }>` width: 10px; height: 10px; border-radius: 50%; - background: linear-gradient(#0ebe57, #096c34); - box-shadow: 0 0 0 5px #0c984680; - animation: 10s ease-out ${dotPulse} infinite; + background: ${(props) => (props.isOn ? 'linear-gradient(#0ebe57, #096c34)' : 'linear-gradient(#ff695f, #bf0c00)')}; + box-shadow: 0 0 0 5px ${(props) => (props.isOn ? '#0c984680' : '#ff695f80')}; + animation: 10s ease-out ${(props) => dotPulse(props)} infinite; ` export const StyledCloseButton = styled(Button)`