Skip to content

Commit

Permalink
feat(mobile): add getting started screen
Browse files Browse the repository at this point in the history
feat(mobile): add terms and privacy policy links to getting started
  • Loading branch information
compojoom committed Feb 6, 2025
1 parent 91d7db7 commit 711aa0d
Show file tree
Hide file tree
Showing 33 changed files with 374 additions and 132 deletions.
96 changes: 59 additions & 37 deletions apps/mobile/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { configureReanimatedLogger, ReanimatedLogLevel } from 'react-native-rean
import { OnboardingHeader } from '@/src/features/Onboarding/components/OnboardingHeader'
import { install } from 'react-native-quick-crypto'
import { getDefaultScreenOptions } from '@/src/navigation/hooks/utils'
import { NavigationGuardHOC } from '@/src/navigation/NavigationGuardHOC'

install()

Expand All @@ -35,45 +36,66 @@ function RootLayout() {
<PersistGate loading={null} persistor={persistor}>
<SafeThemeProvider>
<SafeToastProvider>
<Stack
screenOptions={({ navigation }) => ({
...getDefaultScreenOptions(navigation.goBack),
})}
>
<Stack.Screen
name="index"
options={{
header: OnboardingHeader,
}}
/>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="(import-accounts)" options={{ headerShown: false, presentation: 'modal' }} />
<Stack.Screen name="pending-transactions" options={{ headerShown: true, title: '' }} />
<Stack.Screen name="notifications" options={{ headerShown: true, title: '' }} />
<NavigationGuardHOC>
<Stack
screenOptions={({ navigation }) => ({
...getDefaultScreenOptions(navigation.goBack),
})}
>
{/*<Stack.Screen name="index" />*/}
<Stack.Screen
name="onboarding"
options={{
header: OnboardingHeader,
}}
/>
<Stack.Screen
name="get-started"
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
}}
/>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen
name="(import-accounts)"
options={{ headerShown: false, presentation: 'modal' }}
/>
<Stack.Screen name="pending-transactions" options={{ headerShown: true, title: '' }} />
<Stack.Screen name="notifications" options={{ headerShown: true, title: '' }} />

<Stack.Screen name="signers" options={{ headerShown: false }} />
<Stack.Screen name="import-signers" options={{ headerShown: false }} />
<Stack.Screen name="signers" options={{ headerShown: false }} />
<Stack.Screen name="import-signers" options={{ headerShown: false }} />

<Stack.Screen name="app-settings" options={{ headerShown: true, title: 'Settings' }} />
<Stack.Screen
name="accounts-sheet"
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
}}
/>
<Stack.Screen
name="networks-sheet"
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
}}
/>
<Stack.Screen name="notifications-opt-in" options={{ headerShown: true, title: '' }} />
<Stack.Screen name="+not-found" />
</Stack>
<Stack.Screen name="app-settings" options={{ headerShown: true, title: 'Settings' }} />
<Stack.Screen
name="accounts-sheet"
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
}}
/>
<Stack.Screen
name="networks-sheet"
options={{
headerShown: false,
presentation: 'transparentModal',
animation: 'fade',
}}
/>
<Stack.Screen
name="notifications-opt-in"
options={{
headerShown: false,
presentation: 'modal',
title: '',
}}
/>
<Stack.Screen name="+not-found" />
</Stack>
</NavigationGuardHOC>
</SafeToastProvider>
</SafeThemeProvider>
</PersistGate>
Expand Down
8 changes: 8 additions & 0 deletions apps/mobile/app/get-started.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import React from 'react'
import { GetStarted } from '@/src/features/GetStarted'

function getStartedScreen() {
return <GetStarted />
}

export default getStartedScreen
21 changes: 17 additions & 4 deletions apps/mobile/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,21 @@
import { Onboarding } from '@/src/features/Onboarding'
import React from 'react'
import { View } from 'tamagui'
import { ActivityIndicator } from 'react-native'

function OnboardingPage() {
return <Onboarding />
/**
* This is a dummy screen. Expo automatically renders it when it constructs the app.
* If we don't have an index file it will pick whatever it sees fit. This is a placeholder.
*
* The actual navigation to either onboarding flow or a safe happens inside the NavigationGuardHOC
*
* @constructor
*/
function IndexScreen() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<ActivityIndicator />
</View>
)
}

export default OnboardingPage
export default IndexScreen
8 changes: 8 additions & 0 deletions apps/mobile/app/onboarding.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Onboarding } from '@/src/features/Onboarding'
import React from 'react'

function OnboardingPage() {
return <Onboarding />
}

export default OnboardingPage
9 changes: 9 additions & 0 deletions apps/mobile/src/components/SafeButton/SafeButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,15 @@ export const SafeButton = styled(Button, {
},
},

