This commit is contained in:
Danica Erediano
2022-03-28 11:37:00 +08:00
parent 1fc2dfda26
commit 27a7f44b71
51 changed files with 850 additions and 633 deletions

View File

@@ -5,7 +5,7 @@ import { VC, CredentialSubject } from '../types/vc';
import { Column, Row, Text } from './ui';
import { Colors } from './ui/styleUtils';
export const VidDetails: React.FC<VidDetailsProps> = (props) => {
export const VcDetails: React.FC<VcDetailsProps> = (props) => {
return (
<Column>
<Row padding="16 24">
@@ -14,7 +14,7 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
Generated
</Text>
<Text weight="bold" size="smaller">
{new Date(props.vid?.generatedOn).toLocaleDateString()}
{new Date(props.vc?.generatedOn).toLocaleDateString()}
</Text>
</Column>
<Column fill elevation={1} padding="12 16" margin="0 16 0 0">
@@ -22,7 +22,7 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
{props.vid?.idType}
</Text>
<Text weight="bold" size="smaller">
{props.vid?.id}
{props.vc?.id}
</Text>
</Column>
<Column fill elevation={1} padding="12 16" margin="">
@@ -40,8 +40,8 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
<ListItem.Content>
<Image
source={
props.vid?.credential.biometrics?.face
? { uri: props.vid?.credential.biometrics.face }
props.vc?.credential.biometrics?.face
? { uri: props.vc?.credential.biometrics.face }
: require('../assets/placeholder-photo.png')
}
style={{
@@ -58,7 +58,7 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
<ListItem.Content>
<ListItem.Subtitle>Full name</ListItem.Subtitle>
<ListItem.Title>
{props.vid?.verifiableCredential.credentialSubject.fullName}
{props.vc?.verifiableCredential.credentialSubject.fullName}
</ListItem.Title>
</ListItem.Content>
</ListItem>
@@ -67,7 +67,7 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
<ListItem.Subtitle>Gender</ListItem.Subtitle>
<ListItem.Title>
{getLocalizedField(
props.vid?.verifiableCredential.credentialSubject.gender
props.vc?.verifiableCredential.credentialSubject.gender
)}
</ListItem.Title>
</ListItem.Content>
@@ -76,7 +76,7 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
<ListItem.Content>
<ListItem.Subtitle>Date of birth</ListItem.Subtitle>
<ListItem.Title>
{props.vid?.verifiableCredential.credentialSubject.dateOfBirth}
{props.vc?.verifiableCredential.credentialSubject.dateOfBirth}
</ListItem.Title>
</ListItem.Content>
</ListItem>
@@ -84,7 +84,7 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
<ListItem.Content>
<ListItem.Subtitle>Phone number</ListItem.Subtitle>
<ListItem.Title>
{props.vid?.verifiableCredential.credentialSubject.phone}
{props.vc?.verifiableCredential.credentialSubject.phone}
</ListItem.Title>
</ListItem.Content>
</ListItem>
@@ -92,7 +92,7 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
<ListItem.Content>
<ListItem.Subtitle>Email</ListItem.Subtitle>
<ListItem.Title>
{props.vid?.verifiableCredential.credentialSubject.email}
{props.vc?.verifiableCredential.credentialSubject.email}
</ListItem.Title>
</ListItem.Content>
</ListItem>
@@ -100,15 +100,15 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
<ListItem.Content>
<ListItem.Subtitle>Address</ListItem.Subtitle>
<ListItem.Title>
{getFullAddress(props.vid?.verifiableCredential.credentialSubject)}
{getFullAddress(props.vc?.verifiableCredential.credentialSubject)}
</ListItem.Title>
</ListItem.Content>
</ListItem>
{Boolean(props.vid?.reason) && (
{Boolean(props.vc?.reason) && (
<ListItem bottomDivider>
<ListItem.Content>
<ListItem.Subtitle>Reason for sharing</ListItem.Subtitle>
<ListItem.Title>{props.vid?.reason}</ListItem.Title>
<ListItem.Title>{props.vc?.reason}</ListItem.Title>
</ListItem.Content>
</ListItem>
)}
@@ -116,8 +116,8 @@ export const VidDetails: React.FC<VidDetailsProps> = (props) => {
);
};
interface VidDetailsProps {
vid: VC;
interface VcDetailsProps {
vc: VC;
}
interface LocalizedField {

View File

@@ -4,13 +4,13 @@ import { Pressable, StyleSheet } from 'react-native';
import { CheckBox, Icon } from 'react-native-elements';
import { ActorRefFrom } from 'xstate';
import {
createVidItemMachine,
createVcItemMachine,
selectVerifiableCredential,
selectGeneratedOn,
selectTag,
selectId,
vidItemMachine,
} from '../machines/vidItem';
vcItemMachine,
} from '../machines/vcItem';
import { Column, Row, Text } from './ui';
import { Colors } from './ui/styleUtils';
import { RotatingIcon } from './RotatingIcon';
@@ -42,12 +42,12 @@ const styles = StyleSheet.create({
},
});
export const VidItem: React.FC<VidItemProps> = (props) => {
export const VcItem: React.FC<VcItemProps> = (props) => {
const { appService } = useContext(GlobalContext);
const machine = useRef(
createVidItemMachine(
createVcItemMachine(
appService.getSnapshot().context.serviceRefs,
props.vidKey
props.vcKey
)
);
const service = useInterpret(machine.current);
@@ -103,10 +103,10 @@ export const VidItem: React.FC<VidItemProps> = (props) => {
);
};
interface VidItemProps {
vidKey: string;
interface VcItemProps {
vcKey: string;
margin?: string;
selectable?: boolean;
selected?: boolean;
onPress?: (vidRef?: ActorRefFrom<typeof vidItemMachine>) => void;
onPress?: (vcRef?: ActorRefFrom<typeof vcItemMachine>) => void;
}

View File

@@ -103,10 +103,10 @@ export function createActivityLogMachine(serviceRefs: AppServices) {
}
export interface ActivityLog {
_vidKey: string;
_vcKey: string;
timestamp: number;
deviceName: string;
vidLabel: string;
VCLabel: string;
action: ActivityLogAction;
}

View File

@@ -10,7 +10,7 @@ import { createModel } from 'xstate/lib/model';
import { authMachine, createAuthMachine } from './auth';
import { createSettingsMachine, settingsMachine } from './settings';
import { storeMachine } from './store';
import { createVidMachine, vidMachine } from './vid';
import { createVcMachine, vcMachine } from './vc';
import { createActivityLogMachine, activityLogMachine } from './activityLog';
import { createRequestMachine, requestMachine } from './request';
import { createScanMachine, scanMachine } from './scan';
@@ -166,7 +166,7 @@ export const appMachine = model.createMachine(
createAuthMachine(serviceRefs),
authMachine.id
);
serviceRefs.vid = spawn(createVidMachine(serviceRefs), vidMachine.id);
serviceRefs.vc = spawn(createVcMachine(serviceRefs), vcMachine.id);
serviceRefs.settings = spawn(
createSettingsMachine(serviceRefs),
settingsMachine.id
@@ -190,7 +190,7 @@ export const appMachine = model.createMachine(
logServiceEvents: (context) => {
if (__DEV__) {
context.serviceRefs.auth.subscribe(logState);
context.serviceRefs.vid.subscribe(logState);
context.serviceRefs.vc.subscribe(logState);
context.serviceRefs.settings.subscribe(logState);
context.serviceRefs.activityLog.subscribe(logState);
context.serviceRefs.scan.subscribe(logState);

View File

@@ -10,18 +10,18 @@ import { StoreEvents } from './store';
import { VC } from '../types/vc';
import { AppServices } from '../shared/GlobalContext';
import {
RECEIVED_VIDS_STORE_KEY,
VID_ITEM_STORE_KEY,
RECEIVED_VCS_STORE_KEY,
VC_ITEM_STORE_KEY,
} from '../shared/constants';
import { ActivityLogEvents } from './activityLog';
import { VidEvents } from './vid';
import { VcEvents } from './vc';
const model = createModel(
{
serviceRefs: {} as AppServices,
senderInfo: {} as DeviceInfo,
receiverInfo: {} as DeviceInfo,
incomingVid: {} as VC,
incomingVc: {} as VC,
connectionParams: '',
loggers: [] as EmitterSubscription[],
},
@@ -31,7 +31,7 @@ const model = createModel(
REJECT: () => ({}),
CANCEL: () => ({}),
DISMISS: () => ({}),
VID_RECEIVED: (vid: VC) => ({ vid }),
VC_RECEIVED: (vc: VC) => ({ vc }),
RESPONSE_SENT: () => ({}),
CONNECTED: () => ({}),
DISCONNECT: () => ({}),
@@ -43,8 +43,8 @@ const model = createModel(
STORE_READY: () => ({}),
STORE_RESPONSE: (response: any) => ({ response }),
RECEIVE_DEVICE_INFO: (info: DeviceInfo) => ({ info }),
RECEIVED_VIDS_UPDATED: () => ({}),
VID_RESPONSE: (response: any) => ({ response }),
RECEIVED_VCS_UPDATED: () => ({}),
VC_RESPONSE: (response: any) => ({ response }),
},
}
);
@@ -52,10 +52,10 @@ const model = createModel(
export const RequestEvents = model.events;
type ExchangeDoneEvent = EventFrom<typeof model, 'EXCHANGE_DONE'>;
type VidReceivedEvent = EventFrom<typeof model, 'VID_RECEIVED'>;
type VcReceivedEvent = EventFrom<typeof model, 'VC_RECEIVED'>;
type ReceiveDeviceInfoEvent = EventFrom<typeof model, 'RECEIVE_DEVICE_INFO'>;
type StoreResponseEvent = EventFrom<typeof model, 'STORE_RESPONSE'>;
type VidResponseEvent = EventFrom<typeof model, 'VID_RESPONSE'>;
type VcResponseEvent = EventFrom<typeof model, 'VC_RESPONSE'>;
export const requestMachine = model.createMachine(
{
@@ -132,20 +132,20 @@ export const requestMachine = model.createMachine(
},
on: {
EXCHANGE_DONE: {
target: 'waitingForVid',
target: 'waitingForVc',
actions: ['setSenderInfo'],
},
},
},
waitingForVid: {
waitingForVc: {
invoke: {
src: 'receiveVid',
src: 'receiveVc',
},
on: {
DISCONNECT: 'disconnected',
VID_RECEIVED: {
VC_RECEIVED: {
target: 'reviewing',
actions: ['setIncomingVid'],
actions: ['setIncomingVc'],
},
},
},
@@ -159,34 +159,34 @@ export const requestMachine = model.createMachine(
states: {
idle: {},
accepting: {
initial: 'requestingReceivedVids',
initial: 'requestingReceivedVcs',
states: {
requestingReceivedVids: {
entry: ['requestReceivedVids'],
requestingReceivedVcs: {
entry: ['requestReceivedVcs'],
on: {
VID_RESPONSE: [
VC_RESPONSE: [
{
cond: 'hasExistingVid',
cond: 'hasExistingVc',
target: '#accepted',
},
{
target: 'prependingReceivedVid',
target: 'prependingReceivedVc',
},
],
},
},
prependingReceivedVid: {
entry: ['prependReceivedVid'],
prependingReceivedVc: {
entry: ['prependReceivedVc'],
on: {
STORE_RESPONSE: 'storingVid',
STORE_RESPONSE: 'storingVc',
},
},
storingVid: {
entry: ['storeVid'],
storingVc: {
entry: ['storeVc'],
on: {
STORE_RESPONSE: {
target: '#accepted',
actions: ['sendVidReceived'],
actions: ['sendVcReceived'],
},
},
},
@@ -197,7 +197,7 @@ export const requestMachine = model.createMachine(
id: 'accepted',
invoke: {
src: {
type: 'sendVidResponse',
type: 'sendVcResponse',
status: 'accepted',
},
},
@@ -208,7 +208,7 @@ export const requestMachine = model.createMachine(
rejected: {
invoke: {
src: {
type: 'sendVidResponse',
type: 'sendVcResponse',
status: 'rejected',
},
},
@@ -229,8 +229,8 @@ export const requestMachine = model.createMachine(
},
{
actions: {
requestReceivedVids: send(VidEvents.GET_RECEIVED_VIDS(), {
to: (context) => context.serviceRefs.vid,
requestReceivedVcs: send(VcEvents.GET_RECEIVED_VCS(), {
to: (context) => context.serviceRefs.vc,
}),
requestReceiverInfo: sendParent('REQUEST_DEVICE_INFO'),
@@ -255,8 +255,8 @@ export const requestMachine = model.createMachine(
senderInfo: (_, event: ExchangeDoneEvent) => event.senderInfo,
}),
setIncomingVid: model.assign({
incomingVid: (_, event: VidReceivedEvent) => event.vid,
setIncomingVc: model.assign({
incomingVc: (_, event: VcReceivedEvent) => event.vc,
}),
registerLoggers: model.assign({
@@ -291,20 +291,20 @@ export const requestMachine = model.createMachine(
},
}),
prependReceivedVid: send(
prependReceivedVc: send(
(context) =>
StoreEvents.PREPEND(
RECEIVED_VIDS_STORE_KEY,
VID_ITEM_STORE_KEY(context.incomingVid)
RECEIVED_VCS_STORE_KEY,
VC_ITEM_STORE_KEY(context.incomingVc)
),
{ to: (context) => context.serviceRefs.store }
),
storeVid: send(
storeVc: send(
(context) =>
StoreEvents.SET(
VID_ITEM_STORE_KEY(context.incomingVid),
context.incomingVid
VC_ITEM_STORE_KEY(context.incomingVc),
context.incomingVc
),
{ to: (context) => context.serviceRefs.store }
),
@@ -312,23 +312,23 @@ export const requestMachine = model.createMachine(
logReceived: send(
(context) =>
ActivityLogEvents.LOG_ACTIVITY({
_vidKey: VID_ITEM_STORE_KEY(context.incomingVid),
_vcKey: VC_ITEM_STORE_KEY(context.incomingVc),
action: 'received',
timestamp: Date.now(),
deviceName:
context.senderInfo.name || context.senderInfo.deviceName,
vidLabel: context.incomingVid.tag || context.incomingVid.id,
VCLabel: context.incomingVc.tag || context.incomingVc.id,
}),
{ to: (context) => context.serviceRefs.activityLog }
),
sendVidReceived: send(
sendVcReceived: send(
(context) => {
return VidEvents.VID_RECEIVED(
VID_ITEM_STORE_KEY(context.incomingVid)
return VcEvents.VC_RECEIVED(
VC_ITEM_STORE_KEY(context.incomingVc)
);
},
{ to: (context) => context.serviceRefs.vid }
{ to: (context) => context.serviceRefs.vc }
),
},
@@ -380,7 +380,7 @@ export const requestMachine = model.createMachine(
return () => subscription.remove();
},
receiveVid: () => (callback) => {
receiveVc: () => (callback) => {
const subscription = SmartShare.handleNearbyEvents((event) => {
if (event.type === 'onDisconnected') {
callback({ type: 'DISCONNECT' });
@@ -389,8 +389,8 @@ export const requestMachine = model.createMachine(
if (event.type !== 'msg') return;
const message = Message.fromString<VC>(event.data);
if (message.type === 'send:vid') {
callback({ type: 'VID_RECEIVED', vid: message.data });
if (message.type === 'send:vc') {
callback({ type: 'VC_RECEIVED', vc: message.data });
}
});
@@ -398,8 +398,8 @@ export const requestMachine = model.createMachine(
},
// tslint:disable-next-line
sendVidResponse: (context, event, meta) => (callback) => {
const response = new Message('send:vid:response', {
sendVcResponse: (context, event, meta) => (callback) => {
const response = new Message('send:vc:response', {
status: meta.src.status,
});
@@ -410,10 +410,10 @@ export const requestMachine = model.createMachine(
},
guards: {
hasExistingVid: (context, event: VidResponseEvent) => {
const receivedVids: string[] = event.response;
const vidKey = VID_ITEM_STORE_KEY(context.incomingVid);
return receivedVids.includes(vidKey);
hasExistingVc: (context, event: VcResponseEvent) => {
const receivedVcs: string[] = event.response;
const vcKey = VC_ITEM_STORE_KEY(context.incomingVc);
return receivedVcs.includes(vcKey);
},
},
}
@@ -436,8 +436,8 @@ export function selectConnectionParams(state: State) {
return state.context.connectionParams;
}
export function selectIncomingVid(state: State) {
return state.context.incomingVid;
export function selectIncomingVc(state: State) {
return state.context.incomingVc;
}
export function selectIsReviewing(state: State) {
@@ -468,6 +468,6 @@ export function selectIsExchangingDeviceInfo(state: State) {
return state.matches('exchangingDeviceInfo');
}
export function selectIsWaitingForVid(state: State) {
return state.matches('waitingForVid');
export function selectIsWaitingForVc(state: State) {
return state.matches('waitingForVc');
}

View File

@@ -10,14 +10,14 @@ import { getDeviceNameSync } from 'react-native-device-info';
import { VC } from '../types/vc';
import { AppServices } from '../shared/GlobalContext';
import { ActivityLogEvents } from './activityLog';
import { VID_ITEM_STORE_KEY } from '../shared/constants';
import { VC_ITEM_STORE_KEY } from '../shared/constants';
const model = createModel(
{
serviceRefs: {} as AppServices,
senderInfo: {} as DeviceInfo,
receiverInfo: {} as DeviceInfo,
selectedVid: {} as VC,
selectedVc: {} as VC,
reason: '',
loggers: [] as EmitterSubscription[],
locationConfig: {
@@ -25,17 +25,17 @@ const model = createModel(
alwaysShow: false,
needBle: true,
},
vidName: '',
vcName: '',
},
{
events: {
EXCHANGE_DONE: (receiverInfo: DeviceInfo) => ({ receiverInfo }),
RECEIVE_DEVICE_INFO: (info: DeviceInfo) => ({ info }),
SELECT_VID: (vid: VC) => ({ vid }),
SELECT_VC: (vc: VC) => ({ vc }),
SCAN: (params: string) => ({ params }),
ACCEPT_REQUEST: () => ({}),
VID_ACCEPTED: () => ({}),
VID_REJECTED: () => ({}),
VC_ACCEPTED: () => ({}),
VC_REJECTED: () => ({}),
CANCEL: () => ({}),
DISMISS: () => ({}),
CONNECTED: () => ({}),
@@ -49,7 +49,7 @@ const model = createModel(
FLIGHT_DISABLED: () => ({}),
FLIGHT_REQUEST: () => ({}),
LOCATION_REQUEST: () => ({}),
UPDATE_VID_NAME: (vidName: string) => ({ vidName }),
UPDATE_VC_NAME: (vcName: string) => ({ vcName }),
STORE_RESPONSE: (response: any) => ({ response }),
APP_ACTIVE: () => ({}),
},
@@ -60,7 +60,7 @@ export const ScanEvents = model.events;
type ExchangeDoneEvent = EventFrom<typeof model, 'EXCHANGE_DONE'>;
type ScanEvent = EventFrom<typeof model, 'SCAN'>;
type SelectVidEvent = EventFrom<typeof model, 'SELECT_VID'>;
type selectVcEvent = EventFrom<typeof model, 'SELECT_VC'>;
type UpdateReasonEvent = EventFrom<typeof model, 'UPDATE_REASON'>;
type ReceiveDeviceInfoEvent = EventFrom<typeof model, 'RECEIVE_DEVICE_INFO'>;
@@ -206,7 +206,7 @@ export const scanMachine = model.createMachine(
on: {
CANCEL: 'findingConnection',
DISMISS: 'findingConnection',
ACCEPT_REQUEST: '.selectingVid',
ACCEPT_REQUEST: '.selectingVc',
UPDATE_REASON: {
actions: ['setReason'],
},
@@ -215,26 +215,26 @@ export const scanMachine = model.createMachine(
states: {
idle: {
on: {
ACCEPT_REQUEST: 'selectingVid',
ACCEPT_REQUEST: 'selectingVc',
},
},
selectingVid: {
selectingVc: {
on: {
SELECT_VID: {
target: 'sendingVid',
actions: ['setSelectedVid'],
SELECT_VC: {
target: 'sendingVc',
actions: ['setSelectedVc'],
},
CANCEL: 'idle',
},
},
sendingVid: {
sendingVc: {
invoke: {
src: 'sendVid',
src: 'sendVc',
},
on: {
DISCONNECT: '#scan.disconnected',
VID_ACCEPTED: 'accepted',
VID_REJECTED: 'rejected',
VC_ACCEPTED: 'accepted',
VC_REJECTED: 'rejected',
},
},
accepted: {
@@ -302,10 +302,10 @@ export const scanMachine = model.createMachine(
clearReason: model.assign({ reason: '' }),
setSelectedVid: model.assign({
selectedVid: (context, event: SelectVidEvent) => {
setSelectedVc: model.assign({
selectedVc: (context, event: selectVcEvent) => {
return {
...event.vid,
...event.vc,
reason: context.reason,
};
},
@@ -346,12 +346,12 @@ export const scanMachine = model.createMachine(
logShared: send(
(context) =>
ActivityLogEvents.LOG_ACTIVITY({
_vidKey: VID_ITEM_STORE_KEY(context.selectedVid),
_vcKey: VC_ITEM_STORE_KEY(context.selectedVc),
action: 'shared',
timestamp: Date.now(),
deviceName:
context.receiverInfo.name || context.receiverInfo.deviceName,
vidLabel: context.selectedVid.tag || context.selectedVid.id,
VCLabel: context.selectedVc.tag || context.selectedVc.id,
}),
{ to: (context) => context.serviceRefs.activityLog }
),
@@ -442,15 +442,15 @@ export const scanMachine = model.createMachine(
return () => subscription?.remove();
},
sendVid: (context) => (callback) => {
sendVc: (context) => (callback) => {
let subscription: EmitterSubscription;
const vid = {
...context.selectedVid,
const vc = {
...context.selectedVc,
tag: '',
};
const message = new Message<VC>('send:vid', vid);
const message = new Message<VC>('send:vc', vc);
SmartShare.send(message.toString(), () => {
subscription = SmartShare.handleNearbyEvents((event) => {
@@ -460,13 +460,13 @@ export const scanMachine = model.createMachine(
if (event.type !== 'msg') return;
const response = Message.fromString<SendVidStatus>(event.data);
if (response.type === 'send:vid:response') {
const response = Message.fromString<SendVcStatus>(event.data);
if (response.type === 'send:vc:response') {
callback({
type:
response.data.status === 'accepted'
? 'VID_ACCEPTED'
: 'VID_REJECTED',
? 'VC_ACCEPTED'
: 'VC_REJECTED',
});
}
});
@@ -499,7 +499,7 @@ export function createScanMachine(serviceRefs: AppServices) {
});
}
interface SendVidStatus {
interface SendVcStatus {
status: 'accepted' | 'rejected';
}
@@ -513,8 +513,8 @@ export function selectReason(state: State) {
return state.context.reason;
}
export function selectVidName(state: State) {
return state.context.vidName;
export function selectVcName(state: State) {
return state.context.vcName;
}
export function selectStatusMessage(state: State) {
@@ -533,12 +533,12 @@ export function selectReviewing(state: State) {
return state.matches('reviewing');
}
export function selectSelectingVid(state: State) {
return state.matches('reviewing.selectingVid');
export function selectSelectingVc(state: State) {
return state.matches('reviewing.selectingVc');
}
export function selectSendingVid(state: State) {
return state.matches('reviewing.sendingVid');
export function selectSendingVc(state: State) {
return state.matches('reviewing.sendingVc');
}
export function selectAccepted(state: State) {

View File

@@ -2,23 +2,23 @@ import { ContextFrom, EventFrom, send, StateFrom } from 'xstate';
import { createModel } from 'xstate/lib/model';
import { AppServices } from '../shared/GlobalContext';
import { SETTINGS_STORE_KEY } from '../shared/constants';
import { VIDLabel } from '../types/vc';
import { VCLabel } from '../types/vc';
import { StoreEvents } from './store';
const model = createModel(
{
serviceRefs: {} as AppServices,
name: '',
vidLabel: {
VCLabel: {
singular: 'ID',
plural: 'IDs',
} as VIDLabel,
} as VCLabel,
isBiometricUnlockEnabled: false,
},
{
events: {
UPDATE_NAME: (name: string) => ({ name }),
UPDATE_VID_LABEL: (label: string) => ({ label }),
UPDATE_VC_LABEL: (label: string) => ({ label }),
TOGGLE_BIOMETRIC_UNLOCK: () => ({}),
STORE_RESPONSE: (response: any) => ({ response }),
},
@@ -30,7 +30,7 @@ export const SettingsEvents = model.events;
type Context = ContextFrom<typeof model>;
type UpdateNameEvent = EventFrom<typeof model, 'UPDATE_NAME'>;
type UpdateVidLabelEvent = EventFrom<typeof model, 'UPDATE_VID_LABEL'>;
type UpdateVCLabelEvent = EventFrom<typeof model, 'UPDATE_VC_LABEL'>;
type StoreResponseEvent = EventFrom<typeof model, 'STORE_RESPONSE'>;
export const settingsMachine = model.createMachine(
@@ -62,8 +62,8 @@ export const settingsMachine = model.createMachine(
UPDATE_NAME: {
actions: ['updateName', 'storeContext'],
},
UPDATE_VID_LABEL: {
actions: ['updateVidLabel', 'storeContext'],
UPDATE_VC_LABEL: {
actions: ['updateVCLabel', 'storeContext'],
},
},
},
@@ -95,8 +95,8 @@ export const settingsMachine = model.createMachine(
name: (_, event: UpdateNameEvent) => event.name,
}),
updateVidLabel: model.assign({
vidLabel: (_, event: UpdateVidLabelEvent) => ({
updateVCLabel: model.assign({
VCLabel: (_, event: UpdateVCLabelEvent) => ({
singular: event.label,
plural: event.label + 's',
}),
@@ -124,8 +124,8 @@ export function selectName(state: State) {
return state.context.name;
}
export function selectVidLabel(state: State) {
return state.context.vidLabel;
export function selectVCLabel(state: State) {
return state.context.VCLabel;
}
export function selectBiometricUnlockEnabled(state: State) {

217
machines/vc.ts Normal file
View File

@@ -0,0 +1,217 @@
import { EventFrom, StateFrom } from 'xstate';
import { send, sendParent } from 'xstate';
import { createModel } from 'xstate/lib/model';
import { StoreEvents } from './store';
import { VC } from '../types/vc';
import { AppServices } from '../shared/GlobalContext';
import { log, respond } from 'xstate/lib/actions';
import { VcItemEvents } from './vcItem';
import {
MY_VCS_STORE_KEY,
RECEIVED_VCS_STORE_KEY,
VC_ITEM_STORE_KEY,
} from '../shared/constants';
const model = createModel(
{
serviceRefs: {} as AppServices,
myVcs: [] as string[],
receivedVcs: [] as string[],
vcs: {} as Record<string, VC>,
},
{
events: {
VIEW_VC: (vc: VC) => ({ vc }),
GET_VC_ITEM: (vcKey: string) => ({ vcKey }),
STORE_RESPONSE: (response: any) => ({ response }),
STORE_ERROR: (error: Error) => ({ error }),
VC_ADDED: (vcKey: string) => ({ vcKey }),
VC_RECEIVED: (vcKey: string) => ({ vcKey }),
VC_DOWNLOADED: (vc: VC) => ({ vc }),
REFRESH_MY_VCS: () => ({}),
REFRESH_RECEIVED_VCS: () => ({}),
GET_RECEIVED_VCS: () => ({}),
},
}
);
export const VcEvents = model.events;
type GetVcItemEvent = EventFrom<typeof model, 'GET_VC_ITEM'>;
type StoreResponseEvent = EventFrom<typeof model, 'STORE_RESPONSE'>;
type VcDownloadedEvent = EventFrom<typeof model, 'VC_DOWNLOADED'>;
type VcAddedEvent = EventFrom<typeof model, 'VC_ADDED'>;
type VcReceivedEvent = EventFrom<typeof model, 'VC_RECEIVED'>;
export const vcMachine = model.createMachine(
{
id: 'vc',
context: model.initialContext,
initial: 'init',
states: {
init: {
initial: 'myVcs',
states: {
myVcs: {
entry: ['loadMyVcs'],
on: {
STORE_RESPONSE: {
target: 'receivedVcs',
actions: ['setMyVcs'],
},
},
},
receivedVcs: {
entry: ['loadReceivedVcs'],
on: {
STORE_RESPONSE: {
target: '#ready',
actions: ['setReceivedVcs'],
},
},
},
},
},
ready: {
id: 'ready',
entry: [sendParent('READY')],
on: {
GET_RECEIVED_VCS: {
actions: ['getReceivedVcsResponse'],
},
GET_VC_ITEM: {
actions: ['getVcItemResponse'],
},
VC_ADDED: {
actions: ['prependToMyVcs'],
},
VC_DOWNLOADED: {
actions: ['setDownloadedVc'],
},
VC_RECEIVED: {
actions: ['prependToReceivedVcs'],
},
},
type: 'parallel',
states: {
myVcs: {
initial: 'idle',
states: {
idle: {
on: {
REFRESH_MY_VCS: 'refreshing',
},
},
refreshing: {
entry: ['loadMyVcs'],
on: {
STORE_RESPONSE: {
target: 'idle',
actions: ['setMyVcs'],
},
},
},
},
},
receivedVcs: {
initial: 'idle',
states: {
idle: {
on: {
REFRESH_RECEIVED_VCS: 'refreshing',
},
},
refreshing: {
entry: ['loadReceivedVcs'],
on: {
STORE_RESPONSE: {
target: 'idle',
actions: ['setReceivedVcs'],
},
},
},
},
},
},
},
},
},
{
actions: {
getReceivedVcsResponse: respond((context) => ({
type: 'VC_RESPONSE',
response: context.receivedVcs,
})),
getVcItemResponse: respond((context, event: GetVcItemEvent) => {
const vc = context.vcs[event.vcKey];
return VcItemEvents.GET_VC_RESPONSE(vc);
}),
loadMyVcs: send(StoreEvents.GET(MY_VCS_STORE_KEY), {
to: (context) => context.serviceRefs.store,
}),
loadReceivedVcs: send(StoreEvents.GET(RECEIVED_VCS_STORE_KEY), {
to: (context) => context.serviceRefs.store,
}),
setMyVcs: model.assign({
myVcs: (_, event: StoreResponseEvent) => event.response || [],
}),
setReceivedVcs: model.assign({
receivedVcs: (_, event: StoreResponseEvent) => event.response || [],
}),
setDownloadedVc: (context, event: VcDownloadedEvent) => {
context.vcs[VC_ITEM_STORE_KEY(event.vc)] = event.vc;
},
prependToMyVcs: model.assign({
myVcs: (context, event: VcAddedEvent) => [
event.vcKey,
...context.myVcs,
],
}),
prependToReceivedVcs: model.assign({
receivedVcs: (context, event: VcReceivedEvent) => [
event.vcKey,
...context.receivedVcs,
],
}),
},
}
);
export function createVcMachine(serviceRefs: AppServices) {
return vcMachine.withContext({
...vcMachine.context,
serviceRefs,
});
}
type State = StateFrom<typeof vcMachine>;
export function selectMyVcs(state: State) {
return state.context.myVcs;
}
export function selectShareableVcs(state: State) {
return state.context.myVcs.filter(
(vcKey) => state.context.vcs[vcKey]?.credential != null
);
}
export function selectReceivedVcs(state: State) {
return state.context.receivedVcs;
}
export function selectIsRefreshingMyVcs(state: State) {
return state.matches('ready.myVcs.refreshing');
}
export function selectIsRefreshingReceivedVcs(state: State) {
return state.matches('ready.receivedVcs.refreshing');
}

View File

@@ -1,6 +1,6 @@
import { EventFrom, send, StateFrom } from 'xstate';
import { createModel } from 'xstate/lib/model';
import { VID_ITEM_STORE_KEY } from '../shared/constants';
import { VC_ITEM_STORE_KEY } from '../shared/constants';
import { AppServices } from '../shared/GlobalContext';
import { CredentialDownloadResponse, request } from '../shared/request';
import {
@@ -29,40 +29,40 @@ const model = createModel(
SAVE_TAG: (tag: string) => ({ tag }),
STORE_READY: () => ({}),
DISMISS: () => ({}),
CREDENTIAL_DOWNLOADED: (vid: VC) => ({ vid }),
CREDENTIAL_DOWNLOADED: (vc: VC) => ({ vc }),
STORE_RESPONSE: (response: VC) => ({ response }),
POLL: () => ({}),
DOWNLOAD_READY: () => ({}),
GET_VID_RESPONSE: (vid: VC) => ({ vid }),
GET_VC_RESPONSE: (vc: VC) => ({ vc }),
},
}
);
export const VidItemEvents = model.events;
export const VcItemEvents = model.events;
type SaveTagEvent = EventFrom<typeof model, 'SAVE_TAG'>;
type GetVidResponseEvent = EventFrom<typeof model, 'GET_VID_RESPONSE'>;
type GetVcResponseEvent = EventFrom<typeof model, 'GET_VC_RESPONSE'>;
type StoreResponseEvent = EventFrom<typeof model, 'STORE_RESPONSE'>;
type CredentialDownloadedEvent = EventFrom<
typeof model,
'CREDENTIAL_DOWNLOADED'
>;
type RequestVidDataEvent =
type RequestVcDataEvent =
| StoreResponseEvent
| CredentialDownloadedEvent
| GetVidResponseEvent;
| GetVcResponseEvent;
export const vidItemMachine = model.createMachine(
export const vcItemMachine = model.createMachine(
{
id: 'vid-item',
id: 'vc-item',
context: model.initialContext,
initial: 'checkingVid',
initial: 'checkingVc',
states: {
checkingVid: {
entry: ['requestVidContext'],
checkingVc: {
entry: ['requestVcContext'],
on: {
GET_VID_RESPONSE: [
GET_VC_RESPONSE: [
{
cond: 'hasCredential',
target: 'idle',
@@ -81,7 +81,7 @@ export const vidItemMachine = model.createMachine(
{
cond: 'hasCredential',
target: 'idle',
actions: ['setCredential', 'updateVid'],
actions: ['setCredential', 'updateVc'],
},
{
target: 'checkingServerData',
@@ -118,7 +118,7 @@ export const vidItemMachine = model.createMachine(
actions: [
'setCredential',
'storeContext',
'updateVid',
'updateVc',
'logDownloaded',
],
},
@@ -151,26 +151,26 @@ export const vidItemMachine = model.createMachine(
},
{
actions: {
updateVid: send(
updateVc: send(
(context) => {
const { serviceRefs, ...vid } = context;
return { type: 'VID_DOWNLOADED', vid };
const { serviceRefs, ...vc } = context;
return { type: 'VC_DOWNLOADED', vc };
},
{ to: (context) => context.serviceRefs.vid }
{ to: (context) => context.serviceRefs.vc }
),
requestVidContext: send(
requestVcContext: send(
(context) => ({
type: 'GET_VID_ITEM',
vidKey: VID_ITEM_STORE_KEY(context),
type: 'GET_VC_ITEM',
vcKey: VC_ITEM_STORE_KEY(context),
}),
{
to: (context) => context.serviceRefs.vid,
to: (context) => context.serviceRefs.vc,
}
),
requestStoredContext: send(
(context) => StoreEvents.GET(VID_ITEM_STORE_KEY(context)),
(context) => StoreEvents.GET(VC_ITEM_STORE_KEY(context)),
{
to: (context) => context.serviceRefs.store,
}
@@ -179,7 +179,7 @@ export const vidItemMachine = model.createMachine(
storeContext: send(
(context) => {
const { serviceRefs, ...data } = context;
return StoreEvents.SET(VID_ITEM_STORE_KEY(context), data);
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
},
{
to: (context) => context.serviceRefs.store,
@@ -193,29 +193,29 @@ export const vidItemMachine = model.createMachine(
storeTag: send(
(context) => {
const { serviceRefs, ...data } = context;
return StoreEvents.SET(VID_ITEM_STORE_KEY(context), data);
return StoreEvents.SET(VC_ITEM_STORE_KEY(context), data);
},
{ to: (context) => context.serviceRefs.store }
),
setCredential: model.assign((_, event: RequestVidDataEvent) => {
setCredential: model.assign((_, event: RequestVcDataEvent) => {
switch (event.type) {
case 'STORE_RESPONSE':
return event.response;
case 'GET_VID_RESPONSE':
case 'GET_VC_RESPONSE':
case 'CREDENTIAL_DOWNLOADED':
return event.vid;
return event.vc;
}
}),
logDownloaded: send(
(_, event: CredentialDownloadedEvent) =>
ActivityLogEvents.LOG_ACTIVITY({
_vidKey: VID_ITEM_STORE_KEY(event.vid),
_vcKey: VC_ITEM_STORE_KEY(event.vc),
action: 'downloaded',
timestamp: Date.now(),
deviceName: '',
vidLabel: event.vid.tag || event.vid.id,
VCLabel: event.vc.tag || event.vc.id,
}),
{ to: (context) => context.serviceRefs.activityLog }
),
@@ -294,13 +294,13 @@ export const vidItemMachine = model.createMachine(
}
);
export const createVidItemMachine = (
export const createVcItemMachine = (
serviceRefs: AppServices,
vidKey: string
vcKey: string
) => {
const [_, idType, id, requestId] = vidKey.split(':');
return vidItemMachine.withContext({
...vidItemMachine.context,
const [_, idType, id, requestId] = vcKey.split(':');
return vcItemMachine.withContext({
...vcItemMachine.context,
serviceRefs,
id,
idType: idType as VcIdType,
@@ -308,9 +308,9 @@ export const createVidItemMachine = (
});
};
type State = StateFrom<typeof vidItemMachine>;
type State = StateFrom<typeof vcItemMachine>;
export function selectVid(state: State) {
export function selectVc(state: State) {
const { serviceRefs, ...data } = state.context;
return data;
}

View File

@@ -34,7 +34,7 @@ export const HistoryTab: React.FC<HomeScreenTabProps> = (props) => {
<TextItem
key={activity.timestamp}
label={createLabel(activity)}
text={`${activity.vidLabel} ${activity.action}`}
text={`${activity.VCLabel} ${activity.action}`}
/>
))}
{controller.activities.length === 0 && (

View File

@@ -5,9 +5,9 @@ import { Column, Text } from '../../components/ui';
import { Colors } from '../../components/ui/styleUtils';
import { HomeRouteProps } from '../../routes/main';
import { HistoryTab } from './HistoryTab';
import { MyVidsTab } from './MyVidsTab';
import { ReceivedVidsTab } from './ReceivedVidsTab';
import { ViewVidModal } from './ViewVidModal';
import { MyVcsTab } from './MyVcsTab';
import { ReceivedVcsTab } from './ReceivedVcsTab';
import { ViewVcModal } from './ViewVcModal';
import { ActorRefFrom } from 'xstate';
import { useHomeScreen } from './HomeScreenController';
@@ -34,19 +34,19 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
value={controller.activeTab}
onChange={controller.SELECT_TAB}
indicatorStyle={styles.tabIndicator}>
{TabItem(`My\n${controller.vidLabel.plural}`)}
{TabItem(`Received\n${controller.vidLabel.plural}`)}
{TabItem(`My\n${controller.VCLabel.plural}`)}
{TabItem(`Received\n${controller.VCLabel.plural}`)}
{TabItem('History')}
</Tab>
{controller.haveTabsLoaded && (
<Column fill>
<MyVidsTab
<MyVcsTab
isVisible={controller.activeTab === 0}
service={controller.service.children.get('myVidsTab')}
service={controller.service.children.get('MyVcsTab')}
/>
<ReceivedVidsTab
<ReceivedVcsTab
isVisible={controller.activeTab === 1}
service={controller.service.children.get('receivedVidsTab')}
service={controller.service.children.get('receivedVcsTab')}
/>
<HistoryTab
isVisible={controller.activeTab === 2}
@@ -55,11 +55,11 @@ export const HomeScreen: React.FC<HomeRouteProps> = (props) => {
</Column>
)}
</Column>
{controller.selectedVid && (
<ViewVidModal
isVisible={controller.isViewingVid}
{controller.selectedVc && (
<ViewVcModal
isVisible={controller.isViewingVc}
onDismiss={controller.DISMISS_MODAL}
vidItemActor={controller.selectedVid}
vcItemActor={controller.selectedVc}
/>
)}
</React.Fragment>

View File

@@ -1,15 +1,15 @@
import { useInterpret, useSelector } from '@xstate/react';
import { useContext, useEffect, useRef } from 'react';
import { selectVidLabel } from '../../machines/settings';
import { selectVCLabel } from '../../machines/settings';
import { HomeRouteProps } from '../../routes/main';
import { GlobalContext } from '../../shared/GlobalContext';
import {
HomeScreenEvents,
HomeScreenMachine,
selectActiveTab,
selectSelectedVid,
selectSelectedVc,
selectTabsLoaded,
selectViewingVid,
selectViewingVc,
} from './HomeScreenMachine';
export function useHomeScreen(props: HomeRouteProps) {
@@ -33,10 +33,10 @@ export function useHomeScreen(props: HomeRouteProps) {
service,
activeTab: useSelector(service, selectActiveTab),
vidLabel: useSelector(settingsService, selectVidLabel),
selectedVid: useSelector(service, selectSelectedVid),
VCLabel: useSelector(settingsService, selectVCLabel),
selectedVc: useSelector(service, selectSelectedVc),
isViewingVid: useSelector(service, selectViewingVid),
isViewingVc: useSelector(service, selectViewingVc),
haveTabsLoaded: useSelector(service, selectTabsLoaded),
SELECT_TAB,
@@ -45,8 +45,8 @@ export function useHomeScreen(props: HomeRouteProps) {
function SELECT_TAB(index: number) {
const tabs = [
HomeScreenEvents.SELECT_MY_VIDS,
HomeScreenEvents.SELECT_RECEIVED_VIDS,
HomeScreenEvents.SELECT_MY_VCS,
HomeScreenEvents.SELECT_RECEIVED_VCS,
HomeScreenEvents.SELECT_HISTORY,
];
service.send(tabs[index]());

View File

@@ -1,35 +1,35 @@
import { ActorRefFrom, EventFrom, send, spawn, StateFrom } from 'xstate';
import { createModel } from 'xstate/lib/model';
import { vidItemMachine } from '../../machines/vidItem';
import { vcItemMachine } from '../../machines/vcItem';
import { AppServices } from '../../shared/GlobalContext';
import {
createHistoryTabMachine,
HistoryTabMachine,
} from './HistoryTabMachine';
import { createMyVidsTabMachine, MyVidsTabMachine } from './MyVidsTabMachine';
import { createMyVcsTabMachine, MyVcsTabMachine } from './MyVcsTabMachine';
import {
createReceivedVidsTabMachine,
ReceivedVidsTabMachine,
} from './ReceivedVidsTabMachine';
createReceivedVcsTabMachine,
ReceivedVcsTabMachine,
} from './ReceivedVcsTabMachine';
const model = createModel(
{
serviceRefs: {} as AppServices,
tabRefs: {
myVids: {} as ActorRefFrom<typeof MyVidsTabMachine>,
receivedVids: {} as ActorRefFrom<typeof ReceivedVidsTabMachine>,
myVcs: {} as ActorRefFrom<typeof MyVcsTabMachine>,
receivedVcs: {} as ActorRefFrom<typeof ReceivedVcsTabMachine>,
history: {} as ActorRefFrom<typeof HistoryTabMachine>,
},
selectedVid: null as ActorRefFrom<typeof vidItemMachine>,
selectedVc: null as ActorRefFrom<typeof vcItemMachine>,
activeTab: 0,
},
{
events: {
SELECT_MY_VIDS: () => ({}),
SELECT_RECEIVED_VIDS: () => ({}),
SELECT_MY_VCS: () => ({}),
SELECT_RECEIVED_VCS: () => ({}),
SELECT_HISTORY: () => ({}),
VIEW_VID: (vidItemActor: ActorRefFrom<typeof vidItemMachine>) => ({
vidItemActor,
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) => ({
vcItemActor,
}),
DISMISS_MODAL: () => ({}),
},
@@ -38,7 +38,7 @@ const model = createModel(
export const HomeScreenEvents = model.events;
type ViewVidEvent = EventFrom<typeof model, 'VIEW_VID'>;
type ViewVcEvent = EventFrom<typeof model, 'VIEW_VC'>;
export const HomeScreenMachine = model.createMachine(
{
@@ -50,36 +50,36 @@ export const HomeScreenMachine = model.createMachine(
id: 'tabs',
initial: 'init',
on: {
SELECT_MY_VIDS: '.myVids',
SELECT_RECEIVED_VIDS: '.receivedVids',
SELECT_MY_VCS: '.myVcs',
SELECT_RECEIVED_VCS: '.receivedVcs',
SELECT_HISTORY: '.history',
},
states: {
init: {
entry: ['spawnTabActors'],
after: {
100: 'myVids',
100: 'myVcs',
},
},
myVids: {
myVcs: {
entry: [setActiveTab(0)],
on: {
DISMISS_MODAL: {
actions: [
send('DISMISS', {
to: (context) => context.tabRefs.myVids,
to: (context) => context.tabRefs.myVcs,
}),
],
},
},
},
receivedVids: {
receivedVcs: {
entry: [setActiveTab(1)],
on: {
DISMISS_MODAL: {
actions: [
send('DISMISS', {
to: (context) => context.tabRefs.receivedVids,
to: (context) => context.tabRefs.receivedVcs,
}),
],
},
@@ -94,15 +94,15 @@ export const HomeScreenMachine = model.createMachine(
initial: 'none',
states: {
none: {
entry: ['resetSelectedVid'],
entry: ['resetSelectedVc'],
on: {
VIEW_VID: {
target: 'viewingVid',
actions: ['setSelectedVid'],
VIEW_VC: {
target: 'viewingVc',
actions: ['setSelectedVc'],
},
},
},
viewingVid: {
viewingVc: {
on: {
DISMISS_MODAL: 'none',
},
@@ -115,13 +115,13 @@ export const HomeScreenMachine = model.createMachine(
actions: {
spawnTabActors: model.assign({
tabRefs: (context) => ({
myVids: spawn(
createMyVidsTabMachine(context.serviceRefs),
'myVidsTab'
myVcs: spawn(
createMyVcsTabMachine(context.serviceRefs),
'MyVcsTab'
),
receivedVids: spawn(
createReceivedVidsTabMachine(context.serviceRefs),
'receivedVidsTab'
receivedVcs: spawn(
createReceivedVcsTabMachine(context.serviceRefs),
'receivedVcsTab'
),
history: spawn(
createHistoryTabMachine(context.serviceRefs),
@@ -130,12 +130,12 @@ export const HomeScreenMachine = model.createMachine(
}),
}),
setSelectedVid: model.assign({
selectedVid: (_, event: ViewVidEvent) => event.vidItemActor,
setSelectedVc: model.assign({
selectedVc: (_, event: ViewVcEvent) => event.vcItemActor,
}),
resetSelectedVid: model.assign({
selectedVid: null,
resetSelectedVc: model.assign({
selectedVc: null,
}),
},
}
@@ -151,12 +151,12 @@ export function selectActiveTab(state: State) {
return state.context.activeTab;
}
export function selectSelectedVid(state: State) {
return state.context.selectedVid;
export function selectSelectedVc(state: State) {
return state.context.selectedVc;
}
export function selectViewingVid(state: State) {
return state.matches('modals.viewingVid');
export function selectViewingVc(state: State) {
return state.matches('modals.viewingVc');
}
export function selectTabsLoaded(state: State) {

View File

@@ -1,11 +1,11 @@
import React from 'react';
import { MessageOverlay } from '../../../components/MessageOverlay';
import { AddVidModalProps, useAddVidModal } from './AddVidModalController';
import { AddVcModalProps, useAddVcModal } from './AddVcModalController';
import { OtpVerificationModal } from './OtpVerificationModal';
import { IdInputModal } from './IdInputModal';
export const AddVidModal: React.FC<AddVidModalProps> = (props) => {
const controller = useAddVidModal(props);
export const AddVcModal: React.FC<AddVcModalProps> = (props) => {
const controller = useAddVcModal(props);
return (
<React.Fragment>

View File

@@ -1,15 +1,15 @@
import { useSelector } from '@xstate/react';
import { ActorRefFrom } from 'xstate';
import {
AddVidModalEvents,
AddVidModalMachine,
AddVcModalEvents,
AddVcModalMachine,
selectIsAcceptingOtpInput,
selectIsRequestingCredential,
selectOtpError,
selectIsAcceptingIdInput,
} from './AddVidModalMachine';
} from './AddVcModalMachine';
export function useAddVidModal({ service }: AddVidModalProps) {
export function useAddVcModal({ service }: AddVcModalProps) {
return {
isRequestingCredential: useSelector(service, selectIsRequestingCredential),
@@ -18,12 +18,12 @@ export function useAddVidModal({ service }: AddVidModalProps) {
isAcceptingUinInput: useSelector(service, selectIsAcceptingIdInput),
isAcceptingOtpInput: useSelector(service, selectIsAcceptingOtpInput),
INPUT_OTP: (otp: string) => service.send(AddVidModalEvents.INPUT_OTP(otp)),
INPUT_OTP: (otp: string) => service.send(AddVcModalEvents.INPUT_OTP(otp)),
DISMISS: () => service.send(AddVidModalEvents.DISMISS()),
DISMISS: () => service.send(AddVcModalEvents.DISMISS()),
};
}
export interface AddVidModalProps {
service: ActorRefFrom<typeof AddVidModalMachine>;
export interface AddVcModalProps {
service: ActorRefFrom<typeof AddVcModalMachine>;
}

View File

@@ -9,7 +9,7 @@ import {
} from 'xstate';
import { createModel } from 'xstate/lib/model';
import { BackendResponseError, request } from '../../../shared/request';
import { VID_ITEM_STORE_KEY } from '../../../shared/constants';
import { VC_ITEM_STORE_KEY } from '../../../shared/constants';
import { VcIdType } from '../../../types/vc';
const model = createModel(
@@ -35,16 +35,16 @@ const model = createModel(
}
);
export const AddVidModalEvents = model.events;
export const AddVcModalEvents = model.events;
type ReadyEvent = EventFrom<typeof model, 'READY'>;
type InputIdEvent = EventFrom<typeof model, 'INPUT_ID'>;
type InputOtpEvent = EventFrom<typeof model, 'INPUT_OTP'>;
type SelectIdTypeEvent = EventFrom<typeof model, 'SELECT_ID_TYPE'>;
export const AddVidModalMachine = model.createMachine(
export const AddVcModalMachine = model.createMachine(
{
id: 'AddVidModal',
id: 'AddVcModal',
context: model.initialContext,
initial: 'acceptingIdInput',
states: {
@@ -162,7 +162,7 @@ export const AddVidModalMachine = model.createMachine(
},
done: {
type: 'final',
data: (context) => VID_ITEM_STORE_KEY(context),
data: (context) => VC_ITEM_STORE_KEY(context),
},
},
},
@@ -254,7 +254,7 @@ export const AddVidModalMachine = model.createMachine(
}
);
type State = StateFrom<typeof AddVidModalMachine>;
type State = StateFrom<typeof AddVcModalMachine>;
export function selectId(state: State) {
return state.context.id;

View File

@@ -1,21 +1,21 @@
import React from 'react';
import { Button, Column, Text } from '../../../components/ui';
import { Modal, ModalProps } from '../../../components/ui/Modal';
import { useDownloadingvidModal } from './DownloadingVidModalController';
import { useDownloadingvcModal } from './DownloadingVcModalController';
export const DownloadingVidModal: React.FC<ModalProps> = (props) => {
const controller = useDownloadingvidModal();
export const DownloadingVcModal: React.FC<ModalProps> = (props) => {
const controller = useDownloadingvcModal();
return (
<Modal isVisible={props.isVisible} onDismiss={props.onDismiss}>
<Column fill padding="32 24" align="space-between">
<Column fill>
<Text weight="semibold" align="center">
Downloading your {controller.vidLabel.singular}
Downloading your {controller.VCLabel.singular}
</Text>
<Text align="center">
This may take some time, we will{'\n'}notify you when your{' '}
{controller.vidLabel.singular} has been{'\n'}downloaded and is
{controller.VCLabel.singular} has been{'\n'}downloaded and is
available
</Text>
</Column>

View File

@@ -1,13 +1,13 @@
import { useSelector } from '@xstate/react';
import { useContext } from 'react';
import { selectVidLabel } from '../../../machines/settings';
import { selectVCLabel } from '../../../machines/settings';
import { GlobalContext } from '../../../shared/GlobalContext';
export function useDownloadingvidModal() {
export function useDownloadingvcModal() {
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
return {
vidLabel: useSelector(settingsService, selectVidLabel),
VCLabel: useSelector(settingsService, selectVCLabel),
};
}

View File

@@ -15,7 +15,7 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
<Column fill align="space-between" padding="32 24">
<Text align="center">
Enter the MOSIP-provided UIN or VID{'\n'}of the{' '}
{controller.vidLabel.singular} you wish to retrieve
{controller.VCLabel.singular} you wish to retrieve
</Text>
<Column>
<Row crossAlign="flex-end">
@@ -55,7 +55,7 @@ export const IdInputModal: React.FC<IdInputModalProps> = (props) => {
</Column>
</Row>
<Button
title={`Generate ${controller.vidLabel.singular}`}
title={`Generate ${controller.VCLabel.singular}`}
margin="24 0 0 0"
onPress={controller.VALIDATE_INPUT}
loading={controller.isRequestingOtp}

View File

@@ -1,13 +1,13 @@
import { useContext } from 'react';
import { useSelector } from '@xstate/react';
import { selectVidLabel } from '../../../machines/settings';
import { selectVCLabel } from '../../../machines/settings';
import { GlobalContext } from '../../../shared/GlobalContext';
import { ActorRefFrom } from 'xstate';
import { TextInput } from 'react-native';
import { ModalProps } from '../../../components/ui/Modal';
import {
AddVidModalEvents,
AddVidModalMachine,
AddVcModalEvents,
AddVcModalMachine,
selectIsAcceptingOtpInput,
selectIsInvalid,
selectIsRequestingOtp,
@@ -16,7 +16,7 @@ import {
selectIdError,
selectIdInputRef,
selectIdType,
} from './AddVidModalMachine';
} from './AddVcModalMachine';
import { VcIdType } from '../../../types/vc';
export function useIdInputModal({ service }: IdInputModalProps) {
@@ -27,7 +27,7 @@ export function useIdInputModal({ service }: IdInputModalProps) {
id: useSelector(service, selectId),
idType: useSelector(service, selectIdType),
idInputRef: useSelector(service, selectIdInputRef),
vidLabel: useSelector(settingsService, selectVidLabel),
VCLabel: useSelector(settingsService, selectVCLabel),
idError: useSelector(service, selectIdError),
otpError: useSelector(service, selectOtpError),
@@ -35,16 +35,16 @@ export function useIdInputModal({ service }: IdInputModalProps) {
isAcceptingOtpInput: useSelector(service, selectIsAcceptingOtpInput),
isRequestingOtp: useSelector(service, selectIsRequestingOtp),
INPUT_ID: (id: string) => service.send(AddVidModalEvents.INPUT_ID(id)),
INPUT_ID: (id: string) => service.send(AddVcModalEvents.INPUT_ID(id)),
SELECT_ID_TYPE: (selectedValue: VcIdType) =>
service.send(AddVidModalEvents.SELECT_ID_TYPE(selectedValue)),
VALIDATE_INPUT: () => service.send(AddVidModalEvents.VALIDATE_INPUT()),
INPUT_OTP: (otp: string) => service.send(AddVidModalEvents.INPUT_OTP(otp)),
READY: (input: TextInput) => service.send(AddVidModalEvents.READY(input)),
DISMISS: () => service.send(AddVidModalEvents.DISMISS()),
service.send(AddVcModalEvents.SELECT_ID_TYPE(selectedValue)),
VALIDATE_INPUT: () => service.send(AddVcModalEvents.VALIDATE_INPUT()),
INPUT_OTP: (otp: string) => service.send(AddVcModalEvents.INPUT_OTP(otp)),
READY: (input: TextInput) => service.send(AddVcModalEvents.READY(input)),
DISMISS: () => service.send(AddVcModalEvents.DISMISS()),
};
}
export interface IdInputModalProps extends ModalProps {
service: ActorRefFrom<typeof AddVidModalMachine>;
service: ActorRefFrom<typeof AddVcModalMachine>;
}

View File

@@ -1,60 +1,60 @@
import React from 'react';
import { Button, Column, Text, Centered } from '../../components/ui';
import { VidItem } from '../../components/VidItem';
import { VcItem } from '../../components/VcItem';
import { Icon } from 'react-native-elements';
import { Colors } from '../../components/ui/styleUtils';
import { RefreshControl } from 'react-native';
import { useMyVidsTab } from './MyVidsTabController';
import { useMyVcsTab } from './MyVcsTabController';
import { HomeScreenTabProps } from './HomeScreen';
import { AddVidModal } from './MyVids/AddVidModal';
import { DownloadingVidModal } from './MyVids/DownloadingVidModal';
import { AddVcModal } from './MyVcs/AddVcModal';
import { DownloadingVcModal } from './MyVcs/DownloadingVcModal';
import { OnboardingOverlay } from './OnboardingOverlay';
export const MyVidsTab: React.FC<HomeScreenTabProps> = (props) => {
const controller = useMyVidsTab(props);
export const MyVcsTab: React.FC<HomeScreenTabProps> = (props) => {
const controller = useMyVcsTab(props);
return (
<React.Fragment>
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
<Column fill padding="32 24">
{controller.vidKeys.length > 0 && (
{controller.vcKeys.length > 0 && (
<React.Fragment>
<Column
scroll
refreshControl={
<RefreshControl
refreshing={controller.isRefreshingVids}
refreshing={controller.isRefreshingVcs}
onRefresh={controller.REFRESH}
/>
}>
{controller.vidKeys.map((vidKey) => (
<VidItem
key={vidKey}
vidKey={vidKey}
{controller.vcKeys.map((vcKey) => (
<VcItem
key={vcKey}
vcKey={vcKey}
margin="0 2 8 2"
onPress={controller.VIEW_VID}
onPress={controller.VIEW_VC}
/>
))}
</Column>
<Column elevation={2} margin="0 2">
<Button
type="clear"
disabled={controller.isRefreshingVids}
title={`Add ${controller.vidLabel.singular}`}
onPress={controller.ADD_VID}
disabled={controller.isRefreshingVcs}
title={`Add ${controller.VCLabel.singular}`}
onPress={controller.ADD_VC}
/>
</Column>
</React.Fragment>
)}
{controller.vidKeys.length === 0 && (
{controller.vcKeys.length === 0 && (
<React.Fragment>
<Centered fill>
<Text weight="semibold" margin="0 0 8 0">
Generate your {controller.vidLabel.plural}
Generate your {controller.VCLabel.plural}
</Text>
<Text color={Colors.Grey} align="center">
Tap on "Add {controller.vidLabel.singular}" below to{'\n'}
download your {controller.vidLabel.singular}
Tap on "Add {controller.VCLabel.singular}" below to{'\n'}
download your {controller.VCLabel.singular}
</Text>
<Icon
name="arrow-downward"
@@ -63,20 +63,20 @@ export const MyVidsTab: React.FC<HomeScreenTabProps> = (props) => {
/>
</Centered>
<Button
disabled={controller.isRefreshingVids}
title={`Add ${controller.vidLabel.singular}`}
onPress={controller.ADD_VID}
disabled={controller.isRefreshingVcs}
title={`Add ${controller.VCLabel.singular}`}
onPress={controller.ADD_VC}
/>
</React.Fragment>
)}
</Column>
</Column>
{controller.AddVidModalService && (
<AddVidModal service={controller.AddVidModalService} />
{controller.AddVcModalService && (
<AddVcModal service={controller.AddVcModalService} />
)}
{controller.isRequestSuccessful && (
<DownloadingVidModal
<DownloadingVcModal
isVisible={controller.isRequestSuccessful}
onDismiss={controller.DISMISS}
/>
@@ -85,7 +85,7 @@ export const MyVidsTab: React.FC<HomeScreenTabProps> = (props) => {
<OnboardingOverlay
isVisible={controller.isOnboarding}
onDone={controller.ONBOARDING_DONE}
onAddVid={controller.ADD_VID}
onAddVc={controller.ADD_VC}
/>
</React.Fragment>
);

View File

@@ -0,0 +1,50 @@
import { useSelector } from '@xstate/react';
import { useContext } from 'react';
import { ActorRefFrom } from 'xstate';
import { selectVCLabel } from '../../machines/settings';
import {
selectIsRefreshingMyVcs,
selectMyVcs,
VcEvents,
} from '../../machines/vc';
import { vcItemMachine } from '../../machines/vcItem';
import { GlobalContext } from '../../shared/GlobalContext';
import { HomeScreenTabProps } from './HomeScreen';
import {
MyVcsTabEvents,
MyVcsTabMachine,
selectAddVcModal,
selectIsOnboarding,
selectIsRequestSuccessful,
} from './MyVcsTabMachine';
export function useMyVcsTab(props: HomeScreenTabProps) {
const service = props.service as ActorRefFrom<typeof MyVcsTabMachine>;
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
const vcService = appService.children.get('vc');
return {
service,
AddVcModalService: useSelector(service, selectAddVcModal),
vcKeys: useSelector(vcService, selectMyVcs),
VCLabel: useSelector(settingsService, selectVCLabel),
isRefreshingVcs: useSelector(vcService, selectIsRefreshingMyVcs),
isRequestSuccessful: useSelector(service, selectIsRequestSuccessful),
isOnboarding: useSelector(service, selectIsOnboarding),
DISMISS: () => service.send(MyVcsTabEvents.DISMISS()),
ADD_VC: () => service.send(MyVcsTabEvents.ADD_VC()),
REFRESH: () => vcService.send(VcEvents.REFRESH_MY_VCS()),
VIEW_VC: (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
return service.send(MyVcsTabEvents.VIEW_VC(vcRef));
},
ONBOARDING_DONE: () => service.send(MyVcsTabEvents.ONBOARDING_DONE()),
};
}

View File

@@ -8,14 +8,14 @@ import {
} from 'xstate';
import { createModel } from 'xstate/lib/model';
import { StoreEvents, StoreResponseEvent } from '../../machines/store';
import { VidEvents } from '../../machines/vid';
import { vidItemMachine } from '../../machines/vidItem';
import { VcEvents } from '../../machines/vc';
import { vcItemMachine } from '../../machines/vcItem';
import { AppServices } from '../../shared/GlobalContext';
import {
MY_VIDS_STORE_KEY,
MY_VCS_STORE_KEY,
ONBOARDING_STATUS_STORE_KEY,
} from '../../shared/constants';
import { AddVidModalMachine } from './MyVids/AddVidModalMachine';
import { AddVcModalMachine } from './MyVcs/AddVcModalMachine';
const model = createModel(
{
@@ -24,24 +24,24 @@ const model = createModel(
{
events: {
REFRESH: () => ({}),
VIEW_VID: (vidItemActor: ActorRefFrom<typeof vidItemMachine>) => ({
vidItemActor,
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) => ({
vcItemActor,
}),
DISMISS: () => ({}),
STORE_RESPONSE: (response?: any) => ({ response }),
ADD_VID: () => ({}),
ADD_VC: () => ({}),
ONBOARDING_DONE: () => ({}),
},
}
);
export const MyVidsTabEvents = model.events;
export const MyVcsTabEvents = model.events;
type ViewVidEvent = EventFrom<typeof model, 'VIEW_VID'>;
type ViewVcEvent = EventFrom<typeof model, 'VIEW_VC'>;
export const MyVidsTabMachine = model.createMachine(
export const MyVcsTabMachine = model.createMachine(
{
id: 'MyVidsTab',
id: 'MyVcsTab',
context: model.initialContext,
initial: 'checkingOnboardingStatus',
states: {
@@ -56,8 +56,8 @@ export const MyVidsTabMachine = model.createMachine(
},
onboarding: {
on: {
ADD_VID: {
target: 'addingVid',
ADD_VC: {
target: 'addingVc',
actions: ['completeOnboarding'],
},
ONBOARDING_DONE: {
@@ -69,42 +69,42 @@ export const MyVidsTabMachine = model.createMachine(
idle: {
id: 'idle',
on: {
ADD_VID: 'addingVid',
VIEW_VID: 'viewingVid',
ADD_VC: 'addingVc',
VIEW_VC: 'viewingVc',
},
},
viewingVid: {
viewingVc: {
entry: [
sendParent((_, event: ViewVidEvent) =>
model.events.VIEW_VID(event.vidItemActor)
sendParent((_, event: ViewVcEvent) =>
model.events.VIEW_VC(event.vcItemActor)
),
],
on: {
DISMISS: 'idle',
},
},
addingVid: {
addingVc: {
invoke: {
id: 'AddVidModal',
src: AddVidModalMachine,
id: 'AddVcModal',
src: AddVcModalMachine,
onDone: '.storing',
},
on: {
DISMISS: 'idle',
},
initial: 'waitingForVidKey',
initial: 'waitingForvcKey',
states: {
waitingForVidKey: {},
waitingForvcKey: {},
storing: {
entry: ['storeVidItem'],
entry: ['storeVcItem'],
on: {
STORE_RESPONSE: {
target: 'addVidSuccessful',
actions: ['sendVidAdded'],
target: 'addVcSuccessful',
actions: ['sendVcAdded'],
},
},
},
addVidSuccessful: {
addVcSuccessful: {
on: {
DISMISS: '#idle',
},
@@ -125,17 +125,17 @@ export const MyVidsTabMachine = model.createMachine(
{ to: (context) => context.serviceRefs.store }
),
storeVidItem: send(
storeVcItem: send(
(_, _event: any) => {
const event: DoneInvokeEvent<string> = _event;
return StoreEvents.PREPEND(MY_VIDS_STORE_KEY, event.data);
return StoreEvents.PREPEND(MY_VCS_STORE_KEY, event.data);
},
{ to: (context) => context.serviceRefs.store }
),
sendVidAdded: send(
(_, event: StoreResponseEvent) => VidEvents.VID_ADDED(event.response),
{ to: (context) => context.serviceRefs.vid }
sendVcAdded: send(
(_, event: StoreResponseEvent) => VcEvents.VC_ADDED(event.response),
{ to: (context) => context.serviceRefs.vc }
),
},
@@ -147,17 +147,17 @@ export const MyVidsTabMachine = model.createMachine(
}
);
export function createMyVidsTabMachine(serviceRefs: AppServices) {
return MyVidsTabMachine.withContext({
...MyVidsTabMachine.context,
export function createMyVcsTabMachine(serviceRefs: AppServices) {
return MyVcsTabMachine.withContext({
...MyVcsTabMachine.context,
serviceRefs,
});
}
type State = StateFrom<typeof MyVidsTabMachine>;
type State = StateFrom<typeof MyVcsTabMachine>;
export function selectAddVidModal(state: State) {
return state.children.AddVidModal as ActorRefFrom<typeof AddVidModalMachine>;
export function selectAddVcModal(state: State) {
return state.children.AddVcModal as ActorRefFrom<typeof AddVcModalMachine>;
}
export function selectIsOnboarding(state: State) {
@@ -165,5 +165,5 @@ export function selectIsOnboarding(state: State) {
}
export function selectIsRequestSuccessful(state: State) {
return state.matches('addingVid.addVidSuccessful');
return state.matches('addingVc.addVcSuccessful');
}

View File

@@ -1,50 +0,0 @@
import { useSelector } from '@xstate/react';
import { useContext } from 'react';
import { ActorRefFrom } from 'xstate';
import { selectVidLabel } from '../../machines/settings';
import {
selectIsRefreshingMyVids,
selectMyVids,
VidEvents,
} from '../../machines/vid';
import { vidItemMachine } from '../../machines/vidItem';
import { GlobalContext } from '../../shared/GlobalContext';
import { HomeScreenTabProps } from './HomeScreen';
import {
MyVidsTabEvents,
MyVidsTabMachine,
selectAddVidModal,
selectIsOnboarding,
selectIsRequestSuccessful,
} from './MyVidsTabMachine';
export function useMyVidsTab(props: HomeScreenTabProps) {
const service = props.service as ActorRefFrom<typeof MyVidsTabMachine>;
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
const vidService = appService.children.get('vid');
return {
service,
AddVidModalService: useSelector(service, selectAddVidModal),
vidKeys: useSelector(vidService, selectMyVids),
vidLabel: useSelector(settingsService, selectVidLabel),
isRefreshingVids: useSelector(vidService, selectIsRefreshingMyVids),
isRequestSuccessful: useSelector(service, selectIsRequestSuccessful),
isOnboarding: useSelector(service, selectIsOnboarding),
DISMISS: () => service.send(MyVidsTabEvents.DISMISS()),
ADD_VID: () => service.send(MyVidsTabEvents.ADD_VID()),
REFRESH: () => vidService.send(VidEvents.REFRESH_MY_VIDS()),
VIEW_VID: (vidRef: ActorRefFrom<typeof vidItemMachine>) => {
return service.send(MyVidsTabEvents.VIEW_VID(vidRef));
},
ONBOARDING_DONE: () => service.send(MyVidsTabEvents.ONBOARDING_DONE()),
};
}

View File

@@ -6,7 +6,7 @@ import { Button, Column, Text } from '../../components/ui';
import { Colors } from '../../components/ui/styleUtils';
import { useSelector } from '@xstate/react';
import { GlobalContext } from '../../shared/GlobalContext';
import { selectVidLabel } from '../../machines/settings';
import { selectVCLabel } from '../../machines/settings';
const styles = StyleSheet.create({
overlay: {
@@ -57,30 +57,30 @@ export const OnboardingOverlay: React.FC<OnboardingProps> = (props) => {
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
const vidLabel = useSelector(settingsService, selectVidLabel);
const VCLabel = useSelector(settingsService, selectVCLabel);
const slides = [
{
key: 'one',
title: 'Welcome!',
text: `Keep your digital credential with you at all times. To get started, add ${vidLabel.plural} to your profile.`,
text: `Keep your digital credential with you at all times. To get started, add ${VCLabel.plural} to your profile.`,
},
{
key: 'two',
title: `${vidLabel.singular} management`,
text: `Once generated, ${vidLabel.plural} are safely stored on your mobile and can be renamed or shared at any time.`,
title: `${VCLabel.singular} management`,
text: `Once generated, ${VCLabel.plural} are safely stored on your mobile and can be renamed or shared at any time.`,
},
{
key: 'three',
title: 'Easy sharing',
text: `Share and receive ${vidLabel.plural} switfly using your phone camera to scan QR codes.`,
text: `Share and receive ${VCLabel.plural} switfly using your phone camera to scan QR codes.`,
footer: (
<Button
margin="24 0 0 0"
raised
type="outline"
title={`Get started and add ${vidLabel.singular}`}
onPress={props.onAddVid}
title={`Get started and add ${VCLabel.singular}`}
onPress={props.onAddVc}
/>
),
},
@@ -147,5 +147,5 @@ export const OnboardingOverlay: React.FC<OnboardingProps> = (props) => {
interface OnboardingProps {
isVisible: boolean;
onDone: () => void;
onAddVid: () => void;
onAddVc: () => void;
}

View File

@@ -3,12 +3,12 @@ import { RefreshControl } from 'react-native';
import { Icon } from 'react-native-elements';
import { Centered, Column, Text } from '../../components/ui';
import { Colors } from '../../components/ui/styleUtils';
import { VidItem } from '../../components/VidItem';
import { VcItem } from '../../components/VcItem';
import { HomeScreenTabProps } from './HomeScreen';
import { useReceivedVidsTab } from './ReceivedVidsTabController';
import { useReceivedVcsTab } from './ReceivedVcsTabController';
export const ReceivedVidsTab: React.FC<HomeScreenTabProps> = (props) => {
const controller = useReceivedVidsTab(props);
export const ReceivedVcsTab: React.FC<HomeScreenTabProps> = (props) => {
const controller = useReceivedVcsTab(props);
return (
<Column fill style={{ display: props.isVisible ? 'flex' : 'none' }}>
@@ -17,19 +17,19 @@ export const ReceivedVidsTab: React.FC<HomeScreenTabProps> = (props) => {
padding="32 24"
refreshControl={
<RefreshControl
refreshing={controller.isRefreshingVids}
refreshing={controller.isRefreshingVcs}
onRefresh={controller.REFRESH}
/>
}>
{controller.vidKeys.map((vidKey) => (
<VidItem
key={vidKey}
vidKey={vidKey}
{controller.vcKeys.map((vcKey) => (
<VcItem
key={vcKey}
vcKey={vcKey}
margin="0 2 8 2"
onPress={controller.VIEW_VID}
onPress={controller.VIEW_VC}
/>
))}
{controller.vidKeys.length === 0 && (
{controller.vcKeys.length === 0 && (
<React.Fragment>
<Centered fill>
<Icon
@@ -38,10 +38,10 @@ export const ReceivedVidsTab: React.FC<HomeScreenTabProps> = (props) => {
name="sentiment-dissatisfied"
/>
<Text align="center" weight="semibold" margin="0 0 4 0">
No {controller.vidLabel.plural} available yet
No {controller.VCLabel.plural} available yet
</Text>
<Text align="center" color={Colors.Grey}>
Tap on Request below to receive {controller.vidLabel.singular}
Tap on Request below to receive {controller.VCLabel.singular}
</Text>
</Centered>
</React.Fragment>

View File

@@ -0,0 +1,33 @@
import { useSelector } from '@xstate/react';
import { useContext } from 'react';
import { ActorRefFrom } from 'xstate';
import { selectVCLabel } from '../../machines/settings';
import {
selectIsRefreshingReceivedVcs,
selectReceivedVcs,
} from '../../machines/vc';
import { vcItemMachine } from '../../machines/vcItem';
import { GlobalContext } from '../../shared/GlobalContext';
import { HomeScreenTabProps } from './HomeScreen';
import {
ReceivedVcsTabEvents,
ReceivedVcsTabMachine,
} from './ReceivedVcsTabMachine';
export function useReceivedVcsTab(props: HomeScreenTabProps) {
const service = props.service as ActorRefFrom<typeof ReceivedVcsTabMachine>;
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
const vcService = appService.children.get('vc');
return {
VCLabel: useSelector(settingsService, selectVCLabel),
vcKeys: useSelector(vcService, selectReceivedVcs),
isRefreshingVcs: useSelector(vcService, selectIsRefreshingReceivedVcs),
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) =>
service.send(ReceivedVcsTabEvents.VIEW_VC(vcItemActor)),
REFRESH: () => service.send(ReceivedVcsTabEvents.REFRESH()),
};
}

View File

@@ -4,7 +4,7 @@ import {
sendParent
} from 'xstate';
import { createModel } from 'xstate/lib/model';
import { vidItemMachine } from '../../machines/vidItem';
import { createVcItemMachine, vcItemMachine } from '../../machines/vcItem';
import { AppServices } from '../../shared/GlobalContext';
const model = createModel(
@@ -14,43 +14,43 @@ const model = createModel(
},
{
events: {
VIEW_VID: (vidItemActor: ActorRefFrom<typeof vidItemMachine>) => ({
vidItemActor,
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) => ({
vcItemActor,
}),
REFRESH: () => ({}),
DISMISS: () => ({}),
STORE_RESPONSE: (response?: any) => ({ response }),
STORE_ERROR: (error: Error) => ({ error }),
ERROR: (error: Error) => ({ error }),
GET_RECEIVED_VIDS_RESPONSE: (vidKeys: string[]) => ({ vidKeys }),
GET_RECEIVED_VCS_RESPONSE: (vcKeys: string[]) => ({ vcKeys }),
},
}
);
export const ReceivedVidsTabEvents = model.events;
export const ReceivedVcsTabEvents = model.events;
type ErrorEvent = EventFrom<typeof model, 'ERROR'>;
type ViewVidEvent = EventFrom<typeof model, 'VIEW_VID'>;
type GetReceivedVidListResponseEvent = EventFrom<
type ViewVcEvent = EventFrom<typeof model, 'VIEW_VC'>;
type GetReceivedVcListResponseEvent = EventFrom<
typeof model,
'GET_RECEIVED_VIDS_RESPONSE'
'GET_RECEIVED_VCS_RESPONSE'
>;
export const ReceivedVidsTabMachine = model.createMachine(
export const ReceivedVcsTabMachine = model.createMachine(
{
id: 'ReceivedVidsTab',
id: 'ReceivedVcsTab',
context: model.initialContext,
initial: 'idle',
states: {
idle: {
on: {
VIEW_VID: 'viewingVid',
VIEW_VC: 'viewingVc',
},
},
viewingVid: {
viewingVc: {
entry: [
sendParent((_, event: ViewVidEvent) =>
model.events.VIEW_VID(event.vidItemActor)
sendParent((_, event: ViewVcEvent) =>
model.events.VIEW_VC(event.vcItemActor)
),
],
on: {
@@ -64,9 +64,9 @@ export const ReceivedVidsTabMachine = model.createMachine(
}
);
export function createReceivedVidsTabMachine(serviceRefs: AppServices) {
return ReceivedVidsTabMachine.withContext({
...ReceivedVidsTabMachine.context,
export function createReceivedVcsTabMachine(serviceRefs: AppServices) {
return ReceivedVcsTabMachine.withContext({
...ReceivedVcsTabMachine.context,
serviceRefs,
});
}

View File

@@ -1,33 +0,0 @@
import { useSelector } from '@xstate/react';
import { useContext } from 'react';
import { ActorRefFrom } from 'xstate';
import { selectVidLabel } from '../../machines/settings';
import {
selectIsRefreshingReceivedVids,
selectReceivedVids,
} from '../../machines/vid';
import { vidItemMachine } from '../../machines/vidItem';
import { GlobalContext } from '../../shared/GlobalContext';
import { HomeScreenTabProps } from './HomeScreen';
import {
ReceivedVidsTabEvents,
ReceivedVidsTabMachine,
} from './ReceivedVidsTabMachine';
export function useReceivedVidsTab(props: HomeScreenTabProps) {
const service = props.service as ActorRefFrom<typeof ReceivedVidsTabMachine>;
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
const vidService = appService.children.get('vid');
return {
vidLabel: useSelector(settingsService, selectVidLabel),
vidKeys: useSelector(vidService, selectReceivedVids),
isRefreshingVids: useSelector(vidService, selectIsRefreshingReceivedVids),
VIEW_VID: (vidItemActor: ActorRefFrom<typeof vidItemMachine>) =>
service.send(ReceivedVidsTabEvents.VIEW_VID(vidItemActor)),
REFRESH: () => service.send(ReceivedVidsTabEvents.REFRESH()),
};
}

View File

@@ -4,31 +4,31 @@ import { TextEditOverlay } from '../../components/TextEditOverlay';
import { Column } from '../../components/ui';
import { Modal } from '../../components/ui/Modal';
import { Colors } from '../../components/ui/styleUtils';
import { VidDetails } from '../../components/VidDetails';
import { useViewVidModal, ViewVidModalProps } from './ViewVidModalController';
import { VcDetails } from '../../components/VcDetails';
import { useViewVcModal, ViewVcModalProps } from './ViewVcModalController';
export const ViewVidModal: React.FC<ViewVidModalProps> = (props) => {
const controller = useViewVidModal(props);
export const ViewVcModal: React.FC<ViewVcModalProps> = (props) => {
const controller = useViewVcModal(props);
return (
<Modal
isVisible={props.isVisible}
onDismiss={props.onDismiss}
headerTitle={controller.vid.tag || controller.vid.id}
headerTitle={controller.vc.tag || controller.vc.id}
headerElevation={2}
headerRight={
<Icon name="edit" onPress={controller.EDIT_TAG} color={Colors.Orange} />
}>
<Column scroll backgroundColor={Colors.LightGrey}>
<Column>
<VidDetails vid={controller.vid} />
<VcDetails vc={controller.vc} />
</Column>
</Column>
<TextEditOverlay
isVisible={controller.isEditingTag}
label="Edit Tag"
value={controller.vid.tag}
value={controller.vc.tag}
onDismiss={controller.DISMISS}
onSave={controller.SAVE_TAG}
/>

View File

@@ -0,0 +1,25 @@
import { useSelector } from '@xstate/react';
import { ActorRefFrom } from 'xstate';
import { ModalProps } from '../../components/ui/Modal';
import {
selectIsEditingTag,
selectVc,
VcItemEvents,
vcItemMachine,
} from '../../machines/vcItem';
export function useViewVcModal({ vcItemActor }: ViewVcModalProps) {
return {
vc: useSelector(vcItemActor, selectVc),
isEditingTag: useSelector(vcItemActor, selectIsEditingTag),
EDIT_TAG: () => vcItemActor.send(VcItemEvents.EDIT_TAG()),
SAVE_TAG: (tag: string) => vcItemActor.send(VcItemEvents.SAVE_TAG(tag)),
DISMISS: () => vcItemActor.send(VcItemEvents.DISMISS()),
};
}
export interface ViewVcModalProps extends ModalProps {
vcItemActor: ActorRefFrom<typeof vcItemMachine>;
}

View File

@@ -1,25 +0,0 @@
import { useSelector } from '@xstate/react';
import { ActorRefFrom } from 'xstate';
import { ModalProps } from '../../components/ui/Modal';
import {
selectIsEditingTag,
selectVid,
VidItemEvents,
vidItemMachine,
} from '../../machines/vidItem';
export function useViewVidModal({ vidItemActor }: ViewVidModalProps) {
return {
vid: useSelector(vidItemActor, selectVid),
isEditingTag: useSelector(vidItemActor, selectIsEditingTag),
EDIT_TAG: () => vidItemActor.send(VidItemEvents.EDIT_TAG()),
SAVE_TAG: (tag: string) => vidItemActor.send(VidItemEvents.SAVE_TAG(tag)),
DISMISS: () => vidItemActor.send(VidItemEvents.DISMISS()),
};
}
export interface ViewVidModalProps extends ModalProps {
vidItemActor: ActorRefFrom<typeof vidItemMachine>;
}

View File

@@ -20,8 +20,8 @@ export const ProfileScreen: React.FC<MainRouteProps> = (props) => {
/>
<EditableListItem
label="VC Label"
value={controller.vidLabel.singular}
onEdit={controller.UPDATE_VID_LABEL}
value={controller.VCLabel.singular}
onEdit={controller.UPDATE_VC_LABEL}
/>
<ListItem bottomDivider>
<ListItem.Content>

View File

@@ -4,7 +4,7 @@ import { AuthEvents, selectCanUseBiometrics } from '../../machines/auth';
import {
selectBiometricUnlockEnabled,
selectName,
selectVidLabel,
selectVCLabel,
SettingsEvents,
} from '../../machines/settings';
import { MainRouteProps } from '../../routes/main';
@@ -17,7 +17,7 @@ export function useProfileScreen({ navigation }: MainRouteProps) {
return {
name: useSelector(settingsService, selectName),
vidLabel: useSelector(settingsService, selectVidLabel),
VCLabel: useSelector(settingsService, selectVCLabel),
isBiometricUnlockEnabled: useSelector(
settingsService,
selectBiometricUnlockEnabled
@@ -27,8 +27,8 @@ export function useProfileScreen({ navigation }: MainRouteProps) {
UPDATE_NAME: (name: string) =>
settingsService.send(SettingsEvents.UPDATE_NAME(name)),
UPDATE_VID_LABEL: (label: string) =>
settingsService.send(SettingsEvents.UPDATE_VID_LABEL(label)),
UPDATE_VC_LABEL: (label: string) =>
settingsService.send(SettingsEvents.UPDATE_VC_LABEL(label)),
LOGOUT: () => {
authService.send(AuthEvents.LOGOUT());

View File

@@ -2,12 +2,12 @@ import React from 'react';
import { DeviceInfoList } from '../../components/DeviceInfoList';
import { Button, Column, Text } from '../../components/ui';
import { Colors } from '../../components/ui/styleUtils';
import { VidDetails } from '../../components/VidDetails';
import { VcDetails } from '../../components/VcDetails';
import { Modal, ModalProps } from '../../components/ui/Modal';
import { useReceiveVidModal } from './ReceiveVidModalController';
import { useReceiveVcModal } from './ReceiveVcModalController';
export const ReceiveVidModal: React.FC<ReceveVidModalProps> = (props) => {
const controller = useReceiveVidModal();
export const ReceiveVcModal: React.FC<ReceveVcModalProps> = (props) => {
const controller = useReceiveVcModal();
return (
<Modal {...props}>
@@ -15,13 +15,13 @@ export const ReceiveVidModal: React.FC<ReceveVidModalProps> = (props) => {
<Column>
<DeviceInfoList of="sender" deviceInfo={controller.senderInfo} />
<Text weight="semibold" margin="24 24 0 24">
{controller.vidLabel.singular} details
{controller.VCLabel.singular} details
</Text>
<VidDetails vid={controller.incomingVid} />
<VcDetails vc={controller.incomingVc} />
</Column>
<Column padding="0 24" margin="32 0 0 0">
<Button
title={`Accept request and receive ${controller.vidLabel.singular}`}
title={`Accept request and receive ${controller.VCLabel.singular}`}
margin="12 0 12 0"
onPress={props.onAccept}
/>
@@ -37,7 +37,7 @@ export const ReceiveVidModal: React.FC<ReceveVidModalProps> = (props) => {
);
};
interface ReceveVidModalProps extends ModalProps {
interface ReceveVcModalProps extends ModalProps {
onAccept: () => void;
onReject: () => void;
}

View File

@@ -1,17 +1,17 @@
import { useSelector } from '@xstate/react';
import { useContext } from 'react';
import { selectIncomingVid, selectSenderInfo } from '../../machines/request';
import { selectVidLabel } from '../../machines/settings';
import { selectIncomingVc, selectSenderInfo } from '../../machines/request';
import { selectVCLabel } from '../../machines/settings';
import { GlobalContext } from '../../shared/GlobalContext';
export function useReceiveVidModal() {
export function useReceiveVcModal() {
const { appService } = useContext(GlobalContext);
const requestService = appService.children.get('request');
const settingsService = appService.children.get('settings');
return {
senderInfo: useSelector(requestService, selectSenderInfo),
incomingVid: useSelector(requestService, selectIncomingVid),
vidLabel: useSelector(settingsService, selectVidLabel),
incomingVc: useSelector(requestService, selectIncomingVc),
VCLabel: useSelector(settingsService, selectVCLabel),
};
}

View File

@@ -3,7 +3,7 @@ import QRCode from 'react-native-qrcode-svg';
import { Centered, Column, Text } from '../../components/ui';
import { Colors } from '../../components/ui/styleUtils';
import { MainRouteProps } from '../../routes/main';
import { ReceiveVidModal } from './ReceiveVidModal';
import { ReceiveVcModal } from './ReceiveVcModal';
import { MessageOverlay } from '../../components/MessageOverlay';
import { useRequestScreen } from './RequestScreenController';
@@ -16,12 +16,12 @@ export const RequestScreen: React.FC<MainRouteProps> = (props) => {
{controller.isBluetoothDenied ? (
<Text color={Colors.Red} align="center">
Please enable Bluetooth to be able to request{' '}
{controller.vidLabel.singular}
{controller.VCLabel.singular}
</Text>
) : (
controller.isWaitingForConnection && (
<Text align="center">
Show this QR code to request {controller.vidLabel.singular}
Show this QR code to request {controller.vcLabel.singular}
</Text>
)
)}
@@ -44,25 +44,25 @@ export const RequestScreen: React.FC<MainRouteProps> = (props) => {
</Column>
)}
<ReceiveVidModal
<ReceiveVcModal
isVisible={controller.isReviewing}
onDismiss={controller.REJECT}
onAccept={controller.ACCEPT}
onReject={controller.REJECT}
headerTitle={`Incoming ${controller.vidLabel.singular}`}
headerTitle={`Incoming ${controller.VCLabel.singular}`}
/>
<MessageOverlay
isVisible={controller.isAccepted}
title="Success!"
message={`${controller.vidLabel.singular} has been successfully received from ${controller.senderInfo.deviceName}`}
message={`${controller.vcLabel.singular} has been successfully received from ${controller.senderInfo.deviceName}`}
onBackdropPress={controller.DISMISS}
/>
<MessageOverlay
isVisible={controller.isRejected}
title="Notice"
message={`You rejected ${controller.senderInfo.deviceName}'s ${controller.vidLabel.singular}`}
message={`You rejected ${controller.senderInfo.deviceName}'s ${controller.vcLabel.singular}`}
onBackdropPress={controller.DISMISS}
/>

View File

@@ -12,9 +12,9 @@ import {
selectSenderInfo,
selectIsWaitingForConnection,
selectIsExchangingDeviceInfo,
selectIsWaitingForVid,
selectIsWaitingForVc,
} from '../../machines/request';
import { selectVidLabel } from '../../machines/settings';
import { selectVcLabel } from '../../machines/settings';
import { MainRouteProps } from '../../routes/main';
import { GlobalContext } from '../../shared/GlobalContext';
import BluetoothStateManager from 'react-native-bluetooth-state-manager';
@@ -22,7 +22,7 @@ import BluetoothStateManager from 'react-native-bluetooth-state-manager';
export function useRequestScreen({ navigation }: MainRouteProps) {
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
const vidLabel = useSelector(settingsService, selectVidLabel);
const vcLabel = useSelector(settingsService, selectVcLabel);
const requestService = appService.children.get('request');
const isActive = useSelector(appService, selectIsActive);
@@ -39,15 +39,15 @@ export function useRequestScreen({ navigation }: MainRouteProps) {
requestService,
selectIsExchangingDeviceInfo
);
const isWaitingForVid = useSelector(requestService, selectIsWaitingForVid);
const isWaitingForVc = useSelector(requestService, selectIsWaitingForVc);
let statusMessage = '';
if (isWaitingForConnection) {
statusMessage = 'Waiting for connection...';
} else if (isExchangingDeviceInfo) {
statusMessage = 'Exchanging device info...';
} else if (isWaitingForVid) {
statusMessage = `Connected to device. Waiting for ${vidLabel.singular}...`;
} else if (isWaitingForVc) {
statusMessage = `Connected to device. Waiting for ${vcLabel.singular}...`;
}
useEffect(() => {
@@ -81,13 +81,13 @@ export function useRequestScreen({ navigation }: MainRouteProps) {
}, [isFocused, isActive]);
return {
vidLabel,
vcLabel,
statusMessage,
isWaitingForConnection,
isExchangingDeviceInfo,
isWaitingForVid,
isWaitingForVc,
isBluetoothDenied,
connectionParams: useSelector(requestService, selectConnectionParams),
senderInfo: useSelector(requestService, selectSenderInfo),

View File

@@ -4,7 +4,7 @@ import { Button, Column, Text } from '../../components/ui';
import { Colors } from '../../components/ui/styleUtils';
import { MainRouteProps } from '../../routes/main';
import { MessageOverlay } from '../../components/MessageOverlay';
import { SendVidModal } from './SendVidModal';
import { SendVcModal } from './SendVcModal';
import { useScanScreen } from './ScanScreenController';
export const ScanScreen: React.FC<MainRouteProps> = (props) => {
@@ -36,7 +36,7 @@ export const ScanScreen: React.FC<MainRouteProps> = (props) => {
)
) : (
<Text align="center" margin="16 0" color={Colors.Red}>
No sharable {controller.vidLabel.plural} are available.
No sharable {controller.VCLabel.plural} are available.
</Text>
)}
@@ -47,11 +47,11 @@ export const ScanScreen: React.FC<MainRouteProps> = (props) => {
onBackdropPress={controller.DISMISS_INVALID}
/>
<SendVidModal
<SendVcModal
isVisible={controller.isReviewing}
onDismiss={controller.DISMISS}
headerElevation={2}
headerTitle={`Sharing ${controller.vidLabel.singular}`}
headerTitle={`Sharing ${controller.VCLabel.singular}`}
/>
</Column>
);

View File

@@ -10,8 +10,8 @@ import {
selectScanning,
selectStatusMessage,
} from '../../machines/scan';
import { selectVidLabel } from '../../machines/settings';
import { selectShareableVids } from '../../machines/vid';
import { selectVCLabel } from '../../machines/settings';
import { selectShareableVcs } from '../../machines/vc';
import { MainRouteProps } from '../../routes/main';
import { GlobalContext } from '../../shared/GlobalContext';
@@ -19,9 +19,9 @@ export function useScanScreen({ navigation }: MainRouteProps) {
const { appService } = useContext(GlobalContext);
const scanService = appService.children.get('scan');
const settingsService = appService.children.get('settings');
const vidService = appService.children.get('vid');
const vcService = appService.children.get('vc');
const shareableVids = useSelector(vidService, selectShareableVids);
const shareableVcs = useSelector(vcService, selectShareableVcs);
const isInvalid = useSelector(scanService, selectInvalid);
const isLocationDisabled = useSelector(scanService, selectIsLocationDisabled);
@@ -70,10 +70,10 @@ export function useScanScreen({ navigation }: MainRouteProps) {
return {
locationError,
statusMessage: useSelector(scanService, selectStatusMessage),
vidLabel: useSelector(settingsService, selectVidLabel),
VCLabel: useSelector(settingsService, selectVCLabel),
isInvalid,
isEmpty: !shareableVids.length,
isEmpty: !shareableVcs.length,
isLocationDisabled,
isLocationDenied,
isScanning: useSelector(scanService, selectScanning),

View File

@@ -3,11 +3,11 @@ import { Dimensions, StyleSheet } from 'react-native';
import { Overlay } from 'react-native-elements/dist/overlay/Overlay';
import { Button, Column, Row, Text } from '../../components/ui';
import { Colors, elevation } from '../../components/ui/styleUtils';
import { VidItem } from '../../components/VidItem';
import { VcItem } from '../../components/VcItem';
import {
SelectVidOverlayProps,
useSelectVidOverlay,
} from './SelectVidOverlayController';
SelectVcOverlayProps,
useSelectVcOverlay,
} from './SelectVcOverlayController';
const styles = StyleSheet.create({
overlay: {
@@ -17,8 +17,8 @@ const styles = StyleSheet.create({
},
});
export const SelectVidOverlay: React.FC<SelectVidOverlayProps> = (props) => {
const controller = useSelectVidOverlay(props);
export const SelectVcOverlay: React.FC<SelectVcOverlayProps> = (props) => {
const controller = useSelectVcOverlay(props);
return (
<Overlay isVisible={props.isVisible} overlayStyle={styles.overlay}>
@@ -27,19 +27,19 @@ export const SelectVidOverlay: React.FC<SelectVidOverlayProps> = (props) => {
width={Dimensions.get('screen').width * 0.9}
style={{ maxHeight: Dimensions.get('screen').height * 0.9 }}>
<Text weight="semibold" margin="0 0 16 0">
Share {controller.vidLabel.singular}
Share {controller.VCLabel.singular}
</Text>
<Text margin="0 0 16 0">
Choose the {controller.vidLabel.singular} you'd like to share with{' '}
Choose the {controller.VCLabel.singular} you'd like to share with{' '}
<Text weight="semibold">{props.receiverName}</Text>
</Text>
<Column margin="0 0 32 0" scroll>
{props.vidKeys.map((vidKey, index) => (
<VidItem
key={vidKey}
vidKey={vidKey}
{props.vcKeys.map((vcKey, index) => (
<VcItem
key={vcKey}
vcKey={vcKey}
margin="0 2 8 2"
onPress={controller.selectVidItem(index)}
onPress={controller.selectVcItem(index)}
selectable
selected={index === controller.selectedIndex}
/>

View File

@@ -0,0 +1,42 @@
import { useSelector } from '@xstate/react';
import { useContext, useState } from 'react';
import { ActorRefFrom } from 'xstate';
import { selectVCLabel } from '../../machines/settings';
import { vcItemMachine } from '../../machines/vcItem';
import { GlobalContext } from '../../shared/GlobalContext';
import { VC } from '../../types/vc';
export function useSelectVcOverlay(props: SelectVcOverlayProps) {
const [selectedIndex, setSelectedIndex] = useState<number>(null);
const [selectedVcRef, setSelectedVcRef] =
useState<ActorRefFrom<typeof vcItemMachine>>(null);
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
return {
selectVcItem,
selectedIndex,
VCLabel: useSelector(settingsService, selectVCLabel),
onSelect: () => {
const { serviceRefs, ...vc } = selectedVcRef.getSnapshot().context;
props.onSelect(vc);
},
};
function selectVcItem(index: number) {
return (vcRef: ActorRefFrom<typeof vcItemMachine>) => {
setSelectedIndex(index);
setSelectedVcRef(vcRef);
};
}
}
export interface SelectVcOverlayProps {
isVisible: boolean;
receiverName: string;
vcKeys: string[];
onSelect: (vc: VC) => void;
onCancel: () => void;
}

View File

@@ -1,42 +0,0 @@
import { useSelector } from '@xstate/react';
import { useContext, useState } from 'react';
import { ActorRefFrom } from 'xstate';
import { selectVidLabel } from '../../machines/settings';
import { vidItemMachine } from '../../machines/vidItem';
import { GlobalContext } from '../../shared/GlobalContext';
import { VC } from '../../types/vc';
export function useSelectVidOverlay(props: SelectVidOverlayProps) {
const [selectedIndex, setSelectedIndex] = useState<number>(null);
const [selectedVidRef, setSelectedVidRef] =
useState<ActorRefFrom<typeof vidItemMachine>>(null);
const { appService } = useContext(GlobalContext);
const settingsService = appService.children.get('settings');
return {
selectVidItem,
selectedIndex,
vidLabel: useSelector(settingsService, selectVidLabel),
onSelect: () => {
const { serviceRefs, ...vid } = selectedVidRef.getSnapshot().context;
props.onSelect(vid);
},
};
function selectVidItem(index: number) {
return (vidRef: ActorRefFrom<typeof vidItemMachine>) => {
setSelectedIndex(index);
setSelectedVidRef(vidRef);
};
}
}
export interface SelectVidOverlayProps {
isVisible: boolean;
receiverName: string;
vidKeys: string[];
onSelect: (vid: VC) => void;
onCancel: () => void;
}

View File

@@ -3,13 +3,13 @@ import { Input } from 'react-native-elements';
import { DeviceInfoList } from '../../components/DeviceInfoList';
import { Button, Column } from '../../components/ui';
import { Colors } from '../../components/ui/styleUtils';
import { SelectVidOverlay } from './SelectVidOverlay';
import { SelectVcOverlay } from './SelectVcOverlay';
import { MessageOverlay } from '../../components/MessageOverlay';
import { Modal, ModalProps } from '../../components/ui/Modal';
import { useSendVidModal } from './SendVidModalController';
import { useSendVcModal } from './SendVcModalController';
export const SendVidModal: React.FC<SendVidModalProps> = (props) => {
const controller = useSendVidModal();
export const SendVcModal: React.FC<SendVcModalProps> = (props) => {
const controller = useSendVcModal();
const reasonLabel = 'Reason for sharing (optional)';
@@ -33,24 +33,24 @@ export const SendVidModal: React.FC<SendVidModalProps> = (props) => {
margin="2 0 0 0"
elevation={2}>
<Button
title={`Accept request and choose ${controller.vidLabel.singular}`}
title={`Accept request and choose ${controller.VCLabel.singular}`}
margin="12 0 12 0"
onPress={controller.ACCEPT_REQUEST}
/>
<Button type="clear" title="Reject" onPress={controller.CANCEL} />
</Column>
</Column>
<SelectVidOverlay
isVisible={controller.isSelectingVid}
<SelectVcOverlay
isVisible={controller.isSelectingVc}
receiverName={controller.receiverInfo.deviceName}
onSelect={controller.SELECT_VID}
onSelect={controller.SELECT_VC}
onCancel={controller.CANCEL}
vidKeys={controller.vidKeys}
vcKeys={controller.vcKeys}
/>
<MessageOverlay
isVisible={controller.isSendingVid}
isVisible={controller.isSendingVc}
title="Sharing..."
hasProgress
/>
@@ -58,18 +58,18 @@ export const SendVidModal: React.FC<SendVidModalProps> = (props) => {
<MessageOverlay
isVisible={controller.isAccepted}
title="Success!"
message={`Your ${controller.vidLabel.singular} has been successfully shared with ${controller.receiverInfo.deviceName}`}
message={`Your ${controller.VCLabel.singular} has been successfully shared with ${controller.receiverInfo.deviceName}`}
onBackdropPress={props.onDismiss}
/>
<MessageOverlay
isVisible={controller.isRejected}
title="Notice"
message={`Your ${controller.vidLabel.singular} was rejected by ${controller.receiverInfo.deviceName}`}
message={`Your ${controller.VCLabel.singular} was rejected by ${controller.receiverInfo.deviceName}`}
onBackdropPress={props.onDismiss}
/>
</Modal>
);
};
interface SendVidModalProps extends ModalProps {}
interface SendVcModalProps extends ModalProps {}

View File

@@ -6,40 +6,40 @@ import {
selectReason,
selectReceiverInfo,
selectRejected,
selectSelectingVid,
selectSendingVid,
selectVidName,
selectSelectingVc,
selectSendingVc,
selectVcName,
} from '../../machines/scan';
import { selectVidLabel } from '../../machines/settings';
import { selectShareableVids } from '../../machines/vid';
import { selectVCLabel } from '../../machines/settings';
import { selectShareableVcs } from '../../machines/vc';
import { GlobalContext } from '../../shared/GlobalContext';
import { VC } from '../../types/vc';
export function useSendVidModal() {
export function useSendVcModal() {
const { appService } = useContext(GlobalContext);
const scanService = appService.children.get('scan');
const settingsService = appService.children.get('settings');
const vidService = appService.children.get('vid');
const vcService = appService.children.get('vc');
return {
receiverInfo: useSelector(scanService, selectReceiverInfo),
reason: useSelector(scanService, selectReason),
vidName: useSelector(scanService, selectVidName),
vidLabel: useSelector(settingsService, selectVidLabel),
vidKeys: useSelector(vidService, selectShareableVids),
vcName: useSelector(scanService, selectVcName),
VCLabel: useSelector(settingsService, selectVCLabel),
vcKeys: useSelector(vcService, selectShareableVcs),
isSelectingVid: useSelector(scanService, selectSelectingVid),
isSendingVid: useSelector(scanService, selectSendingVid),
isSelectingVc: useSelector(scanService, selectSelectingVc),
isSendingVc: useSelector(scanService, selectSendingVc),
isAccepted: useSelector(scanService, selectAccepted),
isRejected: useSelector(scanService, selectRejected),
ACCEPT_REQUEST: () => scanService.send(ScanEvents.ACCEPT_REQUEST()),
CANCEL: () => scanService.send(ScanEvents.CANCEL()),
SELECT_VID: (vid: VC) => scanService.send(ScanEvents.SELECT_VID(vid)),
SELECT_VC: (vc: VC) => scanService.send(ScanEvents.SELECT_VC(vc)),
DISMISS: () => scanService.send(ScanEvents.DISMISS()),
UPDATE_REASON: (reason: string) =>
scanService.send(ScanEvents.UPDATE_REASON(reason)),
UPDATE_VID_NAME: (vidName: string) =>
scanService.send(ScanEvents.UPDATE_VID_NAME(vidName)),
UPDATE_VC_NAME: (vcName: string) =>
scanService.send(ScanEvents.UPDATE_VC_NAME(vcName)),
};
}

View File

@@ -7,7 +7,7 @@ import { requestMachine } from '../machines/request';
import { scanMachine } from '../machines/scan';
import { settingsMachine } from '../machines/settings';
import { storeMachine } from '../machines/store';
import { vidMachine } from '../machines/vid';
import { vcMachine } from '../machines/vc';
export const GlobalContext = createContext({} as GlobalServices);
@@ -18,7 +18,7 @@ export interface GlobalServices {
export interface AppServices {
store: ActorRefFrom<typeof storeMachine>;
auth: ActorRefFrom<typeof authMachine>;
vid: ActorRefFrom<typeof vidMachine>;
vc: ActorRefFrom<typeof vcMachine>;
settings: ActorRefFrom<typeof settingsMachine>;
activityLog: ActorRefFrom<typeof activityLogMachine>;
request: ActorRefFrom<typeof requestMachine>;

View File

@@ -1,9 +1,9 @@
import { ActorRefFrom } from 'xstate';
import { vidItemMachine } from '../machines/vidItem';
import { vcItemMachine } from '../machines/vcItem';
export const VidTabEvents = {
VIEW_VID: (vidItemActor: ActorRefFrom<typeof vidItemMachine>) => ({
vidItemActor,
export const VcTabEvents = {
VIEW_VC: (vcItemActor: ActorRefFrom<typeof vcItemMachine>) => ({
vcItemActor,
}),
REFRESH: () => ({}),
};

View File

@@ -5,11 +5,11 @@ export const HOST =
Constants.manifest.extra.backendServiceUrl ||
'https://resident-app.newlogic.dev';
export const MY_VIDS_STORE_KEY = 'myVCs';
export const MY_VCS_STORE_KEY = 'myVCs';
export const RECEIVED_VIDS_STORE_KEY = 'receivedVCs';
export const RECEIVED_VCS_STORE_KEY = 'receivedVCs';
export const VID_ITEM_STORE_KEY = (vc: Partial<VC>) =>
export const VC_ITEM_STORE_KEY = (vc: Partial<VC>) =>
`vc:${vc.idType}:${vc.id}:${vc.requestId}`;
export const ACTIVITY_LOG_STORE_KEY = 'activityLog';

View File

@@ -54,8 +54,8 @@ export type OtpRequestResponse = BackendResponse<{
maskedEmail?: string;
}>;
export type VidGenerateResponse = BackendResponse<{
vid: string;
export type VcGenerateResponse = BackendResponse<{
vc: string;
message: string;
}>;

View File

@@ -61,7 +61,7 @@ export type VerifiableCredentialType =
| 'MOSIPVerfiableCredential'
| string;
export interface VIDLabel {
export interface VCLabel {
singular: string;
plural: string;
}