diff --git a/packages/common/src/schemas/sign-on/finishProfileSchema.ts b/packages/common/src/schemas/sign-on/finishProfileSchema.ts
index 0f79dbb0e69..0df909a389a 100644
--- a/packages/common/src/schemas/sign-on/finishProfileSchema.ts
+++ b/packages/common/src/schemas/sign-on/finishProfileSchema.ts
@@ -15,3 +15,19 @@ export const finishProfileSchema = z.object({
})
.optional()
})
+
+export const finishReferralProfileSchema = z.object({
+ displayName: z
+ .string({ required_error: 'Display name is required.' })
+ .max(MAX_DISPLAY_NAME_LENGTH, ''),
+ profileImage: z
+ .object({
+ url: z.string().optional()
+ })
+ .optional(),
+ coverPhoto: z
+ .object({
+ url: z.string().optional()
+ })
+ .optional()
+})
diff --git a/packages/common/src/utils/route.ts b/packages/common/src/utils/route.ts
index 1fd269279cc..b21c4020e65 100644
--- a/packages/common/src/utils/route.ts
+++ b/packages/common/src/utils/route.ts
@@ -92,7 +92,8 @@ export enum SignUpPath {
selectArtists = 'select-artists',
loading = 'loading',
appCta = 'app-cta',
- completedRedirect = 'completed'
+ completedRedirect = 'completed',
+ completedReferrerRedirect = 'completed-referrer'
}
export const SIGN_UP_EMAIL_PAGE = `/signup/${SignUpPath.createEmail}`
export const SIGN_UP_START_PAGE = SIGN_UP_EMAIL_PAGE // entry point for sign up if needing to redirect to the beginning
@@ -106,6 +107,7 @@ export const SIGN_UP_ARTISTS_PAGE = `/signup/${SignUpPath.selectArtists}`
export const SIGN_UP_APP_CTA_PAGE = `/signup/${SignUpPath.appCta}`
export const SIGN_UP_LOADING_PAGE = `/signup/${SignUpPath.loading}`
export const SIGN_UP_COMPLETED_REDIRECT = `/signup/${SignUpPath.completedRedirect}`
+export const SIGN_UP_COMPLETED_REFERRER_REDIRECT = `/signup/${SignUpPath.completedReferrerRedirect}`
// Param routes.
export const NOTIFICATION_USERS_PAGE = '/notification/:notificationId/users'
diff --git a/packages/web/src/common/store/pages/signon/sagas.ts b/packages/web/src/common/store/pages/signon/sagas.ts
index 87ec6fb8b28..5650f75ee25 100644
--- a/packages/web/src/common/store/pages/signon/sagas.ts
+++ b/packages/web/src/common/store/pages/signon/sagas.ts
@@ -1014,6 +1014,7 @@ function* followArtists(
...(referrer == null ? [] : [referrer])
])
]
+
for (const userId of userIdsToFollow) {
yield* put(
socialActions.followUser(userId as number, FollowSource.SIGN_UP)
diff --git a/packages/web/src/components/track/TrackTileStats.tsx b/packages/web/src/components/track/TrackTileStats.tsx
index f0df874d7d9..1610e7c1374 100644
--- a/packages/web/src/components/track/TrackTileStats.tsx
+++ b/packages/web/src/components/track/TrackTileStats.tsx
@@ -36,7 +36,11 @@ export const TrackTileStats = (props: TrackTileStatsProps) => {
)
if (isLoading || !track) {
- return
+ return (
+
+
+
+ )
}
const { is_unlisted } = track
diff --git a/packages/web/src/components/track/mobile/PlaylistTile.tsx b/packages/web/src/components/track/mobile/PlaylistTile.tsx
index cfbb20e9108..24e1b74396a 100644
--- a/packages/web/src/components/track/mobile/PlaylistTile.tsx
+++ b/packages/web/src/components/track/mobile/PlaylistTile.tsx
@@ -304,12 +304,14 @@ const PlaylistTile = (props: PlaylistTileProps & ExtraProps) => {
-
+
+
+
{
const userName = nameField ?? accountName
const [isOpen, setIsOpen] = useModalState('Welcome')
- const profileImage = profileImageField?.url ?? presavedProfilePic
+ const profileImage =
+ profileImageField?.url ?? (presavedProfilePic || imageProfilePicEmpty)
const Root = isMobile ? Drawer : Modal
const onClose = useCallback(() => {
diff --git a/packages/web/src/pages/sign-up-page/SignUpPage.tsx b/packages/web/src/pages/sign-up-page/SignUpPage.tsx
index 90899286102..4bfa94ba8fb 100644
--- a/packages/web/src/pages/sign-up-page/SignUpPage.tsx
+++ b/packages/web/src/pages/sign-up-page/SignUpPage.tsx
@@ -18,9 +18,11 @@ import { RouteContextProvider } from './utils/RouteContext'
const {
FEED_PAGE,
+ TRENDING_PAGE,
SIGN_UP_APP_CTA_PAGE,
SIGN_UP_ARTISTS_PAGE,
SIGN_UP_COMPLETED_REDIRECT,
+ SIGN_UP_COMPLETED_REFERRER_REDIRECT: SIGN_UP_REFERRER_COMPLETED_REDIRECT,
SIGN_UP_CREATE_LOGIN_DETAILS,
SIGN_UP_EMAIL_PAGE,
SIGN_UP_FINISH_PROFILE_PAGE,
@@ -104,6 +106,9 @@ export const SignUpPage = () => {
+
+
+
diff --git a/packages/web/src/pages/sign-up-page/components/AccountHeader.tsx b/packages/web/src/pages/sign-up-page/components/AccountHeader.tsx
index 6bacaa30fb5..58b61f9f80c 100644
--- a/packages/web/src/pages/sign-up-page/components/AccountHeader.tsx
+++ b/packages/web/src/pages/sign-up-page/components/AccountHeader.tsx
@@ -1,3 +1,4 @@
+import { imageProfilePicEmpty } from '@audius/common/assets'
import { Name, SquareSizes } from '@audius/common/models'
import { accountSelectors } from '@audius/common/store'
import {
@@ -113,7 +114,8 @@ export const AccountHeader = (props: AccountHeaderProps) => {
const { isMobile } = useMedia()
const isSmallSize = isEditing || isMobile || size === 'small'
- const savedProfileImage = profileImageField?.url ?? accountProfilePic
+ const savedProfileImage =
+ profileImageField?.url || accountProfilePic || imageProfilePicEmpty
return (
diff --git a/packages/web/src/pages/sign-up-page/components/SkipButton.tsx b/packages/web/src/pages/sign-up-page/components/SkipButton.tsx
new file mode 100644
index 00000000000..c889236208d
--- /dev/null
+++ b/packages/web/src/pages/sign-up-page/components/SkipButton.tsx
@@ -0,0 +1,17 @@
+import { useCallback } from 'react'
+
+import { route } from '@audius/common/utils'
+import { PlainButton } from '@audius/harmony'
+
+import { useNavigateToPage } from 'hooks/useNavigateToPage'
+
+const { SIGN_UP_LOADING_PAGE } = route
+
+export const SkipButton = () => {
+ const navigate = useNavigateToPage()
+ const handleSkip = useCallback(() => {
+ navigate(SIGN_UP_LOADING_PAGE)
+ }, [navigate])
+
+ return Skip this step
+}
diff --git a/packages/web/src/pages/sign-up-page/pages/CreateEmailPage.tsx b/packages/web/src/pages/sign-up-page/pages/CreateEmailPage.tsx
index 2047e441d25..de934a77592 100644
--- a/packages/web/src/pages/sign-up-page/pages/CreateEmailPage.tsx
+++ b/packages/web/src/pages/sign-up-page/pages/CreateEmailPage.tsx
@@ -133,7 +133,7 @@ export const CreateEmailPage = () => {
>
{({ isSubmitting, setFieldValue, submitForm }) => (
-
+
{isMobile || isSmallDesktop ? (
) : (
diff --git a/packages/web/src/pages/sign-up-page/pages/FinishProfilePage.tsx b/packages/web/src/pages/sign-up-page/pages/FinishProfilePage.tsx
index 41c07b45db5..a2e0d0fac01 100644
--- a/packages/web/src/pages/sign-up-page/pages/FinishProfilePage.tsx
+++ b/packages/web/src/pages/sign-up-page/pages/FinishProfilePage.tsx
@@ -2,7 +2,10 @@ import { useCallback, useRef } from 'react'
import { finishProfilePageMessages } from '@audius/common/messages'
import { Name } from '@audius/common/models'
-import { finishProfileSchema } from '@audius/common/schemas'
+import {
+ finishProfileSchema,
+ finishReferralProfileSchema
+} from '@audius/common/schemas'
import { MAX_DISPLAY_NAME_LENGTH } from '@audius/common/services'
import { route } from '@audius/common/utils'
import { Flex, Paper, PlainButton, Text, useTheme } from '@audius/harmony'
@@ -25,7 +28,8 @@ import {
getIsSocialConnected,
getLinkedSocialOnFirstPage,
getNameField,
- getProfileImageField
+ getProfileImageField,
+ getReferrer
} from 'common/store/pages/signon/selectors'
import { HarmonyTextField } from 'components/form-fields/HarmonyTextField'
import { useMedia } from 'hooks/useMedia'
@@ -36,7 +40,7 @@ import { ImageFieldValue } from '../components/ImageField'
import { OutOfText } from '../components/OutOfText'
import { Heading, Page, PageFooter } from '../components/layout'
-const { SIGN_UP_GENRES_PAGE } = route
+const { SIGN_UP_GENRES_PAGE, SIGN_UP_LOADING_PAGE } = route
export type FinishProfileValues = {
profileImage?: ImageFieldValue
@@ -45,6 +49,7 @@ export type FinishProfileValues = {
}
const formSchema = toFormikValidationSchema(finishProfileSchema)
+const referralformSchema = toFormikValidationSchema(finishReferralProfileSchema)
const ImageUploadErrorText = () => {
const { errors } = useFormikContext()
@@ -89,6 +94,7 @@ export const FinishProfilePage = () => {
const linkedSocialOnFirstPage = useSelector(getLinkedSocialOnFirstPage)
const savedCoverPhoto = useSelector(getCoverPhotoField)
const savedProfileImage = useSelector(getProfileImageField)
+ const hasReferrer = useSelector(getReferrer)
// If the user comes back from a later page we start with whats in the store
const initialValues = {
@@ -130,17 +136,21 @@ export const FinishProfilePage = () => {
dispatch(setField('coverPhoto', coverPhoto))
}
dispatch(setFinishedPhase1(true))
- navigate(SIGN_UP_GENRES_PAGE)
dispatch(signUp())
+ if (hasReferrer && isMobile) {
+ navigate(SIGN_UP_LOADING_PAGE)
+ } else {
+ navigate(SIGN_UP_GENRES_PAGE)
+ }
},
- [navigate, dispatch]
+ [dispatch, hasReferrer, isMobile, navigate]
)
return (
@@ -188,7 +198,9 @@ export const FinishProfilePage = () => {
centered
sticky
buttonProps={{ disabled: !isValid }}
- prefix={isMobile ? : null}
+ prefix={
+ isMobile && !hasReferrer ? : null
+ }
postfix={
isMobile || isSocialConnected ? null : (
diff --git a/packages/web/src/pages/sign-up-page/pages/LoadingAccountPage.tsx b/packages/web/src/pages/sign-up-page/pages/LoadingAccountPage.tsx
index 6476aeaeaae..ad94ab79e54 100644
--- a/packages/web/src/pages/sign-up-page/pages/LoadingAccountPage.tsx
+++ b/packages/web/src/pages/sign-up-page/pages/LoadingAccountPage.tsx
@@ -4,9 +4,14 @@ import { route } from '@audius/common/utils'
import { Flex } from '@audius/harmony'
import { useSelector } from 'react-redux'
-import { getStatus } from 'common/store/pages/signon/selectors'
+import {
+ getStatus,
+ getReferrer,
+ getAccountReady
+} from 'common/store/pages/signon/selectors'
import { EditingStatus } from 'common/store/pages/signon/types'
import LoadingSpinner from 'components/loading-spinner/LoadingSpinner'
+import { useMedia } from 'hooks/useMedia'
import { useNavigateToPage } from 'hooks/useNavigateToPage'
import { Heading, Page } from '../components/layout'
@@ -22,15 +27,23 @@ const messages = {
// The user just waits here until the account is created and before being shown the welcome modal on the trending page
export const LoadingAccountPage = () => {
const navigate = useNavigateToPage()
+ const hasReferrer = useSelector(getReferrer)
+ const accountReady = useSelector(getAccountReady)
+ const { isMobile } = useMedia()
const accountCreationStatus = useSelector(getStatus)
+ const isAccountReady =
+ hasReferrer && isMobile
+ ? accountReady
+ : accountCreationStatus === EditingStatus.SUCCESS
+
useEffect(() => {
- if (accountCreationStatus === EditingStatus.SUCCESS) {
+ if (isAccountReady) {
navigate(SIGN_UP_COMPLETED_REDIRECT)
}
// TODO: what to do in an error scenario? Any way to recover to a valid step?
- }, [navigate, accountCreationStatus])
+ }, [navigate, isAccountReady])
return (
diff --git a/packages/web/src/pages/sign-up-page/pages/PickHandlePage.tsx b/packages/web/src/pages/sign-up-page/pages/PickHandlePage.tsx
index ba0a74cecb3..e8add74b6b1 100644
--- a/packages/web/src/pages/sign-up-page/pages/PickHandlePage.tsx
+++ b/packages/web/src/pages/sign-up-page/pages/PickHandlePage.tsx
@@ -20,7 +20,8 @@ import {
import {
getHandleField,
getIsSocialConnected,
- getLinkedSocialOnFirstPage
+ getLinkedSocialOnFirstPage,
+ getReferrer
} from 'common/store/pages/signon/selectors'
import { ToastContext } from 'components/toast/ToastContext'
import { useMedia } from 'hooks/useMedia'
@@ -117,18 +118,22 @@ export const PickHandlePage = () => {
const { value: handle } = useSelector(getHandleField)
const isLinkingSocialOnFirstPage = useSelector(getLinkedSocialOnFirstPage)
const handleInputRef = useRef(null)
+ const hasReferrer = useSelector(getReferrer)
const handleSubmit = useCallback(
(values: PickHandleValues) => {
const { handle } = values
dispatch(setValueField('handle', handle))
+ if (hasReferrer) {
+ dispatch(setValueField('name', handle))
+ }
navigate(
isLinkingSocialOnFirstPage
? SIGN_UP_CREATE_LOGIN_DETAILS
: SIGN_UP_FINISH_PROFILE_PAGE
)
},
- [dispatch, navigate, isLinkingSocialOnFirstPage]
+ [dispatch, hasReferrer, navigate, isLinkingSocialOnFirstPage]
)
const handleCompleteSocialMediaLogin = useCallback(
diff --git a/packages/web/src/pages/sign-up-page/pages/SelectArtistsPage.tsx b/packages/web/src/pages/sign-up-page/pages/SelectArtistsPage.tsx
index e9748ece871..1859ea42dda 100644
--- a/packages/web/src/pages/sign-up-page/pages/SelectArtistsPage.tsx
+++ b/packages/web/src/pages/sign-up-page/pages/SelectArtistsPage.tsx
@@ -20,7 +20,7 @@ import {
addFollowArtists,
completeFollowArtists
} from 'common/store/pages/signon/actions'
-import { getGenres } from 'common/store/pages/signon/selectors'
+import { getGenres, getReferrer } from 'common/store/pages/signon/selectors'
import {
FollowArtistCard,
FollowArtistTileSkeleton
@@ -33,6 +33,7 @@ import { useSelector } from 'utils/reducer'
import { AccountHeader } from '../components/AccountHeader'
import { PreviewArtistHint } from '../components/PreviewArtistHint'
+import { SkipButton } from '../components/SkipButton'
import {
Heading,
HiddenLegend,
@@ -71,6 +72,7 @@ export const SelectArtistsPage = () => {
const navigate = useNavigateToPage()
const { color } = useTheme()
const headerContainerRef = useRef(null)
+ const hasReferrer = useSelector(getReferrer)
const { isMobile } = useMedia()
const handleChangeGenre = useCallback((e: ChangeEvent) => {
@@ -262,6 +264,7 @@ export const SelectArtistsPage = () => {
disabled: !isValid || isSubmitting,
isLoading: isSubmitting || isValidating
}}
+ prefix={isMobile && hasReferrer ? : null}
postfix={
{selectArtistsPageMessages.selected}{' '}
diff --git a/packages/web/src/pages/sign-up-page/pages/SelectGenresPage.tsx b/packages/web/src/pages/sign-up-page/pages/SelectGenresPage.tsx
index 113b5cd7df7..803f445688d 100644
--- a/packages/web/src/pages/sign-up-page/pages/SelectGenresPage.tsx
+++ b/packages/web/src/pages/sign-up-page/pages/SelectGenresPage.tsx
@@ -11,12 +11,13 @@ import { toFormikValidationSchema } from 'zod-formik-adapter'
import { make } from 'common/store/analytics/actions'
import { setField } from 'common/store/pages/signon/actions'
-import { getGenres } from 'common/store/pages/signon/selectors'
+import { getGenres, getReferrer } from 'common/store/pages/signon/selectors'
import { SelectablePillField } from 'components/form-fields/SelectablePillField'
import { useMedia } from 'hooks/useMedia'
import { useNavigateToPage } from 'hooks/useNavigateToPage'
import { AccountHeader } from '../components/AccountHeader'
+import { SkipButton } from '../components/SkipButton'
import { Heading, Page, PageFooter, ScrollView } from '../components/layout'
const { SIGN_UP_ARTISTS_PAGE } = route
@@ -29,6 +30,8 @@ export const SelectGenresPage = () => {
const [currentGenres, setCurrentGenres] = useState([])
const savedGenres = useSelector(getGenres)
+ const hasReferrer = useSelector(getReferrer)
+ const { isMobile } = useMedia()
const initialValues: SelectGenresValue = {
genres: (savedGenres as Genre[]) ?? []
@@ -64,8 +67,6 @@ export const SelectGenresPage = () => {
}
}
- const { isMobile } = useMedia()
-
return (
@@ -114,7 +115,12 @@ export const SelectGenresPage = () => {
})}
-
+ : null}
+ />
)}
diff --git a/packages/web/src/pages/sign-up-page/utils/useDetermineAllowedRoutes.ts b/packages/web/src/pages/sign-up-page/utils/useDetermineAllowedRoutes.ts
index c2aad875cf0..c0f683ef5fa 100644
--- a/packages/web/src/pages/sign-up-page/utils/useDetermineAllowedRoutes.ts
+++ b/packages/web/src/pages/sign-up-page/utils/useDetermineAllowedRoutes.ts
@@ -5,9 +5,11 @@ import { useSelector } from 'react-redux'
import { useModalState } from 'common/hooks/useModalState'
import {
getAccountAlreadyExisted,
+ getReferrer,
getSignOn
} from 'common/store/pages/signon/selectors'
import { EditingStatus } from 'common/store/pages/signon/types'
+import { useMedia } from 'hooks/useMedia'
import { env } from 'services/env'
const { FEED_PAGE, SignUpPath } = route
@@ -28,6 +30,8 @@ export const useDetermineAllowedRoute = () => {
const followeeCount = useSelector(getAccountFolloweeCount)
const hasAccount = useSelector(getHasAccount)
const hasAlreadySignedUp = useSelector(getAccountAlreadyExisted)
+ const hasReferrer = useSelector(getReferrer)
+ const { isMobile } = useMedia()
const pastAccountPhase = signUpState.finishedPhase1 || hasAccount
@@ -59,6 +63,14 @@ export const useDetermineAllowedRoute = () => {
// Either way the user can't go back any more
allowedRoutes = [SignUpPath.selectGenres]
+ if (isMobile && hasReferrer) {
+ allowedRoutes.push(SignUpPath.selectArtists)
+ allowedRoutes.push(SignUpPath.appCta)
+ allowedRoutes.push(SignUpPath.completedRedirect)
+ allowedRoutes.push(SignUpPath.completedReferrerRedirect)
+ allowedRoutes.push(SignUpPath.loading)
+ }
+
// TODO: These checks below here may need to fall under a different route umbrella separate from sign up
if (signUpState.genres && signUpState.genres.length > 0) {
// Already have genres selected