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-3527] Add native artist previews #6989

Merged
merged 4 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
7 changes: 2 additions & 5 deletions packages/mobile/src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
SafeAreaProvider,
initialWindowMetrics
} from 'react-native-safe-area-context'
import TrackPlayer from 'react-native-track-player'
import { Provider } from 'react-redux'
import { useEffectOnce } from 'react-use'
import { PersistGate } from 'redux-persist/integration/react'
import FlipperAsyncStorage from 'rn-flipper-async-storage-advanced'

import { Audio } from 'app/components/audio/Audio'
import HCaptcha from 'app/components/hcaptcha'
import NavigationContainer from 'app/components/navigation-container'
import { NotificationReminder } from 'app/components/notification-reminder/NotificationReminder'
Expand Down Expand Up @@ -70,10 +70,8 @@ const App = () => {
// Reset libs so that we get a clean app start
useEffectOnce(() => {
setLibs(null)
})

useEffectOnce(() => {
subscribeToNetworkStatusUpdates()
TrackPlayer.setupPlayer()
})

useEnterForeground(() => {
Expand Down Expand Up @@ -110,7 +108,6 @@ const App = () => {
<RootScreen />
<Drawers />
<Modals />
<Audio />
<OAuth />
<NotificationReminder />
<RateCtaReminder />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ type QueueableTrack = {
track: Nullable<Track>
} & Pick<Queueable, 'isPreview'>

export const Audio = () => {
export const AudioPlayer = () => {
const { isEnabled: isNewPodcastControlsEnabled } = useFeatureFlag(
FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED,
FeatureFlags.PODCAST_CONTROL_UPDATES_ENABLED_FALLBACK
Expand Down Expand Up @@ -282,7 +282,6 @@ export const Audio = () => {
// Perform initial setup for the track player
useAsync(async () => {
try {
await TrackPlayer.setupPlayer()
await updatePlayerOptions()
} catch (e) {
// The player has already been set up
Expand Down
2 changes: 1 addition & 1 deletion packages/mobile/src/components/core/UserCoverPhoto.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useCoverPhoto } from '../image/CoverPhoto'

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

export const UserCoverPhoto = (props: UserCoverPhotoProps) => {
const { userId, ...other } = props
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export const FollowButton = (props: FollowButtonProps) => {
const { disabled } = other
const [following, setFollowing] = useState(isFollowing)
const { color, cornerRadius } = useTheme()
const isInput = !!onChange
Copy link
Contributor

Choose a reason for hiding this comment

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

Clever little add


useEffect(() => {
setFollowing(isFollowing)
Expand All @@ -47,8 +48,17 @@ export const FollowButton = (props: FollowButtonProps) => {
setFollowing(!following)
}, [following, onChange, value, onUnfollow, onFollow])

const inputProps = isInput
? {
testID: `follow-${value}`,
accessibilityRole: 'checkbox' as const,
accessibilityState: { checked: following, value },
accessibilityLiveRegion: 'polite' as const
}
: null

return (
<Pressable onPress={handlePress} {...other}>
<Pressable onPress={handlePress} {...other} {...inputProps}>
<Flex
h={size === 'small' ? 28 : 32}
direction='row'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,4 @@ export type FollowButtonProps = {
onUnfollow?: () => void
value?: any
onChange?: (e: ChangeEvent<HTMLInputElement>) => void
// onChange?: (e: { target: { value: any; checked: boolean } }) => void
} & Pick<PressableProps, 'onPress' | 'disabled'>
3 changes: 3 additions & 0 deletions packages/mobile/src/harmony-native/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,3 +230,6 @@ export { default as IconLogoCircleSOL } from '@audius/harmony/src/assets/icons/L
export { default as IconLogoCircleSTR } from '@audius/harmony/src/assets/icons/LogoCircleSTR.svg'
export { default as IconLogoCircleUSD } from '@audius/harmony/src/assets/icons/LogoCircleUSD.svg'
export { default as IconLogoCircleUSDC } from '@audius/harmony/src/assets/icons/LogoCircleUSDC.svg'

export { default as Soundwave } from '@audius/harmony/src/assets/animations/Soundwave.json'
export { default as SoundwaveCircle } from '@audius/harmony/src/assets/animations/SoundwaveCircle.json'
27 changes: 17 additions & 10 deletions packages/mobile/src/screens/app-drawer-screen/AppDrawerScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import type { DrawerNavigationHelpers } from '@react-navigation/drawer/lib/types
import { useNavigation } from '@react-navigation/native'
import { Dimensions } from 'react-native'

import { AudioPlayer } from 'app/components/audio/AudioPlayer'

import { AppScreen } from '../app-screen'

import { AppDrawerContextProvider } from './AppDrawerContext'
Expand Down Expand Up @@ -60,15 +62,20 @@ export const AppDrawerScreen = () => {
const gestureProps = { gesturesDisabled, setGesturesDisabled }

return (
<Drawer.Navigator
// legacy implementation uses reanimated-v1
useLegacyImplementation
screenOptions={drawerScreenOptions}
drawerContent={(props) => <LeftNavDrawer {...gestureProps} {...props} />}
>
<Drawer.Screen name='App'>
{(props) => <AppStack {...props} {...gestureProps} />}
</Drawer.Screen>
</Drawer.Navigator>
<>
<AudioPlayer />
<Drawer.Navigator
// legacy implementation uses reanimated-v1
useLegacyImplementation
screenOptions={drawerScreenOptions}
drawerContent={(props) => (
<LeftNavDrawer {...gestureProps} {...props} />
)}
>
<Drawer.Screen name='App'>
{(props) => <AppStack {...props} {...gestureProps} />}
</Drawer.Screen>
</Drawer.Navigator>
</>
)
}
5 changes: 4 additions & 1 deletion packages/mobile/src/screens/sign-on-screen/SignOnStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ const screenOptionsOverrides = { animationTypeForReplace: 'pop' as const }
export const SignOnStack = () => {
const screenOptions = useScreenOptions(screenOptionsOverrides)
return (
<Stack.Navigator initialRouteName='SignOn' screenOptions={screenOptions}>
<Stack.Navigator
initialRouteName='SelectArtists'
Copy link
Contributor

Choose a reason for hiding this comment

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

oop

screenOptions={screenOptions}
>
<Stack.Screen
name='SignOn'
component={SignOnScreen}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { useCallback, useContext } from 'react'

import type { UserMetadata } from '@audius/common'
import { css } from '@emotion/native'
import { useField } from 'formik'
import LottieView from 'lottie-react-native'
import { Pressable } from 'react-native'
import RadialGradient from 'react-native-radial-gradient'

import type { Icon } from '@audius/harmony-native'
import {
Box,
Flex,
FollowButton,
IconNote,
IconPause,
IconPlay,
IconUser,
Paper,
SoundwaveCircle,
Text,
useTheme
} from '@audius/harmony-native'
Expand All @@ -20,33 +29,109 @@ import {
} from 'app/components/core'
import { StaticSkeleton } from 'app/components/skeleton'

import { SelectArtistsPreviewContext } from './selectArtistPreviewContext'

type FollowArtistFieldProps = {
artist: UserMetadata
showPreviewHint?: boolean
}

export const FollowArtistField = (props: FollowArtistFieldProps) => {
const { artist } = props
const { artist, showPreviewHint } = props
const { user_id, track_count, follower_count } = artist
const { spacing } = useTheme()
const [{ onChange }] = useField({ name: 'selectedArtists', type: 'checkbox' })
const {
hasPlayed,
isPlaying,
nowPlayingArtistId,
togglePreview,
playPreview
} = useContext(SelectArtistsPreviewContext)

const isPreviewing = nowPlayingArtistId === user_id

const handlePress = useCallback(() => {
if (isPreviewing) {
togglePreview()
} else {
playPreview(user_id)
}
}, [isPreviewing, playPreview, togglePreview, user_id])

// The play/pause icon over the user avatar
const renderPreviewElement = () => {
let PreviewIcon: Icon | null = null

if (showPreviewHint && !hasPlayed) {
PreviewIcon = IconPlay
} else if (isPreviewing && isPlaying) {
PreviewIcon = IconPause
} else if (isPreviewing && !isPlaying) {
PreviewIcon = IconPlay
}

if (!PreviewIcon) return null

return (
<RadialGradient
style={css({
width: '100%',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
opacity: 0.73
})}
colors={['rgba(0, 0, 0, 0.50)', 'rgba(0, 0, 0, 0.10)']}
stops={[0, 1]}
center={[0.5, 0.5]}
radius={0.5}
>
<PreviewIcon size='l' color='staticWhite' shadow='drop' />
</RadialGradient>
)
}

return (
<Paper>
<UserCoverPhoto
userId={user_id}
style={css({ height: 68 })}
topCornerRadius='m'
/>
<Pressable onPress={handlePress}>
<UserCoverPhoto
userId={user_id}
style={css({ height: 68 })}
topCornerRadius='m'
>
{isPreviewing && isPlaying ? (
<Box
h='xl'
w='xl'
style={css({
opacity: 0.6,
position: 'absolute',
right: spacing.s,
top: spacing.s
})}
>
<LottieView source={SoundwaveCircle} autoPlay />
</Box>
) : null}
</UserCoverPhoto>
</Pressable>
<Flex
alignItems='center'
pointerEvents='box-none'
style={css({
position: 'absolute',
top: spacing['2xl'],
left: 0,
right: 0
right: 0,
zIndex: 1
})}
>
<ProfilePicture size='large' userId={user_id} variant='strong' />
<Pressable onPress={handlePress}>
<ProfilePicture size='large' userId={user_id} variant='strong'>
{renderPreviewElement()}
</ProfilePicture>
</Pressable>
</Flex>
<Flex pt='unit12' ph='s' pb='l' alignItems='center' gap='l'>
<Flex gap='s'>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { useState } from 'react'

import { IconCloseAlt, IconPlay, Paper, Text } from '@audius/harmony-native'

const messages = {
previewNotice: "Press the artist's photo to preview their music."
}

export const PreviewArtistHint = () => {
const [isOpen, setIsOpen] = useState(true)

if (!isOpen) return null

return (
<Paper
backgroundColor='accent'
mh='l'
ph='l'
pv='s'
mb='xl'
gap='s'
alignItems='center'
justifyContent='space-between'
direction='row'
>
<IconPlay color='staticWhite' size='m' />
<Text variant='body' color='staticWhite' style={{ flex: 1 }}>
{messages.previewNotice}
</Text>
<IconCloseAlt
role='button'
color='staticWhite'
size='m'
onPress={() => setIsOpen(false)}
/>
</Paper>
)
}
Loading