Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[C-3447] Add select-artist native screen #6955

Merged
merged 4 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/common/src/audius-query/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './AudiusQueryContext'
export * from './createApi'
export * from './hooks'
export * from './types'
9 changes: 9 additions & 0 deletions packages/common/src/messages/sign-on/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,12 @@ export const selectGenresPageMessages = {
description: 'Start by picking some of your favorite genres.',
continue: 'Continue'
}

export const selectArtstsPageMessages = {
header: 'Follow At Least 3 Artists',
description:
'Curate your feed with tracks uploaded or reposted by anyone you follow. Click the artist’s photo to preview their music.',
genresLabel: 'Genre',
pickArtists: (genre: string) => `Pick ${genre} Artists`,
selected: 'Selected'
}
1 change: 1 addition & 0 deletions packages/common/src/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './sign-on/passwordSchema'
export * from './sign-on/pickHandleSchema'
export * from './sign-on/finishProfileSchema'
export * from './sign-on/selectGenresSchema'
export * from './sign-on/selectArtistsSchema'
5 changes: 5 additions & 0 deletions packages/common/src/schemas/sign-on/selectArtistsSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { z } from 'zod'

export const selectArtistsSchema = z.object({
selectedArtists: z.array(z.string()).min(3)
})
3 changes: 2 additions & 1 deletion packages/harmony/src/components/avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ export const Avatar = (props: AvatarProps) => {
const sizeMap = {
auto: '100%',
small: '24px',
large: '40px',
medium: '40px',
large: '72px',
xl: '80px'
}

Expand Down
2 changes: 1 addition & 1 deletion packages/harmony/src/components/avatar/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export type AvatarProps = PropsWithChildren<{
* Size
* @default auto
*/
size?: 'auto' | 'small' | 'large' | 'xl'
size?: 'auto' | 'small' | 'medium' | 'large' | 'xl'

/**
* Stroke Width
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import { useSelector } from 'react-redux'

import { makeStyles } from 'app/styles'

import { IconAudioBadge } from '../core/IconAudioBadge'
import { AppDrawer } from '../drawer/AppDrawer'

import { IconAudioBadge } from './IconAudioBadge'
import { TierText } from './TierText'
const { getProfileUserId } = profilePageSelectors

Expand Down
2 changes: 1 addition & 1 deletion packages/mobile/src/components/audio-rewards/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './IconAudioBadge'
export * from '../core/IconAudioBadge'
export * from './TiersExplainerDrawer'
export * from './RewardsBanner'
export * from './TierText'
8 changes: 2 additions & 6 deletions packages/mobile/src/components/core/CardList.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import type { ComponentType } from 'react'
import { useMemo, useCallback, useRef } from 'react'

import type {
FlatListProps,
ListRenderItem,
ListRenderItemInfo
} from 'react-native'
import type { ListRenderItem, ListRenderItemInfo } from 'react-native'
import { View } from 'react-native'

import { useScrollToTop } from 'app/hooks/useScrollToTop'
import { makeStyles } from 'app/styles'

import type { FlatListT } from './FlatList'
import type { FlatListT, FlatListProps } from './FlatList'
import { FlatList } from './FlatList'

export type CardListProps<ItemT> = Omit<FlatListProps<ItemT>, 'data'> & {
Expand Down
24 changes: 17 additions & 7 deletions packages/mobile/src/components/core/FlatList.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import type { MutableRefObject, Ref } from 'react'
import { forwardRef, useContext, useRef } from 'react'

import type { FlatListProps, FlatList as RNFlatList } from 'react-native'
import type {
FlatListProps as RNFlatListProps,
FlatList as RNFlatList
} from 'react-native'
import { Animated, Platform, RefreshControl, View } from 'react-native'
import { useCollapsibleScene } from 'react-native-collapsible-tab-view'

Expand All @@ -17,7 +20,7 @@ export type AnimatedFlatListT<ItemT> = Animated.FlatList<ItemT>

type CollapsibleFlatListProps<ItemT> = {
sceneName: string
} & Animated.AnimatedProps<FlatListProps<ItemT>>
} & Animated.AnimatedProps<RNFlatListProps<ItemT>>

function CollapsibleFlatList<ItemT>(props: CollapsibleFlatListProps<ItemT>) {
const { sceneName, onScroll, ...other } = props
Expand Down Expand Up @@ -48,7 +51,7 @@ function CollapsibleFlatList<ItemT>(props: CollapsibleFlatListProps<ItemT>) {
}

const AnimatedFlatList = forwardRef(function AnimatedFlatList<ItemT>(
props: Animated.AnimatedProps<FlatListProps<ItemT>>,
props: Animated.AnimatedProps<RNFlatListProps<ItemT>>,
ref: MutableRefObject<Animated.FlatList<ItemT> | null>
) {
const { refreshing, onRefresh, onScroll, ...other } = props
Expand Down Expand Up @@ -102,6 +105,10 @@ const AnimatedFlatList = forwardRef(function AnimatedFlatList<ItemT>(
)
})

export type FlatListProps<ItemT> = RNFlatListProps<ItemT> & {
sceneName?: string
}

/**
* Provides either a FlatList or an animated FlatList
* depending on whether or not the list is found in a "collapsible" header tab
Expand All @@ -110,8 +117,11 @@ export const FlatList = forwardRef(function FlatList<ItemT>(
props: FlatListProps<ItemT>,
ref: Ref<FlatListT<ItemT>>
) {
const { ListFooterComponent, ...other } = props
const { sceneName } = useContext(CollapsibleTabNavigatorContext)
const { ListFooterComponent, sceneName: sceneNameProp, ...other } = props
const { sceneName: sceneNameContext } = useContext(
CollapsibleTabNavigatorContext
)
const sceneName = sceneNameProp ?? sceneNameContext
const FooterComponent = ListFooterComponent ? (
<>
{ListFooterComponent}
Expand All @@ -130,14 +140,14 @@ export const FlatList = forwardRef(function FlatList<ItemT>(
return (
<CollapsibleFlatList
sceneName={sceneName}
{...(flatListProps as Animated.AnimatedProps<FlatListProps<ItemT>>)}
{...(flatListProps as Animated.AnimatedProps<RNFlatListProps<ItemT>>)}
/>
)
}
return (
<AnimatedFlatList
ref={ref as Ref<AnimatedFlatListT<ItemT>>}
{...(flatListProps as Animated.AnimatedProps<FlatListProps<ItemT>>)}
{...(flatListProps as Animated.AnimatedProps<RNFlatListProps<ItemT>>)}
/>
)
})
36 changes: 36 additions & 0 deletions packages/mobile/src/components/core/ProfilePicture.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { SquareSizes, type ID, cacheUsersSelectors } from '@audius/common'
import { useSelector } from 'react-redux'

import type { AvatarProps } from '@audius/harmony-native'
import { Avatar } from '@audius/harmony-native'

import { useProfilePicture } from '../image/UserImage'
const { getUser } = cacheUsersSelectors

type ProfilePictureProps = Omit<
AvatarProps,
'source' | 'accessibilityLabel'
> & {
userId: ID
}

export const ProfilePicture = (props: ProfilePictureProps) => {
const { userId, ...other } = props

const userName = useSelector((state) => getUser(state, { id: userId })?.name)
const accessibilityLabel = `Profile picture for ${userName}`

const { source, handleError } = useProfilePicture(
userId,
SquareSizes.SIZE_150_BY_150
)

return (
<Avatar
source={source}
onError={handleError}
accessibilityLabel={accessibilityLabel}
{...other}
/>
)
}
25 changes: 25 additions & 0 deletions packages/mobile/src/components/core/UserCoverPhoto.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import type { ID } from '@audius/common'

import type { CoverPhotoProps } from '@audius/harmony-native'
import { CoverPhoto } from '@audius/harmony-native'

import { useCoverPhoto } from '../image/CoverPhoto'

type UserCoverPhotoProps = {
userId: ID
} & Pick<CoverPhotoProps, 'style' | 'topCornerRadius'>

export const UserCoverPhoto = (props: UserCoverPhotoProps) => {
const { userId, ...other } = props

const { source, handleError, shouldBlur } = useCoverPhoto(userId)

return (
<CoverPhoto
coverPhoto={shouldBlur ? undefined : source}
profilePicture={shouldBlur ? source : undefined}
onError={handleError}
{...other}
/>
)
}
42 changes: 42 additions & 0 deletions packages/mobile/src/components/core/UserDisplayName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { useSelectTierInfo, type ID, cacheUsersSelectors } from '@audius/common'
import { useSelector } from 'react-redux'

import type { TextProps } from '@audius/harmony-native'
import {
Flex,
IconVerified,
Text,
useTheme,
variantStylesMap
} from '@audius/harmony-native'

import { IconAudioBadge } from './IconAudioBadge'

const { getUser } = cacheUsersSelectors

type UserDisplayProps = TextProps & {
userId: ID
}

export const UserDisplayName = (props: UserDisplayProps) => {
const { userId, variant = 'title', size = 's', style, ...other } = props
const { tier, isVerified } = useSelectTierInfo(userId)
const displayName = useSelector(
(state) => getUser(state, { id: userId })?.name
)
const { typography } = useTheme()
const fontSize = typography.size[variantStylesMap[variant].fontSize[size]]
const badgeSize = fontSize - 2

return (
<Flex direction='row' gap='xs' alignItems='center' style={style}>
<Text variant={variant} size={size} {...other}>
{displayName}
</Text>
{isVerified ? (
<IconVerified height={badgeSize} width={badgeSize} />
) : null}
<IconAudioBadge tier={tier} height={fontSize} width={fontSize} />
</Flex>
)
}
3 changes: 3 additions & 0 deletions packages/mobile/src/components/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ export * from './InputErrorMessage'
export * from './DogEar'
export * from './LockedStatusBadge'
export * from './ModalScreen'
export * from './ProfilePicture'
export * from './UserCoverPhoto'
export * from './UserDisplayName'
4 changes: 4 additions & 0 deletions packages/mobile/src/components/user/ProfilePicture.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export type ProfilePictureProps = Partial<FastImageProps> &
}
)

/**
* @deprecated
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💯

* Use image/ProfilePicture instead
*/
export const ProfilePicture = (props: ProfilePictureProps) => {
const { style: styleProp, ...other } = props
const userId = 'userId' in other ? other.userId : other.profile.user_id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export type AvatarProps = Omit<HarmonyAvatarProps, 'src'> &
const sizeMap = {
auto: '100%',
small: 24,
large: 40,
medium: 40,
large: 72,
xl: 80
}

Expand All @@ -28,7 +29,7 @@ export const Avatar = (props: AvatarProps) => {
const {
children,
source,
size = 'auto',
size = 'medium',
strokeWidth = 'default',
variant = 'default',
style,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useMemo, useState } from 'react'
import type { ChangeEvent } from 'react'
import { useCallback, useEffect, useState } from 'react'

import { css } from '@emotion/native'
import { Pressable } from 'react-native'
Expand All @@ -20,6 +21,8 @@ export const FollowButton = (props: FollowButtonProps) => {
onUnfollow,
onFollow,
size = 'default',
value,
onChange,
...other
} = props
const { disabled } = other
Expand All @@ -30,18 +33,19 @@ export const FollowButton = (props: FollowButtonProps) => {
setFollowing(isFollowing)
}, [isFollowing])

const Icon = useMemo(() => {
return following ? IconUserFollowing : IconUserFollow
}, [following])
const Icon = following ? IconUserFollowing : IconUserFollow

const handlePress = useCallback(() => {
if (following) {
onUnfollow?.()
} else {
onFollow?.()
}
onChange?.({
target: { value, checked: true, type: 'checkbox' }
} as ChangeEvent<HTMLInputElement>)
setFollowing(!following)
}, [following, onUnfollow, onFollow])
}, [following, onChange, value, onUnfollow, onFollow])

return (
<Pressable onPress={handlePress} {...other}>
Expand All @@ -52,12 +56,11 @@ export const FollowButton = (props: FollowButtonProps) => {
justifyContent='center'
gap='xs'
pv='s'
border='default'
style={css({
opacity: disabled ? 0.45 : 1,
borderRadius:
variant === 'pill' ? cornerRadius['2xl'] : cornerRadius.s,
borderWidth: 1,
borderStyle: 'solid',
borderColor: color.primary.primary,
backgroundColor: following
? color.primary.primary
Expand All @@ -67,7 +70,7 @@ export const FollowButton = (props: FollowButtonProps) => {
<Icon
height={18}
width={18}
fill={following ? color.text.staticWhite : color.primary.primary}
fill={following ? color.icon.staticWhite : color.primary.primary}
/>
</Flex>
</Pressable>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ChangeEvent } from 'react'

import type { PressableProps } from 'react-native/types'

export type FollowButtonProps = {
Expand Down Expand Up @@ -27,4 +29,7 @@ export type FollowButtonProps = {
* Callback for when an unfollow is triggered.
*/
onUnfollow?: () => void
value?: any
onChange?: (e: ChangeEvent<HTMLInputElement>) => void
// onChange?: (e: { target: { value: any; checked: boolean } }) => void
} & Pick<PressableProps, 'onPress' | 'disabled'>
Loading