diff --git a/suite-native/app/app.config.ts b/suite-native/app/app.config.ts index ee615d02c001..59ef0dc55cc9 100644 --- a/suite-native/app/app.config.ts +++ b/suite-native/app/app.config.ts @@ -126,6 +126,8 @@ const getPlugins = (): ExpoPlugins => { }, ], ]), + + ['react-native-ble-plx', {}], // These should come last './plugins/withRemoveXcodeLocalEnv.js', ['./plugins/withEnvFile.js', { buildType }], diff --git a/suite-native/app/package.json b/suite-native/app/package.json index 6650c4e594f5..36d530e8307d 100644 --- a/suite-native/app/package.json +++ b/suite-native/app/package.json @@ -90,10 +90,12 @@ "react": "18.2.0", "react-intl": "^6.6.2", "react-native": "0.73.6", + "react-native-ble-plx": "^3.1.2", "react-native-flipper": "^0.212.0", "react-native-gesture-handler": "2.15.0", "react-native-keyboard-aware-scroll-view": "0.9.5", "react-native-mmkv": "2.11.0", + "react-native-permissions": "^4.1.5", "react-native-reanimated": "3.8.1", "react-native-restart": "0.0.27", "react-native-safe-area-context": "4.9.0", diff --git a/suite-native/bluetooth/package.json b/suite-native/bluetooth/package.json new file mode 100644 index 000000000000..687117c99a3a --- /dev/null +++ b/suite-native/bluetooth/package.json @@ -0,0 +1,24 @@ +{ + "name": "@suite-native/bluetooth", + "version": "1.0.0", + "private": true, + "license": "See LICENSE.md in repo root", + "sideEffects": false, + "main": "src/index", + "scripts": { + "lint:js": "yarn g:eslint '**/*.{ts,tsx,js}'", + "test:unit": "jest -c ../../jest.config.base.js", + "type-check": "yarn g:tsc --build" + }, + "dependencies": { + "@suite-native/atoms": "workspace:*", + "@suite-native/intl": "workspace:*", + "@trezor/styles": "workspace:*", + "@trezor/transport-native-ble": "workspace:*", + "expo-constants": "15.4.5", + "react": "18.2.0", + "react-native": "0.73.6", + "react-native-ble-plx": "^3.1.2", + "react-native-permissions": "^4.1.5" + } +} diff --git a/suite-native/bluetooth/src/components/BluetoothAdapterStateManager.tsx b/suite-native/bluetooth/src/components/BluetoothAdapterStateManager.tsx new file mode 100644 index 000000000000..adb2d4ba04fb --- /dev/null +++ b/suite-native/bluetooth/src/components/BluetoothAdapterStateManager.tsx @@ -0,0 +1,49 @@ +import { State as AdapterState } from 'react-native-ble-plx'; + +import { AlertBox, Button, Loader, VStack } from '@suite-native/atoms'; + +import { useBluetoothAdapterState } from '../hooks/useBluetoothAdapterState'; +import { BluetoothPermissionErrors } from '../hooks/useBluetoothPermissions'; +import { BluetoothPermissionError } from './BluetoothPermissionError'; + +export const BluetoothAdapterStateManager = () => { + const { bluetoothState, turnOnBluetooth } = useBluetoothAdapterState(); + + if (bluetoothState === AdapterState.PoweredOn) { + // We are good to go + return null; + } + + if (bluetoothState === AdapterState.Unknown || bluetoothState === AdapterState.Resetting) { + return ; + } + + if (bluetoothState === AdapterState.Unsupported) { + return ; + } + + if (bluetoothState === AdapterState.Unauthorized) { + return ( + + ); + } + + if (bluetoothState === AdapterState.PoweredOff) { + return ( + + + + + ); + } + + // Exhaustive check - this should never happen + const _exhaustiveCheck: never = bluetoothState; + + return _exhaustiveCheck; +}; diff --git a/suite-native/bluetooth/src/components/BluetoothPermissionError.tsx b/suite-native/bluetooth/src/components/BluetoothPermissionError.tsx new file mode 100644 index 000000000000..4479f2f352d0 --- /dev/null +++ b/suite-native/bluetooth/src/components/BluetoothPermissionError.tsx @@ -0,0 +1,33 @@ +import { openSettings } from 'react-native-permissions'; + +import { AlertBox, Button, VStack } from '@suite-native/atoms'; + +import { BluetoothPermissionErrors } from '../hooks/useBluetoothPermissions'; + +type BluetoothPermissionErrorProps = { + error: BluetoothPermissionErrors; +}; + +const ERROR_MESSAGES: Record = { + [BluetoothPermissionErrors.BluetoothAccessBlocked]: + 'Please enable Bluetooth permission for the app in your phone settings.', + [BluetoothPermissionErrors.LocationAccessBlocked]: 'Please enable Bluetooth on your phone', + [BluetoothPermissionErrors.NearbyDevicesAccessBlocked]: + 'Please enable Nearby Devices permission for the app in your phone settings.', +}; + +export const BluetoothPermissionError = ({ error }: BluetoothPermissionErrorProps) => { + const handleOpenSettings = async () => { + await openSettings(); + }; + + return ( + + + + + ); +}; diff --git a/suite-native/bluetooth/src/components/DevicesScanner.tsx b/suite-native/bluetooth/src/components/DevicesScanner.tsx new file mode 100644 index 000000000000..c3d77aa25aed --- /dev/null +++ b/suite-native/bluetooth/src/components/DevicesScanner.tsx @@ -0,0 +1,126 @@ +import { useEffect, useState } from 'react'; +import { State as AdapterState, BleError } from 'react-native-ble-plx'; + +import { AlertBox, Box, Button, Loader, Text, VStack } from '@suite-native/atoms'; +import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; +import { BLEScannedDevice, nativeBleManager } from '@trezor/transport-native-ble'; + +import { useBluetoothAdapterState } from '../hooks/useBluetoothAdapterState'; +import { useBluetoothPermissions } from '../hooks/useBluetoothPermissions'; +import { BluetoothAdapterStateManager } from './BluetoothAdapterStateManager'; +import { BluetoothPermissionError } from './BluetoothPermissionError'; +import { ScannedDeviceItem } from './ScannedDeviceItem'; + +const containerStyle = prepareNativeStyle(utils => ({ + flex: 1, + width: '100%', + paddingHorizontal: utils.spacings.medium, +})); + +export const DevicesScanner = () => { + const { applyStyle } = useNativeStyles(); + + const [scannedDevices, setScannedDevices] = useState([]); + const [scanError, setScanError] = useState(); + const [isScanRunning, setIsScanRunning] = useState(false); + const { hasBluetoothPermissions, requestBluetoothPermissions, bluetoothPermissionError } = + useBluetoothPermissions(); + const { bluetoothState } = useBluetoothAdapterState(); + + const stopScanning = async () => { + nativeBleManager.stopDeviceScan(); + setIsScanRunning(false); + setScannedDevices([]); + }; + + const scanDevices = () => { + setScanError(null); + setIsScanRunning(true); + setScannedDevices([]); + + nativeBleManager.scanDevices( + newlyScannedDevices => { + setScannedDevices(newlyScannedDevices); + }, + error => { + setScanError(error); + stopScanning(); + setScannedDevices([]); + }, + ); + }; + + const requestPermissions = () => { + return requestBluetoothPermissions(); + }; + + useEffect(() => { + return () => { + stopScanning(); + }; + }, []); + + const shouldShowRequestPermission = !hasBluetoothPermissions; + const shouldShowScanDevicesButton = + !isScanRunning && hasBluetoothPermissions && bluetoothState === AdapterState.PoweredOn; + const shouldShowBluetoothAdapterManager = + bluetoothState !== AdapterState.PoweredOn && !isScanRunning && hasBluetoothPermissions; + const shouldShowBluetoothPermissionError = !!bluetoothPermissionError; + + return ( + + + hasBluetoothPermissions: {hasBluetoothPermissions ? 'true' : 'false'} {'\n'} + bluetoothPermissionError: {bluetoothPermissionError} {'\n'} + bluetoothState: {bluetoothState} {'\n'} + isScanRunning: {isScanRunning ? 'true' : 'false'} {'\n'} + shouldShowRequestPermission: {shouldShowRequestPermission ? 'true' : 'false'} {'\n'} + + {shouldShowRequestPermission && ( + + + + + )} + {shouldShowBluetoothAdapterManager && } + + {shouldShowBluetoothPermissionError && ( + + )} + {shouldShowScanDevicesButton && } + {isScanRunning && ( + + + Scanning for devices... + + + + + )} + {scanError && ( + + )} + {isScanRunning && ( + + {scannedDevices.map(scannedDevice => ( + + ))} + + )} + + ); +}; diff --git a/suite-native/bluetooth/src/components/ScannedDeviceItem.tsx b/suite-native/bluetooth/src/components/ScannedDeviceItem.tsx new file mode 100644 index 000000000000..3d5d643afd62 --- /dev/null +++ b/suite-native/bluetooth/src/components/ScannedDeviceItem.tsx @@ -0,0 +1,111 @@ +import { useEffect, useState } from 'react'; +import { BleError, State as AdapterState } from 'react-native-ble-plx'; +import { Alert } from 'react-native'; + +import { AlertBox, Box, Button, HStack, Loader, Text, VStack } from '@suite-native/atoms'; +import { Translation } from '@suite-native/intl'; +import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; +import { BLEScannedDevice, nativeBleManager } from '@trezor/transport-native-ble'; +import { Icon, IconName } from '@suite-common/icons'; +import { useActiveColorScheme } from '@suite-native/theme'; + +import { + BluetoothPermissionErrors, + useBluetoothPermissions, +} from '../hooks/useBluetoothPermissions'; +import { BluetoothPermissionError } from './BluetoothPermissionError'; +import { useBluetoothAdapterState } from '../hooks/useBluetoothAdapterState'; +import { BluetoothAdapterStateManager } from './BluetoothAdapterStateManager'; + +type ContainerStylePayload = { + seenQuiteLongAgo: boolean; +}; +const containerStyle = prepareNativeStyle((_, { seenQuiteLongAgo }) => ({ + flexDirection: 'row', + borderWidth: 1, + width: '100%', + justifyContent: 'space-between', + alignItems: 'center', + extend: { + condition: seenQuiteLongAgo, + style: { + opacity: 0.5, + }, + }, +})); + +const deviceItemStyle = prepareNativeStyle(utils => ({ + flexDirection: 'row', + alignItems: 'center', + padding: utils.spacings.extraSmall, + backgroundColor: utils.colors.backgroundSurfaceElevation2, + width: 50, + height: 50, + borderRadius: 25, + justifyContent: 'center', +})); + +const DeviceIcon = () => { + const activeColorScheme = useActiveColorScheme(); + const { applyStyle } = useNativeStyles(); + + const connectedDeviceIcon: IconName = + activeColorScheme === 'standard' ? 'trezorConnectedLight' : 'trezorConnectedDark'; + + return ( + + + + ); +}; + +export const ScannedDeviceItem = ({ device }: { device: BLEScannedDevice }) => { + const { bleDevice } = device; + const { applyStyle } = useNativeStyles(); + const [isConnecting, setIsConnecting] = useState(false); + + const connectDevice = async () => { + setIsConnecting(true); + try { + await nativeBleManager.connectDevice({ + deviceOrId: bleDevice, + }); + } catch (error) { + alert('Error connecting to device'); + Alert.alert('Error connecting to device', error?.message, [{ text: 'OK' }]); + } + setIsConnecting(false); + }; + + const lastSeenInSec = Math.floor((Date.now() - device.lastSeenTimestamp) / 1000); + const seenQuiteLongAgo = lastSeenInSec > 10; + + if (lastSeenInSec > 30) { + // This device is probably not in range anymore or it's not advertising anymore + return null; + } + + return ( + + + + + {bleDevice.name} + {bleDevice.id} + {seenQuiteLongAgo && ( + Last seen: {lastSeenInSec}s ago + )} + + + {isConnecting ? ( + + + + ) : ( + + )} + + ); +}; diff --git a/suite-native/bluetooth/src/hooks/useBluetoothAdapterState.ts b/suite-native/bluetooth/src/hooks/useBluetoothAdapterState.ts new file mode 100644 index 000000000000..f5a6820a9a0e --- /dev/null +++ b/suite-native/bluetooth/src/hooks/useBluetoothAdapterState.ts @@ -0,0 +1,33 @@ +import { useEffect, useState } from 'react'; +import { State as BluetoothAdapterState } from 'react-native-ble-plx'; + +import { nativeBleManager } from '@trezor/transport-native-ble'; + +export const useBluetoothAdapterState = () => { + const [bluetoothState, setState] = useState( + BluetoothAdapterState.Unknown, + ); + + useEffect(() => { + nativeBleManager.bleManager.state().then(newState => { + setState(newState); + }); + + const subscription = nativeBleManager.bleManager.onStateChange(newState => { + setState(newState); + }); + + return () => { + subscription.remove(); + }; + }, []); + + const turnOnBluetooth = async () => { + await nativeBleManager.bleManager.enable(); + }; + + return { + bluetoothState, + turnOnBluetooth, + }; +}; diff --git a/suite-native/bluetooth/src/hooks/useBluetoothPermissions.ts b/suite-native/bluetooth/src/hooks/useBluetoothPermissions.ts new file mode 100644 index 000000000000..4187bcfd204c --- /dev/null +++ b/suite-native/bluetooth/src/hooks/useBluetoothPermissions.ts @@ -0,0 +1,128 @@ +import { useState, useRef, useLayoutEffect } from 'react'; +import { AppState, AppStateStatus, Platform } from 'react-native'; +import { + PERMISSIONS, + RESULTS, + request, + requestMultiple, + check, + PermissionStatus, +} from 'react-native-permissions'; + +import Constants from 'expo-constants'; + +export enum BluetoothPermissionErrors { + BluetoothAccessBlocked = 'BluetoothAccessBlocked', + LocationAccessBlocked = 'LocationAccessBlocked', + NearbyDevicesAccessBlocked = 'NearbyDevicesAccessBlocked', +} + +export const useBluetoothPermissions = () => { + const appState = useRef(AppState.currentState); + const [hasBluetoothPermissions, setHasBluetoothPermissions] = useState(false); + const [bluetoothPermissionError, setBluetoothPermissionError] = + useState(); + const deviceOSVersion = Constants.systemVersion || 0; + + const requestIosPermission = async ({ checkOnly = false }: { checkOnly?: boolean } = {}) => { + const permissionFn = checkOnly ? check : request; + const bluetoothPermissionStatus = await permissionFn(PERMISSIONS.IOS.BLUETOOTH); + const bluetoothAllowed = bluetoothPermissionStatus === RESULTS.GRANTED; + + if (bluetoothAllowed) { + setHasBluetoothPermissions(true); + setBluetoothPermissionError(undefined); + } else { + setBluetoothPermissionError(BluetoothPermissionErrors.BluetoothAccessBlocked); + } + }; + + const requestAndroidPermission = async ({ + checkOnly = false, + }: { checkOnly?: boolean } = {}) => { + let hasError = false; + + if (deviceOSVersion >= 12) { + let result: { + [PERMISSIONS.ANDROID.BLUETOOTH_CONNECT]: PermissionStatus; + [PERMISSIONS.ANDROID.BLUETOOTH_SCAN]: PermissionStatus; + } = { + [PERMISSIONS.ANDROID.BLUETOOTH_CONNECT]: RESULTS.DENIED, + [PERMISSIONS.ANDROID.BLUETOOTH_SCAN]: RESULTS.DENIED, + }; + if (checkOnly) { + result[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] = await check( + PERMISSIONS.ANDROID.BLUETOOTH_CONNECT, + ); + result[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] = await check( + PERMISSIONS.ANDROID.BLUETOOTH_SCAN, + ); + } else { + result = await requestMultiple([ + PERMISSIONS.ANDROID.BLUETOOTH_CONNECT, + PERMISSIONS.ANDROID.BLUETOOTH_SCAN, + ]); + } + + if ( + result[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] !== RESULTS.GRANTED || + result[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] !== RESULTS.GRANTED + ) { + setBluetoothPermissionError(BluetoothPermissionErrors.NearbyDevicesAccessBlocked); + hasError = true; + } + } else { + const permissionFn = checkOnly ? check : request; + const bluetoothPermissionStatus = await permissionFn( + PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION, + ); + + if (bluetoothPermissionStatus !== RESULTS.GRANTED) { + setBluetoothPermissionError(BluetoothPermissionErrors.LocationAccessBlocked); + hasError = true; + } + } + + if (!hasError) { + setHasBluetoothPermissions(true); + setBluetoothPermissionError(undefined); + } + }; + + // Checking if app has required permissions every time the app becomes active + const requestPermissions = async ({ checkOnly = false }: { checkOnly?: boolean } = {}) => { + if (Platform.OS === 'ios') { + await requestIosPermission({ checkOnly }); + } + + if (Platform.OS === 'android') { + await requestAndroidPermission({ checkOnly }); + } + }; + + // External permission changes must be picked up by the app by tracking the app state + useLayoutEffect(() => { + const handleAppStateChange = (nextAppState: AppStateStatus) => { + if (appState.current.match(/inactive|background/) && nextAppState === 'active') { + setBluetoothPermissionError(undefined); + requestPermissions({ checkOnly: true }); + } + + appState.current = nextAppState; + }; + + const subscription = AppState.addEventListener('change', handleAppStateChange); + requestPermissions({ checkOnly: true }); + + return () => { + subscription.remove(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return { + hasBluetoothPermissions, + bluetoothPermissionError, + requestBluetoothPermissions: requestPermissions, + }; +}; diff --git a/suite-native/bluetooth/src/index.ts b/suite-native/bluetooth/src/index.ts new file mode 100644 index 000000000000..c17c100fba0a --- /dev/null +++ b/suite-native/bluetooth/src/index.ts @@ -0,0 +1 @@ +export { DevicesScanner } from './components/DevicesScanner'; diff --git a/suite-native/bluetooth/tsconfig.json b/suite-native/bluetooth/tsconfig.json new file mode 100644 index 000000000000..c7ebe855e214 --- /dev/null +++ b/suite-native/bluetooth/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { "outDir": "libDev" }, + "references": [] +} diff --git a/suite-native/discovery/src/discoveryConfigSlice.ts b/suite-native/discovery/src/discoveryConfigSlice.ts index a228aa77383d..b6df73bd4a69 100644 --- a/suite-native/discovery/src/discoveryConfigSlice.ts +++ b/suite-native/discovery/src/discoveryConfigSlice.ts @@ -76,6 +76,11 @@ export const selectDiscoverySupportedNetworks = memoizeWithArgs( (state: DeviceRootState, areTestnetsEnabled: boolean) => pipe( selectDeviceSupportedNetworks(state), + symbols => { + console.log('selectDeviceSupportedNetworks', symbols); + + return symbols; + }, networkSymbols => filterTestnetNetworks(networkSymbols, areTestnetsEnabled), filterUnavailableNetworks, filterBlacklistedNetworks, diff --git a/suite-native/discovery/src/discoveryMiddleware.ts b/suite-native/discovery/src/discoveryMiddleware.ts index b7024340ed04..e7304463ddfb 100644 --- a/suite-native/discovery/src/discoveryMiddleware.ts +++ b/suite-native/discovery/src/discoveryMiddleware.ts @@ -4,6 +4,7 @@ import { discoveryActions, selectDeviceModel, selectDeviceFirmwareVersion, + DISCOVERY_MODULE_PREFIX, } from '@suite-common/wallet-core'; import { createMiddlewareWithExtraDeps } from '@suite-common/redux-utils'; import { isFirmwareVersionSupported } from '@suite-native/device'; @@ -17,6 +18,10 @@ export const prepareDiscoveryMiddleware = createMiddlewareWithExtraDeps( dispatch(discoveryActions.removeDiscovery(action.payload.state)); } + if (action.type.startsWith(DISCOVERY_MODULE_PREFIX)) { + console.log(action.name, JSON.stringify(action, null, 2)); + } + const device = selectDevice(getState()); const deviceModel = selectDeviceModel(getState()); const deviceFwVersion = selectDeviceFirmwareVersion(getState()); diff --git a/suite-native/discovery/src/discoveryThunks.ts b/suite-native/discovery/src/discoveryThunks.ts index 549c1ea30633..0aa755c27986 100644 --- a/suite-native/discovery/src/discoveryThunks.ts +++ b/suite-native/discovery/src/discoveryThunks.ts @@ -441,6 +441,8 @@ export const createDescriptorPreloadedDiscoveryThunk = createThunk( return; } + console.log('createDescriptorPreloadedDiscoveryThunk', deviceState, supportedNetworks); + const supportedNetworksSymbols = supportedNetworks.map(network => network.symbol); const discoveryNetworksTotalCount = supportedNetworksSymbols.length; diff --git a/suite-native/module-connect-device/package.json b/suite-native/module-connect-device/package.json index 2873c97a391d..0ce2b3892679 100644 --- a/suite-native/module-connect-device/package.json +++ b/suite-native/module-connect-device/package.json @@ -19,6 +19,7 @@ "@suite-common/wallet-core": "workspace:*", "@suite-native/alerts": "workspace:*", "@suite-native/atoms": "workspace:*", + "@suite-native/bluetooth": "workspace:*", "@suite-native/device": "workspace:*", "@suite-native/device-mutex": "workspace:*", "@suite-native/forms": "workspace:*", diff --git a/suite-native/module-connect-device/src/components/ConnectDeviceSreenView.tsx b/suite-native/module-connect-device/src/components/ConnectDeviceSreenView.tsx index 25f5d50da069..c785bd5ee4c1 100644 --- a/suite-native/module-connect-device/src/components/ConnectDeviceSreenView.tsx +++ b/suite-native/module-connect-device/src/components/ConnectDeviceSreenView.tsx @@ -2,7 +2,7 @@ import { ReactNode } from 'react'; import { Box } from '@suite-native/atoms'; import { Screen } from '@suite-native/navigation'; -import { prepareNativeStyle, useNativeStyles, NativeStyleObject } from '@trezor/styles'; +import { NativeStyleObject, prepareNativeStyle, useNativeStyles } from '@trezor/styles'; import { ConnectDeviceScreenHeader } from './ConnectDeviceScreenHeader'; diff --git a/suite-native/module-connect-device/src/screens/ConnectAndUnlockDeviceScreen.tsx b/suite-native/module-connect-device/src/screens/ConnectAndUnlockDeviceScreen.tsx index dd09a981e9a5..eb93734987b7 100644 --- a/suite-native/module-connect-device/src/screens/ConnectAndUnlockDeviceScreen.tsx +++ b/suite-native/module-connect-device/src/screens/ConnectAndUnlockDeviceScreen.tsx @@ -1,16 +1,16 @@ import { useEffect } from 'react'; import { Dimensions } from 'react-native'; -import { useSelector, useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; import { useIsFocused } from '@react-navigation/native'; +import { authorizeDevice, selectDevice, selectIsDeviceAuthorized } from '@suite-common/wallet-core'; import { Text, VStack } from '@suite-native/atoms'; +import { DevicesScanner } from '@suite-native/bluetooth'; +import { requestPrioritizedDeviceAccess } from '@suite-native/device-mutex'; import { Translation } from '@suite-native/intl'; -import { ConnectDeviceAnimation } from '@suite-native/device'; -import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; import { Screen } from '@suite-native/navigation'; -import { selectDevice, selectIsDeviceAuthorized, authorizeDevice } from '@suite-common/wallet-core'; -import { requestPrioritizedDeviceAccess } from '@suite-native/device-mutex'; +import { prepareNativeStyle, useNativeStyles } from '@trezor/styles'; import { ConnectDeviceScreenHeader } from '../components/ConnectDeviceScreenHeader'; @@ -56,7 +56,7 @@ export const ConnectAndUnlockDeviceScreen = () => { - + ); diff --git a/suite-native/receive/src/components/DeviceScreenContent.tsx b/suite-native/receive/src/components/DeviceScreenContent.tsx index af7c9a85175b..2cf608ca7dfd 100644 --- a/suite-native/receive/src/components/DeviceScreenContent.tsx +++ b/suite-native/receive/src/components/DeviceScreenContent.tsx @@ -55,7 +55,14 @@ const deviceToContentStyles = { lineHeight: 25, pagerOffset: 40, }, -} as const satisfies Record; + T3W1: { + fontSource: require('../../../../packages/theme/fonts/RobotoMono-Regular.ttf'), + fontSize: 20, + lineWidth: 230, + lineHeight: 25, + pagerOffset: 60, + }, +} as const satisfies Record; type ContentCanvasStyleProps = { lineWidth: number; diff --git a/suite-native/state/package.json b/suite-native/state/package.json index c4d57a4a5d96..28a3afe7dc67 100644 --- a/suite-native/state/package.json +++ b/suite-native/state/package.json @@ -31,6 +31,7 @@ "@suite-native/toasts": "workspace:*", "@trezor/connect": "workspace:*", "@trezor/transport-native": "workspace:*", + "@trezor/transport-native-ble": "workspace:*", "@trezor/utils": "workspace:*", "expo-device": "5.9.3", "react": "18.2.0", diff --git a/suite-native/state/src/extraDependencies.ts b/suite-native/state/src/extraDependencies.ts index b7d607f1207b..70ddd7f41605 100644 --- a/suite-native/state/src/extraDependencies.ts +++ b/suite-native/state/src/extraDependencies.ts @@ -9,19 +9,21 @@ import { selectDevices } from '@suite-common/wallet-core'; import { selectFiatCurrencyCode, setFiatCurrency } from '@suite-native/module-settings'; import { PROTO } from '@trezor/connect'; import { mergeDeepObject } from '@trezor/utils'; -import { NativeUsbTransport } from '@trezor/transport-native'; +import { NativeTransportBLE } from '@trezor/transport-native-ble'; const deviceType = Device.isDevice ? 'device' : 'emulator'; -const transportsPerDeviceType = { - device: Platform.select({ - ios: ['BridgeTransport', 'UdpTransport'], - android: [new NativeUsbTransport()], - }), - emulator: ['BridgeTransport', 'UdpTransport'], -} as const; +// const transportsPerDeviceType = { +// device: Platform.select({ +// ios: ['BridgeTransport', 'UdpTransport'], +// android: [new NativeUsbTransport()], +// }), +// emulator: ['BridgeTransport', 'UdpTransport'], +// } as const; -const transports = transportsPerDeviceType[deviceType]; +// const transports = transportsPerDeviceType[deviceType]; + +const transports = [new NativeTransportBLE()]; export const extraDependencies: ExtraDependencies = mergeDeepObject(extraDependenciesMock, { selectors: { diff --git a/yarn.lock b/yarn.lock index f6f87b822e2b..fe7c4d6ed73c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3463,7 +3463,7 @@ __metadata: languageName: node linkType: hard -"@expo/config-plugins@npm:7.8.4, @expo/config-plugins@npm:~7.8.0, @expo/config-plugins@npm:~7.8.2": +"@expo/config-plugins@npm:7.8.4, @expo/config-plugins@npm:^7.2.5, @expo/config-plugins@npm:~7.8.0, @expo/config-plugins@npm:~7.8.2": version: 7.8.4 resolution: "@expo/config-plugins@npm:7.8.4" dependencies: @@ -8821,10 +8821,12 @@ __metadata: react: "npm:18.2.0" react-intl: "npm:^6.6.2" react-native: "npm:0.73.6" + react-native-ble-plx: "npm:^3.1.2" react-native-flipper: "npm:^0.212.0" react-native-gesture-handler: "npm:2.15.0" react-native-keyboard-aware-scroll-view: "npm:0.9.5" react-native-mmkv: "npm:2.11.0" + react-native-permissions: "npm:^4.1.5" react-native-reanimated: "npm:3.8.1" react-native-restart: "npm:0.0.27" react-native-safe-area-context: "npm:4.9.0" @@ -8927,6 +8929,22 @@ __metadata: languageName: unknown linkType: soft +"@suite-native/bluetooth@workspace:*, @suite-native/bluetooth@workspace:suite-native/bluetooth": + version: 0.0.0-use.local + resolution: "@suite-native/bluetooth@workspace:suite-native/bluetooth" + dependencies: + "@suite-native/atoms": "workspace:*" + "@suite-native/intl": "workspace:*" + "@trezor/styles": "workspace:*" + "@trezor/transport-native-ble": "workspace:*" + expo-constants: "npm:15.4.5" + react: "npm:18.2.0" + react-native: "npm:0.73.6" + react-native-ble-plx: "npm:^3.1.2" + react-native-permissions: "npm:^4.1.5" + languageName: unknown + linkType: soft + "@suite-native/config@workspace:*, @suite-native/config@workspace:suite-native/config": version: 0.0.0-use.local resolution: "@suite-native/config@workspace:suite-native/config" @@ -9332,6 +9350,7 @@ __metadata: "@suite-common/wallet-core": "workspace:*" "@suite-native/alerts": "workspace:*" "@suite-native/atoms": "workspace:*" + "@suite-native/bluetooth": "workspace:*" "@suite-native/device": "workspace:*" "@suite-native/device-mutex": "workspace:*" "@suite-native/forms": "workspace:*" @@ -9708,6 +9727,7 @@ __metadata: "@suite-native/toasts": "workspace:*" "@trezor/connect": "workspace:*" "@trezor/transport-native": "workspace:*" + "@trezor/transport-native-ble": "workspace:*" "@trezor/utils": "workspace:*" expo-device: "npm:5.9.3" react: "npm:18.2.0" @@ -11230,6 +11250,15 @@ __metadata: languageName: unknown linkType: soft +"@trezor/transport-native-ble@workspace:*, @trezor/transport-native-ble@workspace:packages/transport-native-ble": + version: 0.0.0-use.local + resolution: "@trezor/transport-native-ble@workspace:packages/transport-native-ble" + dependencies: + "@trezor/transport": "workspace:*" + react-native-ble-plx: "npm:^3.1.2" + languageName: unknown + linkType: soft + "@trezor/transport-native@workspace:*, @trezor/transport-native@workspace:packages/transport-native": version: 0.0.0-use.local resolution: "@trezor/transport-native@workspace:packages/transport-native" @@ -33669,6 +33698,18 @@ __metadata: languageName: node linkType: hard +"react-native-ble-plx@npm:^3.1.2": + version: 3.1.2 + resolution: "react-native-ble-plx@npm:3.1.2" + dependencies: + "@expo/config-plugins": "npm:^7.2.5" + peerDependencies: + react: "*" + react-native: "*" + checksum: 10/75e3960fcc8c236d0e39fe07a5abd10f617ae8e1cd4114e2179d0e33876aad0932ce229a39e768914f859b5029445b4e44e21e03d5f031cb680f9eee060e9f43 + languageName: node + linkType: hard + "react-native-flipper@npm:^0.212.0": version: 0.212.0 resolution: "react-native-flipper@npm:0.212.0" @@ -33726,6 +33767,20 @@ __metadata: languageName: node linkType: hard +"react-native-permissions@npm:^4.1.5": + version: 4.1.5 + resolution: "react-native-permissions@npm:4.1.5" + peerDependencies: + react: ">=18.1.0" + react-native: ">=0.70.0" + react-native-windows: ">=0.70.0" + peerDependenciesMeta: + react-native-windows: + optional: true + checksum: 10/2b1eaa879e4b0c3455488c3dfa1d8b203bdbf2cd550b8ee5691d26e03c086d33f9bc495a70f8e1e85d0c16131374e11d12f7927168179e339f158eea9fb04830 + languageName: node + linkType: hard + "react-native-reanimated@npm:3.8.1": version: 3.8.1 resolution: "react-native-reanimated@npm:3.8.1"