diff --git a/packages/atlas/atlas.config.yml b/packages/atlas/atlas.config.yml index 33b3273015..f8f4055f36 100644 --- a/packages/atlas/atlas.config.yml +++ b/packages/atlas/atlas.config.yml @@ -38,7 +38,7 @@ joystream: features: ypp: - enabled: false # Whether the YPP is currently enabled + suspended: true # Whether the YPP is currently suspended yppDelayThreshold: 300 # When the YPP sync backlog exceeds the threshold, Atlas will consider the YPP sync delayed. landingPageOgTitle: 'Gleev Creator Rewards Program' # Open graph title for YPP landing page - used in open graph meta tags in HTML landingPageOgDescription: 'Connect and monetize your content with Web3 native features on Gleev, the video streaming dApp built on Joystream blockchain.' # Open graph description for YPP landing page - used in open graph meta tags in HTML diff --git a/packages/atlas/src/components/_inputs/OptionCardGroup/OptionCardGroup.tsx b/packages/atlas/src/components/_inputs/OptionCardGroup/OptionCardGroup.tsx index 641437e08b..db370a24ca 100644 --- a/packages/atlas/src/components/_inputs/OptionCardGroup/OptionCardGroup.tsx +++ b/packages/atlas/src/components/_inputs/OptionCardGroup/OptionCardGroup.tsx @@ -15,7 +15,13 @@ type Checkbox = { type Radio = { selectedValue?: string | number | boolean | null onChange?: (value: string | number | boolean) => void - options: Array<{ label: string; caption?: string; value: string | number | boolean; icon?: ReactNode }> + options: Array<{ + label: string + caption?: string + value: string | number | boolean + icon?: ReactNode + disabled?: boolean + }> } export type OptionCardGroupProps = { diff --git a/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx b/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx index 622a12a407..37e029ced9 100644 --- a/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx +++ b/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx @@ -60,13 +60,17 @@ export const viewerNavItems = [ }, ] : []), - { - icon: , - name: 'Referrals', - expandedName: 'Referrals program', - to: absoluteRoutes.viewer.referrals(), - bottomNav: true, - }, + ...(!atlasConfig.features.ypp.suspended + ? [ + { + icon: , + name: 'Referrals', + expandedName: 'Referrals program', + to: absoluteRoutes.viewer.referrals(), + bottomNav: true, + }, + ] + : []), ] export const SidenavViewer: FC = () => { const [expanded, setExpanded] = useState(false) diff --git a/packages/atlas/src/components/_referrals/ReferralsBanner/ReferralsBanner.tsx b/packages/atlas/src/components/_referrals/ReferralsBanner/ReferralsBanner.tsx index 15965675d2..a844513c68 100644 --- a/packages/atlas/src/components/_referrals/ReferralsBanner/ReferralsBanner.tsx +++ b/packages/atlas/src/components/_referrals/ReferralsBanner/ReferralsBanner.tsx @@ -11,6 +11,10 @@ export const ReferralsBanner = () => { const navigate = useNavigate() const referralReward = (getTierRewards('diamond')?.referral || 0) * referralMultiplier + if (atlasConfig.features.ypp.suspended) { + return null + } + return ( ( + } + borderColor={cVar('colorTextCaution')} + size="medium" + description={ + <> + + Due to recent technical issues with the YouTube Sync service, the YouTube Partner Program (YPP) has been + temporarily suspended until further notice. + + + The issues we identified: + + + + + Some creators were inadvertently removed from YPP due to a bug in the YouTube Sync service ( + + GitHub issue + + ) + + + + + + Recently imposed YouTube rate limits have caused synchronization delays, leaving over 100,000 videos + stuck in the sync queue. + + + + + + We're actively working to resolve these issues and will provide updates as soon as possible. + + + To minimize disruption, we have implemented the following temporary measures: + + + + We disabled new YPP signups. + + + + We paused all creator payouts and rewards. + + + + We limited content synchronization to selected channels. + + + + + If you're currently enrolled in YPP, you can still disable YouTube sync or opt out of the program via the{' '} + + Settings + {' '} + tab. + + + Please note that these actions are currently irreversible. + + + We apologize for any inconvenience and appreciate your understanding. + + + } + /> +) diff --git a/packages/atlas/src/components/_ypp/YppSuspendedBanner/YppSuspendedBannerStyles.ts b/packages/atlas/src/components/_ypp/YppSuspendedBanner/YppSuspendedBannerStyles.ts new file mode 100644 index 0000000000..f21ad340ae --- /dev/null +++ b/packages/atlas/src/components/_ypp/YppSuspendedBanner/YppSuspendedBannerStyles.ts @@ -0,0 +1,29 @@ +import styled from '@emotion/styled' + +import { cVar, sizes } from '@/styles' + +export const List = styled.ul` + margin: ${sizes(2)} 0; + padding: 0 0 0 ${sizes(4)}; + list-style-type: none; +` +export const ListItem = styled.li` + padding: 0; + margin: 0 0 ${sizes(2)} 0; + display: flex; + align-items: flex-start; +` + +export const ListItemMarker = styled.div` + min-width: 6px; + min-height: 6px; + border-radius: 50%; + width: 6px; + height: 6px; + margin: ${sizes(1.5)} ${sizes(3)} 0 0; + background-color: ${cVar('colorText')}; +` + +export const ListItemContent = styled.div` + flex: 1; +` diff --git a/packages/atlas/src/components/_ypp/YppSuspendedBanner/index.ts b/packages/atlas/src/components/_ypp/YppSuspendedBanner/index.ts new file mode 100644 index 0000000000..883b4219ad --- /dev/null +++ b/packages/atlas/src/components/_ypp/YppSuspendedBanner/index.ts @@ -0,0 +1 @@ +export * from './YppSuspendedBanner' diff --git a/packages/atlas/src/components/_ypp/YppDisabledModal/YppDisabledModal.tsx b/packages/atlas/src/components/_ypp/YppSuspendedModal/YppSuspendedModal.tsx similarity index 66% rename from packages/atlas/src/components/_ypp/YppDisabledModal/YppDisabledModal.tsx rename to packages/atlas/src/components/_ypp/YppSuspendedModal/YppSuspendedModal.tsx index 89516dce16..bbac57d041 100644 --- a/packages/atlas/src/components/_ypp/YppDisabledModal/YppDisabledModal.tsx +++ b/packages/atlas/src/components/_ypp/YppSuspendedModal/YppSuspendedModal.tsx @@ -3,29 +3,33 @@ import { FC } from 'react' import { Text } from '@/components/Text' import { DialogModal } from '@/components/_overlays/DialogModal' -export type ConnectWithYtModalProps = { +export type YppSuspendedModalProps = { + buttonText?: string show: boolean onClose: () => void } -export const YppDisabledModal: FC = ({ show, onClose }) => { - // const smMatch = useMediaMatch('sm') +export const YppSuspendedModal: FC = ({ + show, + onClose, + buttonText = 'Go back to home page', +}) => { return ( - YouTube Partner Program temporarily disabled + YouTube Partner Program temporarily suspended - Due to recent technical issues with the YouTube Synch service, YouTube Partner Program has been temporarily - disabled until further notice. + Due to recent technical issues with the YouTube Sync service, YouTube Partner Program (YPP) has been temporarily + suspended until further notice. You can still create a channel on Gleev, but it will not be connected with your YouTube channel and you will not diff --git a/packages/atlas/src/components/_ypp/YppDisabledModal/YppDisabledModalStyles.ts b/packages/atlas/src/components/_ypp/YppSuspendedModal/YppSuspendedModalStyles.ts similarity index 100% rename from packages/atlas/src/components/_ypp/YppDisabledModal/YppDisabledModalStyles.ts rename to packages/atlas/src/components/_ypp/YppSuspendedModal/YppSuspendedModalStyles.ts diff --git a/packages/atlas/src/components/_ypp/YppSuspendedModal/index.ts b/packages/atlas/src/components/_ypp/YppSuspendedModal/index.ts new file mode 100644 index 0000000000..61a5b956ca --- /dev/null +++ b/packages/atlas/src/components/_ypp/YppSuspendedModal/index.ts @@ -0,0 +1 @@ +export * from './YppSuspendedModal' diff --git a/packages/atlas/src/config/configSchema.ts b/packages/atlas/src/config/configSchema.ts index 927a5ea312..158a8eb89b 100644 --- a/packages/atlas/src/config/configSchema.ts +++ b/packages/atlas/src/config/configSchema.ts @@ -37,7 +37,7 @@ export const configSchema = z.object({ }), features: z.object({ ypp: z.object({ - enabled: z.boolean(), + suspended: z.boolean(), suspensionReasonsLink: z.string().nullable(), yppDelayThreshold: z.number().nullable(), googleConsoleClientId: z.string().nullable(), diff --git a/packages/atlas/src/views/global/ReferralsView/ReferralsView.tsx b/packages/atlas/src/views/global/ReferralsView/ReferralsView.tsx index 46edb9563e..e45f131707 100644 --- a/packages/atlas/src/views/global/ReferralsView/ReferralsView.tsx +++ b/packages/atlas/src/views/global/ReferralsView/ReferralsView.tsx @@ -1,5 +1,9 @@ -import { useEffect } from 'react' +import { useEffect, useState } from 'react' +import { useNavigate } from 'react-router' +import { YppSuspendedModal } from '@/components/_ypp/YppSuspendedModal' +import { atlasConfig } from '@/config' +import { absoluteRoutes } from '@/config/routes' import { useMediaMatch } from '@/hooks/useMediaMatch' import { usePersonalDataStore } from '@/providers/personalData' import { StyledLimitedWidthWrapper } from '@/views/global/ReferralsView/ReferralsView.styles' @@ -10,6 +14,7 @@ import { ReferralsVideo } from '@/views/global/ReferralsView/sections/ReferralsV import { TopReferrals } from '@/views/global/ReferralsView/sections/TopReferrals/TopReferrals' export const ReferralsView = () => { + const [showYppSuspendedModal, setShowYppSuspendedModal] = useState(true) const updateDismissedMessages = usePersonalDataStore((state) => state.actions.updateDismissedMessages) useEffect(() => { updateDismissedMessages('referrals-banner') @@ -18,6 +23,8 @@ export const ReferralsView = () => { const mdMatch = useMediaMatch('md') const xsMatch = useMediaMatch('xs') + const navigate = useNavigate() + return ( { alignItems="center" gap={mdMatch ? 24 : xsMatch ? 16 : 14} > + {atlasConfig.features.ypp.suspended && ( + { + setShowYppSuspendedModal(false) + navigate(absoluteRoutes.viewer.index()) + }} + /> + )} {/**/} diff --git a/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx b/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx index 00ed11fa60..29ea3de7df 100644 --- a/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx +++ b/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx @@ -5,8 +5,9 @@ import { useNavigate } from 'react-router-dom' import { ParallaxProvider } from 'react-scroll-parallax' import { YppReferralBanner } from '@/components/_ypp/YppReferralBanner' +import { YppSuspendedModal } from '@/components/_ypp/YppSuspendedModal' import { atlasConfig } from '@/config' -import { QUERY_PARAMS, absoluteRoutes } from '@/config/routes' +import { absoluteRoutes } from '@/config/routes' import { useHeadTags } from '@/hooks/useHeadTags' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { useUser } from '@/providers/user/user.hooks' @@ -23,13 +24,7 @@ import { YppRewardSection } from './sections/YppRewardSection' import { YppSignupVideo } from './sections/YppSignupVideo' import { useGetYppSyncedChannels } from './useGetYppSyncedChannels' -import { YppDisabledModal } from '../../../components/_ypp/YppDisabledModal' -import { useRouterQuery } from '../../../hooks/useRouterQuery' - export const YppLandingView: FC = () => { - const yppEnabled = atlasConfig.features.ypp.enabled - const queryReferrerId = useRouterQuery(QUERY_PARAMS.REFERRER_ID) - const [showYppDisabledModal, setShowYppDisabledModal] = useState(!!queryReferrerId) const headTags = useHeadTags('YouTube Partner Program') const yppModalOpenName = useYppStore((state) => state.yppModalOpenName) const setYppModalOpen = useYppStore((state) => state.actions.setYppModalOpenName) @@ -41,6 +36,7 @@ export const YppLandingView: FC = () => { const viewerEarningsRef = useRef(null) const [wasSignInTriggered, setWasSignInTriggered] = useState(false) + const [showYppSuspendedModal, setShowYppSuspendedModal] = useState(true) const shouldContinueYppFlowAfterCreatingChannel = useYppStore( (store) => store.shouldContinueYppFlowAfterCreatingChannel ) @@ -112,22 +108,23 @@ export const YppLandingView: FC = () => { return 'have-channel' } + const yppSuspended = atlasConfig.features.ypp.suspended + return ( {headTags} - + {!yppSuspended && } - {yppEnabled ? ( - - ) : ( - { - navigate(absoluteRoutes.viewer.ypp()) - setShowYppDisabledModal(false) + setShowYppSuspendedModal(false) + navigate(absoluteRoutes.viewer.index()) }} /> )} + {!yppSuspended && } setYppModalOpen('ypp-select-channel')} onSignUpClick={handleYppSignUpClick} @@ -137,12 +134,8 @@ export const YppLandingView: FC = () => { onViewerEarnings={handleViewerEarnings} /> - {yppEnabled ? ( - <> - - - - ) : null} + + {/**/} diff --git a/packages/atlas/src/views/global/YppLandingView/sections/CreatorOpportunities.tsx b/packages/atlas/src/views/global/YppLandingView/sections/CreatorOpportunities.tsx index d76634778f..7a76e1ece5 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/CreatorOpportunities.tsx +++ b/packages/atlas/src/views/global/YppLandingView/sections/CreatorOpportunities.tsx @@ -24,16 +24,12 @@ import { } from '@/views/global/YppLandingView/YppLandingView.styles' import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' -const earningsOptions = (yppEnabled: boolean) => [ - ...(yppEnabled - ? [ - { - title: 'YouTubers', - subtitle: 'Get sign up bonus and sync rewards with connecting YouTube channel.', - image: earning_yt, - }, - ] - : []), +const earningsOptions = [ + { + title: 'YouTubers', + subtitle: 'Get sign up bonus and sync rewards with connecting YouTube channel.', + image: earning_yt, + }, { title: 'Creator Tokens', subtitle: `Mint your own token and sell it on open market to raise funding for your ${atlasConfig.general.appName} channel.`, @@ -44,19 +40,14 @@ const earningsOptions = (yppEnabled: boolean) => [ subtitle: 'Mint your NFTs and earn from selling on marketplace and royalties with every future transaction.', image: earning_nfts, }, - ...(yppEnabled - ? [ - { - title: 'More earning', - subtitle: 'Earn with building out community and social promotions.', - image: earning_more, - }, - ] - : []), + { + title: 'More earning', + subtitle: 'Earn with building out community and social promotions.', + image: earning_more, + }, ] export const CreatorOpportunities = ({ onSignUpClick }: { onSignUpClick: () => void }) => { - const yppEnabled = atlasConfig.features.ypp.enabled const setIsYppChannelFlow = useYppStore((state) => state.actions.setIsYppChannelFlow) const setAuthModalOpenName = useAuthStore((state) => state.actions.setAuthModalOpenName) const { trackRewardsCreateChannelButtonClick } = useSegmentAnalytics() @@ -100,7 +91,7 @@ export const CreatorOpportunities = ({ onSignUpClick }: { onSignUpClick: () => v - {earningsOptions(yppEnabled).map(({ title, subtitle, image }, idx) => ( + {earningsOptions.map(({ title, subtitle, image }, idx) => ( {`${title} @@ -125,11 +116,9 @@ export const CreatorOpportunities = ({ onSignUpClick }: { onSignUpClick: () => v colStart={{ base: 1 }} > - {yppEnabled ? ( - - ) : null} + {!memberChannels?.length ? ( diff --git a/packages/atlas/src/views/global/YppLandingView/sections/ViewerOpportunities.tsx b/packages/atlas/src/views/global/YppLandingView/sections/ViewerOpportunities.tsx index 04cbd0ed64..ea08e9f2f0 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/ViewerOpportunities.tsx +++ b/packages/atlas/src/views/global/YppLandingView/sections/ViewerOpportunities.tsx @@ -21,16 +21,12 @@ import { } from '@/views/global/YppLandingView/YppLandingView.styles' import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' -const viewerEarningsOptions = (yppEnabled: boolean) => [ - ...(yppEnabled - ? [ - { - title: 'Earn with Referrals', - subtitle: 'Refer YouTube channels and earn when they sign up using your link.', - image: viewer_earnings_referrals, - }, - ] - : []), +const viewerEarningsOptions = [ + { + title: 'Earn with Referrals', + subtitle: 'Refer YouTube channels and earn when they sign up using your link.', + image: viewer_earnings_referrals, + }, { title: 'Claim Channels Revenue Share', subtitle: `Buy creator tokens and claim part of channel's revenue.`, @@ -53,7 +49,6 @@ const viewerEarningsOptions = (yppEnabled: boolean) => [ ] export const ViewerOpportunities = ({ sectionRef }: { sectionRef: MutableRefObject }) => { - const yppEnabled = atlasConfig.features.ypp.enabled const mdMatch = useMediaMatch('md') const smMatch = useMediaMatch('sm') const [titleVariant, subtitleVariant] = useSectionTextVariants() @@ -92,7 +87,7 @@ export const ViewerOpportunities = ({ sectionRef }: { sectionRef: MutableRefObje - {viewerEarningsOptions(yppEnabled).map(({ title, subtitle, image }, idx) => ( + {viewerEarningsOptions.map(({ title, subtitle, image }, idx) => ( {`${title} diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx b/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx index e925b81d3a..6fd6f0302f 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx +++ b/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx @@ -30,7 +30,6 @@ type YppFooterSectionProps = { } export const YppFooter: FC = ({ onSignUpClick }) => { - const yppEnabled = atlasConfig.features.ypp.enabled const [titleVariant] = useSectionTextVariants() const smMatch = useMediaMatch('sm') const setIsYppChannelFlow = useYppStore((state) => state.actions.setIsYppChannelFlow) @@ -53,7 +52,7 @@ export const YppFooter: FC = ({ onSignUpClick }) => { Get started now - Pave the way to Web3 with your {yppEnabled ? 'YouTube ' : ''}channel right away. + Pave the way to Web3 with your YouTube channel right away. = ({ onSignUpClick }) => { gap={4} marginTop={8} > - {yppEnabled ? ( - - ) : null} + {!memberChannels?.length ? ( diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx b/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx index 8402b177bb..98758fbc1d 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx +++ b/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx @@ -73,7 +73,6 @@ export const YppHero: FC = ({ onSignUpClick, yppAtlasStatus, onVie ? channels?.map((channel) => ) : Array.from({ length: 30 }).map((_, idx) => ) const widgetContentTextVariant = mdMatch ? ('h700' as const) : ('h600' as const) - const yppEnabled = atlasConfig.features.ypp.enabled return ( @@ -141,16 +140,14 @@ export const YppHero: FC = ({ onSignUpClick, yppAtlasStatus, onVie ) : ( - {yppEnabled ? ( - - ) : null} + {!memberChannels?.length ? (