diff --git a/CHANGELOG.md b/CHANGELOG.md index 33de2c8aef..14b5f77322 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.9.2] - 2023-09-22 + +### Changed + +- Captcha was moved to the last signup step + +### Fixed + +- Fixed bug with checking the encoding for non-video assets +- Fixed bug with missing upload button for mobile devices +- Fixed bug with infinite modal on signup + ## [4.9.1] - 2023-09-14 ### Changed diff --git a/packages/atlas/package.json b/packages/atlas/package.json index d0750b8fb2..159063e43a 100644 --- a/packages/atlas/package.json +++ b/packages/atlas/package.json @@ -1,7 +1,7 @@ { "name": "@joystream/atlas", "description": "UI for consuming Joystream - a user governed video platform", - "version": "4.9.1", + "version": "4.9.2", "license": "GPL-3.0", "scripts": { "start": "vite", diff --git a/packages/atlas/src/components/List/List.tsx b/packages/atlas/src/components/List/List.tsx index 0e097f5cf0..6cd0631af9 100644 --- a/packages/atlas/src/components/List/List.tsx +++ b/packages/atlas/src/components/List/List.tsx @@ -15,7 +15,7 @@ type ListProps = { export const List: FC = ({ items, size, className, scrollable = false }) => { return ( - + {items.map((item, index) => { const component = diff --git a/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx b/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx index b1aa641dc3..e437c06ad0 100644 --- a/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx +++ b/packages/atlas/src/components/_auth/SignUpModal/SignUpModal.tsx @@ -7,7 +7,7 @@ import { useGetMembershipsLazyQuery } from '@/api/queries/__generated__/membersh import { Button } from '@/components/_buttons/Button' import { DialogButtonProps } from '@/components/_overlays/Dialog' import { DialogModal } from '@/components/_overlays/DialogModal' -import { AccountFormData, FaucetError, MemberFormData, RegisterError, useCreateMember } from '@/hooks/useCreateMember' +import { AccountFormData, MemberFormData, RegisterError, useCreateMember } from '@/hooks/useCreateMember' import { useMediaMatch } from '@/hooks/useMediaMatch' import { useSegmentAnalytics } from '@/hooks/useSegmentAnalytics' import { useUniqueMemberHandle } from '@/hooks/useUniqueMemberHandle' @@ -214,12 +214,8 @@ export const SignUpModal = () => { onStart: () => { goToStep(SignUpSteps.Creating) }, - onError: (error) => { - if (error === FaucetError.MemberAlreadyCreatedForGoogleAccount) { - setAuthModalOpenName(undefined) - } else { - goToStep(SignUpSteps.CreateMember) - } + onError: () => { + setAuthModalOpenName(undefined) }, }) if (memberId) { diff --git a/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpMembershipStep/SignUpMembershipStep.tsx b/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpMembershipStep/SignUpMembershipStep.tsx index a12e204231..5569bb47a5 100644 --- a/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpMembershipStep/SignUpMembershipStep.tsx +++ b/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpMembershipStep/SignUpMembershipStep.tsx @@ -1,4 +1,3 @@ -import HCaptcha from '@hcaptcha/react-hcaptcha' import debouncePromise from 'awesome-debounce-promise' import { FC, RefObject, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { Controller, useForm } from 'react-hook-form' @@ -9,7 +8,6 @@ import { TextButton } from '@/components/_buttons/Button' import { FormField } from '@/components/_inputs/FormField' import { Input } from '@/components/_inputs/Input' import { ImageCropModal, ImageCropModalImperativeHandle } from '@/components/_overlays/ImageCropModal' -import { atlasConfig } from '@/config' import { MEMBERSHIP_NAME_PATTERN } from '@/config/regex' import { MemberFormData } from '@/hooks/useCreateMember' import { useUniqueMemberHandle } from '@/hooks/useUniqueMemberHandle' @@ -28,7 +26,6 @@ export const SignUpMembershipStep: FC = ({ setPrimaryButtonProps, onSubmit, hasNavigatedBack, - dialogContentRef, avatar, handle, }) => { @@ -53,12 +50,9 @@ export const SignUpMembershipStep: FC = ({ shallow ) const handleInputRef = useRef(null) - const captchaInputRef = useRef(null) const avatarDialogRef = useRef(null) const [isHandleValidating, setIsHandleValidating] = useState(false) - // used to scroll the form to the bottom upon first handle field focus - this is done to let the user see Captcha form field - const hasDoneInitialScroll = useRef(false) const { checkIfMemberIsAvailable } = useUniqueMemberHandle() @@ -109,11 +103,7 @@ export const SignUpMembershipStep: FC = ({ if (errors.handle) { handleInputRef.current?.scrollIntoView({ behavior: 'smooth' }) } - - if (errors.captchaToken) { - captchaInputRef.current?.scrollIntoView({ behavior: 'smooth' }) - } - }, [errors.captchaToken, errors.handle]) + }, [errors.handle]) return ( = ({ error={!!errors.handle} processing={isHandleValidating || isSubmitting} autoComplete="off" - onClick={() => { - if (hasDoneInitialScroll.current || !dialogContentRef?.current) return - hasDoneInitialScroll.current = true - dialogContentRef.current.scrollTo({ top: dialogContentRef.current.scrollHeight, behavior: 'smooth' }) - }} /> - {atlasConfig.features.members.hcaptchaSiteKey && ( - ( - - { - onChange(token) - trigger('captchaToken') - }} - /> - - )} - rules={{ - required: { - value: !!atlasConfig.features.members.hcaptchaSiteKey, - message: "Verify that you're not a robot.", - }, - }} - /> - )} } /> diff --git a/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpPasswordStep/SignUpPasswordStep.tsx b/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpPasswordStep/SignUpPasswordStep.tsx index 6e1da28c23..f4cf86e3cc 100644 --- a/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpPasswordStep/SignUpPasswordStep.tsx +++ b/packages/atlas/src/components/_auth/SignUpModal/SignUpSteps/SignUpPasswordStep/SignUpPasswordStep.tsx @@ -1,11 +1,13 @@ +import HCaptcha from '@hcaptcha/react-hcaptcha' import { zodResolver } from '@hookform/resolvers/zod' import { FC, RefObject, useCallback, useEffect, useRef } from 'react' -import { FormProvider, useForm } from 'react-hook-form' +import { Controller, FormProvider, useForm } from 'react-hook-form' import { AuthenticationModalStepTemplate } from '@/components/_auth/AuthenticationModalStepTemplate' import { PasswordCriterias } from '@/components/_auth/PasswordCriterias' import { FormField } from '@/components/_inputs/FormField' import { Input } from '@/components/_inputs/Input' +import { atlasConfig } from '@/config' import { AccountFormData } from '@/hooks/useCreateMember' import { useHidePasswordInInput } from '@/hooks/useHidePasswordInInput' import { passwordAndRepeatPasswordSchema } from '@/utils/formValidationOptions' @@ -16,6 +18,7 @@ import { SignUpStepsCommonProps } from '../SignUpSteps.types' type PasswordStepForm = { password: string confirmPassword: string + captchaToken?: string } type SignUpPasswordStepProps = { @@ -34,6 +37,7 @@ export const SignUpPasswordStep: FC = ({ }) => { const form = useForm({ shouldFocusError: true, + reValidateMode: 'onSubmit', defaultValues: { password, confirmPassword: password, @@ -44,11 +48,17 @@ export const SignUpPasswordStep: FC = ({ handleSubmit, register, formState: { errors }, + control, + trigger, } = form const [hidePasswordProps] = useHidePasswordInInput() const [hideConfirmPasswordProps] = useHidePasswordInInput() + const captchaRef = useRef(null) + const captchaInputRef = useRef(null) + const handleGoToNextStep = useCallback(() => { + captchaRef.current?.resetCaptcha() handleSubmit((data) => { onPasswordSubmit(data.password) })() @@ -61,7 +71,13 @@ export const SignUpPasswordStep: FC = ({ }) }, [handleGoToNextStep, setPrimaryButtonProps]) - // used to scroll the form to the bottom upon first handle field focus - this is done to let the user see password requirements + useEffect(() => { + if (errors.captchaToken) { + captchaInputRef.current?.scrollIntoView({ behavior: 'smooth' }) + } + }, [errors.captchaToken]) + + // used to scroll the form to the bottom upon first handle field focus - this is done to let the user see password requirements & captcha const hasDoneInitialScroll = useRef(false) return ( @@ -96,6 +112,27 @@ export const SignUpPasswordStep: FC = ({ /> + {atlasConfig.features.members.hcaptchaSiteKey && ( + ( + + { + onChange(token) + trigger('captchaToken') + }} + /> + + )} + /> + )} diff --git a/packages/atlas/src/components/_inputs/MultiFileSelect/MultiFileSelect.tsx b/packages/atlas/src/components/_inputs/MultiFileSelect/MultiFileSelect.tsx index e4d36660f1..debb9b328e 100644 --- a/packages/atlas/src/components/_inputs/MultiFileSelect/MultiFileSelect.tsx +++ b/packages/atlas/src/components/_inputs/MultiFileSelect/MultiFileSelect.tsx @@ -195,7 +195,7 @@ export const MultiFileSelect: FC = memo( } else if (errorCode === 'file-too-large') { setError('File too large') } else { - SentryLogger.error('Unknown file select error', 'MultiFileSelect', null, { error: { code: errorCode } }) + SentryLogger.error('Unknown file select error', 'MultiFileSelect', null, { parsedError: { code: errorCode } }) setError('Unknown error') } }, []) diff --git a/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.styles.ts b/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.styles.ts index 74814feb51..b8fd96b23e 100644 --- a/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.styles.ts +++ b/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.styles.ts @@ -1,6 +1,5 @@ import styled from '@emotion/styled' -import { ActionBar } from '@/components/ActionBar' import { cVar, zIndex } from '@/styles' export const DrawerOverlay = styled.div` @@ -43,7 +42,7 @@ export const Container = styled.div` top: var(--size-topbar-height); left: 0; right: 0; - height: calc(100vh - var(--size-topbar-height)); + height: calc(100% - var(--size-topbar-height)); display: flex; flex-direction: column; background-color: ${cVar('colorBackground')}; @@ -75,20 +74,15 @@ export const Container = styled.div` } ` -type ScrollContainerProps = { - actionBarHeight?: number - fixedScrollbar?: boolean -} -export const ScrollContainer = styled.div` +export const Outer = styled.div` + position: relative; flex: 1; - margin-bottom: ${({ actionBarHeight = 0 }) => actionBarHeight}px; - overflow-y: ${({ fixedScrollbar }) => (fixedScrollbar ? 'scroll' : 'auto')}; - overflow-x: hidden; + width: 100%; ` -export const StyledActionBar = styled(ActionBar)` - position: fixed; - bottom: 0; - right: 0; - left: 0; +export const Inner = styled.div<{ fixedScrollbar?: boolean }>` + position: absolute; + inset: 0; + overflow-x: hidden; + overflow-y: ${({ fixedScrollbar }) => (fixedScrollbar ? 'scroll' : 'auto')}; ` diff --git a/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.tsx b/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.tsx index a275246554..1bb57eea5e 100644 --- a/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.tsx +++ b/packages/atlas/src/components/_overlays/BottomDrawer/BottomDrawer.tsx @@ -1,14 +1,13 @@ import { FC, PropsWithChildren, useEffect, useState } from 'react' import { CSSTransition } from 'react-transition-group' -import useResizeObserver from 'use-resize-observer' -import { ActionBarProps } from '@/components/ActionBar' +import { ActionBar, ActionBarProps } from '@/components/ActionBar' import { DrawerHeader } from '@/components/DrawerHeader' import { useHeadTags } from '@/hooks/useHeadTags' import { useOverlayManager } from '@/providers/overlayManager' import { cVar } from '@/styles' -import { Container, DrawerOverlay, ScrollContainer, StyledActionBar } from './BottomDrawer.styles' +import { Container, DrawerOverlay, Inner, Outer } from './BottomDrawer.styles' export type BottomDrawerProps = PropsWithChildren<{ isOpen: boolean @@ -36,10 +35,6 @@ export const BottomDrawer: FC = ({ const { lastOverlayId, decrementOverlaysOpenCount, incrementOverlaysOpenCount } = useOverlayManager() const [overlayId, setOverlayId] = useState(null) - const actionBarActive = actionBar?.isActive ?? true - const { ref: actionBarRef, height: _actionBarHeight } = useResizeObserver({ box: 'border-box' }) - const actionBarHeight = actionBarActive ? _actionBarHeight : 0 - useEffect(() => { if (isOpen === cachedIsOpen) return @@ -92,14 +87,13 @@ export const BottomDrawer: FC = ({ > - - {children} - - {actionBar ? : null} + + + {children} + + + + {actionBar ? : null} diff --git a/packages/atlas/src/hooks/useCreateMember.ts b/packages/atlas/src/hooks/useCreateMember.ts index 82e5c26db2..075e94b535 100644 --- a/packages/atlas/src/hooks/useCreateMember.ts +++ b/packages/atlas/src/hooks/useCreateMember.ts @@ -146,7 +146,9 @@ export const useCreateMember = () => { const errorCode = isAxiosError(error) ? error.response?.data?.error : null - SentryLogger.error('Failed to create a membership', 'SignUpModal', error, { error: { errorCode } }) + SentryLogger.error(`Failed to create a membership ${ytResponseData ? 'YPP' : ''}`, 'SignUpModal', error, { + parsed: { errorCode }, + }) switch (errorCode) { case 'TooManyRequestsPerIp': diff --git a/packages/atlas/src/hooks/useGetAssetUrl.ts b/packages/atlas/src/hooks/useGetAssetUrl.ts index 5fca22b8e2..0c19fa4053 100644 --- a/packages/atlas/src/hooks/useGetAssetUrl.ts +++ b/packages/atlas/src/hooks/useGetAssetUrl.ts @@ -39,10 +39,19 @@ export const getSingleAssetUrl = async ( return distributionAssetUrl } catch (err) { if (err instanceof MediaError) { - const codec = getVideoCodec(distributionAssetUrl) - UserEventsLogger.logWrongCodecEvent(eventEntry, { codec }) + let codec = '' + if (type === 'video') { + codec = getVideoCodec(distributionAssetUrl) + } + UserEventsLogger.logWrongCodecEvent(eventEntry, { assetType: type, ...(type === 'video' ? { codec } : {}) }) SentryLogger.error('Error during asset download test, media is not supported', 'AssetsManager', err, { - asset: { parent, distributionAssetUrl, mediaError: err, codec }, + asset: { + parent, + distributionAssetUrl, + mediaError: err, + assetType: type, + ...(type === 'video' ? { codec } : {}), + }, }) } } diff --git a/packages/atlas/src/utils/formValidationOptions.ts b/packages/atlas/src/utils/formValidationOptions.ts index 852b676ff9..6f72946657 100644 --- a/packages/atlas/src/utils/formValidationOptions.ts +++ b/packages/atlas/src/utils/formValidationOptions.ts @@ -2,6 +2,8 @@ import { isValid } from 'date-fns' import { RegisterOptions, Validate } from 'react-hook-form' import { z } from 'zod' +import { atlasConfig } from '@/config' + type TextValidationArgs = { name: string maxLength: number @@ -67,6 +69,7 @@ export const passwordAndRepeatPasswordSchema = z .min(9, { message: 'Password has to meet requirements.' }) .max(64, { message: 'Password has to meet requirements.' }), confirmPassword: z.string(), + captchaToken: z.string().optional(), }) .refine( (data) => { @@ -85,3 +88,7 @@ export const passwordAndRepeatPasswordSchema = z message: 'Password has to meet requirements.', } ) + .refine((data) => (atlasConfig.features.members.hcaptchaSiteKey ? !!data.captchaToken : true), { + path: ['captchaToken'], + message: "Verify that you're not a robot.", + }) diff --git a/packages/atlas/src/utils/logs/asset.ts b/packages/atlas/src/utils/logs/asset.ts index 18af9eb36f..b7bb4b3cb7 100644 --- a/packages/atlas/src/utils/logs/asset.ts +++ b/packages/atlas/src/utils/logs/asset.ts @@ -51,6 +51,7 @@ export type DistributorEventEntry = { } & DistributorEventDetails type CodecInfo = { + assetType: string | null codec?: string } diff --git a/packages/atlas/src/utils/logs/sentry.ts b/packages/atlas/src/utils/logs/sentry.ts index 41c286acdc..e788d137bc 100644 --- a/packages/atlas/src/utils/logs/sentry.ts +++ b/packages/atlas/src/utils/logs/sentry.ts @@ -104,8 +104,8 @@ class _SentryLogger { Sentry.captureException(new SentryError(title, message), { contexts: { - error, ...contexts, + error, }, tags, user: { ...this.user, ip_address: '{{auto}}' },