diff --git a/packages/atlas/atlas.config.yml b/packages/atlas/atlas.config.yml index 240bd24255..a0a84f8275 100644 --- a/packages/atlas/atlas.config.yml +++ b/packages/atlas/atlas.config.yml @@ -9,7 +9,7 @@ general: appOgImgPath: '/og-image.webp' # Path to the open graph image - used in open graph meta tags in HTML pioneerMemberUrlPrefix: 'https://dao.joystream.org/#/members' # URL prefix for Pioneer member profile page - used to link to member details joystreamLandingPageUrl: 'https://www.joystream.org' # URL for Joystream landing page - used in the footer and in "Learn more" links - joystreamDiscordUrl: 'https://discord.gg/DE9UN3YpRP' # URL for Joystream Discord - used for support when errors occur + joystreamDiscordUrl: 'https://discord.gg/joystream' # URL for Joystream Discord - used for support when errors occur appContentFocus: 'web3 & crypto' # Content focus of given app. Provide a string, for example `web & crypto` or `sport` crtMaintenanceMode: false storage: diff --git a/packages/atlas/src/assets/images/earnings/earning-crt-1.webp b/packages/atlas/src/assets/images/earnings/earning-crt-1.webp new file mode 100644 index 0000000000..5d3050123e Binary files /dev/null and b/packages/atlas/src/assets/images/earnings/earning-crt-1.webp differ diff --git a/packages/atlas/src/assets/images/earnings/earning-crt.webp b/packages/atlas/src/assets/images/earnings/earning-crt.webp new file mode 100644 index 0000000000..b41bf29d61 Binary files /dev/null and b/packages/atlas/src/assets/images/earnings/earning-crt.webp differ diff --git a/packages/atlas/src/assets/images/earnings/earning-more.webp b/packages/atlas/src/assets/images/earnings/earning-more.webp new file mode 100644 index 0000000000..72e918f332 Binary files /dev/null and b/packages/atlas/src/assets/images/earnings/earning-more.webp differ diff --git a/packages/atlas/src/assets/images/earnings/earning-nfts.webp b/packages/atlas/src/assets/images/earnings/earning-nfts.webp new file mode 100644 index 0000000000..6a42c9a83b Binary files /dev/null and b/packages/atlas/src/assets/images/earnings/earning-nfts.webp differ diff --git a/packages/atlas/src/assets/images/earnings/earning-yt.webp b/packages/atlas/src/assets/images/earnings/earning-yt.webp new file mode 100644 index 0000000000..6cfbe7c1f3 Binary files /dev/null and b/packages/atlas/src/assets/images/earnings/earning-yt.webp differ diff --git a/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-crt-trade.webp b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-crt-trade.webp new file mode 100644 index 0000000000..8756ecd58e Binary files /dev/null and b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-crt-trade.webp differ diff --git a/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-nft.webp b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-nft.webp new file mode 100644 index 0000000000..6754f81e55 Binary files /dev/null and b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-nft.webp differ diff --git a/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-referrals.webp b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-referrals.webp new file mode 100644 index 0000000000..bc69dc905e Binary files /dev/null and b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-referrals.webp differ diff --git a/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-revenue-share.webp b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-revenue-share.webp new file mode 100644 index 0000000000..b41bf29d61 Binary files /dev/null and b/packages/atlas/src/assets/images/viewer-earnings/viewer-earning-revenue-share.webp differ diff --git a/packages/atlas/src/assets/images/ypp-hero/crt-card-hero.webp b/packages/atlas/src/assets/images/ypp-hero/crt-card-hero.webp new file mode 100644 index 0000000000..7f1ebdd3bd Binary files /dev/null and b/packages/atlas/src/assets/images/ypp-hero/crt-card-hero.webp differ diff --git a/packages/atlas/src/assets/images/ypp-hero/crt-dashboard-hero.webp b/packages/atlas/src/assets/images/ypp-hero/crt-dashboard-hero.webp new file mode 100644 index 0000000000..ef40901e61 Binary files /dev/null and b/packages/atlas/src/assets/images/ypp-hero/crt-dashboard-hero.webp differ diff --git a/packages/atlas/src/assets/images/ypp-hero/crt-payments-hero.webp b/packages/atlas/src/assets/images/ypp-hero/crt-payments-hero.webp new file mode 100644 index 0000000000..087f85492a Binary files /dev/null and b/packages/atlas/src/assets/images/ypp-hero/crt-payments-hero.webp differ diff --git a/packages/atlas/src/assets/roadmap/community.svg b/packages/atlas/src/assets/roadmap/community.svg new file mode 100644 index 0000000000..512c038071 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/community.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/content.svg b/packages/atlas/src/assets/roadmap/content.svg new file mode 100644 index 0000000000..fc74bb930d --- /dev/null +++ b/packages/atlas/src/assets/roadmap/content.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/creator-tokens.svg b/packages/atlas/src/assets/roadmap/creator-tokens.svg new file mode 100644 index 0000000000..bd2e830cca --- /dev/null +++ b/packages/atlas/src/assets/roadmap/creator-tokens.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/dollar-sign.svg b/packages/atlas/src/assets/roadmap/dollar-sign.svg new file mode 100644 index 0000000000..d130cb8c47 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/dollar-sign.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/gear.svg b/packages/atlas/src/assets/roadmap/gear.svg new file mode 100644 index 0000000000..505f2ef3b8 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/gear.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 1 staking module.svg b/packages/atlas/src/assets/roadmap/icon 1 staking module.svg new file mode 100644 index 0000000000..a55c4efdb2 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 1 staking module.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 10 content delivery improvements.svg b/packages/atlas/src/assets/roadmap/icon 10 content delivery improvements.svg new file mode 100644 index 0000000000..51c24241c4 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 10 content delivery improvements.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 11 gleev recommended content V1.svg b/packages/atlas/src/assets/roadmap/icon 11 gleev recommended content V1.svg new file mode 100644 index 0000000000..6743a67415 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 11 gleev recommended content V1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 12 js project overview dashboard.svg b/packages/atlas/src/assets/roadmap/icon 12 js project overview dashboard.svg new file mode 100644 index 0000000000..e41956e491 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 12 js project overview dashboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 13 subscan block explorer improvements.svg b/packages/atlas/src/assets/roadmap/icon 13 subscan block explorer improvements.svg new file mode 100644 index 0000000000..926792e680 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 13 subscan block explorer improvements.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 14 pioneer financials page.svg b/packages/atlas/src/assets/roadmap/icon 14 pioneer financials page.svg new file mode 100644 index 0000000000..cc562b113e --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 14 pioneer financials page.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 15 content curator tooling.svg b/packages/atlas/src/assets/roadmap/icon 15 content curator tooling.svg new file mode 100644 index 0000000000..75674ebb59 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 15 content curator tooling.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 16 wallet implementations.svg b/packages/atlas/src/assets/roadmap/icon 16 wallet implementations.svg new file mode 100644 index 0000000000..07081b1533 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 16 wallet implementations.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 17 automated thumbnail generation.svg b/packages/atlas/src/assets/roadmap/icon 17 automated thumbnail generation.svg new file mode 100644 index 0000000000..2b9ed2735e --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 17 automated thumbnail generation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 18 pioneer forum & proposal label system.svg b/packages/atlas/src/assets/roadmap/icon 18 pioneer forum & proposal label system.svg new file mode 100644 index 0000000000..ffb19eaf26 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 18 pioneer forum & proposal label system.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 19 governance tooling.svg b/packages/atlas/src/assets/roadmap/icon 19 governance tooling.svg new file mode 100644 index 0000000000..5813e55f43 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 19 governance tooling.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 2 more exchanges.svg b/packages/atlas/src/assets/roadmap/icon 2 more exchanges.svg new file mode 100644 index 0000000000..cf25ec31b5 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 2 more exchanges.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 20 js cli improvements.svg b/packages/atlas/src/assets/roadmap/icon 20 js cli improvements.svg new file mode 100644 index 0000000000..aaf968ef97 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 20 js cli improvements.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 21 dApp OperatorDeveloper Grants & Support.svg b/packages/atlas/src/assets/roadmap/icon 21 dApp OperatorDeveloper Grants & Support.svg new file mode 100644 index 0000000000..d0e6dd55ec --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 21 dApp OperatorDeveloper Grants & Support.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 22 Non-video Content Uploads.svg b/packages/atlas/src/assets/roadmap/icon 22 Non-video Content Uploads.svg new file mode 100644 index 0000000000..cbdd086f37 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 22 Non-video Content Uploads.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 23 Premium Video Comments & Tips.svg b/packages/atlas/src/assets/roadmap/icon 23 Premium Video Comments & Tips.svg new file mode 100644 index 0000000000..1acf46a721 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 23 Premium Video Comments & Tips.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 24 DAO Creator Channel Payout tooling improvements.svg b/packages/atlas/src/assets/roadmap/icon 24 DAO Creator Channel Payout tooling improvements.svg new file mode 100644 index 0000000000..1dfadca070 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 24 DAO Creator Channel Payout tooling improvements.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 25 Pioneer Working Group Management Module.svg b/packages/atlas/src/assets/roadmap/icon 25 Pioneer Working Group Management Module.svg new file mode 100644 index 0000000000..7b177b6aa0 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 25 Pioneer Working Group Management Module.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 26 Joystream Ethereum Bridging Solution.svg b/packages/atlas/src/assets/roadmap/icon 26 Joystream Ethereum Bridging Solution.svg new file mode 100644 index 0000000000..36aa296104 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 26 Joystream Ethereum Bridging Solution.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 27 bounties module.svg b/packages/atlas/src/assets/roadmap/icon 27 bounties module.svg new file mode 100644 index 0000000000..98d8eaefdb --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 27 bounties module.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 28 Smartphone App for AtlasGleev.svg b/packages/atlas/src/assets/roadmap/icon 28 Smartphone App for AtlasGleev.svg new file mode 100644 index 0000000000..10391affcf --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 28 Smartphone App for AtlasGleev.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 29 NFT Improvements - V1.svg b/packages/atlas/src/assets/roadmap/icon 29 NFT Improvements - V1.svg new file mode 100644 index 0000000000..996393f656 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 29 NFT Improvements - V1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 3 content marketing.svg b/packages/atlas/src/assets/roadmap/icon 3 content marketing.svg new file mode 100644 index 0000000000..26e21bb465 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 3 content marketing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 30 Staking Nomination Pools.svg b/packages/atlas/src/assets/roadmap/icon 30 Staking Nomination Pools.svg new file mode 100644 index 0000000000..67b71fa2b3 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 30 Staking Nomination Pools.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 31 Joystream SDK.svg b/packages/atlas/src/assets/roadmap/icon 31 Joystream SDK.svg new file mode 100644 index 0000000000..b44c50a950 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 31 Joystream SDK.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 32 Video Communities No-tech App creation.svg b/packages/atlas/src/assets/roadmap/icon 32 Video Communities No-tech App creation.svg new file mode 100644 index 0000000000..fe816ceb56 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 32 Video Communities No-tech App creation.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 33 Premium Content Gating Features.svg b/packages/atlas/src/assets/roadmap/icon 33 Premium Content Gating Features.svg new file mode 100644 index 0000000000..f427ef51dc --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 33 Premium Content Gating Features.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 34 Multiple Video Resolutions.svg b/packages/atlas/src/assets/roadmap/icon 34 Multiple Video Resolutions.svg new file mode 100644 index 0000000000..76760bac27 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 34 Multiple Video Resolutions.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 35 Metamask Snap Development.svg b/packages/atlas/src/assets/roadmap/icon 35 Metamask Snap Development.svg new file mode 100644 index 0000000000..a8f67ea5a9 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 35 Metamask Snap Development.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 4 js discord tip bot.svg b/packages/atlas/src/assets/roadmap/icon 4 js discord tip bot.svg new file mode 100644 index 0000000000..83ef1fa798 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 4 js discord tip bot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 5 stablecoin treasury.svg b/packages/atlas/src/assets/roadmap/icon 5 stablecoin treasury.svg new file mode 100644 index 0000000000..818130a78b --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 5 stablecoin treasury.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 6 bounties program.svg b/packages/atlas/src/assets/roadmap/icon 6 bounties program.svg new file mode 100644 index 0000000000..9f70df7fc8 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 6 bounties program.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 7 creator ambassador program.svg b/packages/atlas/src/assets/roadmap/icon 7 creator ambassador program.svg new file mode 100644 index 0000000000..35dd1c7bd4 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 7 creator ambassador program.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 8 mobile design for pioneer.svg b/packages/atlas/src/assets/roadmap/icon 8 mobile design for pioneer.svg new file mode 100644 index 0000000000..f3d2b64507 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 8 mobile design for pioneer.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/icon 9 pioneer email notifications.svg b/packages/atlas/src/assets/roadmap/icon 9 pioneer email notifications.svg new file mode 100644 index 0000000000..2b0ae9b158 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/icon 9 pioneer email notifications.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/list.svg b/packages/atlas/src/assets/roadmap/list.svg new file mode 100644 index 0000000000..5f069c76df --- /dev/null +++ b/packages/atlas/src/assets/roadmap/list.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/lock.svg b/packages/atlas/src/assets/roadmap/lock.svg new file mode 100644 index 0000000000..e1d4659721 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/lock.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/monitor.svg b/packages/atlas/src/assets/roadmap/monitor.svg new file mode 100644 index 0000000000..cbc7dd0203 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/monitor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/play.svg b/packages/atlas/src/assets/roadmap/play.svg new file mode 100644 index 0000000000..c06fff11b4 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/play.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/search.svg b/packages/atlas/src/assets/roadmap/search.svg new file mode 100644 index 0000000000..48c49b93f3 --- /dev/null +++ b/packages/atlas/src/assets/roadmap/search.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/assets/roadmap/visibility.svg b/packages/atlas/src/assets/roadmap/visibility.svg new file mode 100644 index 0000000000..e5dc35868f --- /dev/null +++ b/packages/atlas/src/assets/roadmap/visibility.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/packages/atlas/src/components/AnimatedTimeline/AnimatedTimeline.tsx b/packages/atlas/src/components/AnimatedTimeline/AnimatedTimeline.tsx new file mode 100644 index 0000000000..075f81f754 --- /dev/null +++ b/packages/atlas/src/components/AnimatedTimeline/AnimatedTimeline.tsx @@ -0,0 +1,698 @@ +import styled from '@emotion/styled' +import { useEffect, useRef, useState } from 'react' + +import { Text } from '@/components/Text' +import { Button } from '@/components/_buttons/Button' +import { DialogModal } from '@/components/_overlays/DialogModal' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { cVar } from '@/styles' +import { isMobile } from '@/utils/browser' + +import { iconMap } from './iconMap' + +export let offset = 300 +const PANEL_HIGHLIGHT_OFFSET = 60 + +type Milestone = { + icon: string + title: string + content: string + generalIndex?: number +} + +export type QuartersData = { + year: string + id: string + deliveryMilestones: Milestone[] +} + +type QuarterPanelProps = { + data: { quarters: QuartersData[] } +} + +function QuarterPanel({ data }: QuarterPanelProps) { + const [activeItem, setActiveItem] = useState(0) + const [activeText, setActiveText] = useState(0) + const [isNextItemActive, setIsNextItemActive] = useState(false) + const [dotActiveState, setDotActiveState] = useState(false) + const smMatch = useMediaMatch('sm') + const [modalData, setModalData] = useState({ + isOpen: false, + content: '', + title: '', + }) + + if (isMobile()) { + offset = 200 + } else { + offset = 300 + } + + const activeItemsData = useRef<[boolean, boolean, number]>([false, false, 0]) + const activeTextIndex = useRef(0) + + useEffect(() => { + const handleScroll = () => { + const timelineItems = document.querySelectorAll('.quarter-panel-submain') + const scroll = window.scrollY + const MOVING_CIRCLE_HEIGHT = 24 + timelineItems.forEach((item, index) => { + const itemTop = item.offsetTop + const itemHight = item.offsetHeight + if (index === 0 && scroll < itemTop - offset) { + activeItemsData.current = [false, false, activeItemsData.current[2]] + } else if (index === timelineItems.length - 1 && scroll > itemTop - offset + itemHight - MOVING_CIRCLE_HEIGHT) { + activeItemsData.current = [false, false, activeItemsData.current[2]] + } else if (scroll > itemTop - offset) { + if (timelineItems.length - 1 > index) { + if (timelineItems[index + 1].offsetTop - offset - PANEL_HIGHLIGHT_OFFSET < scroll) { + activeItemsData.current = [true, activeItemsData.current[1], index] + } + + if (timelineItems[index + 1].offsetTop - offset - PANEL_HIGHLIGHT_OFFSET > scroll) { + activeItemsData.current = [false, activeItemsData.current[1], index] + } + } + + activeItemsData.current = [activeItemsData.current[0], true, index] + } + }) + + const timelineText = document.querySelectorAll('.quarter-panel-main__rigth') + timelineText.forEach((item, index) => { + const itemTop = item.offsetTop + if (scroll > itemTop - offset) { + activeTextIndex.current = index + } + }) + + setIsNextItemActive(activeItemsData.current[0]) + setDotActiveState(activeItemsData.current[1]) + setActiveItem(activeItemsData.current[2]) + setActiveText(activeTextIndex.current) + + if ( + scroll > timelineText[activeText].offsetTop + timelineText[activeText].offsetHeight - offset - 200 && + activeText < timelineText.length - 2 + ) { + const opacity = + scroll - timelineText[activeText].offsetTop - timelineText[activeText].offsetHeight - 200 - offset + + timelineText[activeText].style.opacity = String(-opacity / 100 - 9) + } else { + timelineText[activeText].style.opacity = String(1) + } + + if (activeText === timelineText.length - 1) timelineText[activeText].style.opacity = String(0) + } + handleScroll() + window.addEventListener('scroll', handleScroll) + return () => window.removeEventListener('scroll', handleScroll) + }, [activeText]) + + const numberOfItems = data.quarters.reduce((acc, curr) => { + return acc + curr.deliveryMilestones.length + }, 0) + + const isPanelAndRelatedActive = (milestone: Milestone) => + (milestone?.generalIndex === activeItem && dotActiveState) || + (activeItem === 0 && !dotActiveState && milestone?.generalIndex === 0) + + const isPanelActive = (allMilestones: Milestone[], milestoneIndex: number, currentMilestone: Milestone) => + (isPanelAndRelatedActive(currentMilestone) && !isNextItemActive) || + (isPanelAndRelatedActive(allMilestones[milestoneIndex - 1]) && isNextItemActive) + + const shouldMilestoneContentBeTruncated = (milestone: Milestone) => milestone.content.length > 100 && isMobile() + + return ( + + {data.quarters.map((res, index) => { + return ( +
+
+
+
{res.year}
+
{res.id}
+
+
+
+ {res.deliveryMilestones.map((milestone, deliveryMilestoneIndex) => { + return ( +
+
+
+
+
+ +
+
+
+ Mileston icon +
+ {/*
*/} + {/* /!**!/*/} + {/* /!* getLink(milestone.generalIndex)}*!/*/} + {/* /!* />*!/*/} + {/* /!**!/*/} + {/*
*/} +
+ + {milestone.title} + + + {smMatch ? milestone.content : milestone.content.slice(0, 100) + '...'} + + {shouldMilestoneContentBeTruncated(milestone) ? ( + + ) : null} +
+
+ ) + })} +
+
+ ) + })} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ setModalData({ isOpen: false, title: '', content: '' }), + }} + > + + {modalData.title} + + + {modalData.content} + + + + ) +} + +export default function cn(def: string, args?: Record) { + let classes = `${def}` + const entries = Object.entries(args ?? {}) + + entries.forEach(([key, value]) => { + if (value) { + classes += ` ${key}` + } + }) + + return classes +} + +const Box = styled.div` + margin: 100px 0; + + .quarter-panel-submain { + display: grid; + flex-direction: column; + gap: 30px; + grid-gap: 30px; + grid-template-columns: 76px 712px; + + &__bottom { + display: grid; + flex-direction: column; + gap: 30px; + grid-gap: 8px; + grid-template-columns: 76px 712px; + } + + @media (max-width: 1200px) { + grid-template-columns: 76px 550px; + + &__bottom { + grid-template-columns: 76px 550px; + } + } + + @media (max-width: 992px) { + grid-gap: 30px; + grid-template-columns: 40px 540px; + + &__bottom { + width: 475px; + grid-template-columns: 40px 540px; + } + } + + @media (max-width: 768px) { + width: 432px; + gap: 0; + grid-template-columns: 40px auto; + + &__bottom { + width: 432px; + grid-template-columns: 40px auto; + } + } + + @media (max-width: 768px) { + width: auto; + + &__bottom { + width: auto; + } + } + } + + .quarter-panel-main { + display: grid; + flex-direction: column; + width: 1030px; + gap: 30px; + grid-gap: 30px; + grid-template-columns: 182px 818px; + + &__rigth { + position: relative; + display: flex; + align-items: end; + flex-direction: column; + width: 182px; + margin-bottom: -100px; + text-align: right; + } + + &__title { + position: sticky; + top: 300px; + align-items: end; + flex-direction: column; + text-align: right; + opacity: 0.4; + transition: opacity 0.3s ease-in-out; + + &--active { + opacity: 1; + } + + &--last { + margin-bottom: 0; + } + } + + &__subtitle { + position: relative; + width: 160px; + height: 48px; + font-size: 40px; + text-align: right; + } + + &__quarters { + position: relative; + font-size: 32px; + color: ${cVar('colorText')}; + } + + &__timeline { + position: relative; + display: flex; + align-items: center; + flex-direction: column; + + &__bottom { + display: flex; + align-items: center; + flex-direction: column; + } + } + + &__line { + &__dotbottom { + position: absolute; + bottom: 0; + width: 24px; + height: 24px; + min-height: 24px; + background-color: silver; + border-radius: 50%; + + &--active { + background-color: ${cVar('colorBackgroundPrimary')}; + } + } + + &__dot { + width: 24px; + height: 24px; + background-color: ${cVar('colorBackground')}; + border-radius: 50%; + + &__loading { + width: 24px; + height: 24px; + background: ${cVar('colorBackgroundPrimary')}; + border-radius: 50%; + } + + &--active { + position: fixed; + top: 300px; + z-index: 2; + width: 24px !important; + background: ${cVar('colorBackgroundPrimary')}; + } + + &--hide { + opacity: 0; + } + + &--stick { + position: absolute; + top: 0; + background: ${cVar('colorBackgroundPrimary')}; + } + + &--last { + width: 4px; + } + } + + &__line { + position: absolute; + right: 0; + left: 0; + width: 4px; + height: 100%; + margin: 0 auto; + background-color: ${cVar('colorBackground')}; + + &__bottom { + top: 1px; + left: 10px; + width: 4px; + height: 100%; + margin-top: -28px; + background-color: purple; + } + } + } + + &__panel__loading { + width: 500px; + height: 324px; + padding: 24px; + background: ${cVar('colorBackground')}; + border-radius: 4px; + gap: 16px; + } + + &__panel { + width: 712px; + margin-bottom: 48px; + padding: 24px; + background: ${cVar('colorBackgroundMutedAlpha')}; + border-radius: 4px; + opacity: 0.4; + + &--active { + opacity: 1; + } + + &--laster { + margin-bottom: 0; + } + + &__bottom { + width: 500px; + padding: 24px; + background: red; + border-radius: 4px; + } + } + + &__underline { + position: relative; + display: inline-block; + text-decoration: underline; + cursor: pointer; + } + + &__link { + display: grid; + grid-gap: 10px; + grid-template-columns: repeat(2, 1fr); + } + + &__playIcon { + display: inherit; + align-items: center; + width: 48px; + height: 48px; + background-color: #dee3e9; + border: solid 1px #a3c3f230; + border-radius: 50%; + justify-items: center; + + &--active { + background-color: #4038ff26; + } + } + @media (max-width: 1200px) { + width: 818px; + grid-template-columns: 132px 656px; + + &__rigth { + width: 132px; + } + + &__panel { + width: 550px; + } + } + + @media (max-width: 992px) { + width: 700px; + grid-gap: 20px; + grid-template-columns: 70px 610px; + + &__subtitle { + font-size: 28px; + font-weight: 400; + line-height: 36px; + letter-spacing: 0; + } + + &__quarters { + font-size: 24px; + font-weight: 400; + line-height: 32px; + letter-spacing: 0; + } + + &__panel { + width: 540px; + padding: 16px; + gap: 1px; + + &__bottom { + width: 540px; + padding: 16px; + } + } + + &__timeline { + width: 40px; + } + + &__rigth { + width: auto; + } + + &__line { + &__dotbottom { + height: 24px; + min-height: 24px; + } + } + } + + @media (max-width: 768px) { + width: 500px; + grid-gap: 0; + grid-template-columns: 68px 432px; + + &__subtitle { + font-size: 28px; + font-weight: 400; + line-height: 36px; + letter-spacing: 0; + } + + &__quarters { + font-size: 24px; + font-weight: 400; + line-height: 32px; + letter-spacing: 0; + } + + &__panel { + width: 392px; + padding: 16px; + gap: 1px; + + &__bottom { + width: 392px; + padding: 16px; + } + } + + &__title { + top: 300px; + } + + &__timeline { + width: 40px; + } + + &__rigth { + width: auto; + } + + &__line { + &__dotbottom { + height: 24px; + min-height: 24px; + } + + &__dot { + &--active { + top: 200px; + } + } + } + } + + @media (max-width: 768px) { + width: 100%; + grid-template-columns: 68px 1fr; + + &__panel { + width: auto; + } + } + + @media (max-width: 550px) { + &__panel { + &__content { + margin-top: 4px; + } + } + } + } + + .mileston-icon { + width: 24px; + height: 24px; + + &--active { + /* filter configuration comes from CSS filter generator - check NOTE below */ + filter: invert(12%) sepia(63%) saturate(6234%) hue-rotate(222deg) brightness(87%) contrast(156%); + } + } +` + +type QuartersListDataProps = { + data: { quarters: QuartersData[] }[] +} + +export function QuartersListData({ data }: QuartersListDataProps) { + return ( + + {data.map((res, index) => { + return + })} + + ) +} + +const QuartersListContainer = styled.div` + display: grid; + justify-content: center; +` diff --git a/packages/atlas/src/components/AnimatedTimeline/iconMap.ts b/packages/atlas/src/components/AnimatedTimeline/iconMap.ts new file mode 100644 index 0000000000..3131badefa --- /dev/null +++ b/packages/atlas/src/components/AnimatedTimeline/iconMap.ts @@ -0,0 +1,94 @@ +import communityIcon from '../../assets/roadmap/community.svg' +import contentIcon from '../../assets/roadmap/content.svg' +import creatorTokensIcon from '../../assets/roadmap/creator-tokens.svg' +import dollarSignIcon from '../../assets/roadmap/dollar-sign.svg' +import gearIcon from '../../assets/roadmap/gear.svg' +import stakeIcon from '../../assets/roadmap/icon 1 staking module.svg' +import exchangeIcon from '../../assets/roadmap/icon 2 more exchanges.svg' +import contentMarketingIcon from '../../assets/roadmap/icon 3 content marketing.svg' +import tipBotIcon from '../../assets/roadmap/icon 4 js discord tip bot.svg' +import stablecoinIcon from '../../assets/roadmap/icon 5 stablecoin treasury.svg' +import bountyIcon from '../../assets/roadmap/icon 6 bounties program.svg' +import ambassadorIcon from '../../assets/roadmap/icon 7 creator ambassador program.svg' +import mobileDesignIcon from '../../assets/roadmap/icon 8 mobile design for pioneer.svg' +import emailNotificationIcon from '../../assets/roadmap/icon 9 pioneer email notifications.svg' +import contentDeliveryIcon from '../../assets/roadmap/icon 10 content delivery improvements.svg' +import recommendIcon from '../../assets/roadmap/icon 11 gleev recommended content V1.svg' +import dashboardIcon from '../../assets/roadmap/icon 12 js project overview dashboard.svg' +import subscanIcon from '../../assets/roadmap/icon 13 subscan block explorer improvements.svg' +import financialIcon from '../../assets/roadmap/icon 14 pioneer financials page.svg' +import curatorIcon from '../../assets/roadmap/icon 15 content curator tooling.svg' +import walletIcon from '../../assets/roadmap/icon 16 wallet implementations.svg' +import thumbnailIcon from '../../assets/roadmap/icon 17 automated thumbnail generation.svg' +import forumLabelIcon from '../../assets/roadmap/icon 18 pioneer forum & proposal label system.svg' +import governanceIcon from '../../assets/roadmap/icon 19 governance tooling.svg' +import cliIcon from '../../assets/roadmap/icon 20 js cli improvements.svg' +import dappIcon from '../../assets/roadmap/icon 21 dApp OperatorDeveloper Grants & Support.svg' +import nonVideoIcon from '../../assets/roadmap/icon 22 Non-video Content Uploads.svg' +import premiumIcon from '../../assets/roadmap/icon 23 Premium Video Comments & Tips.svg' +import channelPayoutIcon from '../../assets/roadmap/icon 24 DAO Creator Channel Payout tooling improvements.svg' +import wgManagementIcon from '../../assets/roadmap/icon 25 Pioneer Working Group Management Module.svg' +import bridgeIcon from '../../assets/roadmap/icon 26 Joystream Ethereum Bridging Solution.svg' +import smartphoneIcon from '../../assets/roadmap/icon 28 Smartphone App for AtlasGleev.svg' +import nftIcon from '../../assets/roadmap/icon 29 NFT Improvements - V1.svg' +import stakePoolIcon from '../../assets/roadmap/icon 30 Staking Nomination Pools.svg' +import sdkIcon from '../../assets/roadmap/icon 31 Joystream SDK.svg' +import videoCommunityIcon from '../../assets/roadmap/icon 32 Video Communities No-tech App creation.svg' +import contentGatingIcon from '../../assets/roadmap/icon 33 Premium Content Gating Features.svg' +import resolutionsIcon from '../../assets/roadmap/icon 34 Multiple Video Resolutions.svg' +import metamaskSnapIcon from '../../assets/roadmap/icon 35 Metamask Snap Development.svg' +import listIcon from '../../assets/roadmap/list.svg' +import lockIcon from '../../assets/roadmap/lock.svg' +import monitorIcon from '../../assets/roadmap/monitor.svg' +import playIcon from '../../assets/roadmap/play.svg' +import searchIcon from '../../assets/roadmap/search.svg' +import visibilityIcon from '../../assets/roadmap/visibility.svg' + +export const iconMap = { + play: playIcon, + search: searchIcon, + visibility: visibilityIcon, + 'creator-tokens': creatorTokensIcon, + content: contentIcon, + monitor: monitorIcon, + list: listIcon, + 'dollar-sign': dollarSignIcon, + lock: lockIcon, + community: communityIcon, + gear: gearIcon, + + stake: stakeIcon, + exchange: exchangeIcon, + contentMarketing: contentMarketingIcon, + tipBot: tipBotIcon, + stablecoin: stablecoinIcon, + bounty: bountyIcon, + ambassador: ambassadorIcon, + mobileDesign: mobileDesignIcon, + emailNotification: emailNotificationIcon, + contentDelivery: contentDeliveryIcon, + recommend: recommendIcon, + dashboard: dashboardIcon, + subscan: subscanIcon, + financial: financialIcon, + curator: curatorIcon, + wallet: walletIcon, + thumbnail: thumbnailIcon, + forumLabel: forumLabelIcon, + governance: governanceIcon, + cli: cliIcon, + dapp: dappIcon, + nonVideo: nonVideoIcon, + premium: premiumIcon, + channelPayout: channelPayoutIcon, + wgManagement: wgManagementIcon, + bridge: bridgeIcon, + smartphone: smartphoneIcon, + nft: nftIcon, + stakePool: stakePoolIcon, + sdk: sdkIcon, + videoCommunity: videoCommunityIcon, + contentGating: contentGatingIcon, + resolutions: resolutionsIcon, + metamaskSnap: metamaskSnapIcon, +} diff --git a/packages/atlas/src/components/AnimatedTimeline/index.ts b/packages/atlas/src/components/AnimatedTimeline/index.ts new file mode 100644 index 0000000000..9326c678fb --- /dev/null +++ b/packages/atlas/src/components/AnimatedTimeline/index.ts @@ -0,0 +1 @@ +export * from './AnimatedTimeline' diff --git a/packages/atlas/src/components/GlassDetailsWidget/GlassDetailsWidget.tsx b/packages/atlas/src/components/GlassDetailsWidget/GlassDetailsWidget.tsx new file mode 100644 index 0000000000..f9f9c6ca28 --- /dev/null +++ b/packages/atlas/src/components/GlassDetailsWidget/GlassDetailsWidget.tsx @@ -0,0 +1,9 @@ +import styled from '@emotion/styled' + +import { WidgetTile } from '@/components/WidgetTile' +import { cVar } from '@/styles' + +export const GlassDetailsWidget = styled(WidgetTile)` + background: #bcd5fa14; + border-radius: ${cVar('radiusLarge')}; +` diff --git a/packages/atlas/src/components/GlassDetailsWidget/index.ts b/packages/atlas/src/components/GlassDetailsWidget/index.ts new file mode 100644 index 0000000000..49381d4835 --- /dev/null +++ b/packages/atlas/src/components/GlassDetailsWidget/index.ts @@ -0,0 +1 @@ +export * from './GlassDetailsWidget' diff --git a/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx b/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx index 2a4a4ad6ec..3fef8d7cfd 100644 --- a/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx +++ b/packages/atlas/src/components/LayoutGrid/LayoutGrid.tsx @@ -2,6 +2,7 @@ import { css } from '@emotion/react' import styled from '@emotion/styled' import { FlexBox } from '@/components/FlexBox' +import { MarginProps, marginStyles } from '@/components/Text/Text.styles' import { media, sizes } from '@/styles' type ReponsivenessObject = Partial> @@ -24,6 +25,7 @@ export type GridItemProps = { colSpan?: number | ReponsivenessObject rowStart?: number | 'initial' | ReponsivenessObject rowSpan?: number | ReponsivenessObject + margin?: MarginProps } function isResponsivenessObject(prop?: number | 'initial' | ReponsivenessObject): prop is ReponsivenessObject { @@ -32,8 +34,9 @@ function isResponsivenessObject(prop?: number | 'initial' | ReponsivenessObject) const createBreakpointGridItemRules = (breakpointKey: keyof ReponsivenessObject) => - ({ colStart, colSpan, rowStart, rowSpan }: GridItemProps) => + ({ colStart, colSpan, rowStart, rowSpan, margin }: GridItemProps) => css` + ${marginStyles({ margin })} ${breakpointKey === 'base' ? '@media screen and (min-width: 0px)' : media[breakpointKey]} { ${isResponsivenessObject(colStart) && colStart[breakpointKey] && diff --git a/packages/atlas/src/components/Text/Text.styles.ts b/packages/atlas/src/components/Text/Text.styles.ts index d93b01f23b..a435a04d4c 100644 --- a/packages/atlas/src/components/Text/Text.styles.ts +++ b/packages/atlas/src/components/Text/Text.styles.ts @@ -13,7 +13,7 @@ export type TextBaseProps = { truncate?: boolean } -type MarginProps = +export type MarginProps = | { top?: number bottom?: number @@ -30,7 +30,7 @@ const alignStyles = ({ align }: TextBaseProps) => text-align: ${align}; ` -const marginStyles = ({ margin }: TextBaseProps) => +export const marginStyles = ({ margin }: TextBaseProps) => typeof margin !== 'number' && !!margin ? css` margin: ${sizes(margin.top ?? 0)} ${sizes(margin.right ?? 0)} ${sizes(margin.bottom ?? 0)} diff --git a/packages/atlas/src/components/_auth/AuthModals/AuthModals.tsx b/packages/atlas/src/components/_auth/AuthModals/AuthModals.tsx index 77516aacd4..15b04c8789 100644 --- a/packages/atlas/src/components/_auth/AuthModals/AuthModals.tsx +++ b/packages/atlas/src/components/_auth/AuthModals/AuthModals.tsx @@ -1,3 +1,5 @@ +import { useMemo } from 'react' + import { ExternalSignInModal } from '@/components/_auth/ExternalSignInModal' import { LogInModal } from '@/components/_auth/LogInModal' import { SignUpModal } from '@/components/_auth/SignUpModal' @@ -9,17 +11,20 @@ import { ForgotPasswordModal } from '../ForgotPasswordModal/ForgotPasswordModal' export const AuthModals = () => { const { authModalOpenName } = useAuthStore() - if (authModalOpenName) { - return ( - <> - - - - - - - ) - } - - return null + return useMemo(() => { + switch (authModalOpenName) { + case 'createChannel': + return + case 'externalLogIn': + return + case 'forgotPassword': + return + case 'logIn': + return + case 'signUp': + return + default: + return null + } + }, [authModalOpenName]) } diff --git a/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx b/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx index 221603c561..aeab59d28f 100644 --- a/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx +++ b/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx @@ -62,6 +62,7 @@ export const SignUpModal = () => { const memberPollingTries = useRef(0) const haveTriedCreateSession = useRef(false) const ytResponseData = useYppStore((state) => state.ytResponseData) + const isYppChannelFlow = useYppStore((state) => state.isYppChannelFlow) const setYppModalOpenName = useYppStore((state) => state.actions.setYppModalOpenName) const setYtResponseData = useYppStore((state) => state.actions.setYtResponseData) const referrerChannelId = useYppStore((state) => state.referrerId) @@ -174,6 +175,8 @@ export const SignUpModal = () => { setYtResponseData({ ...ytResponseData, email: signUpFormData.current.email }) setAuthModalOpenName(undefined) setYppModalOpenName('ypp-sync-options') + } else if (isYppChannelFlow) { + setAuthModalOpenName('createChannel') } else { setAmountofTokens(amountOfTokens) goToNextStep() @@ -186,6 +189,7 @@ export const SignUpModal = () => { displaySnackbar, goToNextStep, goToStep, + isYppChannelFlow, referrerChannelId, setAnonymousUserId, setAuthModalOpenName, diff --git a/packages/atlas/src/components/_channel/CreateChannelModal/CreateChannelModal.tsx b/packages/atlas/src/components/_channel/CreateChannelModal/CreateChannelModal.tsx index 4a8dcb1839..048182554c 100644 --- a/packages/atlas/src/components/_channel/CreateChannelModal/CreateChannelModal.tsx +++ b/packages/atlas/src/components/_channel/CreateChannelModal/CreateChannelModal.tsx @@ -14,6 +14,7 @@ import { useChannelForm } from '@/hooks/useChannelForm' import { useAuthStore } from '@/providers/auth/auth.store' import { useSnackbar } from '@/providers/snackbars' import { useUser } from '@/providers/user/user.hooks' +import { useYppStore } from '@/providers/ypp/ypp.store' import { media, sizes } from '@/styles' export const CreateChannelModal = () => { @@ -24,7 +25,11 @@ export const CreateChannelModal = () => { } = useAuthStore() const navigate = useNavigate() const { setActiveChannel } = useUser() - const { refs, actions, form, hasAvatarUploadFailed } = useChannelForm({ type: 'new' }) + const isYppChannelFlow = useYppStore((state) => state.isYppChannelFlow) + const setIsYppChannelFlow = useYppStore((state) => state.actions.setIsYppChannelFlow) + const { refs, actions, form, hasAvatarUploadFailed } = useChannelForm({ + type: 'new', + }) const { register, control, @@ -42,8 +47,9 @@ export const CreateChannelModal = () => { onClick: () => handleSubmit((channelId) => { setActiveChannel(channelId) - navigate(absoluteRoutes.viewer.channel(channelId)) + navigate(isYppChannelFlow ? absoluteRoutes.studio.yppDashboard() : absoluteRoutes.viewer.channel(channelId)) setAuthModalOpenName(undefined) + setIsYppChannelFlow(false) }), }} secondaryButton={{ diff --git a/packages/atlas/src/components/_navigation/SidenavBase/SidenavBase.tsx b/packages/atlas/src/components/_navigation/SidenavBase/SidenavBase.tsx index 7620463047..917847f46c 100644 --- a/packages/atlas/src/components/_navigation/SidenavBase/SidenavBase.tsx +++ b/packages/atlas/src/components/_navigation/SidenavBase/SidenavBase.tsx @@ -169,7 +169,7 @@ const SidenavBase: FC = ({ ) : null} - {pathname !== absoluteRoutes.viewer.ypp() || mdMatch ? ( + {![absoluteRoutes.viewer.ypp(), absoluteRoutes.viewer.yppTest()].includes(pathname) || mdMatch ? ( scrollAndToggle(!expanded)} /> ) : null} diff --git a/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx b/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx index 8c7a2b19de..f25aa3a13e 100644 --- a/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx +++ b/packages/atlas/src/components/_navigation/SidenavViewer/SidenavViewer.tsx @@ -1,13 +1,14 @@ +import styled from '@emotion/styled' import { FC, useState } from 'react' import { SvgActionAddChannel, SvgActionMember, + SvgActionMoney, SvgActionNewTab, SvgSidebarHome, SvgSidebarMarketplace, SvgSidebarReferrals, - SvgSidebarYpp, } from '@/assets/icons' import { AppLogo } from '@/components/AppLogo' import { Button } from '@/components/_buttons/Button' @@ -16,11 +17,16 @@ import { absoluteRoutes } from '@/config/routes' import { getCorrectLoginModal } from '@/providers/auth/auth.helpers' import { useAuthStore } from '@/providers/auth/auth.store' import { useUser } from '@/providers/user/user.hooks' +import { square } from '@/styles' import { FollowedChannels } from './FollowedChannels' import { SidenavBase } from '../SidenavBase' +const StyledSvgActionMoney = styled(SvgActionMoney)` + ${square(24)} +` + export const viewerNavItems = [ { icon: , @@ -38,9 +44,9 @@ export const viewerNavItems = [ ...(atlasConfig.features.ypp.googleConsoleClientId ? [ { - icon: , - name: 'YPP', - expandedName: 'YouTube Partner Program', + icon: , + name: 'Earn', + expandedName: 'Creator Rewards', to: absoluteRoutes.viewer.ypp(), bottomNav: true, }, diff --git a/packages/atlas/src/components/_navigation/TopbarViewer/TopbarViewer.tsx b/packages/atlas/src/components/_navigation/TopbarViewer/TopbarViewer.tsx index 3046b8d7bc..8757a32356 100644 --- a/packages/atlas/src/components/_navigation/TopbarViewer/TopbarViewer.tsx +++ b/packages/atlas/src/components/_navigation/TopbarViewer/TopbarViewer.tsx @@ -103,7 +103,10 @@ export const TopbarViewer: FC = () => { const topbarButtonLoading = isAuthenticating || membershipsLoading - if ((pathname === absoluteRoutes.viewer.referrals() && mdMatch) || pathname === absoluteRoutes.viewer.ypp()) { + if ( + (pathname === absoluteRoutes.viewer.referrals() && mdMatch) || + [absoluteRoutes.viewer.ypp(), absoluteRoutes.viewer.yppTest()].includes(pathname) + ) { return null } diff --git a/packages/atlas/src/components/_overlays/TransactionModal/TransactionModal.tsx b/packages/atlas/src/components/_overlays/TransactionModal/TransactionModal.tsx index 24a8326d05..f3ab21424e 100644 --- a/packages/atlas/src/components/_overlays/TransactionModal/TransactionModal.tsx +++ b/packages/atlas/src/components/_overlays/TransactionModal/TransactionModal.tsx @@ -90,7 +90,7 @@ export const TransactionModal: FC = ({ onClose, status, c }, [decrementOverlaysOpenCount]) // @ts-ignore different wallet types before lib integration - const walletLogo = wallet?.logo ? wallet.logo.src : wallet.metadata.logoUrl || null + const walletLogo = wallet?.logo ? wallet.logo.src : wallet?.metadata.logoUrl || null return ( diff --git a/packages/atlas/src/components/_ypp/TierCard/TierCard.tsx b/packages/atlas/src/components/_ypp/TierCard/TierCard.tsx index a2bcbedf73..977acdedb1 100644 --- a/packages/atlas/src/components/_ypp/TierCard/TierCard.tsx +++ b/packages/atlas/src/components/_ypp/TierCard/TierCard.tsx @@ -1,5 +1,4 @@ import { - SvgActionCheck, SvgIconRankBronzeMonochrome, SvgIconRankDiamondMonochrome, SvgIconRankGoldMonochrome, @@ -9,7 +8,6 @@ import { FlexBox } from '@/components/FlexBox' import { Text } from '@/components/Text' import { RewardWrapper, TierBanner, Wrapper } from '@/components/_ypp/TierCard/TierCard.styles' import { capitalizeFirstLetter } from '@/utils/misc' -import { TickWrapper } from '@/views/global/YppLandingView/YppAuthorizationModal/YppAuthorizationSteps/YppAuthorizationRequirementsStep/YppAuthorizationRequirementsStep.styles' export type TierCardProps = { tier: 'bronze' | 'silver' | 'gold' | 'diamond' @@ -44,7 +42,7 @@ const getTierIcon = (tier: TierCardProps['tier']) => { } } -export const TierCard = ({ reqs, rewards, tier }: TierCardProps) => { +export const TierCard = ({ rewards, tier }: TierCardProps) => { return ( @@ -62,18 +60,6 @@ export const TierCard = ({ reqs, rewards, tier }: TierCardProps) => { - - {reqs.map((req, idx) => ( - - - - - - {req} - - - ))} - diff --git a/packages/atlas/src/config/routes.ts b/packages/atlas/src/config/routes.ts index 1a72ef1632..d5d4de3cc8 100644 --- a/packages/atlas/src/config/routes.ts +++ b/packages/atlas/src/config/routes.ts @@ -50,7 +50,7 @@ export const relativeRoutes = { memberNotifications: () => 'notifications/member', marketplace: () => 'marketplace', ypp: (query?: { [QUERY_PARAMS.REFERRER_ID]?: string }) => withQueryParameters('ypp', query), - yppTest: () => 'ypp/test-variation', + yppTest: () => 'ypp/rewards', yppDashboard: () => 'ypp-dashboard', referrals: () => 'referrals', }, diff --git a/packages/atlas/src/hooks/useSegmentAnalytics.ts b/packages/atlas/src/hooks/useSegmentAnalytics.ts index dec40c88c5..761e0433da 100644 --- a/packages/atlas/src/hooks/useSegmentAnalytics.ts +++ b/packages/atlas/src/hooks/useSegmentAnalytics.ts @@ -256,6 +256,12 @@ export const useSegmentAnalytics = () => { }) }, [analytics, getUTMParams]) + const trackRewardsCreateChannelButtonClick = useCallback(() => { + analytics.track('YPP Landing Create Channel Clicked', { + ...getUTMParams(), + }) + }, [analytics, getUTMParams]) + const trackNFTCarouselNext = useCallback( (slideId: string, nftId?: string) => { analytics.track('Featured NFT carousel next slide', { @@ -696,5 +702,6 @@ export const useSegmentAnalytics = () => { trackRewardsBrandingLinkClicked, trackRewardsReferralLinkClicked, trackRoundtableEventsClicked, + trackRewardsCreateChannelButtonClick, } } diff --git a/packages/atlas/src/providers/ypp/ypp.store.ts b/packages/atlas/src/providers/ypp/ypp.store.ts index fa753efdb5..c2778f9a0c 100644 --- a/packages/atlas/src/providers/ypp/ypp.store.ts +++ b/packages/atlas/src/providers/ypp/ypp.store.ts @@ -12,6 +12,7 @@ type YppStoreState = { shouldContinueYppFlowAfterLogin: boolean shouldContinueYppFlowAfterCreatingChannel: boolean ytResponseData: YtResponseData + isYppChannelFlow: boolean } type YppStoreActions = { @@ -24,6 +25,7 @@ type YppStoreActions = { setShouldContinueYppFlowAfterLogin: (shouldContinueYppFlow: boolean) => void setShouldContinueYppFlowAfterCreatingChannel: (shouldContinueYppFlow: boolean) => void setYtResponseData: (ytResponseData: YtResponseData) => void + setIsYppChannelFlow: (value: boolean) => void } export const useYppStore = createStore( @@ -38,8 +40,14 @@ export const useYppStore = createStore( shouldContinueYppFlowAfterLogin: false, shouldContinueYppFlowAfterCreatingChannel: false, ytResponseData: null, + isYppChannelFlow: false, }, actionsFactory: (set) => ({ + setIsYppChannelFlow: (value) => { + set((state) => { + state.isYppChannelFlow = value + }) + }, setReferrerId: (referrerId) => { set((state) => { state.referrerId = referrerId diff --git a/packages/atlas/src/views/global/YppLandingView/YppLandingView.styles.ts b/packages/atlas/src/views/global/YppLandingView/YppLandingView.styles.ts index 54ac8640ba..86eb5c286c 100644 --- a/packages/atlas/src/views/global/YppLandingView/YppLandingView.styles.ts +++ b/packages/atlas/src/views/global/YppLandingView/YppLandingView.styles.ts @@ -6,6 +6,7 @@ import bottomLeftPattern from '@/assets/images/ypp-background-pattern.svg' import { FlexBox } from '@/components/FlexBox' import { GridItem, LayoutGrid } from '@/components/LayoutGrid' import { LimitedWidthContainer } from '@/components/LimitedWidthContainer' +import { Text } from '@/components/Text' import { cVar, media, sizes } from '@/styles' export const imageShadow = css` @@ -65,8 +66,10 @@ export const StyledLimitedWidthContainerHero = styled.div<{ centerText?: boolean overflow: hidden; border-radius: 24px; padding: ${sizes(4)}; + margin-top: ${sizes(6)}; ${media.md} { + margin-top: 0; padding: ${sizes(8)}; border-radius: 32px; } @@ -107,12 +110,12 @@ export const TierCardWrapper = styled(GridItem)` ${media.sm} { display: grid; justify-content: center; - grid-template-columns: repeat(2, auto); + grid-template-columns: repeat(2, minmax(auto, 250px)); } ${media.md} { gap: ${sizes(6)}; - grid-template-columns: repeat(4, auto); + grid-template-columns: repeat(4, minmax(auto, 250px)); } ` @@ -125,6 +128,7 @@ export const HeaderGridItem = styled(GridItem, { })` margin-bottom: ${({ marginBottom = 0 }) => sizes(marginBottom)}; align-self: center; + text-align: center; ` type BackgroundContainerProps = { @@ -163,3 +167,7 @@ export const HeroBackgroundContainer = styled(BackgroundContainer)` ${backgroundPattern}; } ` + +export const RewardsSubText = styled(Text)` + grid-column: 1/3; +` diff --git a/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx b/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx index 0cea3e7fbc..47ec3efd3d 100644 --- a/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx +++ b/packages/atlas/src/views/global/YppLandingView/YppLandingView.tsx @@ -1,6 +1,6 @@ import AOS from 'aos' import 'aos/dist/aos.css' -import { FC, useCallback, useEffect, useState } from 'react' +import { FC, useCallback, useEffect, useRef, useState } from 'react' import { useNavigate } from 'react-router-dom' import { ParallaxProvider } from 'react-scroll-parallax' @@ -10,11 +10,12 @@ import { useHeadTags } from '@/hooks/useHeadTags' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { useUser } from '@/providers/user/user.hooks' import { useYppStore } from '@/providers/ypp/ypp.store' -import { YppConnectionDetails } from '@/views/global/YppLandingView/sections/YppConnectionDetails' +import { CreatorOpportunities } from '@/views/global/YppLandingView/sections/CreatorOpportunities' +import { JoystreamRoadmap } from '@/views/global/YppLandingView/sections/JoystreamRoadmap' +import { ViewerOpportunities } from '@/views/global/YppLandingView/sections/ViewerOpportunities' import { YppAuthorizationModal } from './YppAuthorizationModal' import { Wrapper } from './YppLandingView.styles' -import { YppCardsSections } from './sections/YppCardsSections' import { YppFooter } from './sections/YppFooter' import { YppHero } from './sections/YppHero' import { YppRewardSection } from './sections/YppRewardSection' @@ -30,6 +31,7 @@ export const YppLandingView: FC = () => { const navigate = useNavigate() const { trackYppSignInButtonClick } = useSegmentAnalytics() const selectedChannelTitle = activeMembership?.channels.find((channel) => channel.id === channelId)?.title + const viewerEarningsRef = useRef(null) const [wasSignInTriggered, setWasSignInTriggered] = useState(false) const shouldContinueYppFlowAfterCreatingChannel = useYppStore( @@ -47,6 +49,12 @@ export const YppLandingView: FC = () => { }) }, []) + const handleViewerEarnings = useCallback(() => { + if (viewerEarningsRef.current) { + viewerEarningsRef.current.scrollIntoView({ behavior: 'smooth' }) + } + }, []) + const handleYppSignUpClick = useCallback(async () => { if (isYppSigned) { navigate(absoluteRoutes.studio.yppDashboard()) @@ -109,11 +117,14 @@ export const YppLandingView: FC = () => { yppAtlasStatus={getYppAtlasStatus()} hasAnotherUnsyncedChannel={hasAnotherUnsyncedChannel} selectedChannelTitle={selectedChannelTitle} + onViewerEarnings={handleViewerEarnings} /> + - - + + + {/**/} diff --git a/packages/atlas/src/views/global/YppLandingView/YppLandingViewOld.tsx b/packages/atlas/src/views/global/YppLandingView/YppLandingViewOld.tsx new file mode 100644 index 0000000000..2e7a20bd2a --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/YppLandingViewOld.tsx @@ -0,0 +1,121 @@ +import AOS from 'aos' +import 'aos/dist/aos.css' +import { FC, useCallback, useEffect, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { ParallaxProvider } from 'react-scroll-parallax' + +import { YppReferralBanner } from '@/components/_ypp/YppReferralBanner' +import { absoluteRoutes } from '@/config/routes' +import { useHeadTags } from '@/hooks/useHeadTags' +import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' +import { useUser } from '@/providers/user/user.hooks' +import { useYppStore } from '@/providers/ypp/ypp.store' + +import { YppAuthorizationModal } from './YppAuthorizationModal' +import { Wrapper } from './YppLandingView.styles' +import { YppCardsSections } from './oldSections/YppCardsSections' +import { YppConnectionDetails } from './oldSections/YppConnectionDetails' +import { YppFooter } from './oldSections/YppFooter' +import { YppHero } from './oldSections/YppHero' +import { YppRewardSection } from './oldSections/YppRewardSection' +import { YppSignupVideo } from './oldSections/YppSignupVideo' +import { useGetYppSyncedChannels } from './useGetYppSyncedChannels' + +export const YppLandingViewOld: 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 { setSelectedChannelId, setShouldContinueYppFlowAfterCreatingChannel } = useYppStore((store) => store.actions) + const navigate = useNavigate() + const { trackYppSignInButtonClick } = useSegmentAnalytics() + const selectedChannelTitle = activeMembership?.channels.find((channel) => channel.id === channelId)?.title + + const [wasSignInTriggered, setWasSignInTriggered] = useState(false) + const shouldContinueYppFlowAfterCreatingChannel = useYppStore( + (store) => store.shouldContinueYppFlowAfterCreatingChannel + ) + + const { unsyncedChannels, isLoading, currentChannel } = useGetYppSyncedChannels() + const isYppSigned = !!currentChannel + const hasAnotherUnsyncedChannel = isYppSigned && !!unsyncedChannels?.length + + useEffect(() => { + AOS.init({ + duration: 750, + once: true, + }) + }, []) + + const handleYppSignUpClick = useCallback(async () => { + if (isYppSigned) { + navigate(absoluteRoutes.studio.yppDashboard()) + return + } + + if (!yppModalOpenName) { + trackYppSignInButtonClick() + setYppModalOpen('ypp-requirements') + return + } + }, [isYppSigned, yppModalOpenName, navigate, trackYppSignInButtonClick, setYppModalOpen]) + + useEffect(() => { + // rerun handleYppSignUpClick after sign in flow + if (wasSignInTriggered) { + handleYppSignUpClick() + setWasSignInTriggered(false) + } + }, [handleYppSignUpClick, wasSignInTriggered]) + + useEffect(() => { + if (shouldContinueYppFlowAfterCreatingChannel) { + setSelectedChannelId(channelId) + setShouldContinueYppFlowAfterCreatingChannel(false) + setYppModalOpen('ypp-requirements') + } + }, [ + channelId, + handleYppSignUpClick, + setSelectedChannelId, + setShouldContinueYppFlowAfterCreatingChannel, + setYppModalOpen, + shouldContinueYppFlowAfterCreatingChannel, + ]) + + const getYppAtlasStatus = () => { + if (isLoading) { + return null + } + + if (!activeMembership?.channels.length) { + return 'no-channel' + } + if (isYppSigned) { + return 'ypp-signed' + } + return 'have-channel' + } + + return ( + + {headTags} + + + + setYppModalOpen('ypp-select-channel')} + onSignUpClick={handleYppSignUpClick} + yppAtlasStatus={getYppAtlasStatus()} + hasAnotherUnsyncedChannel={hasAnotherUnsyncedChannel} + selectedChannelTitle={selectedChannelTitle} + /> + + + + + + + + ) +} diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppCardsSection.styles.ts b/packages/atlas/src/views/global/YppLandingView/oldSections/YppCardsSection.styles.ts new file mode 100644 index 0000000000..b70db2d842 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppCardsSection.styles.ts @@ -0,0 +1,47 @@ +import styled from '@emotion/styled' + +import { LayoutGrid } from '@/components/LayoutGrid' +import { media, sizes } from '@/styles' + +import { imageShadow } from '../YppLandingView.styles' + +export const CardsWithImagesContainer = styled.div` + display: grid; + gap: ${sizes(16)}; + ${media.md} { + gap: ${sizes(24)}; + } +` + +export const CardImageRow = styled(LayoutGrid)` + grid-gap: ${sizes(6)}; + ${media.sm} { + align-items: center; + justify-items: center; + } +` + +type ImageContainerProps = { + positionOnMobile?: 'center' | 'unset' | 'flex-end' + /* will prevent overflowing when parallax is applied */ + hiddenOverflow?: boolean +} + +export const ImageContainer = styled.div` + position: relative; + display: flex; + justify-content: ${({ positionOnMobile = 'unset' }) => positionOnMobile}; + ${media.sm} { + overflow: ${({ hiddenOverflow }) => (hiddenOverflow ? 'hidden' : 'unset')}; + justify-content: unset; + } +` + +export const CardImage = styled.img<{ absolute?: boolean; dropShadow?: boolean }>` + width: 100%; + height: auto; + position: ${({ absolute }) => (absolute ? 'absolute' : 'relative')}; + z-index: ${({ absolute }) => (absolute ? 0 : 1)}; + + ${({ dropShadow }) => dropShadow && imageShadow}; +` diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppCardsSections.tsx b/packages/atlas/src/views/global/YppLandingView/oldSections/YppCardsSections.tsx new file mode 100644 index 0000000000..8b2f6b6dd1 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppCardsSections.tsx @@ -0,0 +1,331 @@ +import { FC } from 'react' +import { useParallax } from 'react-scroll-parallax' +import { ParallaxProps } from 'react-scroll-parallax/dist/components/Parallax/types' + +import crt1 from '@/assets/images/illustration-crt-l1.webp' +import crt2 from '@/assets/images/illustration-crt-l2.webp' +import gated1 from '@/assets/images/illustration-gated-l1.webp' +import gated2 from '@/assets/images/illustration-gated-l2.webp' +import tip1 from '@/assets/images/illustration-tip-l1.webp' +import tip2 from '@/assets/images/illustration-tip-l2.webp' +import tip3 from '@/assets/images/illustration-tip-l3.webp' +import videoNfts1 from '@/assets/images/illustration-video-nfts-l1.webp' +import videoNfts2 from '@/assets/images/illustration-video-nfts-l2.webp' +import videoNfts3 from '@/assets/images/illustration-video-nfts-l3.webp' +import videoNfts4 from '@/assets/images/illustration-video-nfts-l4.webp' +import { GridItem } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { ContentCard } from '@/components/_ypp/ContentCard' +import { atlasConfig } from '@/config' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +import { CardImage, CardImageRow, CardsWithImagesContainer, ImageContainer } from './YppCardsSection.styles' + +import { + BackgroundContainer, + CenteredLayoutGrid, + HeaderGridItem, + StyledLimitedWidthContainer, +} from '../YppLandingView.styles' + +export const YppCardsSections: FC = () => { + const smMatch = useMediaMatch('sm') + const endScroll = smMatch ? window.innerHeight / 3 : window.innerHeight + const [titleVariant, subtitleVariant] = useSectionTextVariants() + + const commonParallaxOpts: ParallaxProps = { + disabled: !smMatch, + endScroll, + speed: 0.2, + } + + const appName = atlasConfig.general.appName + + return ( + <> + + + + + + + Monetize your ${appName} channel + + + + + Build a foundation for your {appName} channel with syncing YouTube content and tap into the future of + content monetization. + + + + + + + + + + + + + + + + + + + + + + + + + + {/**/} + {/* */} + {/* */} + {/* */} + {/* */} + {/**/} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +type CardImageWithParallaxEffectProps = { + src: string + alt: string + dropShadow?: boolean + absolute?: boolean + parallaxProps: ParallaxProps + width?: string + height?: string +} + +const CardImageWithParallaxEffect: FC = ({ + src, + alt, + dropShadow, + parallaxProps, + absolute, + ...rest +}) => { + const { ref: imageRef, controller } = useParallax(parallaxProps) + + // updates cached values after image dimensions have loaded + const handleLoad = () => controller?.update() + return ( + + ) +} diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppConnectionDetails.tsx b/packages/atlas/src/views/global/YppLandingView/oldSections/YppConnectionDetails.tsx similarity index 100% rename from packages/atlas/src/views/global/YppLandingView/sections/YppConnectionDetails.tsx rename to packages/atlas/src/views/global/YppLandingView/oldSections/YppConnectionDetails.tsx diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppFooter.styles.ts b/packages/atlas/src/views/global/YppLandingView/oldSections/YppFooter.styles.ts new file mode 100644 index 0000000000..3e20ec69c7 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppFooter.styles.ts @@ -0,0 +1,36 @@ +import styled from '@emotion/styled' + +import bottomLeftPattern from '@/assets/images/ypp-background-pattern.svg' +import topLeftBannerPattern from '@/assets/images/ypp-banner-pattern.svg' +import { GoogleButton } from '@/components/_buttons/GoogleButton' +import { cVar, media, sizes } from '@/styles' + +export const CtaBanner = styled.div` + padding: ${sizes(6)}; + background: ${cVar('colorBackgroundPrimary')}; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + margin: ${sizes(16)} 0; + border-radius: ${cVar('radiusLarge')}; + + ${media.xs} { + padding: ${sizes(8)}; + } + + ${media.sm} { + background-image: url(${bottomLeftPattern}), url(${topLeftBannerPattern}); + background-position: bottom left, top right; + background-repeat: no-repeat, no-repeat; + padding: ${sizes(16)} ${sizes(12)}; + } + + ${media.md} { + margin: ${sizes(24)} 0; + } +` + +export const StyledButton = styled(GoogleButton)` + margin-top: ${sizes(8)}; +` diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppFooter.tsx b/packages/atlas/src/views/global/YppLandingView/oldSections/YppFooter.tsx new file mode 100644 index 0000000000..1edcdb006f --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppFooter.tsx @@ -0,0 +1,68 @@ +import { FC, ReactElement } from 'react' + +import { SvgActionInfo, SvgActionSpeech, SvgActionTokensStack } from '@/assets/icons' +import { GridItem, LayoutGrid } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { CallToActionButton, CallToActionWrapper } from '@/components/_buttons/CallToActionButton' +import { atlasConfig } from '@/config' +import { YppWidgetIcons } from '@/config/configSchema' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +import { CtaBanner, StyledButton } from './YppFooter.styles' + +import { StyledLimitedWidthContainer } from '../YppLandingView.styles' + +export const configYppIconMapper: Record = { + info: , + message: , + tokenStack: , +} + +type YppFooterSectionProps = { + onSignUpClick: () => void +} + +export const YppFooter: FC = ({ onSignUpClick }) => { + const [titleVariant] = useSectionTextVariants() + + return ( + <> + + + + + + Get started now + + + Pave the way to Web3 with your YouTube channel right away. + + + Authorize with YouTube + + + + + {atlasConfig.features.ypp.widgets && ( + + {atlasConfig.features.ypp.widgets.map((widget) => ( + + ))} + + )} + + ) +} diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppHero.styles.ts b/packages/atlas/src/views/global/YppLandingView/oldSections/YppHero.styles.ts new file mode 100644 index 0000000000..8f29f25b97 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppHero.styles.ts @@ -0,0 +1,71 @@ +import styled from '@emotion/styled' + +import { InfiniteCarousel } from '@/components/InfiniteCarousel/InfiniteCarousel' +import { cVar, media, sizes } from '@/styles' + +import { imageShadow } from '../YppLandingView.styles' + +export const HeroImageWrapper = styled.div` + position: relative; + margin: ${sizes(16)} auto 0 auto; + + ${media.lg} { + max-width: 1082px; + } +` + +export const ButtonWrapper = styled.div` + display: flex; + justify-content: center; +` + +export const LogosContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + gap: ${sizes(10)}; + margin-bottom: ${sizes(6)}; +` + +export const SelectDifferentChannelButton = styled.button` + white-space: normal; + border: none; + background: none; + color: ${cVar('colorTextPrimary')}; + padding: 0; + font: inherit; + cursor: pointer; + + :hover, + :focus-visible { + text-decoration: underline; + } +` + +export const FrontImage = styled.img` + width: 100%; + max-width: 100%; + height: auto; + display: block; + border-radius: ${cVar('radiusMedium')}; + margin-top: 10%; + ${imageShadow} +` + +export const BackImage = styled.img` + position: absolute; + width: 80%; + max-width: 100%; + height: auto; + display: block; + left: 10%; + top: -10%; + border-radius: ${cVar('radiusMedium')}; +` + +export const StyledInfiniteCarousel = styled(InfiniteCarousel)` + margin-top: ${sizes(16)}; + ${media.md} { + margin-top: ${sizes(24)}; + } +` diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppHero.tsx b/packages/atlas/src/views/global/YppLandingView/oldSections/YppHero.tsx new file mode 100644 index 0000000000..2a765d52a0 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppHero.tsx @@ -0,0 +1,194 @@ +import { FC } from 'react' +import { useParallax } from 'react-scroll-parallax' +import { CSSTransition, SwitchTransition } from 'react-transition-group' +import useResizeObserver from 'use-resize-observer' + +import { useMostPaidChannels } from '@/api/hooks/channel' +import { SvgActionChevronR, SvgLogoGoogleWhiteFull, SvgLogoYoutubeWhiteFull } from '@/assets/icons' +import hero from '@/assets/images/ypp-hero/hero.webp' +import yt from '@/assets/images/ypp-hero/yt.webp' +import { AppLogo } from '@/components/AppLogo' +import { GridItem, LayoutGrid } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { Button } from '@/components/_buttons/Button' +import { GoogleButton } from '@/components/_buttons/GoogleButton' +import { PaidChannelCard } from '@/components/_channel/ChannelCard' +import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader' +import { atlasConfig } from '@/config' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { cVar, transitions } from '@/styles' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +import { + BackImage, + ButtonWrapper, + FrontImage, + HeroImageWrapper, + LogosContainer, + SelectDifferentChannelButton, + StyledInfiniteCarousel, +} from './YppHero.styles' + +import { + GlowBox, + GlowContainer, + HeroBackgroundContainer, + StyledLimitedWidthContainerHero, +} from '../YppLandingView.styles' + +export type YppAtlasStatus = 'have-channel' | 'no-channel' | 'ypp-signed' | 'connect-wallet' | null + +type YppHeroProps = { + onSignUpClick: () => void + onSelectChannel: () => void + yppAtlasStatus: YppAtlasStatus + hasAnotherUnsyncedChannel?: boolean + selectedChannelTitle?: string | null +} + +export const YppHero: FC = ({ + onSignUpClick, + onSelectChannel, + yppAtlasStatus, + hasAnotherUnsyncedChannel, + selectedChannelTitle, +}) => { + const smMatch = useMediaMatch('sm') + const { ref, width, height } = useResizeObserver({ box: 'border-box' }) + const [, subtitleVariant, titleVariant] = useSectionTextVariants() + const endScroll = smMatch ? window.innerHeight / 3 : window.innerHeight + const { ref: heroImageRef } = useParallax({ + startScroll: 0, + endScroll, + translateY: [0, -15], + }) + + const { channels, loading } = useMostPaidChannels() + const items = !loading + ? channels?.map((channel) => ) + : Array.from({ length: 30 }).map((_, idx) => ) + + return ( + + + + + + + + + + + + Connect your YouTube channel & get paid + + + + + YouTube videos get automatically synced to your {atlasConfig.general.appName} channel, without any + additional effort. + + + + + + + + + + + + {yppAtlasStatus ? ( + yppAtlasStatus === 'ypp-signed' ? ( + + ) : ( + + ) + ) : ( + + )} + + + + + + + + + {hasAnotherUnsyncedChannel && selectedChannelTitle && ( + <> + Your channel "{selectedChannelTitle}" is already part of the YouTube Partner Program.{' '} + + Select a different channel + {' '} + to apply again. + + )} + {yppAtlasStatus !== 'ypp-signed' && 'It takes under 1 minute and is 100% free.'} + + + + + + + + + {items && items.length >= 7 && ( + + )} + + ) +} diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppRewardSection.styles.ts b/packages/atlas/src/views/global/YppLandingView/oldSections/YppRewardSection.styles.ts new file mode 100644 index 0000000000..4f74261f00 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppRewardSection.styles.ts @@ -0,0 +1,58 @@ +import styled from '@emotion/styled' + +import { GridItem } from '@/components/LayoutGrid' +import { Button } from '@/components/_buttons/Button' +import { cVar, media, sizes } from '@/styles' +import { Anchor } from '@/views/global/YppLandingView/YppAuthorizationModal/YppAuthorizationModal.styles' + +export const BenefitsCardButton = styled(Button)` + border-radius: 999px; +` + +export const BenefitsCardsButtonsGroup = styled.div` + text-align: center; + display: grid; + overflow-x: auto; + white-space: nowrap; + margin: ${sizes(16)} 0 ${sizes(8)} 0; + + ::-webkit-scrollbar { + display: none; + } + + gap: ${sizes(2)}; + + ${media.sm} { + grid-template-columns: repeat(2, 1fr); + } + + ${media.md} { + grid-template-columns: repeat(3, 1fr); + } + + ${media.lg} { + grid-template-columns: repeat(6, 1fr); + } +` + +export const BenefitsCardsContainerGridItem = styled(GridItem)` + display: grid; + gap: ${sizes(2)}; +` + +export const ColorAnchor = styled(Anchor)` + color: ${cVar('colorTextPrimary')}; +` + +export const RewardsSubtitleGridItem = styled(GridItem)` + display: grid; + gap: ${sizes(4)}; + margin-top: ${sizes(8)}; +` + +export const RewardsSubtitleWrapper = styled.div` + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-end; +` diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppRewardSection.tsx b/packages/atlas/src/views/global/YppLandingView/oldSections/YppRewardSection.tsx new file mode 100644 index 0000000000..b1b2907e96 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppRewardSection.tsx @@ -0,0 +1,134 @@ +import { FC, useRef } from 'react' + +import { Information } from '@/components/Information' +import { FlexGridItem, GridItem, LayoutGrid } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { TooltipText } from '@/components/Tooltip/Tooltip.styles' +import { TierCard } from '@/components/_ypp/TierCard' +import { atlasConfig } from '@/config' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { getTierRewards } from '@/utils/ypp' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +import { ColorAnchor } from './YppRewardSection.styles' + +import { + BackgroundContainer, + CenteredLayoutGrid, + RewardsSubText, + StyledLimitedWidthContainer, + TierCardWrapper, +} from '../YppLandingView.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 + const [titleVariant, subtitleVariant] = useSectionTextVariants() + const ref = useRef(null) + + if (!tiers?.length) { + return null + } + + return ( + + + + + + Rewards based on quality and popularity + + + + + Each participating channel is reviewed by the verification team and assigned to one of the reward tiers + below + + + + + + {tiers.map((tier) => { + const signupMultiplier = tier.tier === 'bronze' ? 1 : atlasConfig.features.ypp.tierBoostMultiplier || 1 + const referralMultiplier = atlasConfig.features.ypp.tierBoostMultiplier || 1 + const modifiedRewards = [ + tier.rewards[0] * signupMultiplier, + tier.rewards[1], + (getTierRewards('diamond')?.referral || 0) * referralMultiplier, + ] + return + })} + + *Referral rewards depend on the tier of the invited channel. + + + + + Payments are made in {atlasConfig.joystream.tokenTicker} tokens + + + {atlasConfig.joystream.tokenTicker} token is a native crypto asset of Joystream blockchain which + powers {atlasConfig.general.appName}. It is used for trading Creator Tokens, NFTs and covering + blockchain processing fees. It is also used for voting on proposals and partaking in council + elections.{' '} + + Purchase {atlasConfig.joystream.tokenTicker} + + + } + multiline + reference={ref.current} + /> + + + + + ) +} diff --git a/packages/atlas/src/views/global/YppLandingView/oldSections/YppSignupVideo.tsx b/packages/atlas/src/views/global/YppLandingView/oldSections/YppSignupVideo.tsx new file mode 100644 index 0000000000..d43a96c59f --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/oldSections/YppSignupVideo.tsx @@ -0,0 +1,73 @@ +import styled from '@emotion/styled' + +import { GridItem } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { + BackgroundContainer, + CenteredLayoutGrid, + StyledLimitedWidthContainerVideo, +} from '@/views/global/YppLandingView/YppLandingView.styles' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +export const YppSignupVideo = () => { + const mdMatch = useMediaMatch('md') + const [titleVariant, subtitleVariant] = useSectionTextVariants() + + return ( + + + + + + Sign up in 60 seconds + + + Watch the sign up demo by one of Joystream members. + + + + + + + + + + + ) +} + +const PlayerContainer = styled.div` + width: 100%; + position: relative; + aspect-ratio: 16/9; +` + +export const PlayerSkeletonLoader = styled(SkeletonLoader)` + position: absolute; + top: 0; +` + +const IframeVideo = styled.iframe` + border: none; + width: 640px; + height: 364px; + max-width: 100%; + max-height: 55vw; +` diff --git a/packages/atlas/src/views/global/YppLandingView/sections/CreatorOpportunities.tsx b/packages/atlas/src/views/global/YppLandingView/sections/CreatorOpportunities.tsx new file mode 100644 index 0000000000..7a76e1ece5 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/sections/CreatorOpportunities.tsx @@ -0,0 +1,208 @@ +import styled from '@emotion/styled' + +import { SvgLogoDiscordMonochrome } from '@/assets/icons' +import earning_crt from '@/assets/images/earnings/earning-crt.webp' +import earning_more from '@/assets/images/earnings/earning-more.webp' +import earning_nfts from '@/assets/images/earnings/earning-nfts.webp' +import earning_yt from '@/assets/images/earnings/earning-yt.webp' +import { FlexBox } from '@/components/FlexBox' +import { FlexGridItem, GridItem } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { Button, TextButton } from '@/components/_buttons/Button' +import { atlasConfig } from '@/config' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' +import { useAuthStore } from '@/providers/auth/auth.store' +import { useUser } from '@/providers/user/user.hooks' +import { useYppStore } from '@/providers/ypp/ypp.store' +import { cVar, media, sizes } from '@/styles' +import { + BackgroundContainer, + CenteredLayoutGrid, + HeaderGridItem, + StyledLimitedWidthContainer, +} from '@/views/global/YppLandingView/YppLandingView.styles' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +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.`, + image: earning_crt, + }, + { + title: 'NFTs', + subtitle: 'Mint your NFTs and earn from selling on marketplace and royalties with every future transaction.', + image: earning_nfts, + }, + { + title: 'More earning', + subtitle: 'Earn with building out community and social promotions.', + image: earning_more, + }, +] + +export const CreatorOpportunities = ({ onSignUpClick }: { onSignUpClick: () => void }) => { + const setIsYppChannelFlow = useYppStore((state) => state.actions.setIsYppChannelFlow) + const setAuthModalOpenName = useAuthStore((state) => state.actions.setAuthModalOpenName) + const { trackRewardsCreateChannelButtonClick } = useSegmentAnalytics() + const { memberChannels, isLoggedIn } = useUser() + const xsMatch = useMediaMatch('xs') + const mdMatch = useMediaMatch('md') + const smMatch = useMediaMatch('sm') + const [titleVariant, subtitleVariant] = useSectionTextVariants() + const [earningTitleVariant, earningSubtitleVariant] = smMatch + ? (['h600', 't400'] as const) + : (['h500', 't300'] as const) + + return ( + + + + + + Creator Earning Opportunities + + + Join {atlasConfig.general.appName}, grow your community with us and earn JOY tokens with the variety of + Web3 native features. + + + + {earningsOptions.map(({ title, subtitle, image }, idx) => ( + + + {`${title} + + + + + {title} + + + {subtitle} + + + ))} + + + + + {!memberChannels?.length ? ( + + ) : null} + + } + iconPlacement="right" + > + Connect with us on Discord. + + + + + + ) +} + +export const EarningsBox = styled(GridItem)` + display: grid; + grid-template-columns: 1fr; + text-align: left; + row-gap: ${sizes(13)}; + + ${media.sm} { + grid-template-columns: 1fr 1fr; + column-gap: ${sizes(6)}; + row-gap: ${sizes(24)}; + } +` + +const ImageBox = styled.div` + width: 100%; + height: auto; + position: relative; +` + +const Image = styled.img` + width: 100%; + height: 100%; + position: relative; + z-index: 1; +` + +export const ImageBorder = styled.div` + position: absolute; + inset: -2px; + background-color: ${cVar('colorBackground')}; + border-radius: ${cVar('radiusSmall')}; + overflow: hidden; + + ::after { + content: ' '; + position: absolute; + width: 50px; + background-color: #d9d9d9; + left: calc(50% - 50px); + top: calc(50% - 400px); + height: 800px; + filter: blur(30px); + transform: rotate(100deg); + } + + ::before { + content: ' '; + position: absolute; + width: 50px; + background-color: #d9d9d9; + left: calc(50% - 50px); + top: calc(50% - 400px); + height: 800px; + filter: blur(30px); + transform: rotate(30deg); + } +` diff --git a/packages/atlas/src/views/global/YppLandingView/sections/JoystreamRoadmap.tsx b/packages/atlas/src/views/global/YppLandingView/sections/JoystreamRoadmap.tsx new file mode 100644 index 0000000000..1c11bfb3ec --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/sections/JoystreamRoadmap.tsx @@ -0,0 +1,233 @@ +import { SvgActionChevronR } from '@/assets/icons' +import { QuartersData, QuartersListData } from '@/components/AnimatedTimeline' +import { GridItem, LayoutGrid } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { Button } from '@/components/_buttons/Button' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { HeaderGridItem } from '@/views/global/YppLandingView/YppLandingView.styles' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +export const JoystreamRoadmap = () => { + const [titleVariant, subtitleVariant] = useSectionTextVariants() + const mdMatch = useMediaMatch('md') + const smMatch = useMediaMatch('sm') + + return ( + + + + Project Roadmap + + + The project is constantly evolving with support of the builders and operators from the Joystream DAO. + + + + + + + + ) +} + +export const parseQuarters = (data: { quarters: QuartersData[] }[]) => { + if (data.length === 0) return [] + let index = 0 + return data.map((roadmapData) => { + roadmapData.quarters = roadmapData.quarters.map((quarter) => { + quarter.deliveryMilestones = quarter.deliveryMilestones.map((milestone) => { + milestone.generalIndex = index + index++ + return milestone + }) + return quarter + }) + return roadmapData + }) +} + +const data: { quarters: QuartersData[] }[] = [ + { + quarters: [ + // { + // 'year': '2024', + // 'id': 'Q2', + // 'deliveryMilestones': [ + // { + // 'icon': 'exchange', + // 'title': 'New Exchange Listings', + // 'content': + // 'Outreach & discussions with additional cryptocurrency exchanges to provide users with more options for trading the JOY token.', + // }, + // { + // 'icon': 'bridge', + // 'title': 'Ethereum Bridge', + // 'content': + // 'Development & deployment of a bridging solution on Joystream that will allow for interoperability with Ethereum. In this first version, the goal would be to make JOY accessible on the Ethereum mainnet as an ERC20 token, from which it could be bridged further to other L2s and side-chains. This would allow JOY to be traded, custodied and used with a much wider set of tools.', + // }, + // { + // 'icon': 'play', + // 'title': 'Content Recommendations - v2', + // 'content': + // 'Surfacing personalized content for a consumer is a key requirement for any content platform. Key product experiences like home screens, feeds and related content, all depend on having modern Machine learning based content recommendation system. Currently Orion has no infrastructure to produce or serve such recommendations, and app recommendations are being done entirely client-side. The goal of this milestone is to ship the first recommendation capability in Orion, to make Joystream apps much more', + // }, + // { + // 'icon': 'smartphone', + // 'title': 'Progressive Web App for Atlas/Gleev', + // 'content': + // 'Development of a progressive web app for Atlas/Gleev that will allow for the app to be installed on user’s smartphones without having to go through app stores.', + // }, + // { + // 'icon': 'monitor', + // 'title': 'Transcoding & Adaptive Streaming', + // 'content': + // 'Users access content across a wide range of browser, devices, applications and also under heterogeneous and dynamic bandwidth constraints. As it stands, only a single version of each media asset is represented in our metadata standards and backend node software. This means have now way to represent a broad range of encodings and resolutions for media assets, let alone produce all of these. Introducing server-side transcoding in Orion will unlock this, and many other future benefits that come from server-side post-processing (thumbnail extractions, auto-subtitling, etc.). It will also unlock the ability to do adaptive streaming, where a user with a dynamic connection can more quickly see asset resolve and play, and also be able to watch videos under suboptimal circumstances.', + // }, + // { + // 'icon': 'metamaskSnap', + // 'title': 'Metamask Snap Development', + // 'content': + // 'The development of a “Metamask Snap” plugin that will enable Joystream assets to be usable by Metamask users.', + // }, + // { + // 'icon': 'wallet', + // 'title': 'New Wallets - v2', + // 'content': + // 'Implementations with external wallet providers such as hardware & software wallet providers (Tangem, Ledger)', + // }, + // { + // 'icon': 'content', + // 'title': 'API-less YouTube Sync', + // 'content': + // 'This functionality would enable for channels to sync to Joystream via our YouTube Partner Program without relying on YouTube’s API.', + // }, + // { + // 'icon': 'channelPayout', + // 'title': 'Creator Rewards Program', + // 'content': + // "Objective of the program is to offer creators a broader range of activities to be rewarded for. Overall the focus of rewards is shifting from rewards for syncing to holistic engagement and driving the platform growth. As part of the rewards program creators are offered the opportunity to maximize rewards by posting original content to Gleev App ahead of other platforms, and further with utilizing Gleev branding assets. Promoting their channel's NFTs and Tokens on social media and actively participating in the creator's community opens up more additional opportunities for monetizing engagement with Joystream platform.", + // }, + // { + // 'icon': 'ambassador', + // 'title': 'Ambassador Program Expansion - v2', + // 'content': + // 'Further expansion of our Ambassador program which will include more recruitment and funding for content and engagement by creators.', + // }, + // ], + // }, + { + 'year': '2024', + 'id': 'Q3', + 'deliveryMilestones': [ + // { + // 'icon': 'exchange', + // 'title': 'New Exchange Listings', + // 'content': + // 'Outreach & discussions with additional cryptocurrency exchanges to provide users with more options for trading the JOY token.', + // }, + // { + // 'icon': 'list', + // 'title': 'Playlists', + // 'content': + // 'A key primitive for organizing content is through playlists. We have developed designs and metadata formats for this a long time ago, but in this milestone we will finally ship the ability for publishing and consuming playlists.', + // }, + { + 'icon': 'premium', + 'title': 'Premium Video Comments & Tips', + 'content': + 'Add functionality for users to leave comments on videos with a tip attached to them that will be sent to the channel owner. This will also enable functionality for users to tip channels and videos directly.', + }, + // { + // 'icon': 'stakePool', + // 'title': 'Staking Nomination Pools', + // 'content': + // 'Implementation of on-chain nomination pools which will allow for users to far more easily and quickly stake the JOY token for pooled rewards from other users. This will also provide an important revenue stream for wallet developers and make the Joystream project more attractive to be featured on wallets.', + // }, + { + 'icon': 'premium', + 'title': 'Social Collaboration Features', + 'content': + "The power of Web3 is in the community ownership and leveraging the network effects. Gleev will offer new ways to organise community based on content verticals and individual channels, adding more utility and new use cases for Creator Tokens and Joy tokens. Channel token holders will be offered exclusive ways to connect with creators, and social feed will empower viewers to co-curate the platform's trending content, gain exposure on the platform and earn JOY tokens for active participation.", + }, + // { + // 'icon': 'channelPayout', + // 'title': 'Creator Token Improvements', + // 'content': + // 'Various improvements to creator token functionality such as the addition of public sales, whitelisting and transfer of creator tokens.', + // }, + ], + }, + { + 'year': '2024', + 'id': 'Q4', + 'deliveryMilestones': [ + // { + // 'icon': 'exchange', + // 'title': 'New Exchange Listings', + // 'content': + // 'Outreach & discussions with additional cryptocurrency exchanges to provide users with more options for trading the JOY token.', + // }, + { + 'icon': 'community', + 'title': 'Video Communities - v1', + 'content': + 'Launching a new application for your community using Atlas currently requires a lot of deep technical knowledge. You have to obtain hosting and a domain, start the service, configure it, and then maintain it. This is quite difficult. The goal of this milestone is to create the Reddit to phpBB, in terms of the experience of a prospective community creator and moderator. One requires substantially more work and skill. By instead offering a fully hosted and simple point-and-click feature, we can make it orders of magnitude easier to allow third parties to invite their communities to use Joystream.', + }, + // { + // 'icon': 'sdk', + // 'title': 'Joystream SDK', + // 'content': + // 'Building on top of Joystream today is still a challenging experience. There are no robust libraries or builder documentation for application development. Offering an excellent experience for developer to build apps, and not just use Atlas off-the shelf, will require a significant investment in building out the SDK. This milestone has as a goal to extract the core of key existing applications, like Atlas and Pioneer, and then to synthesize it all into a new core developer experience, with a suite of excellent tutorials, documentation, examples and also active outreach.', + // }, + // { + // 'icon': 'lock', + // 'title': 'Content Infrastructure: Authentication & Authorization', + // 'content': + // 'Proper monetization, both for the DAO and individual creators, depends on being able to practically restrict access to content, or make access conditional. Currently, the infrastructure has no awareness of who the consumer is, or the semantics around what the content is, or any associated authorization policy. The goal of this milestone is to introduce basic authentication and authorization at the content layer, enforced by content delivery nodes. This will allow features such as gating videos to only NFT holders for example.', + // }, + // { + // 'icon': 'dapp', + // 'title': 'dApp Operator/Developer Grants & Support', + // 'content': + // 'Development of a grants program to attract dApp operators and developers to build on the Joystream platform and build our ecosystem.', + // }, + { + 'icon': 'contentGating', + 'title': 'Premium Content Gating Features', + 'content': + 'This feature will allow for content creators to restrict access for content to users unless they pay a fee, enabling another revenue stream for premium content.', + }, + { + 'icon': 'smartphone', + 'title': 'Native Mobile App Development', + 'content': 'Development of a native mobile app for major smartphone platforms.', + }, + ], + }, + ], + }, +] diff --git a/packages/atlas/src/views/global/YppLandingView/sections/ViewerOpportunities.tsx b/packages/atlas/src/views/global/YppLandingView/sections/ViewerOpportunities.tsx new file mode 100644 index 0000000000..ea08e9f2f0 --- /dev/null +++ b/packages/atlas/src/views/global/YppLandingView/sections/ViewerOpportunities.tsx @@ -0,0 +1,201 @@ +import styled from '@emotion/styled' +import { MutableRefObject } from 'react' + +import { SvgLogoDiscordMonochrome } from '@/assets/icons' +import viewer_earnings_crt from '@/assets/images/viewer-earnings/viewer-earning-crt-trade.webp' +import viewer_earnings_nft from '@/assets/images/viewer-earnings/viewer-earning-nft.webp' +import viewer_earnings_referrals from '@/assets/images/viewer-earnings/viewer-earning-referrals.webp' +import viewer_earnings_rs from '@/assets/images/viewer-earnings/viewer-earning-revenue-share.webp' +import { FlexBox } from '@/components/FlexBox' +import { FlexGridItem, GridItem } from '@/components/LayoutGrid' +import { Text } from '@/components/Text' +import { TextButton } from '@/components/_buttons/Button' +import { atlasConfig } from '@/config' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { cVar, media, sizes, square } from '@/styles' +import { + BackgroundContainer, + CenteredLayoutGrid, + HeaderGridItem, + StyledLimitedWidthContainer, +} from '@/views/global/YppLandingView/YppLandingView.styles' +import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' + +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.`, + image: viewer_earnings_rs, + }, + { + title: 'Trade Creator Tokens', + subtitle: 'Become an early investor and trade your tokens once the channel grows in popularity.', + image: viewer_earnings_crt, + }, + { + title: 'Trade NFTs', + subtitle: ( + <> + Own and trade collectibles from wide variety of channels. {/*Learn more*/} + + ), + image: viewer_earnings_nft, + }, +] + +export const ViewerOpportunities = ({ sectionRef }: { sectionRef: MutableRefObject }) => { + const mdMatch = useMediaMatch('md') + const smMatch = useMediaMatch('sm') + const [titleVariant, subtitleVariant] = useSectionTextVariants() + const [earningTitleVariant, earningSubtitleVariant] = smMatch + ? (['h600', 't400'] as const) + : (['h500', 't300'] as const) + + return ( + + + + + + Viewers Earning Opportunities + + + Discover the world of possibilities from collective channel ownership, token trading and earning by + contributing to {atlasConfig.general.appName} community growth with referrals. + + + + {viewerEarningsOptions.map(({ title, subtitle, image }, idx) => ( + + + {`${title} + + + + + {title} + + + {subtitle} + + + ))} + + + } + iconPlacement="right" + > + Connect with us on Discord. + + + + + + ) +} + +export const CheckBox = styled.div<{ square: number }>` + background: ${cVar('colorBackgroundSuccess')}; + display: grid; + place-items: center; + padding: ${sizes(2)}; + border-radius: 50%; + width: fit-content; + margin: 0 auto; + ${(props) => square(props.square)}; + + svg { + width: 100%; + height: 100%; + } +` + +export const EarningsBox = styled(GridItem)` + display: grid; + grid-template-columns: 1fr; + text-align: left; + row-gap: ${sizes(13)}; + + ${media.sm} { + grid-template-columns: 1fr 1fr; + column-gap: ${sizes(6)}; + row-gap: ${sizes(24)}; + } +` + +const ImageBox = styled.div` + width: 100%; + height: auto; + position: relative; +` + +const Image = styled.img` + width: 100%; + height: 100%; + position: relative; + z-index: 1; +` + +export const ImageBorder = styled.div` + position: absolute; + inset: -2px; + background-color: ${cVar('colorBackground')}; + border-radius: ${cVar('radiusSmall')}; + overflow: hidden; + + ::after { + content: ' '; + position: absolute; + width: 50px; + background-color: #d9d9d9; + left: calc(50% - 50px); + top: calc(50% - 400px); + height: 800px; + filter: blur(30px); + transform: rotate(100deg); + } + + ::before { + content: ' '; + position: absolute; + width: 50px; + background-color: #d9d9d9; + left: calc(50% - 50px); + top: calc(50% - 400px); + height: 800px; + filter: blur(30px); + transform: rotate(30deg); + } +` diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.styles.ts b/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.styles.ts index 3e20ec69c7..a0ff6bbbfa 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.styles.ts +++ b/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.styles.ts @@ -2,7 +2,6 @@ import styled from '@emotion/styled' import bottomLeftPattern from '@/assets/images/ypp-background-pattern.svg' import topLeftBannerPattern from '@/assets/images/ypp-banner-pattern.svg' -import { GoogleButton } from '@/components/_buttons/GoogleButton' import { cVar, media, sizes } from '@/styles' export const CtaBanner = styled.div` @@ -30,7 +29,3 @@ export const CtaBanner = styled.div` margin: ${sizes(24)} 0; } ` - -export const StyledButton = styled(GoogleButton)` - margin-top: ${sizes(8)}; -` diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx b/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx index 1edcdb006f..6fd6f0302f 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx +++ b/packages/atlas/src/views/global/YppLandingView/sections/YppFooter.tsx @@ -1,14 +1,21 @@ import { FC, ReactElement } from 'react' import { SvgActionInfo, SvgActionSpeech, SvgActionTokensStack } from '@/assets/icons' +import { FlexBox } from '@/components/FlexBox' import { GridItem, LayoutGrid } from '@/components/LayoutGrid' import { Text } from '@/components/Text' +import { Button } from '@/components/_buttons/Button' import { CallToActionButton, CallToActionWrapper } from '@/components/_buttons/CallToActionButton' import { atlasConfig } from '@/config' import { YppWidgetIcons } from '@/config/configSchema' +import { useMediaMatch } from '@/hooks/useMediaMatch' +import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' +import { useAuthStore } from '@/providers/auth/auth.store' +import { useUser } from '@/providers/user/user.hooks' +import { useYppStore } from '@/providers/ypp/ypp.store' import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' -import { CtaBanner, StyledButton } from './YppFooter.styles' +import { CtaBanner } from './YppFooter.styles' import { StyledLimitedWidthContainer } from '../YppLandingView.styles' @@ -24,7 +31,11 @@ type YppFooterSectionProps = { export const YppFooter: FC = ({ onSignUpClick }) => { const [titleVariant] = useSectionTextVariants() - + const smMatch = useMediaMatch('sm') + const setIsYppChannelFlow = useYppStore((state) => state.actions.setIsYppChannelFlow) + const setAuthModalOpenName = useAuthStore((state) => state.actions.setAuthModalOpenName) + const { trackRewardsCreateChannelButtonClick } = useSegmentAnalytics() + const { memberChannels, isLoggedIn } = useUser() return ( <> = ({ onSignUpClick }) => { Pave the way to Web3 with your YouTube channel right away. - Authorize with YouTube + + + {!memberChannels?.length ? ( + + ) : null} + diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppHero.styles.ts b/packages/atlas/src/views/global/YppLandingView/sections/YppHero.styles.ts index 8f29f25b97..3a1fbf69e3 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/YppHero.styles.ts +++ b/packages/atlas/src/views/global/YppLandingView/sections/YppHero.styles.ts @@ -1,5 +1,6 @@ import styled from '@emotion/styled' +import { FlexBox } from '@/components/FlexBox' import { InfiniteCarousel } from '@/components/InfiniteCarousel/InfiniteCarousel' import { cVar, media, sizes } from '@/styles' @@ -19,6 +20,86 @@ export const ButtonWrapper = styled.div` justify-content: center; ` +export const WidgetsContainer = styled(FlexBox)` + text-align: left; + flex-direction: column; + + > * { + flex: 1; + width: 100%; + } + + ${media.sm} { + flex-direction: row; + } +` + +export const ImagesContainer = styled(FlexBox)` + position: relative; + margin: 40px 0; + + ${media.sm} { + margin: 80px 0; + } +` + +export const RightImage = styled.img` + position: absolute; + width: auto; + height: 100%; + object-fit: contain; + display: block; + left: 0%; + z-index: 10; + border-radius: ${cVar('radiusMedium')}; + padding: 40px 0; + box-shadow: 10px blue; + + ${media.lg} { + left: unset; + right: 40%; + } +` + +export const LeftImage = styled.img` + position: absolute; + width: auto; + height: 100%; + object-fit: contain; + display: block; + right: 0%; + z-index: 10; + border-radius: ${cVar('radiusMedium')}; + padding: 40px 0; + + ${media.xs} { + width: auto; + } + + ${media.lg} { + left: 40%; + } +` + +export const FrontImage = styled.img` + max-height: 520px; + width: 95%; + height: 100%; + object-fit: contain; + display: block; + border-radius: ${cVar('radiusMedium')}; + z-index: 20; + ${imageShadow} + + ${media.xs} { + width: auto; + } + + ${media.xs} { + width: 70%; + } +` + export const LogosContainer = styled.div` display: flex; align-items: center; @@ -42,16 +123,6 @@ export const SelectDifferentChannelButton = styled.button` } ` -export const FrontImage = styled.img` - width: 100%; - max-width: 100%; - height: auto; - display: block; - border-radius: ${cVar('radiusMedium')}; - margin-top: 10%; - ${imageShadow} -` - export const BackImage = styled.img` position: absolute; width: 80%; diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx b/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx index 2a765d52a0..085004a183 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx +++ b/packages/atlas/src/views/global/YppLandingView/sections/YppHero.tsx @@ -1,31 +1,35 @@ import { FC } from 'react' -import { useParallax } from 'react-scroll-parallax' import { CSSTransition, SwitchTransition } from 'react-transition-group' import useResizeObserver from 'use-resize-observer' import { useMostPaidChannels } from '@/api/hooks/channel' -import { SvgActionChevronR, SvgLogoGoogleWhiteFull, SvgLogoYoutubeWhiteFull } from '@/assets/icons' -import hero from '@/assets/images/ypp-hero/hero.webp' -import yt from '@/assets/images/ypp-hero/yt.webp' +import { SvgActionChevronR, SvgActionNewTab } from '@/assets/icons' +import crt_card from '@/assets/images/ypp-hero/crt-card-hero.webp' +import crt_dashboard from '@/assets/images/ypp-hero/crt-dashboard-hero.webp' +import payments from '@/assets/images/ypp-hero/crt-payments-hero.webp' import { AppLogo } from '@/components/AppLogo' +import { FlexBox } from '@/components/FlexBox' import { GridItem, LayoutGrid } from '@/components/LayoutGrid' import { Text } from '@/components/Text' -import { Button } from '@/components/_buttons/Button' -import { GoogleButton } from '@/components/_buttons/GoogleButton' +import { Button, TextButton } from '@/components/_buttons/Button' import { PaidChannelCard } from '@/components/_channel/ChannelCard' import { SkeletonLoader } from '@/components/_loaders/SkeletonLoader' import { atlasConfig } from '@/config' import { useMediaMatch } from '@/hooks/useMediaMatch' +import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' +import { useAuthStore } from '@/providers/auth/auth.store' +import { useUser } from '@/providers/user/user.hooks' +import { useYppStore } from '@/providers/ypp/ypp.store' import { cVar, transitions } from '@/styles' import { useSectionTextVariants } from '@/views/global/YppLandingView/sections/useSectionTextVariants' import { - BackImage, ButtonWrapper, FrontImage, - HeroImageWrapper, + ImagesContainer, + LeftImage, LogosContainer, - SelectDifferentChannelButton, + RightImage, StyledInfiniteCarousel, } from './YppHero.styles' @@ -41,28 +45,22 @@ export type YppAtlasStatus = 'have-channel' | 'no-channel' | 'ypp-signed' | 'con type YppHeroProps = { onSignUpClick: () => void onSelectChannel: () => void + onViewerEarnings: () => void yppAtlasStatus: YppAtlasStatus hasAnotherUnsyncedChannel?: boolean selectedChannelTitle?: string | null } -export const YppHero: FC = ({ - onSignUpClick, - onSelectChannel, - yppAtlasStatus, - hasAnotherUnsyncedChannel, - selectedChannelTitle, -}) => { +export const YppHero: FC = ({ onSignUpClick, yppAtlasStatus, onViewerEarnings }) => { + const xsMatch = useMediaMatch('xs') + const xxsMatch = useMediaMatch('xxs') const smMatch = useMediaMatch('sm') const { ref, width, height } = useResizeObserver({ box: 'border-box' }) const [, subtitleVariant, titleVariant] = useSectionTextVariants() - const endScroll = smMatch ? window.innerHeight / 3 : window.innerHeight - const { ref: heroImageRef } = useParallax({ - startScroll: 0, - endScroll, - translateY: [0, -15], - }) - + const setIsYppChannelFlow = useYppStore((state) => state.actions.setIsYppChannelFlow) + const setAuthModalOpenName = useAuthStore((state) => state.actions.setAuthModalOpenName) + const { memberChannels, isLoggedIn } = useUser() + const { trackRewardsCreateChannelButtonClick } = useSegmentAnalytics() const { channels, loading } = useMostPaidChannels() const items = !loading ? channels?.map((channel) => ) @@ -95,7 +93,7 @@ export const YppHero: FC = ({ data-aos-offset="80" data-aos-easing="atlas-easing" > - Connect your YouTube channel & get paid + Embrace Web3 creator economy @@ -109,16 +107,11 @@ export const YppHero: FC = ({ data-aos-offset="40" data-aos-easing="atlas-easing" > - YouTube videos get automatically synced to your {atlasConfig.general.appName} channel, without any - additional effort. + Connect and discover earning opportunities with {atlasConfig.general.appName}. - - - - = ({ Go to dashboard ) : ( - + + + {!memberChannels?.length ? ( + + ) : null} + ) ) : ( @@ -149,8 +164,20 @@ export const YppHero: FC = ({ - - + onViewerEarnings()} + iconPlacement="right" + size="large" + icon={} + > + Earn as viewer + + {/* = ({ )} {yppAtlasStatus !== 'ypp-signed' && 'It takes under 1 minute and is 100% free.'} - + */} - - - - + + {/**/} + {/* */} + {/* */} + {/* */} + {/* Creator Earnings*/} + {/* */} + {/* */} + {/* */} + {/* */} + {/* 204M*/} + {/* */} + {/* }*/} + {/* />*/} + {/* */} + {/* 204M*/} + {/* */} + {/* }*/} + {/* />*/} + {/* */} + {/* 204M*/} + {/* */} + {/* }*/} + {/* />*/} + {/* */} + {/* */} + {/**/} + + + + {xsMatch && ( + <> + + + + )} + {items && items.length >= 7 && ( { data-aos-offset="80" data-aos-easing="atlas-easing" > - Rewards based on quality and popularity + Rewards based on quality @@ -78,13 +82,13 @@ export const YppRewardSection: FC = () => { data-aos-offset="40" data-aos-easing="atlas-easing" > - Each participating channel is reviewed by the verification team and assigned to one of the reward tiers - below + Each participating channel in YouTube partners program is reviewed by the verification team and assigned + to one of the reward tiers below - + {tiers.map((tier) => { const signupMultiplier = tier.tier === 'bronze' ? 1 : atlasConfig.features.ypp.tierBoostMultiplier || 1 const referralMultiplier = atlasConfig.features.ypp.tierBoostMultiplier || 1 @@ -95,19 +99,10 @@ export const YppRewardSection: FC = () => { ] return })} + + *Referral rewards depend on the tier of the invited channel. + - - - *Referral rewards depend on the tier of the invited channel. Referrer gets half of the sign up rewards for - invited channels that are verified. - - { marginTop={mdMatch ? -2 : 0} justifyContent="end" > - - Payments are made in {atlasConfig.joystream.tokenTicker} tokens - - - {atlasConfig.joystream.tokenTicker} token is a native crypto asset of Joystream blockchain which - powers {atlasConfig.general.appName}. It is used for trading Creator Tokens, NFTs and covering - blockchain processing fees. It is also used for voting on proposals and partaking in council - elections.{' '} - - Purchase {atlasConfig.joystream.tokenTicker} - - - } - multiline - reference={ref.current} - /> + + } + iconPlacement="right" + > + Learn more + +
+ + + Payments are made in {atlasConfig.joystream.tokenTicker} tokens + + + {atlasConfig.joystream.tokenTicker} token is a native crypto asset of Joystream blockchain which + powers {atlasConfig.general.appName}. It is used for trading Creator Tokens, NFTs and covering + blockchain processing fees. It is also used for voting on proposals and partaking in council + elections.{' '} + + Purchase {atlasConfig.joystream.tokenTicker} + + + } + multiline + reference={ref.current} + /> + + diff --git a/packages/atlas/src/views/global/YppLandingView/sections/YppSignupVideo.tsx b/packages/atlas/src/views/global/YppLandingView/sections/YppSignupVideo.tsx index d43a96c59f..779d3c90f7 100644 --- a/packages/atlas/src/views/global/YppLandingView/sections/YppSignupVideo.tsx +++ b/packages/atlas/src/views/global/YppLandingView/sections/YppSignupVideo.tsx @@ -21,7 +21,7 @@ export const YppSignupVideo = () => { - Sign up in 60 seconds + Sync your channel from YouTube in 60 seconds { align="center" margin={{ top: 4, bottom: mdMatch ? 16 : 12 }} > - Watch the sign up demo by one of Joystream members. + And get paid in JOY tokens for sign up and new videos synced. diff --git a/packages/atlas/src/views/viewer/ViewerLayout.tsx b/packages/atlas/src/views/viewer/ViewerLayout.tsx index fd8b5e0345..1d2bee806b 100644 --- a/packages/atlas/src/views/viewer/ViewerLayout.tsx +++ b/packages/atlas/src/views/viewer/ViewerLayout.tsx @@ -22,11 +22,12 @@ import { useUser } from '@/providers/user/user.hooks' import { media, transitions } from '@/styles' import { RoutingState } from '@/types/routing' -const YppLandingView = lazy(() => +// Currently the newest version is at main file and the old one was moved to new file +const YppLandingViewTest = lazy(() => import('@/views/global/YppLandingView').then((module) => ({ default: module.YppLandingView })) ) -const YppLandingViewTest = lazy(() => - import('@/views/global/YppLandingViewTest').then((module) => ({ default: module.YppLandingViewTest })) +const YppLandingView = lazy(() => + import('@/views/global/YppLandingView/YppLandingViewOld').then((module) => ({ default: module.YppLandingViewOld })) ) const MemberNotificationsView = lazy(() => import('@/views/notifications').then((module) => ({ default: module.MemberNotificationsView }))