From 5b20b243f703418dd1ccd27dcc91d6bd9ca886ed Mon Sep 17 00:00:00 2001 From: Fara Woolf Date: Wed, 18 Sep 2024 20:08:56 -0400 Subject: [PATCH] feat: settings security, closes leather-io/issues#304 --- apps/mobile/src/app/(home)/(tabs)/_layout.tsx | 6 +- .../src/app/(home)/create-new-wallet.tsx | 6 +- .../app/(home)/developer-console/index.tsx | 6 +- .../developer-console/wallet-manager.tsx | 2 +- .../src/app/(home)/settings/display/index.tsx | 15 +- .../app/(home)/settings/networks/index.tsx | 10 +- .../app/(home)/settings/security/index.tsx | 39 +- .../settings/security/security-cell.tsx | 29 + apps/mobile/src/app/_layout.tsx | 8 +- .../src/components/action-bar/index.tsx | 6 +- .../add-wallet/add-wallet-sheet.tsx | 6 +- .../components/browser/approval-ux-modal.tsx | 6 +- .../src/components/browser/browser-in-use.tsx | 6 +- .../headers/containers/blurred-header.tsx | 6 +- .../components/sheets/input-sheet.layout.tsx | 6 +- .../sheets/notify-user-sheet.layout.tsx | 6 +- .../sheets/warning-sheet.layout.tsx | 6 +- .../src/features/account-selector-sheet.tsx | 10 +- .../account-identifier-sheet.tsx | 2 +- .../src/features/settings/analytics-sheet.tsx | 29 +- .../settings/app-authentication-sheet.tsx | 29 +- .../bitcoin-unit-sheet/bitcoin-unit-sheet.tsx | 4 +- .../conversion-unit-cell.tsx | 12 +- .../conversion-unit-sheet.tsx | 8 +- .../features/settings/email-address-sheet.tsx | 6 +- .../settings/email-notifications-sheet.tsx | 6 +- .../settings/push-notifications-sheet.tsx | 6 +- .../settings/settings-sheet.layout.tsx | 6 +- .../settings/theme-sheet/theme-cell.tsx | 8 +- .../settings/theme-sheet/theme-sheet.tsx | 10 +- apps/mobile/src/hooks/create-wallet.ts | 10 +- apps/mobile/src/locales/en/messages.po | 78 +- .../src/locales/pseudo-locale/messages.po | 78 +- .../bitcoin/bitcoin-keychains.read.ts | 4 +- .../keychains/stacks/stacks-keychains.read.ts | 4 +- .../src/store/settings/settings.read.ts | 43 + apps/mobile/src/store/settings/settings.ts | 127 + .../src/store/settings/settings.write.ts | 155 +- apps/mobile/src/store/utils.ts | 4 +- apps/mobile/src/utils/when-theme.ts | 2 +- packages/constants/src/index.ts | 21 +- packages/models/src/index.ts | 1 + packages/models/src/settings.model.ts | 10 + packages/models/src/types.ts | 7 - packages/ui/native.ts | 1 + .../ui/src/assets/icons/terminal-16-16.svg | 2 +- .../ui/src/assets/icons/terminal-24-24.svg | 2 +- .../src/components/switch/switch.native.tsx | 28 + pnpm-lock.yaml | 2957 +++-------------- 49 files changed, 1043 insertions(+), 2796 deletions(-) create mode 100644 apps/mobile/src/app/(home)/settings/security/security-cell.tsx create mode 100644 apps/mobile/src/store/settings/settings.read.ts create mode 100644 apps/mobile/src/store/settings/settings.ts create mode 100644 packages/models/src/settings.model.ts create mode 100644 packages/ui/src/components/switch/switch.native.tsx diff --git a/apps/mobile/src/app/(home)/(tabs)/_layout.tsx b/apps/mobile/src/app/(home)/(tabs)/_layout.tsx index 94bc1ce37..36e26fda1 100644 --- a/apps/mobile/src/app/(home)/(tabs)/_layout.tsx +++ b/apps/mobile/src/app/(home)/(tabs)/_layout.tsx @@ -6,7 +6,7 @@ import { ActionBarContainer, ActionBarContext } from '@/components/action-bar/co import { BlurredHeader } from '@/components/headers/containers/blurred-header'; import { TabBar } from '@/components/tab-bar'; import { AppRoutes } from '@/routes'; -import { useSettings } from '@/store/settings/settings.write'; +import { useSettings } from '@/store/settings/settings'; import { t } from '@lingui/macro'; import { Tabs, usePathname, useRouter } from 'expo-router'; @@ -88,7 +88,7 @@ export default function TabLayout() { const ref = useRef(null); const bottomSheetRef = useRef(null); const insets = useSafeAreaInsets(); - const { theme: themeVariant } = useSettings(); + const { themeDerivedFromThemePreference } = useSettings(); const NavigationHeader = ( - + {t`Dummy modal text 🎉 Add blocks to see responsive modal`} diff --git a/apps/mobile/src/app/(home)/create-new-wallet.tsx b/apps/mobile/src/app/(home)/create-new-wallet.tsx index b550572f7..f28231934 100644 --- a/apps/mobile/src/app/(home)/create-new-wallet.tsx +++ b/apps/mobile/src/app/(home)/create-new-wallet.tsx @@ -4,7 +4,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { MnemonicDisplay } from '@/components/create-new-wallet/mnemonic-display'; import { useCreateWallet } from '@/hooks/create-wallet'; -import { useSettings } from '@/store/settings/settings.write'; +import { useSettings } from '@/store/settings/settings'; import { tempMnemonicStore } from '@/store/storage-persistors'; import { Trans, t } from '@lingui/macro'; import { useTheme } from '@shopify/restyle'; @@ -25,7 +25,7 @@ export default function CreateNewWallet() { const { bottom } = useSafeAreaInsets(); const theme = useTheme(); - const { theme: themeVariant } = useSettings(); + const { themeDerivedFromThemePreference } = useSettings(); const [isHidden, setIsHidden] = useState(true); const [mnemonic, setMnemonic] = useState(null); const { navigateAndCreateWallet } = useCreateWallet(); @@ -71,7 +71,7 @@ export default function CreateNewWallet() { {isHidden && ( - + addWalletSheetRef.current?.present()} title={t`Create wallet`} diff --git a/apps/mobile/src/app/(home)/developer-console/wallet-manager.tsx b/apps/mobile/src/app/(home)/developer-console/wallet-manager.tsx index cce2fca0c..f84f4ea81 100644 --- a/apps/mobile/src/app/(home)/developer-console/wallet-manager.tsx +++ b/apps/mobile/src/app/(home)/developer-console/wallet-manager.tsx @@ -4,7 +4,7 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { WalletList } from '@/components/wallet-manager'; import { useKeyStore } from '@/store/key-store'; -import { useSettings } from '@/store/settings/settings.write'; +import { useSettings } from '@/store/settings/settings'; import { clearAllPersistedStorage } from '@/store/utils'; import { useWallets } from '@/store/wallets/wallets.read'; import { nextAnimationFrame } from '@/utils/next-animation-frame'; diff --git a/apps/mobile/src/app/(home)/settings/display/index.tsx b/apps/mobile/src/app/(home)/settings/display/index.tsx index d731f7bd2..e98d04b4c 100644 --- a/apps/mobile/src/app/(home)/settings/display/index.tsx +++ b/apps/mobile/src/app/(home)/settings/display/index.tsx @@ -4,7 +4,7 @@ import { AccountIdentifierSheet } from '@/features/settings/account-identifier/a import { BitcoinUnitSheet } from '@/features/settings/bitcoin-unit-sheet/bitcoin-unit-sheet'; import { ConversionUnitSheet } from '@/features/settings/conversion-unit-sheet/conversion-unit-sheet'; import { ThemeSheet } from '@/features/settings/theme-sheet/theme-sheet'; -import { useSettings } from '@/store/settings/settings.write'; +import { useSettings } from '@/store/settings/settings'; import { t } from '@lingui/macro'; import { useLingui } from '@lingui/react'; @@ -26,7 +26,12 @@ export default function SettingsDisplayScreen() { const bitcoinUnitSheetRef = useRef(null); const conversionUnitSheetRef = useRef(null); const accountIdentifierSheetRef = useRef(null); - const { accountDisplayPreference, bitcoinUnit, conversionUnit, themeStore } = useSettings(); + const { + accountDisplayPreference, + bitcoinUnitPreference, + fiatCurrencyPreference, + themePreference, + } = useSettings(); const { i18n } = useLingui(); return ( @@ -34,7 +39,7 @@ export default function SettingsDisplayScreen() { } onCreateSheetRef={() => { themeSheetRef.current?.present(); @@ -42,7 +47,7 @@ export default function SettingsDisplayScreen() { /> } onCreateSheetRef={() => { bitcoinUnitSheetRef.current?.present(); @@ -50,7 +55,7 @@ export default function SettingsDisplayScreen() { /> } onCreateSheetRef={() => { conversionUnitSheetRef.current?.present(); diff --git a/apps/mobile/src/app/(home)/settings/networks/index.tsx b/apps/mobile/src/app/(home)/settings/networks/index.tsx index cc21e053c..d86a5e3d0 100644 --- a/apps/mobile/src/app/(home)/settings/networks/index.tsx +++ b/apps/mobile/src/app/(home)/settings/networks/index.tsx @@ -1,5 +1,5 @@ import { useToastContext } from '@/components/toast/toast-context'; -import { defaultNetworks, useSettings } from '@/store/settings/settings.write'; +import { defaultNetworkPreferences, useSettings } from '@/store/settings/settings'; import { t } from '@lingui/macro'; import { useLingui } from '@lingui/react'; @@ -32,18 +32,18 @@ export default function SettingsNetworksScreen() { const { i18n } = useLingui(); function onChangeNetwork(network: DefaultNetworkConfigurations) { - settings.changeNetwork(network); + settings.changeNetworkPreference(network); displayToast({ title: t`Network changed`, type: 'success' }); } return ( - {defaultNetworks.map(network => ( + {defaultNetworkPreferences.map(network => ( onChangeNetwork(network)} title={i18n._(capitalize(network))} /> diff --git a/apps/mobile/src/app/(home)/settings/security/index.tsx b/apps/mobile/src/app/(home)/settings/security/index.tsx index 1c7e16896..459027f4b 100644 --- a/apps/mobile/src/app/(home)/settings/security/index.tsx +++ b/apps/mobile/src/app/(home)/settings/security/index.tsx @@ -1,47 +1,40 @@ import { useRef } from 'react'; -import { ScrollView } from 'react-native-gesture-handler'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { AnalyticsSheet } from '@/features/settings/analytics-sheet'; import { AppAuthenticationSheet } from '@/features/settings/app-authentication-sheet'; +import { useSettings } from '@/store/settings/settings'; import { t } from '@lingui/macro'; -import { useTheme } from '@shopify/restyle'; -import { Box, Cell, CookieIcon, KeyholeIcon, SheetRef, Theme } from '@leather.io/ui/native'; +import { Box, CookieIcon, KeyholeIcon, SheetRef } from '@leather.io/ui/native'; + +import SettingsScreenLayout from '../settings-screen.layout'; +import { SecurityCell } from './security-cell'; export default function SettingsSecurityScreen() { - const { bottom } = useSafeAreaInsets(); const analyticsSheetRef = useRef(null); const appAuthenticationSheetRef = useRef(null); - const theme = useTheme(); + const settings = useSettings(); return ( - - + { + caption={settings.analyticsPreference === 'consent-given' ? t`Enabled` : t`Disabled`} + icon={} + onCreateSheetRef={() => { analyticsSheetRef.current?.present(); }} /> - { + caption={settings.securityLevelPreference === 'secure' ? t`Enabled` : t`Disabled`} + icon={} + onCreateSheetRef={() => { appAuthenticationSheetRef.current?.present(); }} /> - + diff --git a/apps/mobile/src/app/(home)/settings/security/security-cell.tsx b/apps/mobile/src/app/(home)/settings/security/security-cell.tsx new file mode 100644 index 000000000..4327565d0 --- /dev/null +++ b/apps/mobile/src/app/(home)/settings/security/security-cell.tsx @@ -0,0 +1,29 @@ +import { ReactNode } from 'react'; + +import { + Avatar, + ChevronRightIcon, + Flag, + ItemLayout, + TouchableOpacity, +} from '@leather.io/ui/native'; + +interface SecurityCellProps { + caption: string; + icon: ReactNode; + onCreateSheetRef(): void; + title: string; +} +export function SecurityCell({ caption, icon, onCreateSheetRef, title }: SecurityCellProps) { + return ( + + {icon}}> + } + captionLeft={caption} + titleLeft={title} + /> + + + ); +} diff --git a/apps/mobile/src/app/_layout.tsx b/apps/mobile/src/app/_layout.tsx index 0d18dd6c0..a946f4c2d 100644 --- a/apps/mobile/src/app/_layout.tsx +++ b/apps/mobile/src/app/_layout.tsx @@ -8,7 +8,7 @@ import { ToastWrapper } from '@/components/toast/toast-context'; import { initiateI18n } from '@/locales'; import { queryClient } from '@/queries/query'; import { persistor, store } from '@/store'; -import { useSettings } from '@/store/settings/settings.write'; +import { useSettings } from '@/store/settings/settings'; import { HasChildren } from '@/utils/types'; import { i18n } from '@lingui/core'; import { I18nProvider } from '@lingui/react'; @@ -62,8 +62,10 @@ export default function RootLayout() { } function ThemeProvider({ children }: HasChildren) { - const { theme } = useSettings(); - return {children}; + const { themeDerivedFromThemePreference } = useSettings(); + return ( + {children} + ); } function AppRouter() { diff --git a/apps/mobile/src/components/action-bar/index.tsx b/apps/mobile/src/components/action-bar/index.tsx index c5ab3721b..2268404dd 100644 --- a/apps/mobile/src/components/action-bar/index.tsx +++ b/apps/mobile/src/components/action-bar/index.tsx @@ -7,7 +7,7 @@ import Animated, { } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; -import { useSettings } from '@/store/settings/settings.write'; +import { useSettings } from '@/store/settings/settings'; import { useTheme } from '@shopify/restyle'; import { BlurView, Box, Theme } from '@leather.io/ui/native'; @@ -31,7 +31,7 @@ export interface ActionBarMethods { export const ActionBar = forwardRef(function (props, ref) { const { bottom } = useSafeAreaInsets(); const theme = useTheme(); - const { theme: themeVariant } = useSettings(); + const { themeDerivedFromThemePreference } = useSettings(); const animatedBottom = useSharedValue(ACTION_BAR_BOTTOM_OFFSET + bottom); const animatedOpacity = useSharedValue(1); @@ -85,7 +85,7 @@ export const ActionBar = forwardRef(function ( > (null); - const { theme: themeVariant } = useSettings(); + const { themeDerivedFromThemePreference } = useSettings(); const router = useRouter(); const [optionData, setOptionData] = useState(null); const createWallet = useCallback(() => { @@ -43,7 +43,7 @@ export function AddWalletSheet({ addWalletSheetRef }: AddWalletSheetBaseProps) { createWallet={createWallet} restoreWallet={restoreWallet} addWalletSheetRef={addWalletSheetRef} - themeVariant={themeVariant} + themeVariant={themeDerivedFromThemePreference} /> (null); const stacksAccount = useStacksSigners().fromAccountIndex(fingerprint, accountIndex)[0]; @@ -58,7 +58,7 @@ export function ApproverSheet(props: ApproverSheetProps) { } return ( - +