Skip to content

Commit

Permalink
Merge branch 'dev' into fix/add-suspense-viewer
Browse files Browse the repository at this point in the history
  • Loading branch information
attemka authored Aug 16, 2023
2 parents 9fe7f21 + ccdc6fa commit 47389c1
Show file tree
Hide file tree
Showing 32 changed files with 504 additions and 116 deletions.
14 changes: 11 additions & 3 deletions packages/atlas/atlas.config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@ joystream:

features:
ypp:
yppDelayThreshold: 300 # When the YPP sync backlog exceeds the threshold, Atlas will consider the YPP sync delayed.
landingPageOgTitle: null # Open graph title for YPP landing page - used in open graph meta tags in HTML
landingPageOgDescription: null # Open graph description for YPP landing page - used in open graph meta tags in HTML
landingPageOgImgPath: null # Path to the open graph image for the YPP landing page - if not set, the default image will be used
googleConsoleClientId: '$VITE_GOOGLE_CONSOLE_CLIENT_ID'
youtubeSyncApiUrl: '$VITE_YOUTUBE_SYNC_API_URL'
suspensionReasonsLink: https://www.notion.so/joystream/YouTube-Partner-Program-Outline-d492c2eb88ff4ace955b5f2902ec21fb?pvs=4#e03bb48ed7f7480c8896a908f63f2594 # Link with explanation what might be the reason of ypp channel suspension
suspendedSupportLink: https://discord.com/channels/811216481340751934/1053294778529353788 # Link that will be displayed for users suspended in YPP
suspendedLinkText: '#ypp-support channel on our Discord server'
youtubeCollaboratorMemberId: '$VITE_YOUTUBE_COLLABORATOR_MEMBER_ID'
Expand All @@ -56,7 +58,7 @@ features:
multiplier: 2.5
- minimumSubscribers: 50000
multiplier: 5
tiersTooltip: The more subscribers you have on YouTube, the higher the payouts in the program. For 5-50K subscribers, payouts are multiplied by 2.5, for more than 50K subscribers, payouts are 5x as high. Below displayed rewards are calculated for your channel.
tiersTooltip: The more subscribers you have on YouTube, the higher the payouts in the program.
rewards:
- title: Sign Up to YouTube Partner Program
showInDashboard: false # Optional. If false the reward will be shown only on YouTube partner program landing page. Default true
Expand All @@ -70,9 +72,10 @@ features:
- Make sure the auto-sync feature enabled.
- Wait till your videos get synced to your $VITE_APP_NAME channel.
- Publish new videos to your YouTube channel.
- Get rewarded for every new video synced to $VITE_APP_NAME.
- Get rewarded for every new video synced to $VITE_APP_NAME. Min length of rewarded video is 5 minutes. Sync rewards capped to 3 per week.
baseAmount: null
baseUsdAmount: 3
baseUsdAmount: 1
customMultiplier: [1, 5, 10]
- title: Refer another YouTube creator
shortDescription: Get JOY for every new creator who signs up to YPP program using your referral link. Referrals multiplier depends on the popularity tier of the channel signed up using referral link.
stepsDescription: Earn when another YouTube creator signs up to the program by using your your referral link.
Expand All @@ -93,6 +96,11 @@ features:
linkText: Go to Notion # Used only on YPP Dashboard - if empty defaults to "Go to {title}"
label: Notion # Used for YPP Dashboard to inform user which vendor given feature uses - if empty defaults to title
icon: info # Optional icon to be displayed. Possible icons: message, info, tokenStack
- title: Payments
link: /studio/payments
linkText: Go to Payments
label: Studio
icon: tokenStack
- title: Support
link: https://discord.com/channels/811216481340751934/1053294778529353788
linkText: Go to Discord
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FC, Fragment } from 'react'
import { FC } from 'react'

import { SPECIAL_CHARACTERS } from '@/config/regex'

Expand All @@ -14,7 +14,7 @@ export const ResultTitle: FC<ResultTitleProps> = ({ title, query }) => {
return null
}
if (!query) {
return <>{title}</>
return <span>{title}</span>
}

