Skip to content

Commit

Permalink
Connect Confirm connection modal
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeljscript committed May 3, 2024
1 parent 51516ec commit 49605fe
Show file tree
Hide file tree
Showing 13 changed files with 440 additions and 70 deletions.
1 change: 1 addition & 0 deletions apps/wallet-mobile/.storybook/storybook.requires.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions apps/wallet-mobile/src/components/Icon/Connection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react'
import Svg, {Path} from 'react-native-svg'

type Props = {
size?: number
color?: string
}

export const Connection = ({size = 36, color = '#000000'}: Props) => {
return (
<Svg width={size} height={size} viewBox="0 0 21 20" fill="none">
<Path
d="M16.207.293a1 1 0 10-1.414 1.414L17.086 4H7.5a1 1 0 000 2h9.586l-2.293 2.293a1 1 0 001.414 1.414l4-4a1 1 0 000-1.414l-4-4zM6.207 10.293a1 1 0 010 1.414L3.914 14H13.5a1 1 0 110 2H3.914l2.293 2.293a1 1 0 11-1.414 1.414l-4-4a1 1 0 010-1.414l4-4a1 1 0 011.414 0z"
fill={color}
/>
</Svg>
)
}
4 changes: 4 additions & 0 deletions apps/wallet-mobile/src/components/Icon/Icon.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,10 @@ storiesOf('Icon', module).add('Gallery', () => {
<Item icon={<Icon.Square />} title="Square" />

<Item icon={<Icon.Google />} title="Google" />

<Item icon={<Icon.Connection />} title="Connection" />

<Item icon={<Icon.YoroiApp />} title="YoroiApp" />
</ScrollView>
</FilterProvider>
)
Expand Down
31 changes: 31 additions & 0 deletions apps/wallet-mobile/src/components/Icon/YoroiApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'

Check failure on line 1 in apps/wallet-mobile/src/components/Icon/YoroiApp.tsx

View workflow job for this annotation

GitHub Actions / check

Run autofix to sort these imports!
import Svg, {Path, Rect, Defs, LinearGradient, Stop} from 'react-native-svg'

type Props = {
size?: number
}

export const YoroiApp = ({size = 36}: Props) => {
return (
<Svg width={size} height={size} viewBox="0 0 49 48" fill="none">
<Rect x={0.5} width={48} height={48} rx={8} fill="url(#paint0_linear_16084_126466)" />
<Path

Check warning on line 12 in apps/wallet-mobile/src/components/Icon/YoroiApp.tsx

View workflow job for this annotation

GitHub Actions / check

JSX element should start in a new line
d="M23.702 21.15L10 11h4.304l10.16 7.293L34.674 11H39L25.23 21.15a1.318 1.318 0 01-1.527 0zM29.06 32.771L12.298 20.762v-3.44l19.465 13.834-2.703 1.615zM29.871 25.748l5.948-4.424v-3.3l-8.246 6.039 2.298 1.685zM35.143 29.54l.676-.561v-3.23l-2.974 2.106 2.298 1.686zM12.298 28.205L23.383 36l2.77-1.545-13.855-9.832v3.582z"
fill="#fff"
/>
<Defs>

Check warning on line 16 in apps/wallet-mobile/src/components/Icon/YoroiApp.tsx

View workflow job for this annotation

GitHub Actions / check

JSX element should start in a new line
<LinearGradient
id="paint0_linear_16084_126466"
x1={50.6518}
y1={77.0646}
x2={108.781}
y2={-23.2391}
gradientUnits="userSpaceOnUse"
>
<Stop stopColor="#244ABF" />
<Stop offset={1} stopColor="#4760FF" />

Check warning on line 26 in apps/wallet-mobile/src/components/Icon/YoroiApp.tsx

View workflow job for this annotation

GitHub Actions / check

JSX element should start in a new line
</LinearGradient>
</Defs>
</Svg>
)
}
4 changes: 4 additions & 0 deletions apps/wallet-mobile/src/components/Icon/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ import {Warning} from './Warning'
import {WingRiders} from './WingRiders'
import {YoroiNightly} from './YoroiNightly'
import {YoroiWallet} from './YoroiWallet'
import {Connection} from './Connection'
import {YoroiApp} from './YoroiApp'

export const Icon = {
Ada,
Expand Down Expand Up @@ -244,4 +246,6 @@ export const Icon = {
Reload,
Square,
Google,
Connection,
YoroiApp,
}
26 changes: 21 additions & 5 deletions apps/wallet-mobile/src/components/Modal/ModalContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {useNavigation} from '@react-navigation/native'
import {NavigationProp, useNavigation} from '@react-navigation/native'
import React from 'react'
import {Keyboard} from 'react-native'

Expand All @@ -10,7 +10,12 @@ type ModalState = {
isLoading: boolean
}
type ModalActions = {
openModal: (title: ModalState['title'], content: ModalState['content'], height?: ModalState['height']) => void
openModal: (
title: ModalState['title'],
content: ModalState['content'],
height?: ModalState['height'],
onClose?: () => void,
) => void
closeModal: () => void
startLoading: () => void
stopLoading: () => void
Expand All @@ -35,18 +40,25 @@ export const ModalProvider = ({
}) => {
const [state, dispatch] = React.useReducer(modalReducer, {...defaultState, ...initialState})
const navigation = useNavigation()
const onCloseRef = React.useRef<() => void>()
const actions = React.useRef<ModalActions>({
closeModal: () => {
const lastRouteName = navigation.getState().routes.slice(-1)[0].name
if (lastRouteName === 'modal') {
if (getLastRouteName(navigation) === 'modal') {
dispatch({type: 'close'})
navigation.goBack()
onCloseRef.current?.()
}
},
openModal: (title: ModalState['title'], content: ModalState['content'], height?: ModalState['height']) => {
openModal: (
title: ModalState['title'],
content: ModalState['content'],
height?: ModalState['height'],
onClose?: () => void,
) => {
Keyboard.dismiss()
dispatch({type: 'open', title, content, height})
navigation.navigate('modal')
onCloseRef.current = onClose
},
startLoading: () => dispatch({type: 'startLoading'}),
stopLoading: () => dispatch({type: 'stopLoading'}),
Expand Down Expand Up @@ -96,3 +108,7 @@ const defaultState: ModalState = Object.freeze({
isOpen: false,
isLoading: false,
})

const getLastRouteName = (navigation: NavigationProp<ReactNavigation.RootParamList>) => {
return navigation.getState().routes.slice(-1)[0].name
}
33 changes: 29 additions & 4 deletions apps/wallet-mobile/src/features/Discover/DiscoverNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {createStackNavigator} from '@react-navigation/stack'

Check failure on line 1 in apps/wallet-mobile/src/features/Discover/DiscoverNavigator.tsx

View workflow job for this annotation

GitHub Actions / check

Run autofix to sort these imports!
import {useAsyncStorage} from '@yoroi/common'
import {DappConnectorProvider} from '@yoroi/dapp-connector'
import {DappConnectorProvider, DappConnector} from '@yoroi/dapp-connector'
import {useTheme} from '@yoroi/theme'
import * as React from 'react'

Expand All @@ -13,16 +13,15 @@ import {createDappConnector} from './common/helpers'
import {useStrings} from './common/useStrings'
import {ListSkeleton} from './useCases/SelectDappFromList/ListSkeleton'
import {SelectDappFromListScreen} from './useCases/SelectDappFromList/SelectDappFromListScreen'
import {useOpenConfirmConnectionModal} from './common/ConfirmConnectionModal'

const Stack = createStackNavigator<DiscoverRoutes>()

export const DiscoverNavigator = () => {
const {atoms, color} = useTheme()
const strings = useStrings()

const appStorage = useAsyncStorage()
const wallet = useSelectedWallet()
const manager = React.useMemo(() => createDappConnector(appStorage, wallet), [appStorage, wallet])
const manager = useDappConnectorManager()

return (
<DappConnectorProvider manager={manager}>
Expand Down Expand Up @@ -50,3 +49,29 @@ export const DiscoverNavigator = () => {
</DappConnectorProvider>
)
}

const useDappConnectorManager = () => {
const appStorage = useAsyncStorage()
const wallet = useSelectedWallet()
const {openConfirmConnectionModal} = useOpenConfirmConnectionModal()
const confirmConnection = React.useCallback(
async (origin: string, manager: DappConnector) => {
const recommendedDApps = await manager.getDAppList()
const selectedDapp = recommendedDApps.dapps.find((dapp) => dapp.origins.includes(origin))
return new Promise<boolean>((resolve) => {
openConfirmConnectionModal({
name: selectedDapp?.name ?? origin,
website: origin,
logo: selectedDapp?.logo ?? '', // TODO: Add placeholder
onConfirm: () => resolve(true),
onClose: () => {
console.log('confirmConnection onClose') // TODO: Fix close
resolve(false)
},
})
})
},
[openConfirmConnectionModal],
)
return createDappConnector({appStorage, wallet, confirmConnection})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import {storiesOf} from '@storybook/react-native'

Check failure on line 1 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.stories.tsx

View workflow job for this annotation

GitHub Actions / check

Run autofix to sort these imports!
import * as React from 'react'
import {ConfirmConnectionModal, useOpenConfirmConnectionModal} from './ConfirmConnectionModal'
import {action} from '@storybook/addon-actions'
import {Button, ModalProvider, Spacer} from '../../../components'

Check failure on line 5 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.stories.tsx

View workflow job for this annotation

GitHub Actions / check

'ModalProvider' is defined but never used. Allowed unused vars must match /^_/u

Check failure on line 5 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.stories.tsx

View workflow job for this annotation

GitHub Actions / check

'Spacer' is defined but never used. Allowed unused vars must match /^_/u
import {View} from 'react-native'

storiesOf('Discover ConfirmConnectionModal', module)
.addDecorator((story) => <View style={{padding: 20}}>{story()}</View>)
.add('initial', () => <Initial />)
.add('With Button', () => <WithButton />)

const Initial = () => {
return (
<ConfirmConnectionModal
logo={'https://daehx1qv45z7c.cloudfront.net/cardano-spot.png'}

Check warning on line 16 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.stories.tsx

View workflow job for this annotation

GitHub Actions / check

Curly braces are unnecessary here
onConfirm={action('onConfirm')}
name="Example DApp"
website={'example.com'}

Check warning on line 19 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.stories.tsx

View workflow job for this annotation

GitHub Actions / check

Curly braces are unnecessary here
/>
)
}

const WithButton = () => {
const {openConfirmConnectionModal} = useOpenConfirmConnectionModal()

const handleOnPress = () => {
openConfirmConnectionModal({
onConfirm: action('onConfirm'),
website: 'example.com',
name: 'Example DApp',
logo: 'https://daehx1qv45z7c.cloudfront.net/cardano-spot.png',
onClose: action('onClose'),
})
}

return <Button title={'Open Modal'} shelleyTheme onPress={handleOnPress} />

Check warning on line 37 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.stories.tsx

View workflow job for this annotation

GitHub Actions / check

Curly braces are unnecessary here
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as React from 'react'

Check failure on line 1 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.tsx

View workflow job for this annotation

GitHub Actions / check

Run autofix to sort these imports!
import {View, Text, Image, StyleSheet} from 'react-native'
import {Button, Icon, Spacer, useModal} from '../../../components'
import {useTheme} from '@yoroi/theme'
import {useStrings} from './useStrings'

type Props = {
name: string
website: string
logo: string
onConfirm: () => void
}

export const confirmConnectionModalHeight = 400

export const useOpenConfirmConnectionModal = () => {
const {openModal, closeModal} = useModal()
const strings = useStrings()
const open = React.useCallback((props: Props & {onClose: () => void}) => {
openModal(
strings.confirmConnectionModalTitle,
<ConfirmConnectionModal
name={props.name}
website={props.website}
logo={props.logo}
onConfirm={() => {
props.onConfirm()
closeModal()
}}
/>,
confirmConnectionModalHeight,
props.onClose,
)
}, [])

Check warning on line 34 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.tsx

View workflow job for this annotation

GitHub Actions / check

React Hook React.useCallback has missing dependencies: 'closeModal', 'openModal', and 'strings.confirmConnectionModalTitle'. Either include them or remove the dependency array
return {openConfirmConnectionModal: open, closeModal}
}

export const ConfirmConnectionModal = ({name, website, onConfirm, logo}: Props) => {
const {styles, colors} = useStyles()
const strings = useStrings()

return (
<View>
<View style={styles.imagesLine}>
<Icon.YoroiApp size={48} />
<Icon.Connection size={20} color={colors.connection} />

Check warning on line 46 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.tsx

View workflow job for this annotation

GitHub Actions / check

JSX element should start in a new line
<Image source={{uri: logo}} style={styles.image} />

Check warning on line 47 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.tsx

View workflow job for this annotation

GitHub Actions / check

JSX element should start in a new line
</View>
<Spacer height={8} />

Check warning on line 49 in apps/wallet-mobile/src/features/Discover/common/ConfirmConnectionModal.tsx

View workflow job for this annotation

GitHub Actions / check

JSX element should start in a new line

<View style={styles.line}>
<Text style={styles.text}>{strings.confirmConnectionModalConnectTo}</Text>
<Text style={styles.bold}>{name}</Text>
</View>
<Spacer height={8} />
<View style={styles.line}>
<Text style={styles.text}>{website}</Text>
</View>
<Spacer height={16} />

<View>
<Text style={styles.text}>{strings.confirmConnectionModalAllowThisDAppTo}</Text>
<Text style={styles.text}>{`\u2022 ${strings.confirmConnectionModalPermission1}`}</Text>
<Text style={styles.text}>{`\u2022 ${strings.confirmConnectionModalPermission2}`}</Text>
</View>
<Spacer height={46} />
<Button title={strings.confirmConnectionModalConnect} shelleyTheme onPress={onConfirm} />
</View>
)
}

const useStyles = () => {
const theme = useTheme()
const colors = {connection: theme.color.black_static}
const styles = StyleSheet.create({
imagesLine: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 24,
},
image: {
width: 48,
height: 48,
},
line: {
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 4,
},
text: {
color: theme.color.gray_c900,
fontSize: 16,
lineHeight: 24,
},
bold: {
fontWeight: 'bold',
color: theme.color.gray_c900,
fontSize: 16,
lineHeight: 24,
},
})
return {styles, colors}
}
24 changes: 12 additions & 12 deletions apps/wallet-mobile/src/features/Discover/common/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import {connectionStorageMaker, dappConnectorApiMaker, dappConnectorMaker, ResolverWallet} from '@yoroi/dapp-connector'

Check failure on line 1 in apps/wallet-mobile/src/features/Discover/common/helpers.ts

View workflow job for this annotation

GitHub Actions / check

Run autofix to sort these imports!
import {App} from '@yoroi/types'
import {Alert} from 'react-native'

import {YoroiWallet} from '../../../yoroi-wallets/cardano/types'
import {DappConnector} from '@yoroi/dapp-connector'

export const validUrl = (url: string) => {
return /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w.-]+)+[\w\-._~:/?#[\]@!&',,=.+]+$/g.test(url)
Expand Down Expand Up @@ -56,7 +56,14 @@ export const getGoogleSearchItem = (searchQuery: string): DAppItem => ({

export const isGoogleSearchItem = (dApp: DAppItem) => dApp.id === googleDappId

export const createDappConnector = (appStorage: App.Storage, wallet: YoroiWallet) => {
type CreateDappConnectorOptions = {
appStorage: App.Storage
wallet: YoroiWallet
confirmConnection: (origin: string, manager: DappConnector) => Promise<boolean>
}

export const createDappConnector = (options: CreateDappConnectorOptions) => {
const {wallet, appStorage, confirmConnection} = options
const api = dappConnectorApiMaker()
const handlerWallet: ResolverWallet = {
id: wallet.id,
Expand All @@ -79,16 +86,9 @@ export const createDappConnector = (appStorage: App.Storage, wallet: YoroiWallet
if (!result) return [] // TODO: return null if value was given
return Promise.all(result.map((v) => v.toHex()))
},
confirmConnection: async (origin: string) => {
return new Promise<boolean>((resolve) => {
// TODO: Use modal with translations here instead of alert
Alert.alert('Confirm connection', `Do you want to connect to ${origin}?`, [
{text: 'Cancel', style: 'cancel', onPress: () => resolve(false)},
{text: 'OK', onPress: () => resolve(true)},
])
})
},
confirmConnection: (origin: string) => confirmConnection(origin, manager),
}
const storage = connectionStorageMaker({storage: appStorage.join('dapp-connections/')})
return dappConnectorMaker(storage, handlerWallet, api)
const manager = dappConnectorMaker(storage, handlerWallet, api)
return manager
}

0 comments on commit 49605fe

Please sign in to comment.