feat: change mail and change password
This commit is contained in:
@@ -82,6 +82,7 @@ const ProjectDropDown = forwardRef(
|
||||
selectedProject,
|
||||
onSelectProject,
|
||||
onModifyProject,
|
||||
onPlayProject,
|
||||
onCreateProject,
|
||||
formatDate = defaultFormatDate,
|
||||
allowEmptySelection = false,
|
||||
@@ -269,6 +270,7 @@ const ProjectDropDown = forwardRef(
|
||||
isSelected={currentProject?.id === project?.id}
|
||||
onSelect={handleSelect}
|
||||
onModify={handleModify}
|
||||
onPlay={onPlayProject}
|
||||
/>
|
||||
)}
|
||||
nestedScrollEnabled
|
||||
@@ -348,7 +350,15 @@ const ProjectDropDown = forwardRef(
|
||||
|
||||
export default ProjectDropDown
|
||||
|
||||
const ProjectRow = ({ project, formatDate, onSelect, onModify, isTitle = false, isOpen }) => {
|
||||
const ProjectRow = ({
|
||||
project,
|
||||
formatDate,
|
||||
onSelect,
|
||||
onModify,
|
||||
onPlay,
|
||||
isTitle = false,
|
||||
isOpen,
|
||||
}) => {
|
||||
const rowRef = useRef(null)
|
||||
const [layout, setLayout] = useState(null)
|
||||
const [menuPos, setMenuPos] = useState({
|
||||
@@ -429,6 +439,18 @@ const ProjectRow = ({ project, formatDate, onSelect, onModify, isTitle = false,
|
||||
resizeMode="contain"
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.rowActions}>
|
||||
{!!project?.songUrl && (
|
||||
<Pressable
|
||||
hitSlop={8}
|
||||
onPress={(event) => {
|
||||
event.stopPropagation?.()
|
||||
onPlay?.(project)
|
||||
}}
|
||||
>
|
||||
<Image source={icons.play} style={styles.playIcon} resizeMode="contain" />
|
||||
</Pressable>
|
||||
)}
|
||||
<Pressable
|
||||
hitSlop={8}
|
||||
onPress={(event) => {
|
||||
@@ -438,6 +460,7 @@ const ProjectRow = ({ project, formatDate, onSelect, onModify, isTitle = false,
|
||||
>
|
||||
<Image source={icons.threeDots} style={styles.moreIcon} resizeMode="contain" />
|
||||
</Pressable>
|
||||
</View>
|
||||
)}
|
||||
</ThemedBlurView>
|
||||
</Pressable>
|
||||
@@ -558,6 +581,16 @@ const styles = StyleSheet.create({
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
},
|
||||
rowActions: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
},
|
||||
playIcon: {
|
||||
width: 18,
|
||||
height: 18,
|
||||
tintColor: Palette.white,
|
||||
},
|
||||
moreIcon: {
|
||||
width: 18,
|
||||
height: 18,
|
||||
|
||||
@@ -21,6 +21,7 @@ export default {
|
||||
|
||||
_isLoading: false,
|
||||
_loadingMessage: null,
|
||||
_tooltip: null,
|
||||
|
||||
_config: {
|
||||
colors: {
|
||||
|
||||
@@ -8,6 +8,7 @@ import NotificationProvider from './NotificationProvider'
|
||||
import PlayerProvider from './PlayerProvider'
|
||||
import SplashAnimationProvider from './SplashAnimationProvider'
|
||||
import StripeProvider from './StripeProvider'
|
||||
import TooltipProvider from './TooltipProvider'
|
||||
import UniversalLinkProvider from './UniversalLinkProvider'
|
||||
import UserDataProvider from './UserDataProvider'
|
||||
import WebViewProvider from './WebViewProvider'
|
||||
@@ -16,6 +17,7 @@ const SharedProviders = ({ children }) => {
|
||||
const providers = [
|
||||
[SafeAreaProvider, {}],
|
||||
[ActionSheetProvider, {}],
|
||||
[TooltipProvider, {}],
|
||||
[SplashAnimationProvider, {}],
|
||||
[WebViewProvider, {}],
|
||||
[UserDataProvider, {}],
|
||||
|
||||
+23
-11
@@ -25,6 +25,7 @@ import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import { getCreationStageStates, getStageAction } from '../../utils/projectStages'
|
||||
import { openCoinPackModal } from '../../utils/coinPackModal'
|
||||
import useNavigateToMusicDetails from '../../hooks/useNavigateToMusicDetails'
|
||||
import usePlayer from '../../hooks/usePlayer'
|
||||
import StageCard from './components/StageCard'
|
||||
import ClubCard from './components/ClubCard'
|
||||
import { BlurView } from 'expo-blur'
|
||||
@@ -158,6 +159,8 @@ const Home = ({ navigation, route }) => {
|
||||
const projectDropdownRef = useRef(null)
|
||||
const [hasLocalAdventureFlag, setHasLocalAdventureFlag] = useState(false)
|
||||
|
||||
const { play } = usePlayer()
|
||||
|
||||
const handleSelectProject = useCallback(
|
||||
(project) => {
|
||||
if (!project?.id) return
|
||||
@@ -210,6 +213,25 @@ const Home = ({ navigation, route }) => {
|
||||
menuAnchorRef.current = normalizedAnchor
|
||||
}, [])
|
||||
|
||||
const handlePlayProject = useCallback(
|
||||
(project) => {
|
||||
if (!project?.id || !project?.songUrl) return
|
||||
const cover = project.coverUrl || project.coverUri || project.cover
|
||||
play({
|
||||
id: project.id,
|
||||
url: project.songUrl,
|
||||
title: project.title || 'Sans titre',
|
||||
artist: 'MusicLand',
|
||||
artwork: cover,
|
||||
coverUrl: typeof cover === 'string' ? cover : cover?.uri,
|
||||
metadata: {
|
||||
projectId: project.id,
|
||||
},
|
||||
})
|
||||
},
|
||||
[play]
|
||||
)
|
||||
|
||||
const moreMenuItems = useMemo(() => {
|
||||
if (!menuProject?.id) return []
|
||||
|
||||
@@ -228,17 +250,6 @@ const Home = ({ navigation, route }) => {
|
||||
})
|
||||
},
|
||||
})
|
||||
items.push({
|
||||
label: 'Jouer la musique',
|
||||
onPress: () => {
|
||||
projectDropdownRef.current?.close?.()
|
||||
navigateToMusicDetails({
|
||||
projectId: menuProject.id,
|
||||
songUrl: menuProject.songUrl,
|
||||
project: menuProject,
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
items.push({
|
||||
@@ -540,6 +551,7 @@ const Home = ({ navigation, route }) => {
|
||||
allowEmptySelection
|
||||
onSelectProject={handleSelectProject}
|
||||
onModifyProject={handleModifyProject}
|
||||
onPlayProject={handlePlayProject}
|
||||
onCreateProject={handleStartNew}
|
||||
formatDate={formatDate}
|
||||
/>
|
||||
|
||||
@@ -53,28 +53,45 @@ const ChangeEmailAddress = () => {
|
||||
const uid = user?.uid
|
||||
if (!user || !uid) return
|
||||
|
||||
// Reauthenticate user with current password before sensitive update
|
||||
try {
|
||||
const credential = firebase.auth.EmailAuthProvider.credential(user.email, currentPassword)
|
||||
await user.reauthenticateWithCredential(credential)
|
||||
} catch (reauthErr) {
|
||||
throw {
|
||||
code: 'auth/wrong-password',
|
||||
message: 'Mot de passe incorrect.',
|
||||
}
|
||||
setTooltip({ type: 'error', text: 'Mot de passe actuel incorrect.' })
|
||||
return
|
||||
}
|
||||
|
||||
// Use verifyBeforeUpdateEmail instead of updateEmail for newer Firebase projects
|
||||
// This sends a verification email to the new address and only changes it once verified.
|
||||
// However, for some projects, we might still need to update Firestore record if allowed.
|
||||
try {
|
||||
if (typeof user.verifyBeforeUpdateEmail === 'function') {
|
||||
await user.verifyBeforeUpdateEmail(val)
|
||||
setTooltip({
|
||||
type: 'success',
|
||||
text: 'Un email de vérification a été envoyé à votre nouvelle adresse.',
|
||||
})
|
||||
} else {
|
||||
await user.updateEmail(val)
|
||||
await usersRef.doc(uid).set({ email: val }, { merge: true })
|
||||
setTooltip({ type: 'success', text: 'Email mis à jour' })
|
||||
setTooltip({ type: 'success', text: 'Adresse email mise à jour' })
|
||||
}
|
||||
goBack()
|
||||
} catch (e) {
|
||||
console.log('ChangeEmailAddress error', e?.message)
|
||||
const msg =
|
||||
e?.code === 'auth/requires-recent-login'
|
||||
? "Veuillez vous reconnecter pour changer l'adresse email"
|
||||
: e?.message || 'Mise à jour impossible'
|
||||
} catch (updateErr) {
|
||||
console.log('UpdateEmail error detail', updateErr)
|
||||
let msg = 'Mise à jour impossible'
|
||||
if (updateErr.code === 'auth/operation-not-allowed') {
|
||||
msg = "La modification d'email directe n'est pas autorisée. Contactez le support."
|
||||
} else if (updateErr.message?.includes('verify')) {
|
||||
msg = "Veuillez vérifier votre nouvel email avant d'effectuer le changement."
|
||||
} else {
|
||||
msg = updateErr.message || msg
|
||||
}
|
||||
setTooltip({ type: 'error', text: msg })
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('ChangeEmailAddress error', e)
|
||||
setTooltip({ type: 'error', text: e?.message || 'Une erreur est survenue' })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
@@ -82,7 +99,7 @@ const ChangeEmailAddress = () => {
|
||||
return (
|
||||
<Page
|
||||
headerType="NAVIGATE"
|
||||
title="Paramètres"
|
||||
title="Modifier mon email"
|
||||
backgroundImg={background.profileBG}
|
||||
contentContainerStyle={{
|
||||
paddingBottom: gutters * 4,
|
||||
|
||||
@@ -3,13 +3,13 @@ import React, { useState } from 'react'
|
||||
import { Platform, Pressable, Text, View } from 'react-native'
|
||||
import { responsiveHeight } from 'react-native-responsive-dimensions'
|
||||
import { useGlobal } from 'reactn'
|
||||
import { checkIfPasswordIsStrongEnough } from '../../actions/signupActions'
|
||||
import { background } from '../../assets'
|
||||
import GradientButton from '../../components/GradientButton'
|
||||
import firebase from '../../config/firebase'
|
||||
import { isWeb } from '../../hooks/useLayoutType'
|
||||
import Page from '../../layouts/Page'
|
||||
import { Routes } from '../../navigation'
|
||||
import { navigate } from '../../navigation/NavigationService'
|
||||
import { goBack } from '../../navigation/NavigationService'
|
||||
import { gutters, Palette } from '../../styles'
|
||||
import { FONT_FAMILY } from '../../styles/Fonts'
|
||||
import EditInput from './components/EditInput'
|
||||
@@ -17,26 +17,55 @@ import EditInput from './components/EditInput'
|
||||
const ChangePassword = () => {
|
||||
const [oldPassword, setOldPassword] = useState('')
|
||||
const [newPassword, setNewPassword] = useState('')
|
||||
const [passwordError, setPasswordError] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [, setTooltip] = useGlobal('_tooltip')
|
||||
|
||||
const onChangeNewPassword = (val) => {
|
||||
setNewPassword(val)
|
||||
if (!val) {
|
||||
setPasswordError('')
|
||||
return
|
||||
}
|
||||
if (!checkIfPasswordIsStrongEnough({ password: val })) {
|
||||
setPasswordError(
|
||||
'Le mot de passe doit contenir au moins 6 caractères, une majuscule et un chiffre.'
|
||||
)
|
||||
} else {
|
||||
setPasswordError('')
|
||||
}
|
||||
}
|
||||
|
||||
const onSave = async () => {
|
||||
const oldVal = (oldPassword || '').trim()
|
||||
const newVal = (newPassword || '').trim()
|
||||
if (!oldVal || !newVal) return
|
||||
if (passwordError) return
|
||||
|
||||
try {
|
||||
setLoading(true)
|
||||
const user = firebase.auth().currentUser
|
||||
if (!user?.email) return
|
||||
|
||||
try {
|
||||
const credential = firebase.auth.EmailAuthProvider.credential(user.email, oldVal)
|
||||
await user.reauthenticateWithCredential(credential)
|
||||
} catch (reauthErr) {
|
||||
throw {
|
||||
code: 'auth/wrong-password',
|
||||
message: 'Mot de passe actuel incorrect.',
|
||||
}
|
||||
}
|
||||
|
||||
await user.updatePassword(newVal)
|
||||
setTooltip({ type: 'success', text: 'Mot de passe mis à jour' })
|
||||
navigate(Routes.Settings)
|
||||
goBack()
|
||||
} catch (e) {
|
||||
console.log('ChangePassword error', e?.message)
|
||||
const msg = e?.message || 'Mise à jour impossible'
|
||||
const msg =
|
||||
e?.code === 'auth/wrong-password'
|
||||
? 'Mot de passe actuel incorrect.'
|
||||
: e?.message || 'Mise à jour impossible'
|
||||
setTooltip({ type: 'error', text: msg })
|
||||
} finally {
|
||||
setLoading(false)
|
||||
@@ -45,7 +74,7 @@ const ChangePassword = () => {
|
||||
return (
|
||||
<Page
|
||||
headerType="NAVIGATE"
|
||||
title="Paramètres"
|
||||
title="Modifier mon mot de passe"
|
||||
backgroundImg={background.profileBG}
|
||||
contentContainerStyle={{
|
||||
paddingBottom: gutters * 4,
|
||||
@@ -81,8 +110,20 @@ const ChangePassword = () => {
|
||||
label="Nouveau mot de passe"
|
||||
type="password"
|
||||
value={newPassword}
|
||||
setValue={setNewPassword}
|
||||
setValue={onChangeNewPassword}
|
||||
/>
|
||||
{!!passwordError && (
|
||||
<Text
|
||||
style={{
|
||||
fontSize: 12,
|
||||
color: Palette.red,
|
||||
fontFamily: FONT_FAMILY.InterRegular,
|
||||
marginTop: 6,
|
||||
}}
|
||||
>
|
||||
{passwordError}
|
||||
</Text>
|
||||
)}
|
||||
<Pressable>
|
||||
<Text
|
||||
style={{
|
||||
@@ -103,7 +144,7 @@ const ChangePassword = () => {
|
||||
alignSelf: 'center',
|
||||
}}
|
||||
onPress={onSave}
|
||||
disabled={loading || !oldPassword?.trim() || !newPassword?.trim()}
|
||||
disabled={loading || !oldPassword?.trim() || !newPassword?.trim() || !!passwordError}
|
||||
/>
|
||||
</Page>
|
||||
)
|
||||
|
||||
@@ -14,7 +14,7 @@ import { gutters, Palette } from '../../styles'
|
||||
|
||||
const SETTINGS = [
|
||||
{
|
||||
title: 'Adresse mail',
|
||||
title: 'Modifier mon adresse mail',
|
||||
action: () => {
|
||||
navigate(Routes.ChangeEmailAddress)
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user