const filteredQuery = query.replace(SPECIAL_CHARACTERS, '\\$&').replace(/\s+/g, '|')
Expand All @@ -23,7 +23,7 @@ export const ResultTitle: FC<ResultTitleProps> = ({ title, query }) => {
const match = title.match(regex)

if (!match || !match.length) {
return <>{title}</>
return <span>{title}</span>
}

return (
Expand All @@ -32,7 +32,7 @@ export const ResultTitle: FC<ResultTitleProps> = ({ title, query }) => {
if (match.includes(word)) {
return <HighlightedWord key={`${word}-${idx}`}>{word} </HighlightedWord>
}
return <Fragment key={`${word}-${idx}`}>{word} </Fragment>
return <span key={`${word}-${idx}`}>{word} </span>
})}
</>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export const ExternalSignInModal: FC = () => {
}, [currentStep])

useEffect(() => {
authModalOpenName === 'externalLogIn' && trackPageView(stepToPageName[currentStep] ?? '')
authModalOpenName === 'externalLogIn' &&
trackPageView(stepToPageName[currentStep] ?? 'External login - unknown page')
}, [authModalOpenName, currentStep, trackPageView])

const renderStep = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const stepToPageName: Partial<Record<SignUpSteps, string>> = {
[SignUpSteps.SignUpSeed]: 'Signup modal - seed',
[SignUpSteps.SignUpPassword]: 'Signup modal - password',
[SignUpSteps.SignUpEmail]: 'Signup modal - email',
[SignUpSteps.Creating]: 'Signup modal - creating',
[SignUpSteps.Creating]: 'Signup modal - creating account',
[SignUpSteps.Success]: 'Signup modal - success',
}

Expand Down Expand Up @@ -308,7 +308,8 @@ export const SignUpModal = () => {
}, [isSuccess, signUpFormData.current.email, signUpFormData.current.handle, trackMembershipCreation])

useEffect(() => {
authModalOpenName === 'signUp' && trackPageView(stepToPageName[currentStep] ?? '', { isYppFlow })
authModalOpenName === 'signUp' &&
trackPageView(stepToPageName[currentStep] ?? 'Sign up - unknown page', { isYppFlow })
}, [authModalOpenName, currentStep, isYppFlow, trackPageView])

const smMatch = useMediaMatch('sm')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { ContextMenu } from '@/components/_overlays/ContextMenu'
import { atlasConfig } from '@/config'
import { useGetAssetUrl } from '@/hooks/useGetAssetUrl'
import { useConfirmationModal } from '@/providers/confirmationModal'
import { useSnackbar } from '@/providers/snackbars'
import { SubtitlesInput } from '@/types/subtitles'
import { SentryLogger } from '@/utils/logs'

