diff --git a/CHANGELOG.md b/CHANGELOG.md
index d8f912c41ed..3e4576736b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,7 @@
## 4.31.0 (staging)
+- added: "Change Username" setting
- added: Support for Zano alias name resolution.
- changed: Don't allow multiple pending EVM transactions.
diff --git a/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap b/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
index 28069fb8953..2fe527519c7 100644
--- a/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
+++ b/src/__tests__/scenes/__snapshots__/SettingsScene.test.tsx.snap
@@ -807,6 +807,153 @@ exports[`SettingsScene should render SettingsScene 1`] = `
]
}
/>
+
+
+ Change Username
+
+
+
+
+
+
+
+
+
+
+
+
+
> {
+): ThunkAction> {
return async (dispatch, getState) => {
const {
message,
@@ -64,6 +64,6 @@ export function validatePassword(
/>
))
- return password != null
+ return password
}
}
diff --git a/src/actions/SettingsActions.tsx b/src/actions/SettingsActions.tsx
index c77c0e2bc2b..36993cebd0a 100644
--- a/src/actions/SettingsActions.tsx
+++ b/src/actions/SettingsActions.tsx
@@ -288,16 +288,18 @@ export async function showReEnableOtpModal(
}
}
-export function showUnlockSettingsModal(): ThunkAction> {
+export function showUnlockSettingsModal(): ThunkAction<
+ Promise
+> {
return async (dispatch, getState) => {
- const passwordValid = await dispatch(validatePassword())
- if (passwordValid) {
+ const password = await dispatch(validatePassword())
+ if (password != null) {
dispatch({
type: 'UI/SETTINGS/SET_SETTINGS_LOCK',
data: false
})
}
- return passwordValid
+ return password
}
}
diff --git a/src/actions/WalletListMenuActions.tsx b/src/actions/WalletListMenuActions.tsx
index e331419e95f..7781220edd8 100644
--- a/src/actions/WalletListMenuActions.tsx
+++ b/src/actions/WalletListMenuActions.tsx
@@ -277,13 +277,14 @@ export function walletListMenuAction(
// Show the scam warning modal if needed
await showScamWarningModal('firstPrivateKeyView')
- const passwordValid = await dispatch(
- validatePassword({
- title: lstrings.fragment_wallets_get_seed_title,
- submitLabel: lstrings.fragment_wallets_get_seed_wallet,
- warningMessage: lstrings.fragment_wallets_get_seed_warning_message
- })
- )
+ const passwordValid =
+ (await dispatch(
+ validatePassword({
+ title: lstrings.fragment_wallets_get_seed_title,
+ submitLabel: lstrings.fragment_wallets_get_seed_wallet,
+ warningMessage: lstrings.fragment_wallets_get_seed_warning_message
+ })
+ )) != null
if (passwordValid) {
const { name, id, type } = wallet
@@ -322,14 +323,15 @@ export function walletListMenuAction(
case 'getRawKeys': {
return async (dispatch, getState) => {
- const passwordValid = await dispatch(
- validatePassword({
- title: lstrings.fragment_wallets_get_raw_keys_title,
- warningMessage:
- lstrings.fragment_wallets_get_raw_keys_warning_message,
- submitLabel: lstrings.string_get_raw_keys
- })
- )
+ const passwordValid =
+ (await dispatch(
+ validatePassword({
+ title: lstrings.fragment_wallets_get_raw_keys_title,
+ warningMessage:
+ lstrings.fragment_wallets_get_raw_keys_warning_message,
+ submitLabel: lstrings.string_get_raw_keys
+ })
+ )) != null
if (passwordValid) {
const state = getState()
const { account } = state.core
diff --git a/src/components/Main.tsx b/src/components/Main.tsx
index 4940dfe9495..2802bc16731 100644
--- a/src/components/Main.tsx
+++ b/src/components/Main.tsx
@@ -57,6 +57,7 @@ import { AssetSettingsScene as AssetSettingsSceneComponent } from './scenes/Asse
import { ChangeMiningFeeScene as ChangeMiningFeeSceneComponent } from './scenes/ChangeMiningFeeScene'
import { ChangePasswordScene as ChangePasswordSceneComponent } from './scenes/ChangePasswordScene'
import { ChangePinScene as ChangePinSceneComponent } from './scenes/ChangePinScene'
+import { ChangeUsernameScene as ChangeUsernameSceneComponent } from './scenes/ChangeUsernameScene'
import { CoinRankingDetailsScene as CoinRankingDetailsSceneComponent } from './scenes/CoinRankingDetailsScene'
import { CoinRankingScene as CoinRankingSceneComponent } from './scenes/CoinRankingScene'
import { ConfirmScene as ConfirmSceneComponent } from './scenes/ConfirmScene'
@@ -164,6 +165,7 @@ const BuyScene = ifLoggedIn(BuySceneComponent)
const ChangeMiningFeeScene = ifLoggedIn(ChangeMiningFeeSceneComponent)
const ChangePasswordScene = ifLoggedIn(ChangePasswordSceneComponent)
const ChangePinScene = ifLoggedIn(ChangePinSceneComponent)
+const ChangeUsernameScene = ifLoggedIn(ChangeUsernameSceneComponent)
const DuressPinScene = ifLoggedIn(DuressPinSceneComponent)
const ChangeRecoveryScene = ifLoggedIn(ChangeRecoverySceneComponent)
const CoinRankingDetailsScene = ifLoggedIn(CoinRankingDetailsSceneComponent)
@@ -625,6 +627,14 @@ const EdgeAppStack = () => {
headerRight: () => null
}}
/>
+ null
+ }}
+ />
{}
+
+export const ChangeUsernameScene = (props: Props) => {
+ const { navigation, route } = props
+ const { password } = route.params
+ const dispatch = useDispatch()
+
+ const account = useSelector(state => state.core.account)
+ const context = useSelector(state => state.core.context)
+
+ const handleComplete = useHandler(() => {
+ navigation.goBack()
+ })
+
+ const handleLogEvent = useHandler((event, values) => {
+ dispatch(logEvent(event, values))
+ })
+
+ return (
+
+
+
+ )
+}
diff --git a/src/components/scenes/SettingsScene.tsx b/src/components/scenes/SettingsScene.tsx
index 3afd382aa43..bd3cff963eb 100644
--- a/src/components/scenes/SettingsScene.tsx
+++ b/src/components/scenes/SettingsScene.tsx
@@ -91,7 +91,6 @@ export const SettingsScene = (props: Props) => {
const [localContactPermissionOn, setLocalContactsPermissionOn] =
React.useState(false)
-
const [isDarkTheme, setIsDarkTheme] = React.useState(
theme === config.darkTheme
)
@@ -106,6 +105,7 @@ export const SettingsScene = (props: Props) => {
const [touchIdText, setTouchIdText] = React.useState(
lstrings.settings_button_use_touchID
)
+ const [validatedPassword, setValidatedPassword] = React.useState()
const iconSize = theme.rem(1.25)
const isLightAccount = username == null
@@ -127,25 +127,31 @@ export const SettingsScene = (props: Props) => {
showBackupModal({ navigation: navigation as NavigationBase })
})
- const handleUnlock = useHandler(() => {
+ const handleLockUnlock = useHandler(async () => {
if (!isLocked) {
dispatch({
type: 'UI/SETTINGS/SET_SETTINGS_LOCK',
data: true
})
+ setValidatedPassword(undefined)
} else {
- handleShowUnlockSettingsModal().catch(err => showError(err))
+ const password = await handleShowUnlockSettingsModal().catch(err => {
+ showError(err)
+ return undefined
+ })
+ setValidatedPassword(password)
}
})
/** Returns true if the settings are locked. Otherwise false if they're unlocked. */
const hasLock = async (): Promise => {
if (isLocked) {
- const passwordValid = await handleShowUnlockSettingsModal().catch(err => {
+ const password = await handleShowUnlockSettingsModal().catch(err => {
showError(err)
- return false
+ return undefined
})
- if (!passwordValid) return true
+ if (password == null) return true
+ setValidatedPassword(password)
dispatch({
type: 'UI/SETTINGS/SET_SETTINGS_LOCK',
data: false
@@ -225,6 +231,14 @@ export const SettingsScene = (props: Props) => {
navigation.navigate('changePin')
})
+ const handleChangeUsername = useHandler(async (): Promise => {
+ // Check if settings are locked
+ if ((await hasLock()) || validatedPassword == null) return
+
+ // Navigate to change username with the validated password
+ navigation.navigate('changeUsername', { password: validatedPassword })
+ })
+
const handleChangeOtp = useHandler(async (): Promise => {
if (await hasLock()) return
navigation.navigate('otpSetup')
@@ -421,7 +435,7 @@ export const SettingsScene = (props: Props) => {
? lstrings.settings_button_unlock_settings
: lstrings.settings_button_lock_settings
}
- onPress={handleUnlock}
+ onPress={handleLockUnlock}
/>
{
label={lstrings.settings_button_pin}
onPress={handleChangePin}
/>
+