Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions packages/common/src/messages/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const settingsMessages = {
labelAccountCardTitle: 'Label Account',
notificationsCardTitle: 'Configure Notifications',
accountRecoveryCardTitle: 'Resend Recovery Email',
emailVerificationCardTitle: 'Email Verification',
changeEmailCardTitle: 'Change Email',
changePasswordCardTitle: 'Change Password',
accountsYouManageTitle: 'Accounts You Manage',
Expand All @@ -46,6 +47,8 @@ export const settingsMessages = {
notificationsCardDescription: 'Review your notification preferences.',
accountRecoveryCardDescription:
'Resend your password reset email and store it safely. This email is the only way to recover your account if you forget your password.',
emailVerificationCardDescription:
'Verify that you can receive email at the address connected to your Audius account.',
changeEmailCardDescription:
'Change the email you use to sign in and receive emails.',
changePasswordCardDescription: 'Change the password to your Audius account.',
Expand All @@ -61,6 +64,11 @@ export const settingsMessages = {
commentSettingsButtonText: 'Comment Settings',
notificationsButtonText: 'Configure Notifications',
accountRecoveryButtonText: 'Resend Email',
emailVerificationButtonText: 'Resend Verification Email',
emailVerificationSent: 'Verification email sent!',
emailVerificationAlreadyVerified: 'Your email is already verified.',
emailVerificationNotSent:
'Unable to send verification email. Please try again!',
changeEmailButtonText: 'Change Email',
changePasswordButtonText: 'Change Password',
desktopAppButtonText: 'Get The App',
Expand Down
18 changes: 18 additions & 0 deletions packages/common/src/services/auth/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ type CreateStripeSessionResponse = {
status: string
}

type ResendEmailVerificationResponse = {
status: true
alreadyVerified?: boolean
}

enum TransactionMetadataType {
PURCHASE_SOL_AUDIO_SWAP = 'PURCHASE_SOL_AUDIO_SWAP'
}
Expand Down Expand Up @@ -232,6 +237,19 @@ export class IdentityService {
return res.email
}

/**
* Resend the current user's email verification link.
*/
async resendEmailVerification() {
const headers = await this.getAuthHeaders()

return await this._makeRequest<ResendEmailVerificationResponse>({
url: '/email/resend-verification',
method: 'post',
headers
})
}

/**
* Change the user's email used for notifications and display.
*/
Expand Down
2 changes: 2 additions & 0 deletions packages/common/src/utils/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export const HOME_PAGE = '/'
export const NOT_FOUND_PAGE = '/404'
export const SIGN_IN_PAGE = '/signin'
export const SIGN_IN_CONFIRM_EMAIL_PAGE = '/signin/confirm-email'
export const EMAIL_VERIFICATION_PAGE = '/verify-email'
export const SIGN_UP_PAGE = '/signup'
export const SIGN_ON_ALIASES = Object.freeze([
'/login',
Expand Down Expand Up @@ -285,6 +286,7 @@ export const publicSiteRoutes = [
// ordered list of routes the App attempts to match in increasing order of route selectivity
export const orderedRoutes = [
SIGN_IN_PAGE,
EMAIL_VERIFICATION_PAGE,
SIGN_UP_PAGE,
...SIGN_ON_ALIASES,
SIGN_UP_EMAIL_PAGE,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { useCallback } from 'react'
import { useCallback, useState } from 'react'

import {
useCurrentAccountUser,
useQueryContext,
useResendRecoveryEmail
} from '@audius/common/api'
import { modalsActions, useTierAndVerifiedForUser } from '@audius/common/store'
Expand Down Expand Up @@ -43,6 +44,14 @@ const messages = {
recoveryButtonTitle: 'Resend Recovery Email',
recoveryEmailSent: 'Recovery Email Sent!',
recoveryEmailNotSent: 'Unable to send recovery email. Please try again!',
emailVerificationTitle: 'Email Verification',
emailVerificationDescription:
'Verify that you can receive email at the address connected to your Audius account.',
emailVerificationButtonTitle: 'Resend Verification Email',
emailVerificationSent: 'Verification email sent!',
emailVerificationAlreadyVerified: 'Your email is already verified.',
emailVerificationNotSent:
'Unable to send verification email. Please try again!',
verifyTitle: 'Verification',
verifyDescription:
'Verify your Audius profile by completing identity verification.',
Expand Down Expand Up @@ -78,6 +87,9 @@ export const AccountSettingsScreen = () => {
const styles = useStyles()
const { toast } = useToast()
const dispatch = useDispatch()
const { identityService } = useQueryContext()
const [isSendingVerificationEmail, setIsSendingVerificationEmail] =
useState(false)
const { data: accountData } = useCurrentAccountUser({
select: (user) => pick(user, ['user_id', 'handle', 'name'])
})
Expand All @@ -102,6 +114,23 @@ export const AccountSettingsScreen = () => {
})
}, [resendRecoveryEmail, toast])

const handlePressEmailVerification = useCallback(async () => {
setIsSendingVerificationEmail(true)
try {
const result = await identityService.resendEmailVerification()
toast({
content: result.alreadyVerified
? messages.emailVerificationAlreadyVerified
: messages.emailVerificationSent,
type: 'info'
})
} catch (e) {
toast({ content: messages.emailVerificationNotSent, type: 'error' })
} finally {
setIsSendingVerificationEmail(false)
}
}, [identityService, toast])

const handlePressChangeEmail = useCallback(() => {
navigation.push('ChangeEmail')
}, [navigation])
Expand Down Expand Up @@ -155,6 +184,14 @@ export const AccountSettingsScreen = () => {
buttonTitle={messages.recoveryButtonTitle}
onPress={handlePressRecoveryEmail}
/>
<AccountSettingsItem
title={messages.emailVerificationTitle}
titleIcon={IconEmailAddress}
description={messages.emailVerificationDescription}
buttonTitle={messages.emailVerificationButtonTitle}
onPress={handlePressEmailVerification}
disabled={isSendingVerificationEmail}
/>
<AccountSettingsItem
title={messages.verifyTitle}
titleIcon={IconVerified}
Expand Down
10 changes: 10 additions & 0 deletions packages/web/src/app/web-player/WebPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ const ContestsPage = lazy(() =>
}))
)
const SettingsPage = lazy(() => import('pages/settings-page/SettingsPage'))
const VerifyEmailPage = lazy(() => import('pages/verify-email-page'))
const TrackCommentsPage = lazy(() =>
import('pages/track-page/TrackCommentsPage').then((m) => ({
default: m.TrackCommentsPage
Expand Down Expand Up @@ -294,6 +295,7 @@ const {
COIN_EXCLUSIVE_TRACKS_PAGE,
COIN_EXCLUSIVE_TRACKS_MOBILE_ROUTE,
CHECK_PAGE,
EMAIL_VERIFICATION_PAGE,
TRENDING_PLAYLISTS_PAGE,
TRENDING_PLAYLISTS_PAGE_LEGACY,
DEACTIVATE_PAGE,
Expand Down Expand Up @@ -1055,6 +1057,10 @@ const WebPlayer = (props: WebPlayerProps) => {
path={LABEL_ACCOUNT_SETTINGS_PAGE}
element={<SettingsPage containerRef={mainContentRef} />}
/>
<Route
path={EMAIL_VERIFICATION_PAGE}
element={<VerifyEmailPage />}
/>
<Route path={CHECK_PAGE} element={<CheckPage />} />
{isMobile ? (
<>
Expand Down Expand Up @@ -1512,6 +1518,10 @@ const WebPlayer = (props: WebPlayerProps) => {
path={LABEL_ACCOUNT_SETTINGS_PAGE}
element={<SettingsPage containerRef={mainContentRef} />}
/>
<Route
path={EMAIL_VERIFICATION_PAGE}
element={<VerifyEmailPage />}
/>
<Route path={CHECK_PAGE} element={<CheckPage />} />
<Route
path={ACCOUNT_SETTINGS_PAGE}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,20 @@ export const SettingsPage = () => {
setIsNotificationSettingsModalVisible
] = useState(false)
const [isEmailToastVisible, setIsEmailToastVisible] = useState(false)
const [isEmailVerificationToastVisible, setIsEmailVerificationToastVisible] =
useState(false)
const [isEmailVerificationLoading, setIsEmailVerificationLoading] =
useState(false)
const [isChangePasswordModalVisible, setIsChangePasswordModalVisible] =
useState(false)
const [isChangeEmailModalVisible, setIsChangeEmailModalVisible] =
useState(false)
const [emailToastText, setEmailToastText] = useState(
settingsMessages.emailSent
)
const [emailVerificationToastText, setEmailVerificationToastText] = useState(
settingsMessages.emailVerificationSent
)
const [, setIsInboxSettingsModalVisible] = useModalState('InboxSettings')
const [, setIsCommentSettingsModalVisible] = useModalState('CommentSettings')

Expand Down Expand Up @@ -260,6 +267,35 @@ export const SettingsPage = () => {
dispatch
])

const showEmailVerificationToast = useCallback(() => {
const fn = async () => {
setIsEmailVerificationLoading(true)
try {
const result = await identityService.resendEmailVerification()
setEmailVerificationToastText(
result.alreadyVerified
? settingsMessages.emailVerificationAlreadyVerified
: settingsMessages.emailVerificationSent
)
setIsEmailVerificationToastVisible(true)
} catch (e) {
console.error(e)
setEmailVerificationToastText(settingsMessages.emailVerificationNotSent)
setIsEmailVerificationToastVisible(true)
} finally {
setIsEmailVerificationLoading(false)
}
setTimeout(() => {
setIsEmailVerificationToastVisible(false)
}, EMAIL_TOAST_TIMEOUT)
}
fn()
}, [
setIsEmailVerificationToastVisible,
setEmailVerificationToastText,
identityService
])

const handleDownloadDesktopAppClicked = useCallback(() => {
dispatch(make(Name.ACCOUNT_HEALTH_DOWNLOAD_DESKTOP, { source: 'settings' }))
window.location.href = `https://audius.co${DOWNLOAD_LINK}`
Expand Down Expand Up @@ -607,6 +643,30 @@ export const SettingsPage = () => {
</Toast>
</SettingsCard>
) : null}
{!isManagedAccount ? (
<SettingsCard
icon={<IconEmailAddress color='accent' />}
title={settingsMessages.emailVerificationCardTitle}
description={settingsMessages.emailVerificationCardDescription}
>
<Toast
text={emailVerificationToastText}
open={isEmailVerificationToastVisible}
className={styles.cardToast}
anchorOrigin={{ horizontal: 'center', vertical: 'bottom' }}
transformOrigin={{ horizontal: 'center', vertical: 'top' }}
>
<Button
onClick={showEmailVerificationToast}
variant='secondary'
fullWidth
isLoading={isEmailVerificationLoading}
>
{settingsMessages.emailVerificationButtonText}
</Button>
</Toast>
</SettingsCard>
) : null}
{!isManagedAccount ? (
<SettingsCard
icon={<IconVerified color='accent' size='l' />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ const messages = {
recoveryButtonTitle: 'Resend Recovery Email',
recoveryEmailSent: 'Recovery Email Sent!',
recoveryEmailNotSent: 'Unable to send recovery email. Please try again!',
emailVerificationTitle: 'Email Verification',
emailVerificationDescription:
'Verify that you can receive email at the address connected to your Audius account.',
emailVerificationButtonTitle: 'Resend Verification Email',
emailVerificationSent: 'Verification email sent!',
emailVerificationAlreadyVerified: 'Your email is already verified.',
emailVerificationNotSent:
'Unable to send verification email. Please try again!',
verifyTitle: 'Verify Your Account',
verifyDescription:
'Verify your Audius profile by completing identity verification',
Expand Down Expand Up @@ -140,6 +148,8 @@ const AccountSettingsPage = () => {
})
const { userId, handle, name } = accountData ?? {}
const [showModalSignOut, setShowModalSignOut] = useState(false)
const [isSendingVerificationEmail, setIsSendingVerificationEmail] =
useState(false)
const { toast } = useContext(ToastContext)
const { isVerified } = useTierAndVerifiedForUser(userId)

Expand Down Expand Up @@ -173,6 +183,22 @@ const AccountSettingsPage = () => {
[authService, identityService, toast, record]
)

const onClickResendVerificationEmail = useCallback(async () => {
setIsSendingVerificationEmail(true)
try {
const result = await identityService.resendEmailVerification()
toast(
result.alreadyVerified
? messages.emailVerificationAlreadyVerified
: messages.emailVerificationSent
)
} catch (e) {
toast(messages.emailVerificationNotSent)
} finally {
setIsSendingVerificationEmail(false)
}
}, [identityService, toast])

const goToChangePasswordSettingsPage = useCallback(() => {
goToRoute(CHANGE_PASSWORD_SETTINGS_PAGE)
}, [goToRoute])
Expand Down Expand Up @@ -206,6 +232,14 @@ const AccountSettingsPage = () => {
buttonTitle={messages.recoveryButtonTitle}
onClick={onClickRecover}
/>
<AccountSettingsItem
icon={IconEmailAddress}
title={messages.emailVerificationTitle}
description={messages.emailVerificationDescription}
buttonTitle={messages.emailVerificationButtonTitle}
onClick={onClickResendVerificationEmail}
disabled={isSendingVerificationEmail}
/>
<AccountSettingsItem
icon={IconVerified}
title={messages.verifyTitle}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.wrapper {
display: flex;
min-height: 100%;
align-items: center;
justify-content: center;
padding: var(--harmony-unit-8);
}

.panel {
display: flex;
width: min(100%, 480px);
flex-direction: column;
align-items: center;
gap: var(--harmony-unit-6);
text-align: center;
}

.icon {
display: flex;
height: 64px;
width: 64px;
align-items: center;
justify-content: center;
border-radius: 50%;
background: var(--harmony-secondary);
}

.copy {
display: flex;
flex-direction: column;
gap: var(--harmony-unit-3);
}

.actions {
display: flex;
width: 100%;
max-width: 360px;
flex-direction: column;
gap: var(--harmony-unit-3);
}

@media (max-width: 480px) {
.wrapper {
align-items: flex-start;
padding: var(--harmony-unit-6);
padding-top: var(--harmony-unit-12);
}
}
Loading
Loading