Skip to content

Commit

Permalink
[C-3174] Add select genre page to sign up (#6241)
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanjeffers authored Oct 5, 2023
1 parent b233a8c commit ce1c9a9
Show file tree
Hide file tree
Showing 31 changed files with 491 additions and 106 deletions.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions packages/mobile/e2e/matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ export function byRole(role: Role, options: ByRoleOptions) {
return labelOnly
? element(by.traits(['button']).and(by.label(name)))
: element(by.traits(['button']).withDescendant(by.label(name)))
case 'checkbox':
return element(by.id(name))
case 'radio':
return element(by.id(name))
default:
return element(by.traits([role]).and(by.label(name)))
}
Expand Down
37 changes: 36 additions & 1 deletion packages/mobile/e2e/signUp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ describe('Sign up', () => {
await assertOnSignUp()
})

it.only('should create an account', async () => {
it('should create an account', async () => {
const testUser = generateTestUser()
const { email, password, handle, name } = testUser

await byRole('textbox', { name: /email/i }).typeText(email)
await byRole('button', { name: /sign up free/i }).tap()

Expand Down Expand Up @@ -95,5 +96,39 @@ describe('Sign up', () => {

await byRole('textbox', { name: /display name/i }).typeText(name)
await byRole('button', { name: /continue/i }).tap()

await expect(
byRole('heading', { name: /select your genres/i })
).toBeVisible()
await expect(
byText(/start by picking some of your favorite genres./i)
).toBeVisible()

const genres = [/^acoustic/i, /^pop/i, /^lo-fi/i, /^electronic/i]

for (const genre of genres) {
await byRole('checkbox', { name: genre }).tap()
await expect(byRole('checkbox', { name: genre })).toHaveValue(
'checkbox, checked'
)
}

await element(by.id('genreScrollView')).scrollTo('bottom')
await byRole('button', { name: /continue/i }).tap()

await expect(
byRole('heading', { name: /follow at least 3 artists/i })
).toBeVisible()
await expect(
byText(/curate your feed with tracks uploaded .*/i)
).toBeVisible()

expect(byRole('radio', { name: /featured/i })).toHaveValue(
'radio button, checked'
)

for (const genre of genres) {
expect(byRole('radio', { name: genre })).toBeVisible()
}
})
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { artistCategories } from 'audius-client/src/common/store/pages/signon/types'
import { artistCategories } from 'common/store/pages/signon/types'
import { View } from 'react-native'
import { useSelector } from 'react-redux'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'
import { CreatePasswordScreen } from './screens/CreatePasswordScreen'
import { FinishProfileScreen } from './screens/FinishProfileScreen'
import { PickHandleScreen } from './screens/PickHandleScreen'
import { SelectArtistsScreen } from './screens/SelectArtistsScreen'
import { SelectGenreScreen } from './screens/SelectGenreScreen'
import { SignUpScreen } from './screens/SignUpScreen'

const Stack = createNativeStackNavigator()
Expand All @@ -15,6 +17,8 @@ export const SignUpRootScreen = () => {
<Stack.Screen name='CreatePassword' component={CreatePasswordScreen} />
<Stack.Screen name='PickHandle' component={PickHandleScreen} />
<Stack.Screen name='FinishProfile' component={FinishProfileScreen} />
<Stack.Screen name='SelectGenre' component={SelectGenreScreen} />
<Stack.Screen name='SelectArtists' component={SelectArtistsScreen} />
</Stack.Navigator>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback } from 'react'

import { setValueField } from 'audius-client/src/common/store/pages/signon/actions'
import { setValueField } from 'common/store/pages/signon/actions'
import { Formik } from 'formik'
import { View } from 'react-native'
import { useDispatch } from 'react-redux'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { useCallback } from 'react'

import { setValueField } from 'common/store/pages/signon/actions'
import { Formik } from 'formik'
import { View } from 'react-native'
import { useDispatch } from 'react-redux'

import { Button, Text } from 'app/components/core'
import { TextField } from 'app/components/fields'
import { useNavigation } from 'app/hooks/useNavigation'

import { CoverPhotoField } from '../components/CoverPhotoField'
import { ProfilePictureField } from '../components/ProfilePictureField'
import type { SignUpScreenParamList } from '../types'

const messages = {
header: 'Finish Your Profile',
Expand All @@ -28,7 +32,17 @@ type FinishProfileValues = {
}

export const FinishProfileScreen = () => {
const handleSubmit = useCallback((values: FinishProfileValues) => {}, [])
const navigation = useNavigation<SignUpScreenParamList>()
const dispatch = useDispatch()

const handleSubmit = useCallback(
(values: FinishProfileValues) => {
const { displayName } = values
dispatch(setValueField('name', displayName))
navigation.navigate('SelectGenre')
},
[dispatch, navigation]
)

return (
<View>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { useCallback } from 'react'

import { setValueField } from 'common/store/pages/signon/actions'
import { Formik } from 'formik'
import { View } from 'react-native'
import { useDispatch } from 'react-redux'

import { Button, Text } from 'app/components/core'
import { TextField } from 'app/components/fields'
Expand All @@ -27,12 +29,15 @@ type PickHandleValues = {

export const PickHandleScreen = () => {
const navigation = useNavigation<SignUpScreenParamList>()
const dispatch = useDispatch()

const handleSubmit = useCallback(
(values: PickHandleValues) => {
const { handle } = values
dispatch(setValueField('handle', handle))
navigation.navigate('FinishProfile')
},
[navigation]
[dispatch, navigation]
)

return (
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useState } from 'react'

import { getGenres } from 'common/store/pages/signon/selectors'
import { Pressable, View } from 'react-native'
import { useSelector } from 'react-redux'

import { Text } from 'app/components/core'

const messages = {
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.',
continue: 'Continue'
}

export const SelectArtistsScreen = () => {
const genres = useSelector((state: any) => ['Featured', ...getGenres(state)])
const [currentGenre, setCurrentGenre] = useState('Featured')

return (
<View>
<View>
<Text>{messages.header}</Text>
<Text>{messages.description}</Text>
</View>
<View accessibilityRole='radiogroup'>
{genres.map((genre) => {
const checked = genre === currentGenre
return (
<Pressable
key={genre}
testID={genre}
accessibilityRole='radio'
accessibilityState={{ checked }}
accessibilityLiveRegion='polite'
onPress={() => setCurrentGenre(genre)}
style={{ backgroundColor: checked ? 'purple' : undefined }}
>
<Text>{genre}</Text>
</Pressable>
)
})}
</View>
</View>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import { useCallback } from 'react'

import { GENRES, convertGenreLabelToValue } from '@audius/common'
import { setField } from 'common/store/pages/signon/actions'
import {
getHandleField,
getNameField
} from 'common/store/pages/signon/selectors'
import { Formik } from 'formik'
import { Pressable, ScrollView, View } from 'react-native'
import { useDispatch, useSelector } from 'react-redux'

import { Button, Text } from 'app/components/core'
import { useNavigation } from 'app/hooks/useNavigation'

import type { SignUpScreenParamList } from '../types'

const messages = {
header: 'Select Your Genres',
description: 'Start by picking some of your favorite genres.',
continue: 'Continue'
}

const genres = GENRES.map((genre) => ({
value: genre,
label: convertGenreLabelToValue(genre)
}))

type SelectGenreValues = Record<string, boolean>

const initialValues = genres.reduce(
(acc, genre) => ({
...acc,
[genre.value]: false
}),
{} as SelectGenreValues
)

export const SelectGenreScreen = () => {
const { value: displayName } = useSelector(getNameField)
const { value: handle } = useSelector(getHandleField)
const dispatch = useDispatch()
const navigation = useNavigation<SignUpScreenParamList>()

const handleSubmit = useCallback(
(values: SelectGenreValues) => {
const genres = Object.keys(values).filter((genre) => values[genre])
dispatch(setField('genres', genres))
navigation.navigate('SelectArtists')
},
[dispatch, navigation]
)

return (
<ScrollView testID='genreScrollView'>
<View>
<Text>{displayName}</Text>
<Text>{handle}</Text>
</View>
<View>
<Text>{messages.header}</Text>
<Text>{messages.description}</Text>
</View>
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
{({ values, setValues, handleSubmit }) => (
<View>
{genres.map((genre) => {
const { label, value } = genre
const checked = !!values[value]

const handleChange = () => {
setValues({
...values,
[value]: !checked
})
}

return (
<Pressable
key={value}
testID={label}
accessibilityRole='checkbox'
accessibilityState={{ checked }}
accessibilityLiveRegion='polite'
onPress={handleChange}
style={{ backgroundColor: checked ? 'purple' : undefined }}
>
<Text>{label}</Text>
</Pressable>
)
})}
<Button title={messages.continue} onPress={() => handleSubmit()} />
</View>
)}
</Formik>
</ScrollView>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useCallback } from 'react'

import { setValueField } from 'audius-client/src/common/store/pages/signon/actions'
import { setValueField } from 'common/store/pages/signon/actions'
import { Formik } from 'formik'
import { View } from 'react-native'
import { useDispatch } from 'react-redux'
Expand Down
2 changes: 2 additions & 0 deletions packages/mobile/src/screens/sign-up-screen/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ export type SignUpScreenParamList = {
CreatePassword: CreatePasswordParams
PickHandle: undefined
FinishProfile: undefined
SelectGenre: undefined
SelectArtists: undefined
}
11 changes: 5 additions & 6 deletions packages/mobile/src/screens/signon/ProfileManual.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
getProfileImageField,
getIsVerified
} from 'common/store/pages/signon/selectors'
import type { EditableField } from 'common/store/pages/signon/types'
import { EditingStatus } from 'common/store/pages/signon/types'
import {
Animated,
Expand Down Expand Up @@ -254,10 +253,10 @@ const ProfileManual = ({ navigation }: ProfileManualProps) => {
const styles = useStyles()
const dispatch = useDispatch()

const handleField: EditableField = useSelector(getHandleField)
const nameField: EditableField = useSelector(getNameField)
const emailField: EditableField = useSelector(getEmailField)
const profileImage: Image = useSelector(getProfileImageField)
const handleField = useSelector(getHandleField)
const nameField = useSelector(getNameField)
const emailField = useSelector(getEmailField)
const profileImage = useSelector(getProfileImageField)
const isVerified: boolean = useSelector(getIsVerified)

const [showHandleConfirmingSpinner, setShowHandleConfirmingSpinner] =
Expand Down Expand Up @@ -394,7 +393,7 @@ const ProfileManual = ({ navigation }: ProfileManualProps) => {
hasSelectedImage={!!profileImage}
photoBtnIsHidden={photoBtnIsHidden}
setPhotoBtnIsHidden={setPhotoBtnIsHidden}
profileImage={profileImage}
profileImage={profileImage as any}
/>
<PhotoButton
hasSelectedImage={!!profileImage}
Expand Down
8 changes: 4 additions & 4 deletions packages/mobile/src/store/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import {
} from '@audius/common'
import backend from 'audius-client/src/common/store/backend/reducer'
import type { BackendState } from 'audius-client/src/common/store/backend/types'
import signOnReducer from 'audius-client/src/common/store/pages/signon/reducer'
import searchBar from 'audius-client/src/common/store/search-bar/reducer'
import type { SearchBarState } from 'audius-client/src/common/store/search-bar/types'
import signOnReducer from 'common/store/pages/signon/reducer'
import type {
SignOnPageState,
SignOnPageReducer
} from 'audius-client/src/common/store/pages/signon/types'
import searchBar from 'audius-client/src/common/store/search-bar/reducer'
import type { SearchBarState } from 'audius-client/src/common/store/search-bar/types'
} from 'common/store/pages/signon/types'
import RNRestart from 'react-native-restart'
import type { Store } from 'redux'
import { createStore, combineReducers, applyMiddleware } from 'redux'
Expand Down
Loading

0 comments on commit ce1c9a9

Please sign in to comment.