import {
InvisibleInput,
Expand Down Expand Up @@ -39,21 +41,30 @@ export const SubtitlesBox: FC<SubtitleBoxProps> = ({
}) => {
const inputRef = useRef<HTMLInputElement>(null)
const [openUnsuportedFileDialog, closeUnsuportedFileDialog] = useConfirmationModal()
const { displaySnackbar } = useSnackbar()
const hasFile = !!file || !!id
const { mutateAsync: subtitlesFetch } = useMutation('subtitles-fetch', (url: string) =>
axiosInstance.get(url, { responseType: 'blob' })
)
const { url } = useGetAssetUrl(asset?.resolvedUrls, 'subtitle')

const handleDownload = async (url = '') => {
const response = await subtitlesFetch(url)
const objectURL = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement('a')
link.href = objectURL
link.setAttribute('download', `${id}-${languageIso.toLowerCase()}.vtt`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
try {
const response = await subtitlesFetch(url)
const objectURL = window.URL.createObjectURL(new Blob([response.data]))
const link = document.createElement('a')
link.href = objectURL
link.setAttribute('download', `${id}-${languageIso.toLowerCase()}.vtt`)
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
} catch (error) {
SentryLogger.error('Failed to fetch subtitles for download', 'handleDownload', error)
displaySnackbar({
title: 'Failed to connect to distributor',
iconType: 'error',
})
}
}

const contexMenuItems: ListItemProps[] = [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ChangeEvent, FC, MouseEvent, useCallback, useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { useLocation, useSearchParams } from 'react-router-dom'
import { CSSTransition, SwitchTransition } from 'react-transition-group'
import shallow from 'zustand/shallow'

Expand All @@ -12,6 +12,7 @@ import { NotificationsWidget } from '@/components/_notifications/NotificationsWi
import { MemberDropdown } from '@/components/_overlays/MemberDropdown'
import { QUERY_PARAMS, absoluteRoutes } from '@/config/routes'
import { useMediaMatch } from '@/hooks/useMediaMatch'
import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics'
import { getMemberAvatar } from '@/providers/assets/assets.helpers'
import { getCorrectLoginModal } from '@/providers/auth/auth.helpers'
import { useAuth } from '@/providers/auth/auth.hooks'
Expand All @@ -35,6 +36,9 @@ import {
export const TopbarViewer: FC = () => {
const { activeMembership, isLoggedIn, membershipsLoading } = useUser()
const { isAuthenticating } = useAuth()
const [searchParams] = useSearchParams()
const [utmSource, utmCampaign] = [searchParams.get('utm_source'), searchParams.get('utm_campaign')]
const { trackClickTopBarSignInButton } = useSegmentAnalytics()
const [isMemberDropdownActive, setIsMemberDropdownActive] = useState(false)

const { urls: memberAvatarUrls, isLoadingAsset: memberAvatarLoading } = getMemberAvatar(activeMembership)
Expand Down Expand Up @@ -156,7 +160,10 @@ export const TopbarViewer: FC = () => {
icon={<SvgActionMember />}
iconPlacement="left"
size="medium"
onClick={() => setAuthModalOpenName(getCorrectLoginModal())}
onClick={() => {
trackClickTopBarSignInButton(utmSource, utmCampaign)
setAuthModalOpenName(getCorrectLoginModal())
}}
>
Sign in
</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { FormField } from '@/components/_inputs/FormField'
import { TokenInput } from '@/components/_inputs/TokenInput'
import { DialogModal } from '@/components/_overlays/DialogModal'
import { atlasConfig } from '@/config'
import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics'
import { hapiBnToTokenNumber, tokenNumberToHapiBn } from '@/joystream-lib/utils'
import { useFee, useJoystream, useTokenPrice } from '@/providers/joystream'
import { useTransaction } from '@/providers/transactions/transactions.hooks'
Expand Down Expand Up @@ -55,6 +56,7 @@ export const WithdrawFundsDialog: FC<WithdrawFundsDialogProps> = ({
setValue,
formState: { errors },
} = useForm<{ amount: number | null }>()
const { trackWithdrawnFunds } = useSegmentAnalytics()
const { fetchPaymentsData } = useChannelPaymentsHistory(channelId || '')
const { convertHapiToUSD } = useTokenPrice()
const amountBn = tokenNumberToHapiBn(watch('amount') || 0)
Expand Down Expand Up @@ -93,6 +95,7 @@ export const WithdrawFundsDialog: FC<WithdrawFundsDialogProps> = ({
),
onTxSync: async () => {
fetchPaymentsData()
trackWithdrawnFunds(channelId, formatNumber(data.amount || 0))
onExitClick()
},
})
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Meta, StoryFn } from '@storybook/react'

import { YppStatusPill } from '@/components/_ypp/YppStatusPill/YppStatusPill'

export default {
title: 'components/YppStatusPill',
component: YppStatusPill,
} as Meta

const Template: StoryFn = () => <YppStatusPill />

export const Default = Template.bind({})
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { css, keyframes } from '@emotion/react'
import styled from '@emotion/styled'

import { cVar, sizes } from '@/styles'

export const Container = styled.div`
display: flex;
align-items: center;
justify-content: center;
gap: ${sizes(2)};
background-color: ${cVar('colorCoreNeutral800Lighten')};
padding: ${sizes(2)} ${sizes(4)};
border-radius: 99px;
width: fit-content;
`

type DotProps = {
status: 'operational' | 'delayed' | 'stopped'
}

const getStatusDotStyles = ({ status }: DotProps) => {
switch (status) {
case 'delayed':
return css`
background: linear-gradient(#f1c804, #947b01);
box-shadow: 0 0 0 5px #caa80280;
`
case 'stopped':
return css`
background: linear-gradient(#ff695f, #bf0c00);
box-shadow: 0 0 0 5px #ff695f80;
`
case 'operational':
return css`
background: linear-gradient(#0ebe57, #096c34);
box-shadow: 0 0 0 5px #0c984680;
`
}
}

const getDotAnimation = ({ status }: DotProps) => keyframes`
0% {
box-shadow: none;
}
10% {
box-shadow: 0 0 0 3px ${status === 'stopped' ? '#ff695f80' : status === 'delayed' ? '#caa80280' : '#0c984680'};
}
20%, 100% {
box-shadow: none;
}
`

export const StatusDot = styled.div<DotProps>`
width: 10px;
height: 10px;
border-radius: 50%;
${getStatusDotStyles};
animation: 10s ease-out ${getDotAnimation} infinite;
`

export const TooltipBox = styled.div`
padding: ${sizes(1)};
display: grid;
gap: ${sizes(2)};
`
74 changes: 74 additions & 0 deletions packages/atlas/src/components/_ypp/YppStatusPill/YppStatusPill.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { useRef } from 'react'
import { useQuery } from 'react-query'

import { axiosInstance } from '@/api/axios'
import { Text } from '@/components/Text'
import { Tooltip } from '@/components/Tooltip'
import { atlasConfig } from '@/config'
import { ConsoleLogger } from '@/utils/logs'

import { Container, StatusDot, TooltipBox } from './YppStatusPill.styles'

export type YppStatusType = 'operational' | 'delayed' | 'stopped'

const getTooltipText = (status: YppStatusType) => {
switch (status) {
case 'delayed':
return ['OPERATING WITH DELAYS', 'We are experiencing a bigger amount of network traffic right now.']
case 'operational':
return ['FULLY OPERATIONAL', 'Everything works as expected!']
case 'stopped':
return ['ON HOLD', 'The sync network experienced a major outage - we are currently working on fixing the issue.']
}
}

const YOUTUBE_BACKEND_URL = atlasConfig.features.ypp.youtubeSyncApiUrl
const YPP_DELAY_THRESHOLD = atlasConfig.features.ypp.yppDelayThreshold ?? 500

type YppStatusDto = {
version: string
syncStatus: string
syncBacklog: number
}

export const YppStatusPill = () => {
const statusRef = useRef<HTMLDivElement>(null)
const { data, isLoading } = useQuery('ypp-status', () =>
axiosInstance<YppStatusDto>(`${YOUTUBE_BACKEND_URL}/status`).catch(() =>
ConsoleLogger.warn('Failed to fetch YPP status')
)
)

if (!data || isLoading) {
return null
}

const isDelayed = data.data.syncBacklog > YPP_DELAY_THRESHOLD
const status: YppStatusType = isDelayed ? 'delayed' : data.data.syncStatus === 'enabled' ? 'operational' : 'stopped'
const [tooltipTitle, tooltipText] = getTooltipText(status)

return (
<>
<Container ref={statusRef}>
<Text variant="t200" as="p">
YPP Sync Status:
</Text>
<StatusDot status={status} />
</Container>
<Tooltip
reference={statusRef}
placement="bottom-start"
customContent={
<TooltipBox>
<Text variant="h100" as="h1" color="colorTextStrong">
{tooltipTitle}
</Text>
<Text variant="t100" as="p" color="colorTextStrong">
{tooltipText}
</Text>
</TooltipBox>
}
/>
</>
)
}
1 change: 1 addition & 0 deletions packages/atlas/src/components/_ypp/YppStatusPill/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './YppStatusPill'
3 changes: 3 additions & 0 deletions packages/atlas/src/config/configSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const configSchema = z.object({
}),
features: z.object({
ypp: z.object({
suspensionReasonsLink: z.string().nullable(),
yppDelayThreshold: z.number().nullable(),
googleConsoleClientId: z.string().nullable(),
youtubeSyncApiUrl: z.string().nullable(),
suspendedSupportLink: z.string().nullable(),
Expand Down Expand Up @@ -82,6 +84,7 @@ export const configSchema = z.object({
}),
])
.nullable(),
customMultiplier: z.array(z.number()).optional(),
actionButtonText: z.string().optional(),
actionButtonAction: z
.string()
Expand Down
Loading

0 comments on commit 47389c1

Please sign in to comment.