diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 770d431..8e27f3c 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -16,19 +16,14 @@
android:usesCleartextTraffic="true"
android:supportsRtl="true"
>
-
-
-
-
-
-
-
+
@@ -56,8 +51,38 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- @drawable/rn_edit_text_material
+
+ - #2e2e2e
diff --git a/android/link-assets-manifest.json b/android/link-assets-manifest.json
index 20fd8ca..1e68bd5 100644
--- a/android/link-assets-manifest.json
+++ b/android/link-assets-manifest.json
@@ -2,8 +2,8 @@
"migIndex": 1,
"data": [
{
- "path": "assets/fonts/Gluten-Regular.ttf",
- "sha1": "96270757924e60e26763e8814aaa2000f470d352"
+ "path": "assets/fonts/HammersmithOne-Regular.ttf",
+ "sha1": "132e0c6ad83dff514d1f6d2719aae6c90255d92f"
},
{
"path": "assets/icons/nostr.png",
diff --git a/assets/fonts/Gluten-Regular.ttf b/assets/fonts/Gluten-Regular.ttf
deleted file mode 100644
index ae2e3c2..0000000
Binary files a/assets/fonts/Gluten-Regular.ttf and /dev/null differ
diff --git a/assets/fonts/HammersmithOne-Regular.ttf b/assets/fonts/HammersmithOne-Regular.ttf
new file mode 100644
index 0000000..94d0f1d
Binary files /dev/null and b/assets/fonts/HammersmithOne-Regular.ttf differ
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 94986a1..46ce969 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1434,6 +1434,8 @@ PODS:
- React-RCTFBReactNativeSpec
- ReactCommon/turbomodule/core
- ReactNativeDependencies
+ - react-native-change-icon (3.2.0):
+ - React
- react-native-hce (0.3.0):
- React
- react-native-mmkv (3.3.3):
@@ -2444,6 +2446,7 @@ DEPENDENCIES:
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
- React-microtasksnativemodule (from `../node_modules/react-native/ReactCommon/react/nativemodule/microtasks`)
+ - react-native-change-icon (from `../node_modules/react-native-change-icon`)
- react-native-hce (from `../node_modules/react-native-hce`)
- react-native-mmkv (from `../node_modules/react-native-mmkv`)
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
@@ -2614,6 +2617,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/ReactCommon"
React-microtasksnativemodule:
:path: "../node_modules/react-native/ReactCommon/react/nativemodule/microtasks"
+ react-native-change-icon:
+ :path: "../node_modules/react-native-change-icon"
react-native-hce:
:path: "../node_modules/react-native-hce"
react-native-mmkv:
@@ -2790,6 +2795,7 @@ SPEC CHECKSUMS:
React-logger: dd3e5f78709d5b029336be395a67bbcdc775e2ed
React-Mapbuffer: 2bdeed9ba58c94ac99a2711a3a722d2d5b3a8a81
React-microtasksnativemodule: d1acdc86b1b2ee5622af4950f3908f2155eddfcf
+ react-native-change-icon: af3d0eb3ffee2ab3c584d12a68c29f89b7108cf8
react-native-hce: ad5462e792d84e87924308dda1ee516757f1c0eb
react-native-mmkv: 07f0ae0bd7e8d458077e1750c502ba67975b3702
react-native-netinfo: cec9c4e86083cb5b6aba0e0711f563e2fbbff187
diff --git a/ios/link-assets-manifest.json b/ios/link-assets-manifest.json
index 20fd8ca..1e68bd5 100644
--- a/ios/link-assets-manifest.json
+++ b/ios/link-assets-manifest.json
@@ -2,8 +2,8 @@
"migIndex": 1,
"data": [
{
- "path": "assets/fonts/Gluten-Regular.ttf",
- "sha1": "96270757924e60e26763e8814aaa2000f470d352"
+ "path": "assets/fonts/HammersmithOne-Regular.ttf",
+ "sha1": "132e0c6ad83dff514d1f6d2719aae6c90255d92f"
},
{
"path": "assets/icons/nostr.png",
diff --git a/ios/minibits_wallet.xcodeproj/project.pbxproj b/ios/minibits_wallet.xcodeproj/project.pbxproj
index 277d8cd..6becfa8 100644
--- a/ios/minibits_wallet.xcodeproj/project.pbxproj
+++ b/ios/minibits_wallet.xcodeproj/project.pbxproj
@@ -13,7 +13,7 @@
902F38392D92C792005DB998 /* OpenSSL.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 902F38382D92C792005DB998 /* OpenSSL.xcframework */; };
9044DC252D95937D00F918A6 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9044DC242D95937D00F918A6 /* GoogleService-Info.plist */; };
A1B9A6F7058C4F12AB677FD4 /* nostr.png in Resources */ = {isa = PBXBuildFile; fileRef = AB8A46AD84194C2583FF9193 /* nostr.png */; };
- C3AB98FDBCF446F99A4EA648 /* Gluten-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 6DA8A3418B7A4B348B67B9E7 /* Gluten-Regular.ttf */; };
+ A2E4F8C1D3B547A890123456 /* HammersmithOne-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = B3D7A2E5F1C648B9A0234567 /* HammersmithOne-Regular.ttf */; };
CD7DC745CFA9F47C2D6FFA1D /* Pods_minibits_wallet.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4ACC08249CA13DFDBBBD7A8B /* Pods_minibits_wallet.framework */; };
D610B97F2318E8AB9D0962D2 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB81A68108700A75B9A /* PrivacyInfo.xcprivacy */; };
/* End PBXBuildFile section */
@@ -26,7 +26,7 @@
3B4392A12AC88292D35C810B /* Pods-minibits_wallet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-minibits_wallet.debug.xcconfig"; path = "Target Support Files/Pods-minibits_wallet/Pods-minibits_wallet.debug.xcconfig"; sourceTree = ""; };
4ACC08249CA13DFDBBBD7A8B /* Pods_minibits_wallet.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_minibits_wallet.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5709B34CF0A7D63546082F79 /* Pods-minibits_wallet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-minibits_wallet.release.xcconfig"; path = "Target Support Files/Pods-minibits_wallet/Pods-minibits_wallet.release.xcconfig"; sourceTree = ""; };
- 6DA8A3418B7A4B348B67B9E7 /* Gluten-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "Gluten-Regular.ttf"; path = "../assets/fonts/Gluten-Regular.ttf"; sourceTree = ""; };
+ B3D7A2E5F1C648B9A0234567 /* HammersmithOne-Regular.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "HammersmithOne-Regular.ttf"; path = "../assets/fonts/HammersmithOne-Regular.ttf"; sourceTree = ""; };
761780EC2CA45674006654EE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = minibits_wallet/AppDelegate.swift; sourceTree = ""; };
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = minibits_wallet/LaunchScreen.storyboard; sourceTree = ""; };
902F38382D92C792005DB998 /* OpenSSL.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:67RAULRX93:Marcin Krzyzanowski"; lastKnownFileType = wrapper.xcframework; name = OpenSSL.xcframework; path = "Pods/OpenSSL-Universal/OpenSSL.xcframework"; sourceTree = ""; };
@@ -75,7 +75,7 @@
4B24E47762034A51AEE63594 /* Resources */ = {
isa = PBXGroup;
children = (
- 6DA8A3418B7A4B348B67B9E7 /* Gluten-Regular.ttf */,
+ B3D7A2E5F1C648B9A0234567 /* HammersmithOne-Regular.ttf */,
AB8A46AD84194C2583FF9193 /* nostr.png */,
);
name = Resources;
@@ -186,7 +186,7 @@
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
D610B97F2318E8AB9D0962D2 /* PrivacyInfo.xcprivacy in Resources */,
- C3AB98FDBCF446F99A4EA648 /* Gluten-Regular.ttf in Resources */,
+ A2E4F8C1D3B547A890123456 /* HammersmithOne-Regular.ttf in Resources */,
A1B9A6F7058C4F12AB677FD4 /* nostr.png in Resources */,
9044DC252D95937D00F918A6 /* GoogleService-Info.plist in Resources */,
);
diff --git a/ios/minibits_wallet/Info.plist b/ios/minibits_wallet/Info.plist
index a086e09..a095804 100644
--- a/ios/minibits_wallet/Info.plist
+++ b/ios/minibits_wallet/Info.plist
@@ -51,7 +51,7 @@
UIAppFonts
- Gluten-Regular.ttf
+ HammersmithOne-Regular.ttf
UIBackgroundModes
@@ -78,5 +78,23 @@
HOT_UPDATER_FINGERPRINT_HASH
98c1f26187f02f38ddef1fcb75384246a1c5fa03
+ CFBundleIcons
+
+ CFBundlePrimaryIcon
+
+ CFBundleIconName
+ AppIcon
+
+ CFBundleAlternateIcons
+
+ Golden
+
+ CFBundleIconName
+ AppIconGolden
+ UIPrerenderedIcon
+
+
+
+
\ No newline at end of file
diff --git a/package.json b/package.json
index 133c3d8..0c45445 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,9 @@
"private": true,
"scripts": {
"android:clean": "cd android && ./gradlew clean",
- "android:dev": "react-native run-android --active-arch-only",
- "android:test": "react-native run-android --mode=release --active-arch-only",
+ "android:reinstall": "$HOME/Library/Android/sdk/platform-tools/adb uninstall com.minibits_wallet; yarn android:dev",
+ "android:dev": "react-native run-android --active-arch-only --main-activity .MainActivityDefault",
+ "android:test": "react-native run-android --mode=release --active-arch-only --main-activity .MainActivityDefault",
"android:bundle": "react-native build-android --mode=release",
"android:apk": "cd android && ./gradlew assembleRelease",
"ios:dev": "npx react-native run-ios --scheme \"minibits_wallet\"",
@@ -59,6 +60,7 @@
"react-native": "0.84.0",
"react-native-animated-pagination-dots": "^0.1.73",
"react-native-camera-kit": "17.0.1",
+ "react-native-change-icon": "^3.0.0",
"react-native-exit-app": "^2.0.0",
"react-native-fast-image": "^8.6.3",
"react-native-flash-message": "^0.4.2",
diff --git a/patches/react-native-change-icon+3.2.0.patch b/patches/react-native-change-icon+3.2.0.patch
new file mode 100644
index 0000000..18df631
--- /dev/null
+++ b/patches/react-native-change-icon+3.2.0.patch
@@ -0,0 +1,43 @@
+diff --git a/node_modules/react-native-change-icon/android/build.gradle b/node_modules/react-native-change-icon/android/build.gradle
+index aa5226c..bf54025 100644
+--- a/node_modules/react-native-change-icon/android/build.gradle
++++ b/node_modules/react-native-change-icon/android/build.gradle
+@@ -4,7 +4,7 @@ buildscript {
+
+ repositories {
+ google()
+- jcenter()
++ mavenCentral()
+ }
+
+ dependencies {
+@@ -52,7 +52,6 @@ android {
+
+ repositories {
+ mavenCentral()
+- jcenter()
+ google()
+
+ def found = false
+diff --git a/node_modules/react-native-change-icon/android/src/main/java/com/reactnativechangeicon/ChangeIconModule.kt b/node_modules/react-native-change-icon/android/src/main/java/com/reactnativechangeicon/ChangeIconModule.kt
+index 70c3e2c..724b3fe 100644
+--- a/node_modules/react-native-change-icon/android/src/main/java/com/reactnativechangeicon/ChangeIconModule.kt
++++ b/node_modules/react-native-change-icon/android/src/main/java/com/reactnativechangeicon/ChangeIconModule.kt
+@@ -21,7 +21,7 @@ class ChangeIconModule(reactContext: ReactApplicationContext, private val packag
+
+ @ReactMethod
+ fun changeIcon(enableIcon: String, promise: Promise) {
+- val activity: Activity? = currentActivity
++ val activity: Activity? = reactApplicationContext.currentActivity
+ if (activity == null || enableIcon.isEmpty()) {
+ promise.reject("Icon string is empty.")
+ return
+@@ -46,7 +46,7 @@ class ChangeIconModule(reactContext: ReactApplicationContext, private val packag
+
+ private fun completeIconChange() {
+ if (iconChanged) {
+- val activity: Activity? = currentActivity
++ val activity: Activity? = reactApplicationContext.currentActivity
+ if (activity == null) {
+ return
+ }
diff --git a/src/App.tsx b/src/App.tsx
index e392c80..b09379f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,28 +1,33 @@
import React, { useCallback, useEffect, useRef, useState } from 'react'
import FlashMessage from "react-native-flash-message"
-import {
+import {
SafeAreaProvider,
} from 'react-native-safe-area-context'
import {
- setSizeMattersBaseHeight,
+ setSizeMattersBaseHeight,
setSizeMattersBaseWidth
} from '@gocodingnow/rn-size-matters'
import RNExitApp from 'react-native-exit-app'
+import { changeIcon } from 'react-native-change-icon'
import {AppNavigator} from './navigation'
import {useInitialRootStore, useStores} from './models'
import {KeyChain, WalletKeys} from './services'
import {ErrorBoundary} from './screens/ErrorScreen/ErrorBoundary'
import Config from './config'
import {log} from './services'
-import { Image, Pressable, Text as RNText, TextStyle, View } from 'react-native'
-import { colors, spacing, typography } from './theme'
+import { Image, TextStyle, View } from 'react-native'
+import { ThemeCode, colors, spacing, typography } from './theme'
+import useColorScheme from './theme/useThemeColor'
import { displayName } from '../app.json'
import { Text } from './components/Text'
import useIsInternetReachable from './utils/useIsInternetReachable'
+import { MMKVStorage } from './services'
+import { Button, Screen } from './components'
setSizeMattersBaseWidth(375)
setSizeMattersBaseHeight(812)
+
function App() {
const {
userSettingsStore,
@@ -32,9 +37,11 @@ function App() {
walletProfileStore
} = useStores()
+ const colorScheme = useColorScheme()
+
const [isUserAuthenticated, setIsUserAuthenticated] = useState(false)
const [isDeviceAuthenticated, setIsDeviceAuthenticated] = useState(false)
- const [isAuthLocked, setIsAuthLocked] = useState(false)
+ const [isAppLocked, setIsAppLocked] = useState(false)
const isAuthInProgressRef = useRef(false)
const isInternetReachable = useIsInternetReachable() // boolean | null
@@ -47,37 +54,40 @@ function App() {
}
}, [isInternetReachable])
- const attemptAuth = useCallback(async () => {
+ const attemptUserAuthentication = useCallback(async () => {
if (isAuthInProgressRef.current) {
- log.trace('[App] attemptAuth skipped, auth already in progress')
+ log.trace('[App] attemptUserAuthentication skipped, auth already in progress')
return
}
isAuthInProgressRef.current = true
try {
- const isAuthEnabled = userSettingsStore.isAuthOn
- log.trace('[App] attemptAuth called')
+ const {isAuthOn} = userSettingsStore
+ log.trace('[App] attemptUserAuthentication called', {isAuthOn})
- const result = await KeyChain.authenticateOnAppStart(isAuthEnabled)
- log.trace('[App] attemptAuth result:', { success: result.success, shouldExitApp: result.shouldExitApp })
+ const result = await KeyChain.authenticateOnAppStart(isAuthOn)
+ log.trace('[App] attemptUserAuthentication result:', { success: result.success, shouldExitApp: result.shouldExitApp })
if (result.success) {
setIsUserAuthenticated(true)
- setIsAuthLocked(false)
+ setIsAppLocked(false)
return
- }
+ }
if (result.shouldExitApp) {
+ // Fix: show locked UI before exiting so the user isn't stuck on a
+ // blank splash if exitApp() has any delay or fails silently.
+ setIsAppLocked(true)
RNExitApp.exitApp()
return
}
- log.trace('[App] attemptAuth failed, locking app')
- setIsAuthLocked(true)
+ log.trace('[App] attemptUserAuthentication failed, locking app')
+ setIsAppLocked(true)
} catch (e: any) {
- log.error('[App] attemptAuth caught error', { message: e.message })
- setIsAuthLocked(true)
+ log.error('[App] attemptUserAuthentication caught error', { message: e.message })
+ setIsAppLocked(true)
} finally {
isAuthInProgressRef.current = false
}
@@ -89,18 +99,23 @@ function App() {
// User authentication (biometrics / PIN)
if (userSettingsStore.isAuthOn) {
- // Keep UI in locked mode until authentication succeeds.
- setIsAuthLocked(true)
- attemptAuth()
+ // Keep UI in locked mode until authentication succeeds.
+ attemptUserAuthentication()
} else {
setIsUserAuthenticated(true)
}
- // Theme sync
- if (userSettingsStore.theme !== userSettingsStore.nextTheme) {
- userSettingsStore.setTheme(userSettingsStore.nextTheme)
+ // Theme sync — apply queued theme on startup and persist to dedicated MMKV key
+ const appliedTheme = MMKVStorage.loadTheme()
+ if (appliedTheme !== userSettingsStore.nextTheme) {
+ MMKVStorage.saveTheme(userSettingsStore.nextTheme)
}
+ // Icon sync — keeps launcher icon in sync with the applied theme.
+ // Handles upgrades from versions before icon switching existed.
+ const targetIcon = MMKVStorage.loadTheme() === ThemeCode.GOLDEN ? 'Golden' : 'Default'
+ changeIcon(targetIcon).catch(() => {})
+
// Reset relay statuses
relaysStore.resetStatuses()
})
@@ -108,7 +123,7 @@ function App() {
useEffect(() => {
if (
isInternetReachable === null ||
- isDeviceAuthenticated ||
+ isDeviceAuthenticated ||
!rehydrated
) {
log.trace('[App] Not yet ready for device auth check')
@@ -130,10 +145,10 @@ function App() {
try {
const walletKeys: WalletKeys = await walletStore.getCachedWalletKeys()
const deviceId = walletProfileStore.device
-
+
await authStore.clearTokens()
await authStore.enrollDevice(walletKeys.NOSTR, deviceId)
-
+
log.trace('[App] Device re-enrollment successful')
} catch (e: any) {
log.error('[App] Device re-enrollment failed', { message: e.message })
@@ -146,7 +161,7 @@ function App() {
log.trace('[App] Network is online and refresh token valid → device authenticated')
setIsDeviceAuthenticated(true)
}
-
+
} else {
// Confirmed offline → skip re-enrollment, proceed safely
log.trace('[App] Network confirmed offline → skipping refresh token check, device authenticated')
@@ -162,36 +177,53 @@ function App() {
!rehydrated ||
!isUserAuthenticated ||
!isDeviceAuthenticated ||
- isAuthLocked
+ isAppLocked
) {
+ const splashTheme = MMKVStorage.loadTheme()
+ const isLight = splashTheme === ThemeCode.LIGHT ||
+ (splashTheme === ThemeCode.DEFAULT && colorScheme === 'light')
+ const isGolden = splashTheme === ThemeCode.GOLDEN
+ const splashBg = isLight ? colors.palette.neutral200 : colors.palette.neutral700
+ const splashTextColor = isLight ? colors.palette.neutral800 : colors.palette.neutral100
+
+ log.trace('[App] Rendering splash/auth screen', {isAppLocked})
+
return (
-
-
-
-
-
-
-
- {isAuthLocked && (
-
-
- Minibits is locked. Authentication is required to continue.
-
-
- Authenticate
-
- RNExitApp.exitApp()}
- >
- Close app
-
-
- )}
-
+
+
+
+
+
+
+
+
+ {isAppLocked && (
+ <>
+
+ Minibits is locked. Authentication is required to continue.
+
+
+
+
+
)
}
@@ -209,7 +241,6 @@ const $title: TextStyle = {
textAlign: "center",
fontFamily: typography.logo.normal,
fontSize: spacing.large,
- color: 'white'
}
-export default App
\ No newline at end of file
+export default App
diff --git a/src/models/UserSettingsStore.ts b/src/models/UserSettingsStore.ts
index 0787ff6..05e96c7 100644
--- a/src/models/UserSettingsStore.ts
+++ b/src/models/UserSettingsStore.ts
@@ -7,7 +7,6 @@ import { ThemeCode } from '../theme'
export type UserSettings = {
preferredUnit: MintUnit
exchangeCurrency: CurrencyCode
- theme: ThemeCode
nextTheme: ThemeCode
isOnboarded: boolean
isAuthOn: boolean
@@ -24,7 +23,6 @@ export const UserSettingsStoreModel = types
.props({
preferredUnit: types.optional(types.frozen(), 'sat'),
exchangeCurrency: types.optional(types.frozen(), CurrencyCode.USD),
- theme: types.optional(types.frozen(), ThemeCode.DEFAULT),
nextTheme: types.optional(types.frozen(), ThemeCode.DEFAULT),
isOnboarded: types.optional(types.boolean, false),
isAuthOn: types.optional(types.boolean, false),
@@ -43,11 +41,7 @@ export const UserSettingsStoreModel = types
self.exchangeCurrency = exchangeCurrency
return exchangeCurrency
},
- setTheme: (theme: ThemeCode) => {
- self.theme = theme
- return theme
- },
- setNextTheme: (theme: ThemeCode) => {
+ setNextTheme: (theme: ThemeCode) => {
self.nextTheme = theme
return theme
},
diff --git a/src/screens/Contacts/OwnName.tsx b/src/screens/Contacts/OwnName.tsx
index 3a6014d..752b349 100644
--- a/src/screens/Contacts/OwnName.tsx
+++ b/src/screens/Contacts/OwnName.tsx
@@ -349,7 +349,7 @@ export const OwnName = observer(function (props: {pubkey: string}) {
{' '}
diff --git a/src/screens/SecurityScreen.tsx b/src/screens/SecurityScreen.tsx
index 2e159c9..1e1fd30 100644
--- a/src/screens/SecurityScreen.tsx
+++ b/src/screens/SecurityScreen.tsx
@@ -18,6 +18,7 @@ import {useStores} from '../models'
import AppError from '../utils/AppError'
import {ResultModalInfo} from './Wallet/ResultModalInfo'
import { StaticScreenProps, useNavigation } from '@react-navigation/native'
+import { KeyChain } from '../services'
type Props = StaticScreenProps
@@ -42,6 +43,9 @@ export const SecurityScreen = observer(function SecurityScreen({ route }: Props)
try {
setIsLoading(true)
+ // require auth to turn on or off
+ await KeyChain.getOrCreateAuthToken(isBiometricAuthOn)
+
const result = await userSettingsStore.setIsAuthOn(
!isBiometricAuthOn,
)
@@ -198,8 +202,8 @@ export const SecurityScreen = observer(function SecurityScreen({ route }: Props)
title={
isBiometricAuthOn ? 'Authentication is on' : 'Authentication is off'
}
- message={resultMessage as string}
- />
+ message={resultMessage as string}
+ />
}
onBackButtonPress={toggleAuthModal}
onBackdropPress={toggleAuthModal}
@@ -229,8 +233,8 @@ const $card: ViewStyle = {
}
const $bottomModal: ViewStyle = {
- flex: 1,
- alignItems: 'center',
+ // flex: 1,
+ // alignItems: 'center',
paddingVertical: spacing.large,
paddingHorizontal: spacing.small,
}
diff --git a/src/screens/SettingsScreen.tsx b/src/screens/SettingsScreen.tsx
index 5f83d17..30723ea 100644
--- a/src/screens/SettingsScreen.tsx
+++ b/src/screens/SettingsScreen.tsx
@@ -13,10 +13,11 @@ import {
HOT_UPDATER_URL,
} from '@env'
import {ThemeCode, Themes, colors, spacing, useThemeColor} from '../theme'
+import { changeIcon } from 'react-native-change-icon'
import {ListItem, Screen, Text, Card, NwcIcon, Button, BottomModal, InfoModal, Icon, Header, AnimatedHeader} from '../components'
import {useStores} from '../models'
import {translate} from '../i18n'
-import { log } from '../services'
+import { MMKVStorage, log } from '../services'
import { Currencies, CurrencyCode, availableExchangeCurrencies } from '../services/wallet/currency'
import { NotificationService } from '../services/notificationService'
import { SvgXml } from 'react-native-svg'
@@ -57,7 +58,7 @@ export const SettingsScreen = observer(function SettingsScreen({ route }: Props)
const [isUpdateAvailable, setIsUpdateAvailable] = useState(false)
const [updateDescription, setUpdateDescription] = useState('')
- const [currentTheme, setCurrentTheme] = useState(userSettingsStore.theme)
+ const [currentTheme, setCurrentTheme] = useState(MMKVStorage.loadTheme())
const [updateSize, setUpdateSize] = useState('')
const [isCurrencyModalVisible, setIsCurrencyModalVisible] = useState(false)
@@ -286,17 +287,19 @@ export const SettingsScreen = observer(function SettingsScreen({ route }: Props)
toggleCurrencyModal()
}
- const onSelectTheme = async function(theme: ThemeCode) {
+ const onSelectTheme = async function(theme: ThemeCode) {
if(currentTheme !== theme) {
- try {
+ try {
userSettingsStore.setNextTheme(theme)
setCurrentTheme(theme)
setInfo(translate('settingsScreen_restartTheme'))
+ const targetIcon = theme === ThemeCode.GOLDEN ? 'Golden' : 'Default'
+ await changeIcon(targetIcon)
} catch (e: any) {
log.warn('[onSelectTheme]', e.message)
}
}
- toggleThemeModal()
+ toggleThemeModal()
}
diff --git a/src/screens/Wallet/ResultModalInfo.tsx b/src/screens/Wallet/ResultModalInfo.tsx
index ddb820f..fb51334 100644
--- a/src/screens/Wallet/ResultModalInfo.tsx
+++ b/src/screens/Wallet/ResultModalInfo.tsx
@@ -66,7 +66,7 @@ export const ResultModalInfo = function(props: {
const $iconContainer: ViewStyle = {
alignItems: 'center',
- justifyContent: 'center',
+ justifyContent: 'center',
width: 120,
height: 120,
}
\ No newline at end of file
diff --git a/src/services/keyChain.ts b/src/services/keyChain.ts
index f900193..6a2af5d 100644
--- a/src/services/keyChain.ts
+++ b/src/services/keyChain.ts
@@ -446,14 +446,7 @@ const authenticateOnAppStart = async function (isAuthOn: boolean): Promise {
export function useThemeColor(
colorName: keyof typeof colors.light & keyof typeof colors.dark & keyof typeof colors.golden,
) {
- const { userSettingsStore } = rootStoreInstance
-
- if(userSettingsStore.theme === ThemeCode.DEFAULT) {
+ // loadTheme() returns an in-memory cached value after first read — O(1), no MMKV overhead
+ const theme = MMKVStorage.loadTheme()
+
+ if(theme === ThemeCode.DEFAULT) {
const colorScheme = useColorScheme()
return colors[colorScheme][colorName] as ColorValue
}
- return colors[userSettingsStore.theme][colorName] as ColorValue
+ return colors[theme][colorName] as ColorValue
}
diff --git a/yarn.lock b/yarn.lock
index 78da8b7..ea3072d 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -14669,6 +14669,7 @@ __metadata:
react-native: 0.84.0
react-native-animated-pagination-dots: ^0.1.73
react-native-camera-kit: 17.0.1
+ react-native-change-icon: ^3.0.0
react-native-clean-project: ^4.0.3
react-native-dotenv: ^3.4.11
react-native-exit-app: ^2.0.0
@@ -16499,6 +16500,16 @@ __metadata:
languageName: node
linkType: hard
+"react-native-change-icon@npm:^3.0.0":
+ version: 3.2.0
+ resolution: "react-native-change-icon@npm:3.2.0"
+ peerDependencies:
+ react: "*"
+ react-native: "*"
+ checksum: d361e9e06fbd2969d85373b5e07531aa4af6a6472ede16928cd3afb0cf6bad6871a9bfa5022b5959ce50787aeb6664eea13e09cd8471ce97067cf7735ed6a20b
+ languageName: node
+ linkType: hard
+
"react-native-clean-project@npm:^4.0.3":
version: 4.0.3
resolution: "react-native-clean-project@npm:4.0.3"