outlined: {
true: {
backgroundColor: 'transparent',
borderWidth: 2,
borderColor: '$color',
color: '$color',
},
},

text: {
true: {
backgroundColor: 'transparent',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const getTokenDetails = (txInfo: TransferTransactionInfo): tokenDetails => {
const unnamedToken = 'Unnamed token'
const nativeCurrency = useAppSelector(selectActiveChainCurrency)

if (isNativeTokenTransfer(transfer)) {
if (isNativeTokenTransfer(transfer) && nativeCurrency) {
return {
value: formatValue(transfer.value || '0', nativeCurrency.decimals),
// take it from the native currency slice
Expand Down
6 changes: 6 additions & 0 deletions apps/mobile/src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ export const GATEWAY_URL_PRODUCTION =
process.env.NEXT_PUBLIC_GATEWAY_URL_PRODUCTION || 'https://safe-client.safe.global'
export const GATEWAY_URL_STAGING = process.env.NEXT_PUBLIC_GATEWAY_URL_STAGING || 'https://safe-client.staging.5afe.dev'
export const GATEWAY_URL = isProduction ? GATEWAY_URL_PRODUCTION : GATEWAY_URL_STAGING

/**
* The version of the onboarding flow.
* If we change it and need all users to see it again, we can bump the version here.
*/
export const ONBOARDING_VERSION = 'v1'
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const useEditAccountItem = () => {

const onSafeDeleted = useCallback(
(address: Address) => () => {
if (activeSafe.address === address) {
if (activeSafe?.address === address) {
const safe = Object.values(safes).find((item) => item.SafeInfo.address.value !== address)

if (safe) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { AccountItem } from '../AccountItem'
import { SafesSliceItem } from '@/src/store/safesSlice'
import { Address } from '@/src/types/address'
import { useDispatch, useSelector } from 'react-redux'
import { selectActiveSafe, setActiveSafe } from '@/src/store/activeSafeSlice'
import { setActiveSafe } from '@/src/store/activeSafeSlice'
import { getChainsByIds } from '@/src/store/chains'
import { RootState } from '@/src/store'
import { useMyAccounts } from './hooks/useMyAccounts'
import { useDefinedActiveSafe } from '@/src/store/hooks/activeSafe'

interface MyAccountsContainerProps {
item: SafesSliceItem
Expand All @@ -20,7 +21,7 @@ export function MyAccountsContainer({ item, isDragging, drag, onClose }: MyAccou
useMyAccounts(item)

const dispatch = useDispatch()
const activeSafe = useSelector(selectActiveSafe)
const activeSafe = useDefinedActiveSafe()
const filteredChains = useSelector((state: RootState) => getChainsByIds(state, item.chains))

const handleAccountSelected = () => {
Expand Down
23 changes: 1 addition & 22 deletions apps/mobile/src/features/Assets/Assets.container.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
import React, { useEffect } from 'react'
import React from 'react'

import { SafeTab } from '@/src/components/SafeTab'

import { TokensContainer } from '@/src/features/Assets/components/Tokens'
import { NFTsContainer } from '@/src/features/Assets/components/NFTs'
import { AssetsHeaderContainer } from '@/src/features/Assets/components/AssetsHeader'
import useNotifications from '@/src/hooks/useNotifications'
import { useRouter } from 'expo-router'
import { useAppDispatch } from '@/src/store/hooks'
import { updatePromptAttempts } from '@/src/store/notificationsSlice'

const tabItems = [
{
Expand All @@ -22,22 +18,5 @@ const tabItems = [
]

export function AssetsContainer() {
const { isAppNotificationEnabled, promptAttempts } = useNotifications()
const dispatch = useAppDispatch()
const router = useRouter()

/*
* If the user has not enabled notifications and has not been prompted to enable them,
* redirect to the opt-in screen
* */

const shouldShowOptIn = !isAppNotificationEnabled && !promptAttempts

useEffect(() => {
if (shouldShowOptIn) {
dispatch(updatePromptAttempts(1))
router.navigate('/notifications-opt-in')
}
}, [])
return <SafeTab items={tabItems} headerHeight={200} renderHeader={AssetsHeaderContainer} />
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { selectActiveSafe } from '@/src/store/activeSafeSlice'
import { SafeOverviewResult } from '@safe-global/store/gateway/types'
import { POLLING_INTERVAL } from '@/src/config/constants'
import { getChainsByIds, selectAllChains, selectChainById } from '@/src/store/chains'
Expand All @@ -10,10 +9,11 @@ import { useAppSelector } from '@/src/store/hooks'
import { useSafesGetOverviewForManyQuery } from '@safe-global/store/gateway/safes'
import React from 'react'
import { useSelector } from 'react-redux'
import { useDefinedActiveSafe } from '@/src/store/hooks/activeSafe'

export function BalanceContainer() {
const chains = useAppSelector(selectAllChains)
const activeSafe = useAppSelector(selectActiveSafe)
const activeSafe = useDefinedActiveSafe()
const activeSafeInfo = useAppSelector((state: RootState) => selectSafeInfo(state, activeSafe.address))
const activeSafeChains = useAppSelector((state: RootState) => getChainsByIds(state, activeSafeInfo.chains))
const { data, isLoading } = useSafesGetOverviewForManyQuery<SafeOverviewResult>(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { safelyDecodeURIComponent } from 'expo-router/build/fork/getStateFromPath-forks'
import React, { useState } from 'react'
import { useSelector } from 'react-redux'

import { SafeTab } from '@/src/components/SafeTab'
import { POLLING_INTERVAL } from '@/src/config/constants'
import { selectActiveSafe } from '@/src/store/activeSafeSlice'
import {
Collectible,
CollectiblePage,
Expand All @@ -14,9 +12,10 @@ import {
import { Fallback } from '../Fallback'
import { NFTItem } from './NFTItem'
import { useInfiniteScroll } from '@/src/hooks/useInfiniteScroll'
import { useDefinedActiveSafe } from '@/src/store/hooks/activeSafe'

export function NFTsContainer() {
const activeSafe = useSelector(selectActiveSafe)
const activeSafe = useDefinedActiveSafe()
const [pageUrl, setPageUrl] = useState<string>()

const { data, isFetching, error, refetch } = useCollectiblesGetCollectiblesV2Query(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,21 @@ import { Balance, useBalancesGetBalancesV1Query } from '@safe-global/store/gatew
import { formatValue } from '@/src/utils/formatters'

import { Fallback } from '../Fallback'
import { skipToken } from '@reduxjs/toolkit/query'

export function TokensContainer() {
const activeSafe = useSelector(selectActiveSafe)

const { data, isFetching, error } = useBalancesGetBalancesV1Query(
{
chainId: activeSafe.chainId,
fiatCode: 'USD',
safeAddress: activeSafe.address,
excludeSpam: false,
trusted: true,
},
!activeSafe
? skipToken
: {
chainId: activeSafe.chainId,
fiatCode: 'USD',
safeAddress: activeSafe.address,
excludeSpam: false,
trusted: true,
},
{
pollingInterval: POLLING_INTERVAL,
},
Expand Down
67 changes: 67 additions & 0 deletions apps/mobile/src/features/GetStarted/GetStarted.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React from 'react'
import { Link, useRouter } from 'expo-router'
import { View, Text, YStack } from 'tamagui'
import { SafeButton } from '@/src/components/SafeButton'
import { SafeFontIcon } from '@/src/components/SafeFontIcon'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { BlurView } from 'expo-blur'

export const GetStarted = () => {
const router = useRouter()
const insets = useSafeAreaInsets()
return (
<YStack justifyContent={'flex-end'} flex={1}>
<BlurView intensity={100} style={{ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}>
<View
flex={1}
onPress={() => {
router.back()
}}
></View>
</BlurView>
<YStack
gap={'$3'}
paddingHorizontal={'$4'}
backgroundColor={'$background'}
paddingBottom={insets.bottom}
paddingTop={'$5'}
borderTopLeftRadius={'$9'}
borderTopRightRadius={'$9'}
>
<Text
fontSize={'$6'}
fontWeight={'600'}
textAlign={'center'}
marginBottom={'$2'}
paddingHorizontal={'$10'}
lineHeight={'$9'}
>
How would you like to continue?
</Text>
<SafeButton outlined icon={<SafeFontIcon name={'add-owner'} />}>
Join Account
</SafeButton>
<Link href={'/(import-accounts)'} asChild>
<SafeButton outlined icon={<SafeFontIcon name={'plus-outlined'} />}>
Add account
</SafeButton>
</Link>
<Text paddingHorizontal={'$10'} marginTop={'$2'} textAlign={'center'} fontSize={'$3'} color={'$colorSecondary'}>
By continuing, you agree to our{' '}
<Link href={'https://app.safe.global/terms'} target={'_blank'}>
<Text textDecorationLine={'underline'} color={'$colorSecondary'}>
User Terms
</Text>
</Link>{' '}
and{' '}
<Link href={'https://app.safe.global/privacy'} target={'_blank'} asChild>
<Text textDecorationLine={'underline'} color={'$colorSecondary'}>
Privacy Policy
</Text>
</Link>
.
</Text>
</YStack>
</YStack>
)
}
Loading

0 comments on commit 711aa0d

Please sign in to comment.