diff --git a/components/ui/Layout.tsx b/components/ui/Layout.tsx index d0838da0..336e07da 100644 --- a/components/ui/Layout.tsx +++ b/components/ui/Layout.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { FlexStyle, StyleProp, - SafeAreaView, + View, ViewStyle, StyleSheet, ScrollView, @@ -50,7 +50,7 @@ function createLayout( {props.children} ) : ( - {props.children} + {props.children} ); }; diff --git a/components/ui/Text.tsx b/components/ui/Text.tsx index 9c55b15a..b8964484 100644 --- a/components/ui/Text.tsx +++ b/components/ui/Text.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { StyleProp, TextStyle, StyleSheet, Text as RNText } from 'react-native'; -import { Colors, spacing } from './styleUtils'; +import { Colors, Spacing, spacing } from './styleUtils'; const styles = StyleSheet.create({ base: { @@ -52,7 +52,7 @@ interface TextProps { color?: string; weight?: 'regular' | 'semibold' | 'bold'; align?: TextStyle['textAlign']; - margin?: string; + margin?: Spacing; size?: 'small' | 'smaller' | 'regular'; lineHeight?: number; numLines?: number; diff --git a/machines/request.ts b/machines/request.ts index 7392e82b..0a0a6979 100644 --- a/machines/request.ts +++ b/machines/request.ts @@ -1,5 +1,7 @@ -// import SmartShare from '@idpass/smartshare-react-native'; -const SmartShare = {}; +import SmartshareReactNative from '@idpass/smartshare-react-native'; +const { IdpassSmartshare, GoogleNearbyMessages } = SmartshareReactNative; + +import uuid from 'react-native-uuid'; import BluetoothStateManager from 'react-native-bluetooth-state-manager'; import { EmitterSubscription, Platform } from 'react-native'; import { assign, EventFrom, send, sendParent, StateFrom } from 'xstate'; @@ -17,11 +19,10 @@ import { } from '../shared/constants'; import { ActivityLogEvents } from './activityLog'; import { VcEvents } from './vc'; -import { - addOnErrorListener, - connect, - publish, -} from 'react-native-google-nearby-messages'; +import { ConnectionParams } from '@idpass/smartshare-react-native/lib/typescript/IdpassSmartshare'; +import { gnmSubscribe } from '../shared/smartshare'; + +type SharingProtocol = 'OFFLINE' | 'ONLINE'; const model = createModel( { @@ -31,6 +32,9 @@ const model = createModel( incomingVc: {} as VC, connectionParams: '', loggers: [] as EmitterSubscription[], + sharingProtocol: (Platform.OS === 'ios' + ? 'ONLINE' + : 'OFFLINE') as SharingProtocol, }, { events: { @@ -39,7 +43,6 @@ const model = createModel( CANCEL: () => ({}), DISMISS: () => ({}), VC_RECEIVED: (vc: VC) => ({ vc }), - RESPONSE_SENT: () => ({}), CONNECTED: () => ({}), DISCONNECT: () => ({}), EXCHANGE_DONE: (senderInfo: DeviceInfo) => ({ senderInfo }), @@ -52,6 +55,7 @@ const model = createModel( RECEIVE_DEVICE_INFO: (info: DeviceInfo) => ({ info }), RECEIVED_VCS_UPDATED: () => ({}), VC_RESPONSE: (response: unknown) => ({ response }), + SWITCH_PROTOCOL: (value: boolean) => ({ value }), }, } ); @@ -73,6 +77,10 @@ export const requestMachine = model.createMachine( on: { SCREEN_BLUR: 'inactive', SCREEN_FOCUS: 'checkingBluetoothService', + SWITCH_PROTOCOL: { + target: 'checkingBluetoothService', + actions: 'switchProtocol', + }, }, states: { inactive: { @@ -247,6 +255,11 @@ export const requestMachine = model.createMachine( }, { actions: { + switchProtocol: assign({ + sharingProtocol: (_context, event) => + event.value ? 'ONLINE' : 'OFFLINE', + }), + requestReceivedVcs: send(VcEvents.GET_RECEIVED_VCS(), { to: (context) => context.serviceRefs.vc, }), @@ -257,20 +270,29 @@ export const requestMachine = model.createMachine( receiverInfo: (_context, event) => event.info, }), - disconnect: () => { + disconnect: (context) => { try { - Platform.OS === 'android' && SmartShare.destroyConnection(); + if (context.sharingProtocol === 'OFFLINE') { + IdpassSmartshare.destroyConnection(); + } else { + GoogleNearbyMessages.disconnect(); + } } catch (e) { // pass } }, generateConnectionParams: assign({ - connectionParams: () => { - if (Platform.OS === 'android') { - return SmartShare.getConnectionParameters(); + connectionParams: (context) => { + if (context.sharingProtocol === 'OFFLINE') { + return IdpassSmartshare.getConnectionParameters(); } else { - return 'TEST'; + const cid = uuid.v4(); + console.log('ONLINE', cid); + return JSON.stringify({ + pk: '', + cid, + }); } }, }), @@ -285,26 +307,26 @@ export const requestMachine = model.createMachine( registerLoggers: assign({ loggers: () => { - // if (__DEV__) { - // return [ - // SmartShare.handleNearbyEvents((event) => { - // console.log( - // getDeviceNameSync(), - // '', - // JSON.stringify(event) - // ); - // }), - // SmartShare.handleLogEvents((event) => { - // console.log( - // getDeviceNameSync(), - // '', - // JSON.stringify(event) - // ); - // }), - // ]; - // } else { - return []; - // } + if (__DEV__) { + return [ + IdpassSmartshare.handleNearbyEvents((event) => { + console.log( + getDeviceNameSync(), + '', + JSON.stringify(event) + ); + }), + IdpassSmartshare.handleLogEvents((event) => { + console.log( + getDeviceNameSync(), + '', + JSON.stringify(event) + ); + }), + ]; + } else { + return []; + } }, }), @@ -373,14 +395,14 @@ export const requestMachine = model.createMachine( services: { checkBluetoothService: () => (callback) => { - // const subscription = BluetoothStateManager.onStateChange((state) => { - // if (state === 'PoweredOn') { - // callback(model.events.BLUETOOTH_ENABLED()); - // } else { - // callback(model.events.BLUETOOTH_DISABLED()); - // } - // }, true); - // return () => subscription.remove(); + const subscription = BluetoothStateManager.onStateChange((state) => { + if (state === 'PoweredOn') { + callback(model.events.BLUETOOTH_ENABLED()); + } else { + callback(model.events.BLUETOOTH_DISABLED()); + } + }, true); + return () => subscription.remove(); }, requestBluetooth: () => (callback) => { @@ -389,8 +411,46 @@ export const requestMachine = model.createMachine( .catch(() => callback(model.events.BLUETOOTH_DISABLED())); }, + advertiseDevice: (context) => (callback) => { + if (context.sharingProtocol === 'OFFLINE') { + IdpassSmartshare.createConnection('advertiser', () => { + callback({ type: 'CONNECTED' }); + }); + } else { + GoogleNearbyMessages.addOnErrorListener((kind, message) => + console.log('\n\n[request] GNM Error', kind, message) + ); + + GoogleNearbyMessages.connect({ + apiKey: GNM_API_KEY, + discoveryMediums: ['ble'], + discoveryModes: ['broadcast'], + }).then(() => { + console.log('[request] GNM connected!'); + + gnmSubscribe( + 'pairing', + async (scannedQrParams) => { + try { + const generatedParams = JSON.parse( + context.connectionParams + ) as ConnectionParams; + if (scannedQrParams.cid === generatedParams.cid) { + const message = new Message('pairing', 'cid:matches'); + await GoogleNearbyMessages.publish(message.toString()); + callback({ type: 'CONNECTED' }); + } + } catch (e) { + console.error('Could not parse message.', e); + } + } + ); + }); + } + }, + checkConnection: () => (callback) => { - const subscription = SmartShare.handleNearbyEvents((event) => { + const subscription = IdpassSmartshare.handleNearbyEvents((event) => { if (event.type === 'onDisconnected') { callback({ type: 'DISCONNECT' }); } @@ -399,53 +459,73 @@ export const requestMachine = model.createMachine( return () => subscription.remove(); }, - advertiseDevice: () => (callback) => { - SmartShare.createConnection('advertiser', () => { - callback({ type: 'CONNECTED' }); - }); - }, - exchangeDeviceInfo: (context) => (callback) => { - const subscription = SmartShare.handleNearbyEvents((event) => { - if (event.type !== 'msg') return; + const response = new Message( + 'exchange:receiver-info', + context.receiverInfo + ); - const message = Message.fromString(event.data); - if (message.type === 'exchange:sender-info') { - const response = new Message( - 'exchange:receiver-info', - context.receiverInfo - ); - SmartShare.send(response.toString(), () => { - callback({ type: 'EXCHANGE_DONE', senderInfo: message.data }); - }); - } - }); + if (context.sharingProtocol === 'OFFLINE') { + const subscription = IdpassSmartshare.handleNearbyEvents((event) => { + if (event.type !== 'msg') return; - return () => subscription.remove(); + const message = Message.fromString(event.data); + if (message.type === 'exchange:sender-info') { + IdpassSmartshare.send(response.toString(), () => { + callback({ type: 'EXCHANGE_DONE', senderInfo: message.data }); + }); + } + }); + + return () => subscription.remove(); + } else { + gnmSubscribe( + 'exchange:sender-info', + async (senderInfo) => { + await GoogleNearbyMessages.unpublish(); + await GoogleNearbyMessages.publish(response.toString()); + callback({ type: 'EXCHANGE_DONE', senderInfo }); + } + ); + } }, - receiveVc: () => (callback) => { - // const subscription = SmartShare.handleNearbyEvents((event) => { - // if (event.type === 'onDisconnected') { - // callback({ type: 'DISCONNECT' }); - // } - // if (event.type !== 'msg') return; - // const message = Message.fromString(event.data); - // if (message.type === 'send:vc') { - // callback({ type: 'VC_RECEIVED', vc: message.data }); - // } - // }); - // return () => subscription.remove(); + receiveVc: (context) => (callback) => { + if (context.sharingProtocol === 'OFFLINE') { + const subscription = IdpassSmartshare.handleNearbyEvents((event) => { + if (event.type === 'onDisconnected') { + callback({ type: 'DISCONNECT' }); + } + + if (event.type !== 'msg') return; + + const message = Message.fromString(event.data); + if (message.type === 'send:vc') { + callback({ type: 'VC_RECEIVED', vc: message.data }); + } + }); + + return () => subscription.remove(); + } else { + gnmSubscribe('send:vc', async (vc) => { + await GoogleNearbyMessages.unpublish(); + callback({ type: 'VC_RECEIVED', vc }); + }); + } }, - sendVcResponse: (_context, _event, meta) => (callback) => { - const response = new Message('send:vc:response', { + sendVcResponse: (context, _event, meta) => () => { + const message = new Message('send:vc:response', { status: meta.data.status, }); - SmartShare.send(response.toString(), () => { - callback({ type: 'RESPONSE_SENT' }); - }); + if (context.sharingProtocol === 'OFFLINE') { + IdpassSmartshare.send(message.toString(), () => { + /*pass*/ + }); + } else { + GoogleNearbyMessages.publish(message.toString()); + } }, }, @@ -484,6 +564,10 @@ export function selectIncomingVc(state: State) { return state.context.incomingVc; } +export function selectSharingProtocol(state: State) { + return state.context.sharingProtocol; +} + export function selectIsReviewing(state: State) { return state.matches('reviewing'); } diff --git a/machines/request.typegen.ts b/machines/request.typegen.ts index dbbfee70..04411409 100644 --- a/machines/request.typegen.ts +++ b/machines/request.typegen.ts @@ -3,20 +3,20 @@ export interface Typegen0 { '@@xstate/typegen': true; 'internalEvents': { - 'xstate.stop': { type: 'xstate.stop' }; '': { type: '' }; 'xstate.after(CLEAR_DELAY)#clearingConnection': { type: 'xstate.after(CLEAR_DELAY)#clearingConnection'; }; 'xstate.init': { type: 'xstate.init' }; + 'xstate.stop': { type: 'xstate.stop' }; }; 'invokeSrcNameMap': { - checkConnection: 'done.invoke.request:invocation[0]'; - checkBluetoothService: 'done.invoke.request.checkingBluetoothService.checking:invocation[0]'; - requestBluetooth: 'done.invoke.request.checkingBluetoothService.requesting:invocation[0]'; advertiseDevice: 'done.invoke.waitingForConnection:invocation[0]'; + checkBluetoothService: 'done.invoke.request.checkingBluetoothService.checking:invocation[0]'; + checkConnection: 'done.invoke.request:invocation[0]'; exchangeDeviceInfo: 'done.invoke.request.exchangingDeviceInfo:invocation[0]'; receiveVc: 'done.invoke.request.waitingForVc:invocation[0]'; + requestBluetooth: 'done.invoke.request.checkingBluetoothService.requesting:invocation[0]'; sendVcResponse: | 'done.invoke.accepted:invocation[0]' | 'done.invoke.request.reviewing.rejected:invocation[0]'; @@ -29,42 +29,49 @@ export interface Typegen0 { }; 'eventsCausingActions': { disconnect: - | 'SCREEN_BLUR' - | 'SCREEN_FOCUS' - | 'xstate.stop' - | 'DISMISS' | '' - | 'DISCONNECT'; - setReceiverInfo: 'RECEIVE_DEVICE_INFO'; - setSenderInfo: 'EXCHANGE_DONE'; - setIncomingVc: 'VC_RECEIVED'; - removeLoggers: + | 'DISCONNECT' + | 'DISMISS' | 'SCREEN_BLUR' | 'SCREEN_FOCUS' - | 'xstate.init' - | 'xstate.after(CLEAR_DELAY)#clearingConnection' - | 'DISMISS'; - registerLoggers: 'xstate.after(CLEAR_DELAY)#clearingConnection' | 'DISMISS'; + | 'SWITCH_PROTOCOL' + | 'xstate.stop'; generateConnectionParams: - | 'xstate.after(CLEAR_DELAY)#clearingConnection' - | 'DISMISS'; - requestReceiverInfo: 'CONNECTED'; - requestReceivedVcs: 'ACCEPT'; - requestExistingVc: 'VC_RESPONSE'; + | 'DISMISS' + | 'xstate.after(CLEAR_DELAY)#clearingConnection'; + logReceived: 'STORE_RESPONSE'; mergeIncomingVc: 'STORE_RESPONSE'; prependReceivedVc: 'VC_RESPONSE'; - storeVc: 'STORE_RESPONSE'; + registerLoggers: 'DISMISS' | 'xstate.after(CLEAR_DELAY)#clearingConnection'; + removeLoggers: + | 'DISMISS' + | 'SCREEN_BLUR' + | 'SCREEN_FOCUS' + | 'SWITCH_PROTOCOL' + | 'xstate.after(CLEAR_DELAY)#clearingConnection' + | 'xstate.init'; + requestExistingVc: 'VC_RESPONSE'; + requestReceivedVcs: 'ACCEPT'; + requestReceiverInfo: 'CONNECTED'; sendVcReceived: 'STORE_RESPONSE'; - logReceived: 'STORE_RESPONSE'; + setIncomingVc: 'VC_RECEIVED'; + setReceiverInfo: 'RECEIVE_DEVICE_INFO'; + setSenderInfo: 'EXCHANGE_DONE'; + storeVc: 'STORE_RESPONSE'; + switchProtocol: 'SWITCH_PROTOCOL'; }; 'eventsCausingServices': { - checkConnection: 'SCREEN_BLUR' | 'SCREEN_FOCUS' | 'xstate.init'; - checkBluetoothService: 'SCREEN_FOCUS'; - requestBluetooth: 'BLUETOOTH_DISABLED'; - advertiseDevice: 'xstate.after(CLEAR_DELAY)#clearingConnection' | 'DISMISS'; + advertiseDevice: 'DISMISS' | 'xstate.after(CLEAR_DELAY)#clearingConnection'; + checkBluetoothService: 'SCREEN_FOCUS' | 'SWITCH_PROTOCOL'; + checkConnection: + | 'SCREEN_BLUR' + | 'SCREEN_FOCUS' + | 'SWITCH_PROTOCOL' + | 'xstate.init'; exchangeDeviceInfo: 'RECEIVE_DEVICE_INFO'; receiveVc: 'EXCHANGE_DONE'; - sendVcResponse: 'STORE_RESPONSE' | 'REJECT' | 'CANCEL'; + requestBluetooth: 'BLUETOOTH_DISABLED'; + sendVcResponse: 'CANCEL' | 'REJECT' | 'STORE_RESPONSE'; }; 'eventsCausingGuards': { hasExistingVc: 'VC_RESPONSE'; @@ -73,43 +80,43 @@ export interface Typegen0 { CLEAR_DELAY: ''; }; 'matchesStates': - | 'inactive' + | 'bluetoothDenied' | 'checkingBluetoothService' | 'checkingBluetoothService.checking' - | 'checkingBluetoothService.requesting' | 'checkingBluetoothService.enabled' - | 'bluetoothDenied' + | 'checkingBluetoothService.requesting' | 'clearingConnection' - | 'waitingForConnection' - | 'preparingToExchangeInfo' + | 'disconnected' | 'exchangingDeviceInfo' - | 'waitingForVc' + | 'inactive' + | 'preparingToExchangeInfo' | 'reviewing' - | 'reviewing.idle' + | 'reviewing.accepted' | 'reviewing.accepting' - | 'reviewing.accepting.requestingReceivedVcs' - | 'reviewing.accepting.requestingExistingVc' | 'reviewing.accepting.mergingIncomingVc' | 'reviewing.accepting.prependingReceivedVc' + | 'reviewing.accepting.requestingExistingVc' + | 'reviewing.accepting.requestingReceivedVcs' | 'reviewing.accepting.storingVc' - | 'reviewing.accepted' - | 'reviewing.rejected' + | 'reviewing.idle' | 'reviewing.navigatingToHome' - | 'disconnected' + | 'reviewing.rejected' + | 'waitingForConnection' + | 'waitingForVc' | { - checkingBluetoothService?: 'checking' | 'requesting' | 'enabled'; + checkingBluetoothService?: 'checking' | 'enabled' | 'requesting'; reviewing?: - | 'idle' - | 'accepting' | 'accepted' - | 'rejected' + | 'accepting' + | 'idle' | 'navigatingToHome' + | 'rejected' | { accepting?: - | 'requestingReceivedVcs' - | 'requestingExistingVc' | 'mergingIncomingVc' | 'prependingReceivedVc' + | 'requestingExistingVc' + | 'requestingReceivedVcs' | 'storingVc'; }; }; diff --git a/machines/scan.ts b/machines/scan.ts index fc4f7a04..89c42e0a 100644 --- a/machines/scan.ts +++ b/machines/scan.ts @@ -1,15 +1,13 @@ -import SmartShare from '@idpass/smartshare-react-native'; +import SmartshareReactNative from '@idpass/smartshare-react-native'; +import { ConnectionParams } from '@idpass/smartshare-react-native/lib/typescript/IdpassSmartshare'; +const { IdpassSmartshare, GoogleNearbyMessages } = SmartshareReactNative; + // import LocationEnabler from 'react-native-location-enabler'; const LocationEnabler = {}; import SystemSetting from 'react-native-system-setting'; import { assign, EventFrom, send, sendParent, StateFrom } from 'xstate'; import { createModel } from 'xstate/lib/model'; -import { - EmitterSubscription, - Linking, - PermissionsAndroid, - Platform, -} from 'react-native'; +import { EmitterSubscription, Linking, PermissionsAndroid } from 'react-native'; import { DeviceInfo } from '../components/DeviceInfoList'; import { Message } from '../shared/Message'; import { getDeviceNameSync } from 'react-native-device-info'; @@ -17,16 +15,14 @@ import { VC } from '../types/vc'; import { AppServices } from '../shared/GlobalContext'; import { ActivityLogEvents } from './activityLog'; import { GNM_API_KEY, VC_ITEM_STORE_KEY } from '../shared/constants'; -import { - addOnErrorListener, - connect, - subscribe, -} from 'react-native-google-nearby-messages'; +import { gnmSubscribe, issSubscribe } from '../shared/smartshare'; const checkingAirplaneMode = '#checkingAirplaneMode'; const checkingLocationService = '#checkingLocationService'; const findingConnection = '#scan.findingConnection'; +type SharingProtocol = 'OFFLINE' | 'ONLINE'; + const model = createModel( { serviceRefs: {} as AppServices, @@ -41,6 +37,8 @@ const model = createModel( needBle: true, }, vcName: '', + sharingProtocol: 'OFFLINE' as SharingProtocol, + scannedQrParams: '', }, { events: { @@ -83,7 +81,7 @@ export const scanMachine = model.createMachine( id: 'scan', initial: 'inactive', invoke: { - src: 'checkConnection', + src: 'monitorConnection', }, on: { SCREEN_BLUR: 'inactive', @@ -177,17 +175,19 @@ export const scanMachine = model.createMachine( }, findingConnection: { id: 'findingConnection', - entry: ['removeLoggers', 'registerLoggers'], - invoke: { - src: 'findConnection', - }, + entry: ['removeLoggers', 'registerLoggers', 'clearScannedQrParams'], on: { SCAN: [ { - cond: 'isQrValid', + cond: 'isQrOffline', target: 'preparingToConnect', actions: ['setConnectionParams'], }, + { + cond: 'isQrOnline', + target: 'preparingToConnect', + actions: ['setScannedQrParams'], + }, { target: 'invalid' }, ], FLIGHT_ENABLED: checkingAirplaneMode, @@ -299,18 +299,31 @@ export const scanMachine = model.createMachine( SystemSetting.switchAirplane(); }, - disconnect: () => { + disconnect: (context) => { try { - Platform.OS === 'android' && SmartShare.destroyConnection(); + if (context.sharingProtocol === 'OFFLINE') { + IdpassSmartshare.destroyConnection(); + } else { + GoogleNearbyMessages.disconnect(); + } } catch (e) { // } }, - setConnectionParams: (_, event) => { - SmartShare.setConnectionParameters(event.params); + setConnectionParams: (_context, event) => { + IdpassSmartshare.setConnectionParameters(event.params); }, + setScannedQrParams: model.assign({ + scannedQrParams: (_context, event) => event.params, + sharingProtocol: 'ONLINE', + }), + + clearScannedQrParams: assign({ + scannedQrParams: '', + }), + setReceiverInfo: model.assign({ receiverInfo: (_, event) => event.receiverInfo, }), @@ -332,27 +345,27 @@ export const scanMachine = model.createMachine( }), registerLoggers: assign({ - loggers: () => { - // if (__DEV__) { - // return [ - // SmartShare.handleNearbyEvents((event) => { - // console.log( - // getDeviceNameSync(), - // '', - // JSON.stringify(event) - // ); - // }), - // SmartShare.handleLogEvents((event) => { - // console.log( - // getDeviceNameSync(), - // '', - // JSON.stringify(event) - // ); - // }), - // ]; - // } else { - return []; - // } + loggers: (context) => { + if (context.sharingProtocol === 'OFFLINE' && __DEV__) { + return [ + IdpassSmartshare.handleNearbyEvents((event) => { + console.log( + getDeviceNameSync(), + '', + JSON.stringify(event) + ); + }), + IdpassSmartshare.handleLogEvents((event) => { + console.log( + getDeviceNameSync(), + '', + JSON.stringify(event) + ); + }), + ]; + } else { + return []; + } }, }), @@ -382,16 +395,6 @@ export const scanMachine = model.createMachine( }, services: { - checkConnection: () => (callback) => { - const subscription = SmartShare.handleNearbyEvents((event) => { - if (event.type === 'onDisconnected') { - callback({ type: 'DISCONNECT' }); - } - }); - - return () => subscription.remove(); - }, - checkLocationPermission: () => async (callback) => { try { // wait a bit for animation to finish when app becomes active @@ -418,7 +421,17 @@ export const scanMachine = model.createMachine( } }, - checkLocationStatus: (context) => (callback) => { + monitorConnection: () => (callback) => { + const subscription = IdpassSmartshare.handleNearbyEvents((event) => { + if (event.type === 'onDisconnected') { + callback({ type: 'DISCONNECT' }); + } + }); + + return () => subscription.remove(); + }, + + checkLocationStatus: () => (callback) => { // const listener = LocationEnabler.addListener(({ locationEnabled }) => { // if (locationEnabled) { // callback(model.events.LOCATION_ENABLED()); @@ -428,6 +441,7 @@ export const scanMachine = model.createMachine( // }); // LocationEnabler.checkSettings(context.locationConfig); // return () => listener.remove(); + callback(model.events.LOCATION_ENABLED()); }, checkAirplaneMode: () => (callback) => { @@ -440,30 +454,83 @@ export const scanMachine = model.createMachine( }); }, - discoverDevice: () => (callback) => { - SmartShare.createConnection('discoverer', () => { - callback({ type: 'CONNECTED' }); - }); + discoverDevice: (context) => (callback) => { + if (context.sharingProtocol === 'OFFLINE') { + IdpassSmartshare.createConnection('discoverer', () => { + callback({ type: 'CONNECTED' }); + }); + } else { + GoogleNearbyMessages.addOnErrorListener((kind, message) => + console.log('\n\n[scan] GNM Error', kind, message) + ); + + GoogleNearbyMessages.connect({ + apiKey: GNM_API_KEY, + discoveryMediums: ['ble'], + discoveryModes: ['scan'], + }).then(() => { + console.log('[scan] GNM connected!'); + + gnmSubscribe('pairing', async (status) => { + await GoogleNearbyMessages.unpublish(); + if (status === 'cid:matches') { + callback({ type: 'CONNECTED' }); + } + }); + + const message = new Message( + 'pairing', + JSON.parse(context.scannedQrParams) + ); + GoogleNearbyMessages.publish(message.toString()); + }); + + // GoogleNearbyMessages.subscribe( + // async (message) => { + // console.log('[scan] discoverDevice FOUND:', message); + // await GoogleNearbyMessages.unpublish(); + // message === 'CID_MATCHED' && callback({ type: 'CONNECTED' }); + // }, + // (message) => console.log('[scan] discoverDevice LOST:', message) + // ); + } }, exchangeDeviceInfo: (context) => (callback) => { - let subscription: EmitterSubscription; - const message = new Message('exchange:sender-info', context.senderInfo); - SmartShare.send(message.toString(), () => { - subscription = SmartShare.handleNearbyEvents((event) => { - if (event.type !== 'msg') return; - const response = Message.fromString(event.data); - if (response.type === 'exchange:receiver-info') { - callback({ - type: 'EXCHANGE_DONE', - receiverInfo: response.data, - }); - } - }); - }); - return () => subscription?.remove(); + if (context.sharingProtocol === 'OFFLINE') { + let subscription: EmitterSubscription; + + IdpassSmartshare.send(message.toString(), () => { + subscription = IdpassSmartshare.handleNearbyEvents((event) => { + if (event.type === 'onDisconnected') { + callback({ type: 'DISCONNECT' }); + } + + if (event.type !== 'msg') return; + const response = Message.fromString(event.data); + if (response.type === 'exchange:receiver-info') { + callback({ + type: 'EXCHANGE_DONE', + receiverInfo: response.data, + }); + } + }); + }); + + return () => subscription?.remove(); + } else { + gnmSubscribe( + 'exchange:receiver-info', + async (receiverInfo) => { + await GoogleNearbyMessages.unpublish(); + callback({ type: 'EXCHANGE_DONE', receiverInfo }); + } + ); + + GoogleNearbyMessages.publish(message.toString()); + } }, sendVc: (context) => (callback) => { @@ -476,39 +543,49 @@ export const scanMachine = model.createMachine( const message = new Message('send:vc', vc); - SmartShare.send(message.toString(), () => { - subscription = SmartShare.handleNearbyEvents((event) => { - if (event.type === 'onDisconnected') { - callback({ type: 'DISCONNECT' }); - } - - if (event.type !== 'msg') return; - - const response = Message.fromString(event.data); - if (response.type === 'send:vc:response') { - callback({ - type: - response.data.status === 'accepted' - ? 'VC_ACCEPTED' - : 'VC_REJECTED', - }); - } + const statusCallback = (data: SendVcStatus) => { + callback({ + type: data.status === 'accepted' ? 'VC_ACCEPTED' : 'VC_REJECTED', }); - }); + }; - return () => subscription?.remove(); + if (context.sharingProtocol === 'OFFLINE') { + IdpassSmartshare.send(message.toString(), () => { + subscription = issSubscribe( + 'send:vc:response', + statusCallback + ); + }); + + return () => subscription?.remove(); + } else { + gnmSubscribe('send:vc:response', statusCallback); + GoogleNearbyMessages.publish(message.toString()); + } }, }, guards: { - isQrValid: (_, event) => { - // const param: SmartShare.ConnectionParams = Object.create(null); - // try { - // Object.assign(param, JSON.parse(event.params)); - // return 'cid' in param && 'pk' in param; - // } catch (e) { - return false; - // } + isQrOffline: (_context, event) => { + console.log('isQrOffline', event.params); + const param: ConnectionParams = Object.create(null); + try { + Object.assign(param, JSON.parse(event.params)); + return 'cid' in param && 'pk' in param && param.pk !== ''; + } catch (e) { + return false; + } + }, + + isQrOnline: (_context, event) => { + console.log('isQrOnline', event.params); + const param: ConnectionParams = Object.create(null); + try { + Object.assign(param, JSON.parse(event.params)); + return 'cid' in param && 'pk' in param && param.pk === ''; + } catch (e) { + return false; + } }, }, diff --git a/machines/scan.typegen.ts b/machines/scan.typegen.ts index a51b2362..a4aedb78 100644 --- a/machines/scan.typegen.ts +++ b/machines/scan.typegen.ts @@ -3,134 +3,135 @@ export interface Typegen0 { '@@xstate/typegen': true; 'internalEvents': { - 'xstate.stop': { type: 'xstate.stop' }; 'xstate.after(CLEAR_DELAY)#clearingConnection': { type: 'xstate.after(CLEAR_DELAY)#clearingConnection'; }; 'xstate.init': { type: 'xstate.init' }; + 'xstate.stop': { type: 'xstate.stop' }; }; 'invokeSrcNameMap': { - checkConnection: 'done.invoke.scan:invocation[0]'; checkAirplaneMode: 'done.invoke.scan.checkingAirplaneMode.checkingStatus:invocation[0]'; - checkLocationStatus: 'done.invoke.checkingLocationService:invocation[0]'; checkLocationPermission: 'done.invoke.scan.checkingLocationService.checkingPermission:invocation[0]'; - findConnection: 'done.invoke.findingConnection:invocation[0]'; + checkLocationStatus: 'done.invoke.checkingLocationService:invocation[0]'; discoverDevice: 'done.invoke.scan.connecting:invocation[0]'; exchangeDeviceInfo: 'done.invoke.scan.exchangingDeviceInfo:invocation[0]'; + monitorConnection: 'done.invoke.scan:invocation[0]'; sendVc: 'done.invoke.scan.reviewing.sendingVc:invocation[0]'; }; 'missingImplementations': { actions: never; - services: 'findConnection'; + services: never; guards: never; delays: never; }; 'eventsCausingActions': { - disconnect: - | 'SCREEN_BLUR' - | 'SCREEN_FOCUS' - | 'xstate.stop' - | 'CANCEL' - | 'DISMISS' - | 'DISCONNECT' - | 'LOCATION_ENABLED'; clearReason: + | 'CANCEL' + | 'DISCONNECT' + | 'DISMISS' | 'SCREEN_BLUR' | 'SCREEN_FOCUS' - | 'xstate.stop' + | 'xstate.stop'; + clearScannedQrParams: | 'CANCEL' + | 'DISCONNECT' | 'DISMISS' - | 'DISCONNECT'; + | 'xstate.after(CLEAR_DELAY)#clearingConnection'; + disconnect: + | 'CANCEL' + | 'DISCONNECT' + | 'DISMISS' + | 'LOCATION_ENABLED' + | 'SCREEN_BLUR' + | 'SCREEN_FOCUS' + | 'xstate.stop'; + logShared: 'VC_ACCEPTED'; openSettings: 'LOCATION_REQUEST'; - setConnectionParams: 'SCAN'; - setSenderInfo: 'RECEIVE_DEVICE_INFO'; - setReceiverInfo: 'EXCHANGE_DONE'; - setReason: 'UPDATE_REASON'; - setSelectedVc: 'SELECT_VC'; + registerLoggers: + | 'CANCEL' + | 'DISCONNECT' + | 'DISMISS' + | 'xstate.after(CLEAR_DELAY)#clearingConnection'; removeLoggers: + | 'CANCEL' + | 'DISCONNECT' + | 'DISMISS' | 'SCREEN_BLUR' | 'SCREEN_FOCUS' - | 'xstate.init' | 'xstate.after(CLEAR_DELAY)#clearingConnection' - | 'CANCEL' - | 'DISMISS' - | 'DISCONNECT'; + | 'xstate.init'; + requestSenderInfo: 'SCAN'; requestToDisableFlightMode: 'FLIGHT_REQUEST'; requestToEnableLocation: 'LOCATION_DISABLED' | 'LOCATION_REQUEST'; - registerLoggers: - | 'xstate.after(CLEAR_DELAY)#clearingConnection' - | 'CANCEL' - | 'DISMISS' - | 'DISCONNECT'; - requestSenderInfo: 'SCAN'; - logShared: 'VC_ACCEPTED'; + setConnectionParams: 'SCAN'; + setReason: 'UPDATE_REASON'; + setReceiverInfo: 'EXCHANGE_DONE'; + setScannedQrParams: 'SCAN'; + setSelectedVc: 'SELECT_VC'; + setSenderInfo: 'RECEIVE_DEVICE_INFO'; }; 'eventsCausingServices': { - checkConnection: 'SCREEN_BLUR' | 'SCREEN_FOCUS' | 'xstate.init'; - checkAirplaneMode: 'SCREEN_FOCUS' | 'FLIGHT_ENABLED' | 'APP_ACTIVE'; + checkAirplaneMode: 'APP_ACTIVE' | 'FLIGHT_ENABLED' | 'SCREEN_FOCUS'; + checkLocationPermission: 'APP_ACTIVE' | 'LOCATION_ENABLED'; checkLocationStatus: 'FLIGHT_DISABLED'; - checkLocationPermission: 'LOCATION_ENABLED' | 'APP_ACTIVE'; - findConnection: - | 'xstate.after(CLEAR_DELAY)#clearingConnection' - | 'CANCEL' - | 'DISMISS' - | 'DISCONNECT'; discoverDevice: 'RECEIVE_DEVICE_INFO'; exchangeDeviceInfo: 'CONNECTED'; + monitorConnection: 'SCREEN_BLUR' | 'SCREEN_FOCUS' | 'xstate.init'; sendVc: 'SELECT_VC'; }; 'eventsCausingGuards': { - isQrValid: 'SCAN'; + isQrOffline: 'SCAN'; + isQrOnline: 'SCAN'; }; 'eventsCausingDelays': { CLEAR_DELAY: 'LOCATION_ENABLED'; }; 'matchesStates': - | 'inactive' | 'checkingAirplaneMode' | 'checkingAirplaneMode.checkingStatus' - | 'checkingAirplaneMode.requestingToDisable' | 'checkingAirplaneMode.enabled' + | 'checkingAirplaneMode.requestingToDisable' | 'checkingLocationService' - | 'checkingLocationService.checkingStatus' - | 'checkingLocationService.requestingToEnable' | 'checkingLocationService.checkingPermission' + | 'checkingLocationService.checkingStatus' | 'checkingLocationService.denied' | 'checkingLocationService.disabled' + | 'checkingLocationService.requestingToEnable' | 'clearingConnection' - | 'findingConnection' - | 'preparingToConnect' | 'connecting' + | 'disconnected' | 'exchangingDeviceInfo' + | 'findingConnection' + | 'inactive' + | 'invalid' + | 'preparingToConnect' | 'reviewing' + | 'reviewing.accepted' + | 'reviewing.cancelled' | 'reviewing.idle' + | 'reviewing.navigatingToHome' + | 'reviewing.rejected' | 'reviewing.selectingVc' | 'reviewing.sendingVc' - | 'reviewing.accepted' - | 'reviewing.rejected' - | 'reviewing.cancelled' - | 'reviewing.navigatingToHome' - | 'disconnected' - | 'invalid' | { checkingAirplaneMode?: | 'checkingStatus' - | 'requestingToDisable' - | 'enabled'; + | 'enabled' + | 'requestingToDisable'; checkingLocationService?: - | 'checkingStatus' - | 'requestingToEnable' | 'checkingPermission' + | 'checkingStatus' | 'denied' - | 'disabled'; + | 'disabled' + | 'requestingToEnable'; reviewing?: - | 'idle' - | 'selectingVc' - | 'sendingVc' | 'accepted' - | 'rejected' | 'cancelled' - | 'navigatingToHome'; + | 'idle' + | 'navigatingToHome' + | 'rejected' + | 'selectingVc' + | 'sendingVc'; }; 'tags': never; } diff --git a/machines/smartshare.ts b/machines/smartshare.ts deleted file mode 100644 index 2277daa9..00000000 --- a/machines/smartshare.ts +++ /dev/null @@ -1,148 +0,0 @@ -import SmartShare from '@idpass/smartshare-react-native'; -import { EmitterSubscription } from 'react-native'; -import { createModel } from 'xstate/lib/model'; -import { Message } from '../shared/Message'; -import { EventFrom } from 'xstate'; - -const model = createModel( - { - eventChannel: {} as EmitterSubscription, - logChannel: {} as EmitterSubscription, - connectedTo: {} as DeviceInfo, - mode: '' as SmartShare.ConnectionMode, - }, - { - events: { - DISCOVER: () => ({}), - ADVERTISE: (params: SmartShare.ConnectionParams) => ({ params }), - DEVICE_FOUND: (qrCode: string) => ({ qrCode }), - EVENT: (event: SmartShare.NearbyEvent) => ({ event }), - SEND: (message: Message) => ({ message }), - SENT: () => ({}), - LOG: (message: string) => ({ message }), - DESTROY: () => ({}), - // $msg: () => ({}), - // $transferupdate: () => ({}), - // $onDisconnected: () => ({}), - }, - } -); - -type DeviceFoundEvent = EventFrom; - -export const smartShareMachine = model.createMachine( - { - id: 'smartshare', - context: model.initialContext, - invoke: { - src: 'listenForLogs', - }, - initial: 'idle', - states: { - idle: { - on: { - DISCOVER: { - target: 'connecting', - actions: ['setDiscoverer'], - }, - ADVERTISE: { - target: 'connecting', - actions: ['setAdvertiser'], - }, - }, - }, - connecting: { - invoke: { - src: 'connect', - }, - on: { - DEVICE_FOUND: [ - { - cond: 'isQrValid', - target: 'connected', - }, - { target: 'invalid' }, - ], - }, - }, - connected: { - invoke: { - src: 'listenForEvents', - }, - on: {}, - }, - disconnected: { - type: 'final', - }, - invalid: { - type: 'final', - }, - }, - }, - { - actions: { - setAdvertiser: model.assign({ mode: 'advertiser' }), - - setDiscoverer: model.assign({ mode: 'discoverer' }), - }, - - services: { - connect: (context) => (callback) => { - SmartShare.createConnection(context.mode, () => { - callback(model.events.DEVICE_FOUND()); - }); - }, - - listenForEvents: () => (callback, onReceive) => { - const subscription = SmartShare.handleNearbyEvents((event) => { - callback(model.events.EVENT(event)); - }); - - onReceive((event: EventFrom) => { - switch (event.type) { - case 'SEND': - SmartShare.send(event.message.toString(), () => { - callback(model.events.SENT()); - }); - break; - case 'DESTROY': - SmartShare.destroyConnection(); - break; - } - }); - - return () => subscription.remove(); - }, - - listenForLogs: () => (callback) => { - const subscription = SmartShare.handleLogEvents((event) => { - callback(model.events.LOG(event.log)); - }); - - return () => subscription.remove(); - }, - }, - - guards: { - isQrValid: (context, event: DeviceFoundEvent) => { - const params: SmartShare.ConnectionParams = Object.create(null); - try { - Object.assign(params, JSON.parse(event.qrCode)); - return 'cid' in params && 'pk' in params; - } catch (e) { - return false; - } - }, - }, - - delays: { - // TIMEOUT: 5000 - }, - } -); - -export interface DeviceInfo { - deviceName: string; - name: string; - deviceId: string; -} diff --git a/machines/store.typegen.ts b/machines/store.typegen.ts index 52e31e83..54e92639 100644 --- a/machines/store.typegen.ts +++ b/machines/store.typegen.ts @@ -2,19 +2,6 @@ export interface Typegen0 { '@@xstate/typegen': true; - 'eventsCausingActions': { - setEncryptionKey: 'KEY_RECEIVED'; - forwardStoreRequest: - | 'GET' - | 'SET' - | 'APPEND' - | 'PREPEND' - | 'REMOVE' - | 'CLEAR'; - notifyParent: - | 'KEY_RECEIVED' - | 'done.invoke.store.resettingStorage:invocation[0]'; - }; 'internalEvents': { 'error.platform.store.resettingStorage:invocation[0]': { type: 'error.platform.store.resettingStorage:invocation[0]'; @@ -45,11 +32,24 @@ export interface Typegen0 { guards: never; delays: never; }; + 'eventsCausingActions': { + setEncryptionKey: 'KEY_RECEIVED'; + forwardStoreRequest: + | 'GET' + | 'SET' + | 'APPEND' + | 'PREPEND' + | 'REMOVE' + | 'CLEAR'; + notifyParent: + | 'KEY_RECEIVED' + | 'done.invoke.store.resettingStorage:invocation[0]'; + }; 'eventsCausingServices': { getEncryptionKey: 'xstate.init'; - store: 'KEY_RECEIVED' | 'done.invoke.store.resettingStorage:invocation[0]'; generateEncryptionKey: 'ERROR'; clear: 'KEY_RECEIVED'; + store: 'KEY_RECEIVED' | 'done.invoke.store.resettingStorage:invocation[0]'; }; 'eventsCausingGuards': {}; 'eventsCausingDelays': {}; diff --git a/package-lock.json b/package-lock.json index 1cd68081..b6c6e29d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "@digitalcredentials/vc": "^1.1.2", "@expo-google-fonts/poppins": "^0.2.0", "@expo/metro-config": "^0.3.12", - "@idpass/smartshare-react-native": "0.2.2", + "@idpass/smartshare-react-native": "idpass/smartshare-react-native#pmigueld/nearby-messages", "@react-native-async-storage/async-storage": "~1.15.0", "@react-native-community/netinfo": "7.1.3", "@react-native-picker/picker": "2.2.1", @@ -49,7 +49,6 @@ "react-native-device-info": "^8.4.8", "react-native-elements": "^3.4.2", "react-native-gesture-handler": "~2.1.0", - "react-native-google-nearby-messages": "^1.0.22", "react-native-keychain": "^8.0.0", "react-native-location-enabler": "^4.1.0", "react-native-popable": "^0.4.3", @@ -60,6 +59,7 @@ "react-native-simple-markdown": "^1.1.0", "react-native-svg": "12.1.1", "react-native-system-setting": "^1.7.6", + "react-native-uuid": "^2.0.1", "react-native-vector-icons": "^8.1.0", "xstate": "^4.26.0" }, @@ -4257,7 +4257,7 @@ }, "node_modules/@idpass/smartshare-react-native": { "version": "0.2.2", - "resolved": "git+ssh://git@github.com/idpass/smartshare-react-native.git#ba5b89bda5f2f394883e8579ab00243fcae73b29", + "resolved": "git+ssh://git@github.com/idpass/smartshare-react-native.git#c6199477299014d07f845bd6e5aacd1b55622441", "license": "MIT", "peerDependencies": { "react": "*", @@ -11468,6 +11468,20 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/expo-camera/node_modules/react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/expo-cli": { "version": "5.5.1", "resolved": "https://registry.npmjs.org/expo-cli/-/expo-cli-5.5.1.tgz", @@ -22096,15 +22110,6 @@ "prop-types": "^15.7.2" } }, - "node_modules/react-native-google-nearby-messages": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/react-native-google-nearby-messages/-/react-native-google-nearby-messages-1.0.22.tgz", - "integrity": "sha512-uiBL6BR31fBnxccOM0KeQRCMI5YpYLOuLPo6xEoO92pNxsd09jDsqtOdexyLaXM0yWBdXM3ERyHNZTDDlvRhyg==", - "peerDependencies": { - "react": "^16.8.1", - "react-native": ">=0.60.0-rc.0 <1.0.x" - } - }, "node_modules/react-native-keychain": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/react-native-keychain/-/react-native-keychain-8.1.1.tgz", @@ -22225,6 +22230,15 @@ "resolved": "https://registry.npmjs.org/react-native-system-setting/-/react-native-system-setting-1.7.6.tgz", "integrity": "sha512-nBnIK5Xnyu8XRRA3BMzRI54oYlSBKc0oOTQdZOCEeOvn4ltS1nk2shj/vtMQe6khXvuhai3vIc+g7DcsOcr5+w==" }, + "node_modules/react-native-uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-native-uuid/-/react-native-uuid-2.0.1.tgz", + "integrity": "sha512-cptnoIbL53GTCrWlb/+jrDC6tvb7ypIyzbXNJcpR3Vab0mkeaaVd5qnB3f0whXYzS+SMoSQLcUUB0gEWqkPC0g==", + "engines": { + "node": ">=10.0.0", + "npm": ">=6.0.0" + } + }, "node_modules/react-native-vector-icons": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-8.1.0.tgz", @@ -26043,282 +26057,15 @@ "watchpack-chokidar2": "^2.0.1" } }, - "node_modules/watchpack-chokidar2": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz", - "integrity": "sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==", + "node_modules/watchpack/chokidar2": { + "version": "2.0.0", "dev": true, "optional": true, "dependencies": { "chokidar": "^2.1.8" - } - }, - "node_modules/watchpack-chokidar2/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "optional": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/watchpack-chokidar2/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "optional": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "optional": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "optional": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", - "dev": true, - "optional": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/watchpack-chokidar2/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "optional": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "optional": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", - "dev": true, - "optional": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", - "dev": true, - "optional": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", - "dev": true, - "optional": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "optional": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "optional": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "optional": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/watchpack-chokidar2/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "optional": true, - "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/watchpack-chokidar2/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "optional": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" + "node": "<8.10.0" } }, "node_modules/wbuf": { @@ -31721,8 +31468,9 @@ "dev": true }, "@idpass/smartshare-react-native": { - "version": "git+ssh://git@github.com/idpass/smartshare-react-native.git#ba5b89bda5f2f394883e8579ab00243fcae73b29", - "from": "@idpass/smartshare-react-native@0.2.2" + "version": "git+ssh://git@github.com/idpass/smartshare-react-native.git#c6199477299014d07f845bd6e5aacd1b55622441", + "from": "@idpass/smartshare-react-native@idpass/smartshare-react-native#pmigueld/nearby-messages", + "requires": {} }, "@jest/create-cache-key-function": { "version": "26.6.2", @@ -32665,12 +32413,14 @@ "@react-native-community/netinfo": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-7.1.3.tgz", - "integrity": "sha512-E8q3yuges6NYhrXBDQdzwCnG0bBQXATRjs6fpTjRJ37nURmdpe4HLq1awvooG4ymGTpZhrx4elcs/aUM7PTgrA==" + "integrity": "sha512-E8q3yuges6NYhrXBDQdzwCnG0bBQXATRjs6fpTjRJ37nURmdpe4HLq1awvooG4ymGTpZhrx4elcs/aUM7PTgrA==", + "requires": {} }, "@react-native-picker/picker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@react-native-picker/picker/-/picker-2.2.1.tgz", - "integrity": "sha512-EC7yv22QLHlTfnbC1ez9IUdXTOh1W31x96Oir0PfskSGFFJMWWdLTg4VrcE2DsGLzbfjjkBk123c173vf2a5MQ==" + "integrity": "sha512-EC7yv22QLHlTfnbC1ez9IUdXTOh1W31x96Oir0PfskSGFFJMWWdLTg4VrcE2DsGLzbfjjkBk123c173vf2a5MQ==", + "requires": {} }, "@react-native/assets": { "version": "1.0.0", @@ -32712,7 +32462,8 @@ "@react-navigation/elements": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.3.tgz", - "integrity": "sha512-Lv2lR7si5gNME8dRsqz57d54m4FJtrwHRjNQLOyQO546ZxO+g864cSvoLC6hQedQU0+IJnPTsZiEI2hHqfpEpw==" + "integrity": "sha512-Lv2lR7si5gNME8dRsqz57d54m4FJtrwHRjNQLOyQO546ZxO+g864cSvoLC6hQedQU0+IJnPTsZiEI2hHqfpEpw==", + "requires": {} }, "@react-navigation/native": { "version": "6.0.11", @@ -33484,7 +33235,8 @@ "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "requires": {} }, "address": { "version": "1.1.2", @@ -33518,13 +33270,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true + "dev": true, + "requires": {} }, "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true + "dev": true, + "requires": {} }, "alphanum-sort": { "version": "1.0.2", @@ -33960,7 +33714,8 @@ "babel-core": { "version": "7.0.0-bridge.0", "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-7.0.0-bridge.0.tgz", - "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==" + "integrity": "sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==", + "requires": {} }, "babel-loader": { "version": "8.1.0", @@ -34849,6 +34604,12 @@ } } }, + "chokidar2": { + "version": "file:node_modules/watchpack/chokidar2", + "requires": { + "chokidar": "^2.1.8" + } + }, "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", @@ -36959,7 +36720,8 @@ "version": "0.13.0", "resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.13.0.tgz", "integrity": "sha512-t3m7ta0EspzDxSOZh3cEOJIJVZgN/TlJYaBGnQlK6W/PZNbWep8q4RQskkJkA7/zwNpX0BaoEOSUUrqaADVoqA==", - "dev": true + "dev": true, + "requires": {} }, "eslint-scope": { "version": "5.1.1", @@ -37713,7 +37475,8 @@ "expo-application": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/expo-application/-/expo-application-4.0.2.tgz", - "integrity": "sha512-ngTaFplTkWn0X45gMC+VNXGyJfGxX4wOwKmtr17rNMVWOQUhhLlyMkTj9bAamzsuwZh35l3S/eD/N1aMWWUwMw==" + "integrity": "sha512-ngTaFplTkWn0X45gMC+VNXGyJfGxX4wOwKmtr17rNMVWOQUhhLlyMkTj9bAamzsuwZh35l3S/eD/N1aMWWUwMw==", + "requires": {} }, "expo-asset": { "version": "8.4.6", @@ -37753,6 +37516,17 @@ "requires": { "dequal": "^1.0.0" } + }, + "react": { + "version": "16.14.0", + "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz", + "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } } } }, @@ -38133,7 +37907,8 @@ "version": "3.0.5", "resolved": "https://registry.npmjs.org/expo-error-recovery/-/expo-error-recovery-3.0.5.tgz", "integrity": "sha512-VM6OOecjt0aPu5/eCdGGJfNjvAZIemaQym0JF/+SA5IlLiPpEfbVCDTO/5yiS8Zb5fKpeABx+GCRmtfnFqvRRw==", - "optional": true + "optional": true, + "requires": {} }, "expo-file-system": { "version": "13.1.4", @@ -38248,7 +38023,8 @@ "expo-image-loader": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/expo-image-loader/-/expo-image-loader-3.1.1.tgz", - "integrity": "sha512-ZX4Bh3K4CCX1aZflnmbOgFNLS+c0/GUys4wdvqxO+4A4KU1NNb3jE7RVa/OFYNPDcGhEw20c1QjyE/WsVURJpg==" + "integrity": "sha512-ZX4Bh3K4CCX1aZflnmbOgFNLS+c0/GUys4wdvqxO+4A4KU1NNb3jE7RVa/OFYNPDcGhEw20c1QjyE/WsVURJpg==", + "requires": {} }, "expo-json-utils": { "version": "0.2.1", @@ -38258,7 +38034,8 @@ "expo-keep-awake": { "version": "10.0.2", "resolved": "https://registry.npmjs.org/expo-keep-awake/-/expo-keep-awake-10.0.2.tgz", - "integrity": "sha512-Ro1lgyKldbFs4mxhWM+goX9sg0S2SRR8FiJJeOvaRzf8xNhrZfWA00Zpr+/3ocCoWQ3eEL+X9UF4PXXHf0KoOg==" + "integrity": "sha512-Ro1lgyKldbFs4mxhWM+goX9sg0S2SRR8FiJJeOvaRzf8xNhrZfWA00Zpr+/3ocCoWQ3eEL+X9UF4PXXHf0KoOg==", + "requires": {} }, "expo-local-authentication": { "version": "12.0.1", @@ -39139,7 +38916,8 @@ "expo-updates-interface": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-0.5.1.tgz", - "integrity": "sha512-RLvC69o1BkhHP6hNaWiIvSiTgXABB9v4HnoietoXKFHlAyxlQCupy6ki164KpZNrOS/PFJ2WWqZOvKfiyDVO+w==" + "integrity": "sha512-RLvC69o1BkhHP6hNaWiIvSiTgXABB9v4HnoietoXKFHlAyxlQCupy6ki164KpZNrOS/PFJ2WWqZOvKfiyDVO+w==", + "requires": {} }, "express": { "version": "4.16.4", @@ -45815,7 +45593,8 @@ "ws": { "version": "7.5.7", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.7.tgz", - "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==" + "integrity": "sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A==", + "requires": {} } } }, @@ -45828,7 +45607,8 @@ "react-freeze": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.0.tgz", - "integrity": "sha512-yQaiOqDmoKqks56LN9MTgY06O0qQHgV4FUrikH357DydArSZHQhl0BJFqGKIZoTqi8JizF9Dxhuk1FIZD6qCaw==" + "integrity": "sha512-yQaiOqDmoKqks56LN9MTgY06O0qQHgV4FUrikH357DydArSZHQhl0BJFqGKIZoTqi8JizF9Dxhuk1FIZD6qCaw==", + "requires": {} }, "react-i18next": { "version": "11.18.3", @@ -45911,7 +45691,8 @@ "react-native-bluetooth-state-manager": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/react-native-bluetooth-state-manager/-/react-native-bluetooth-state-manager-1.3.3.tgz", - "integrity": "sha512-hE4oG6/jFFIQMz9p0gitV90k61MAddJ0Ri86x9BvytMbdfreeeOU29eJwJWrM+PsNJWUH0w7ycbisj9i4RXp0A==" + "integrity": "sha512-hE4oG6/jFFIQMz9p0gitV90k61MAddJ0Ri86x9BvytMbdfreeeOU29eJwJWrM+PsNJWUH0w7ycbisj9i4RXp0A==", + "requires": {} }, "react-native-codegen": { "version": "0.0.6", @@ -45926,7 +45707,8 @@ "react-native-device-info": { "version": "8.7.0", "resolved": "https://registry.npmjs.org/react-native-device-info/-/react-native-device-info-8.7.0.tgz", - "integrity": "sha512-Zr0JNHSEPy2RWObWwUp9npTEWSG5HyY+nR/mRkf6ZTU5zd8oskarnPcCxAgwYhJbvshHSZbZIDRxCVCskXSpEA==" + "integrity": "sha512-Zr0JNHSEPy2RWObWwUp9npTEWSG5HyY+nR/mRkf6ZTU5zd8oskarnPcCxAgwYhJbvshHSZbZIDRxCVCskXSpEA==", + "requires": {} }, "react-native-elements": { "version": "3.4.2", @@ -45971,11 +45753,6 @@ "prop-types": "^15.7.2" } }, - "react-native-google-nearby-messages": { - "version": "1.0.22", - "resolved": "https://registry.npmjs.org/react-native-google-nearby-messages/-/react-native-google-nearby-messages-1.0.22.tgz", - "integrity": "sha512-uiBL6BR31fBnxccOM0KeQRCMI5YpYLOuLPo6xEoO92pNxsd09jDsqtOdexyLaXM0yWBdXM3ERyHNZTDDlvRhyg==" - }, "react-native-keychain": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/react-native-keychain/-/react-native-keychain-8.1.1.tgz", @@ -45984,12 +45761,14 @@ "react-native-location-enabler": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/react-native-location-enabler/-/react-native-location-enabler-4.1.1.tgz", - "integrity": "sha512-1gXJ+NzH5JKpaR23t1P8afEB7TKKfBkdXk2avhNUuTBlhBrqa2HrL4uDwYHoc4ZcwchsVC2P0Q7gP9L5Ot+KJw==" + "integrity": "sha512-1gXJ+NzH5JKpaR23t1P8afEB7TKKfBkdXk2avhNUuTBlhBrqa2HrL4uDwYHoc4ZcwchsVC2P0Q7gP9L5Ot+KJw==", + "requires": {} }, "react-native-popable": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/react-native-popable/-/react-native-popable-0.4.3.tgz", - "integrity": "sha512-khoK9TVLfJV+mBO5FTRx0eZip7djYVDfjaTRDhFPgVq2Exq9X6139Rfjg1sWHPvAQ2VCszTuC45o6iT373kFPw==" + "integrity": "sha512-khoK9TVLfJV+mBO5FTRx0eZip7djYVDfjaTRDhFPgVq2Exq9X6139Rfjg1sWHPvAQ2VCszTuC45o6iT373kFPw==", + "requires": {} }, "react-native-qrcode-svg": { "version": "6.1.2", @@ -46011,7 +45790,8 @@ "react-native-safe-area-context": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-3.3.2.tgz", - "integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q==" + "integrity": "sha512-yOwiiPJ1rk+/nfK13eafbpW6sKW0jOnsRem2C1LPJjM3tfTof6hlvV5eWHATye3XOpu2cJ7N+HdkUvUDGwFD2Q==", + "requires": {} }, "react-native-screens": { "version": "3.10.2", @@ -46042,7 +45822,8 @@ "react-native-size-matters": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz", - "integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==" + "integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==", + "requires": {} }, "react-native-svg": { "version": "12.1.1", @@ -46058,6 +45839,11 @@ "resolved": "https://registry.npmjs.org/react-native-system-setting/-/react-native-system-setting-1.7.6.tgz", "integrity": "sha512-nBnIK5Xnyu8XRRA3BMzRI54oYlSBKc0oOTQdZOCEeOvn4ltS1nk2shj/vtMQe6khXvuhai3vIc+g7DcsOcr5+w==" }, + "react-native-uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-native-uuid/-/react-native-uuid-2.0.1.tgz", + "integrity": "sha512-cptnoIbL53GTCrWlb/+jrDC6tvb7ypIyzbXNJcpR3Vab0mkeaaVd5qnB3f0whXYzS+SMoSQLcUUB0gEWqkPC0g==" + }, "react-native-vector-icons": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-8.1.0.tgz", @@ -48928,12 +48714,14 @@ "use-isomorphic-layout-effect": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz", - "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==" + "integrity": "sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==", + "requires": {} }, "use-subscription": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/use-subscription/-/use-subscription-1.6.0.tgz", - "integrity": "sha512-0Y/cTLlZfw547tJhJMoRA16OUbVqRm6DmvGpiGbmLST6BIA5KU5cKlvlz8DVMrACnWpyEjCkgmhLatthP4jUbA==" + "integrity": "sha512-0Y/cTLlZfw547tJhJMoRA16OUbVqRm6DmvGpiGbmLST6BIA5KU5cKlvlz8DVMrACnWpyEjCkgmhLatthP4jUbA==", + "requires": {} }, "util": { "version": "0.11.1", diff --git a/package.json b/package.json index 562a326f..4411f580 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@digitalcredentials/vc": "^1.1.2", "@expo-google-fonts/poppins": "^0.2.0", "@expo/metro-config": "^0.3.12", - "@idpass/smartshare-react-native": "0.2.2", + "@idpass/smartshare-react-native": "idpass/smartshare-react-native#pmigueld/nearby-messages", "@react-native-async-storage/async-storage": "~1.15.0", "@react-native-community/netinfo": "7.1.3", "@react-native-picker/picker": "2.2.1", @@ -53,7 +53,6 @@ "react-native-device-info": "^8.4.8", "react-native-elements": "^3.4.2", "react-native-gesture-handler": "~2.1.0", - "react-native-google-nearby-messages": "^1.0.22", "react-native-keychain": "^8.0.0", "react-native-location-enabler": "^4.1.0", "react-native-popable": "^0.4.3", @@ -64,6 +63,7 @@ "react-native-simple-markdown": "^1.1.0", "react-native-svg": "12.1.1", "react-native-system-setting": "^1.7.6", + "react-native-uuid": "^2.0.1", "react-native-vector-icons": "^8.1.0", "xstate": "^4.26.0" }, diff --git a/screens/Request/RequestScreen.tsx b/screens/Request/RequestScreen.tsx index 00610457..8ed7fa67 100644 --- a/screens/Request/RequestScreen.tsx +++ b/screens/Request/RequestScreen.tsx @@ -1,12 +1,13 @@ import React from 'react'; import QRCode from 'react-native-qrcode-svg'; -import { Centered, Column, Text } from '../../components/ui'; +import { Centered, Column, Row, Text } from '../../components/ui'; import { Colors } from '../../components/ui/styleUtils'; import { MainRouteProps } from '../../routes/main'; import { ReceiveVcModal } from './ReceiveVcModal'; import { MessageOverlay } from '../../components/MessageOverlay'; import { useRequestScreen } from './RequestScreenController'; import { useTranslation } from 'react-i18next'; +import { Switch } from 'react-native-elements'; export const RequestScreen: React.FC = (props) => { const { t } = useTranslation('RequestScreen'); @@ -39,6 +40,16 @@ export const RequestScreen: React.FC = (props) => { ) : null} + + Offline + + Online + + {controller.statusMessage !== '' && ( {controller.statusMessage} diff --git a/screens/Request/RequestScreenController.ts b/screens/Request/RequestScreenController.ts index cd62fa9e..3f819645 100644 --- a/screens/Request/RequestScreenController.ts +++ b/screens/Request/RequestScreenController.ts @@ -13,6 +13,7 @@ import { selectIsWaitingForConnection, selectIsExchangingDeviceInfo, selectIsWaitingForVc, + selectSharingProtocol, } from '../../machines/request'; import { selectVcLabel } from '../../machines/settings'; import { MainRouteProps } from '../../routes/main'; @@ -85,6 +86,7 @@ export function useRequestScreen({ navigation }: MainRouteProps) { return { vcLabel, statusMessage, + sharingProtocol: useSelector(requestService, selectSharingProtocol), isWaitingForConnection, isExchangingDeviceInfo, @@ -102,5 +104,7 @@ export function useRequestScreen({ navigation }: MainRouteProps) { ACCEPT: () => requestService.send(RequestEvents.ACCEPT()), REJECT: () => requestService.send(RequestEvents.REJECT()), REQUEST: () => requestService.send(RequestEvents.SCREEN_FOCUS()), + SWITCH_PROTOCOL: (value: boolean) => + requestService.send(RequestEvents.SWITCH_PROTOCOL(value)), }; } diff --git a/shared/smartshare.ts b/shared/smartshare.ts new file mode 100644 index 00000000..e74df05e --- /dev/null +++ b/shared/smartshare.ts @@ -0,0 +1,47 @@ +// TODO: fix export from smartshare library +// import { +// IdpassSmartshare, +// GoogleNearbyMessages, +// } from '@idpass/smartshare-react-native'; +import Smartshare from '@idpass/smartshare-react-native'; +const { IdpassSmartshare, GoogleNearbyMessages } = Smartshare; + +import { Message } from './Message'; + +export function gnmSubscribe( + messageType: string, + callback: (data: T) => void +) { + return GoogleNearbyMessages.subscribe( + (foundMessage) => { + if (__DEV__) { + console.log('\n[request] MESSAGE_FOUND', foundMessage); + } + + const message = Message.fromString(foundMessage); + if (message.type === messageType) { + GoogleNearbyMessages.unsubscribe(); + callback(message.data); + } + }, + (lostMessage) => { + if (__DEV__) { + console.log('\n[request] MESSAGE_LOST', lostMessage); + } + } + ); +} + +export function issSubscribe( + messageType: string, + callback: (data: T) => void +) { + return IdpassSmartshare.handleNearbyEvents((event) => { + if (event.type !== 'msg') return; + + const response = Message.fromString(event.data); + if (response.type === messageType) { + callback(response.data); + } + }); +} diff --git a/types/idpass__smartshare-react-native/index.d.ts b/types/idpass__smartshare-react-native/index.d.ts deleted file mode 100644 index 24e953cc..00000000 --- a/types/idpass__smartshare-react-native/index.d.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { EmitterSubscription } from 'react-native'; -import { BluetoothState } from 'react-native-bluetooth-state-manager'; - -declare module '@idpass/smartshare-react-native' { - class IdpassSmartshare { - getConnectionParameters: () => string; - - setConnectionParameters: (params: string) => void; - - getConnectionParametersDebug: () => string; - - createConnection: (mode: ConnectionMode, callback: () => void) => void; - - destroyConnection: () => void; - - send: (message: string, callback: () => void) => void; - - handleNearbyEvents: ( - callback: (event: NearbyEvent) => void - ) => EmitterSubscription; - - handleLogEvents: ( - callback: (event: NearbyLog) => void - ) => EmitterSubscription; - } - - export type ConnectionMode = 'dual' | 'advertiser' | 'discoverer'; - - export type TransferUpdateStatus = - | 'SUCCESS' - | 'FAILURE' - | 'IN_PROGRESS' - | 'CANCELED'; - - export type NearbyEvent = - | { type: 'msg'; data: string } - | { type: 'transferupdate'; data: TransferUpdateStatus } - | { type: 'onDisconnected'; data: string }; - - export interface NearbyLog { - log: string; - } - - export interface ConnectionParams { - // appId: string; - cid: string; - pk: string; - } - - export = new IdpassSmartshare(); -} - -declare module 'react-native-bluetooth-state-manager' { - type StateChangeCallback = (state: BluetoothState) => void; - - type StateChangeEmitter = Pick; - - export function onStateChange( - callback: StateChangeCallback, - emitCurrentState: boolean - ): StateChangeEmitter; -} diff --git a/types/react-native-bluetooth-state-manager/index.d.ts b/types/react-native-bluetooth-state-manager/index.d.ts new file mode 100644 index 00000000..146614f8 --- /dev/null +++ b/types/react-native-bluetooth-state-manager/index.d.ts @@ -0,0 +1,13 @@ +import { EmitterSubscription } from 'react-native'; +import { BluetoothState } from 'react-native-bluetooth-state-manager'; + +declare module 'react-native-bluetooth-state-manager' { + type StateChangeCallback = (state: BluetoothState) => void; + + type StateChangeEmitter = Pick; + + export function onStateChange( + callback: StateChangeCallback, + emitCurrentState: boolean + ): StateChangeEmitter; +}