From d10f098253827191d5961c02c8fe84f0f959d6a4 Mon Sep 17 00:00:00 2001 From: poonam-ghewande <133869533+poonam-ghewande@users.noreply.github.com> Date: Sun, 19 Nov 2023 19:12:36 +0530 Subject: [PATCH 01/16] fix: close contact modal after deletion (#141) Signed-off-by: Poonam Ghewande --- app/screens/Connection.tsx | 91 +++++++++++++++---------- app/screens/ContactDetails.tsx | 101 +++++++++++++++------------- app/screens/OrganizationDetails.tsx | 38 +++++++---- 3 files changed, 134 insertions(+), 96 deletions(-) diff --git a/app/screens/Connection.tsx b/app/screens/Connection.tsx index f97fad674..3d3eb7140 100644 --- a/app/screens/Connection.tsx +++ b/app/screens/Connection.tsx @@ -15,6 +15,7 @@ import { useNotifications } from '../hooks/notifications' import { Screens, TabStacks, DeliveryStackParams, Stacks } from '../types/navigators' import { useAppAgent } from '../utils/agent' import { testIdWithKey } from '../utils/testable' +import { heightPercentageToDP } from 'react-native-responsive-screen' type ConnectionProps = StackScreenProps @@ -72,6 +73,11 @@ const Connection: React.FC = ({ navigation, route }) => { marginTop: 'auto', margin: 20, }, + controlsContainerHome: { + justifyContent: 'center', + alignSelf: 'center', + marginTop: heightPercentageToDP('50%'), + }, delayMessageText: { textAlign: 'center', marginTop: 20, @@ -151,7 +157,7 @@ const Connection: React.FC = ({ navigation, route }) => { oobRecord && (!goalCode || (!goalCode.startsWith('aries.vc.verify') && !goalCode.startsWith('aries.vc.issue'))) ) { - navigation.navigate(Stacks.ConnectionStack, { + navigation.navigate(Stacks.ContactStack, { screen: Screens.ContactDetails, params: { connectionId: connectionId }, }) @@ -196,44 +202,55 @@ const Connection: React.FC = ({ navigation, route }) => { }, [notifications]) return ( - { - dispatch({ isVisible: false }) - }}> - - - - - {t('Connection.JustAMoment')} - - + + { + dispatch({ isVisible: false }) + }}> + + + + + {t('Connection.JustAMoment')} + + - - - + + + - {state.shouldShowDelayMessage && ( - - {t('Connection.TakingTooLong')} - - )} - - - diff --git a/app/screens/ProofRequest.tsx b/app/screens/ProofRequest.tsx index 2b9ba7c4a..818cdd998 100644 --- a/app/screens/ProofRequest.tsx +++ b/app/screens/ProofRequest.tsx @@ -6,7 +6,7 @@ import { AnonCredsCredentialsForProofRequest, AnonCredsRequestedAttributeMatch, AnonCredsRequestedPredicateMatch, - deleteConnectionById, + deleteConnectionRecordById, getProofFormatData, selectCredentialsForProofRequest, acceptProofRequest, @@ -330,7 +330,7 @@ const ProofRequest: React.FC = ({ navigation, route }) => { proofFormats: automaticRequestedCreds.proofFormats, }) if (proof.connectionId && goalCode && goalCode.endsWith('verify.once')) { - await deleteConnectionById(agent, proof.connectionId) + await deleteConnectionRecordById(agent, proof.connectionId) } } catch (err: unknown) { setPendingModalVisible(false) @@ -349,7 +349,7 @@ const ProofRequest: React.FC = ({ navigation, route }) => { if (proof.connectionId) { await sendProofProblemReport(agent, { proofRecordId: proof.id, description: t('ProofRequest.Declined') }) if (goalCode && goalCode.endsWith('verify.once')) { - await deleteConnectionById(agent, proof.connectionId) + await deleteConnectionRecordById(agent, proof.connectionId) } } } diff --git a/app/screens/ProofRequesting.tsx b/app/screens/ProofRequesting.tsx index b8a4da19b..a8a109ed3 100644 --- a/app/screens/ProofRequesting.tsx +++ b/app/screens/ProofRequesting.tsx @@ -1,6 +1,6 @@ import type { StackScreenProps } from '@react-navigation/stack' -import { useProofById, DidExchangeState, deleteConnectionById } from '@adeya/ssi' +import { useProofById, DidExchangeState, deleteConnectionRecordById } from '@adeya/ssi' import { useIsFocused } from '@react-navigation/core' import { useFocusEffect } from '@react-navigation/native' import React, { useCallback, useEffect, useState } from 'react' @@ -186,7 +186,7 @@ const ProofRequesting: React.FC = ({ route, navigation }) useEffect(() => { if (proofRecord && (isPresentationReceived(proofRecord) || isPresentationFailed(proofRecord))) { if (goalCode?.endsWith('verify.once')) { - deleteConnectionById(agent, record?.id ?? '') + deleteConnectionRecordById(agent, record?.id ?? '') } navigation.navigate(Screens.ProofDetails, { recordId: proofRecord.id }) } diff --git a/app/screens/Scan.tsx b/app/screens/Scan.tsx index 3971349a9..e06f7670a 100644 --- a/app/screens/Scan.tsx +++ b/app/screens/Scan.tsx @@ -17,7 +17,13 @@ import { BifoldError, QrCodeScanError } from '../types/error' import { ConnectStackParams, Screens, Stacks } from '../types/navigators' import { PermissionContract } from '../types/permissions' import { useAppAgent } from '../utils/agent' -import { connectFromInvitation, getJson, getUrl, receiveMessageFromUrlRedirect } from '../utils/helpers' +import { + checkIfAlreadyConnected, + connectFromInvitation, + getJson, + getUrl, + receiveMessageFromUrlRedirect, +} from '../utils/helpers' export type ScanProps = StackScreenProps @@ -36,6 +42,19 @@ const Scan: React.FC = ({ navigation, route }) => { const handleInvitation = async (value: string): Promise => { try { setLoading(true) + + const isAlreadyConnected = await checkIfAlreadyConnected(agent, value) + + if (isAlreadyConnected) { + setLoading(false) + + Toast.show({ + type: ToastType.Warn, + text1: t('Contacts.AlreadyConnected'), + }) + return + } + const { connectionRecord } = await connectFromInvitation(agent, value) setLoading(false) navigation.getParent()?.navigate(Stacks.ConnectionStack, { diff --git a/app/utils/helpers.ts b/app/utils/helpers.ts index 87902175d..762d71011 100644 --- a/app/utils/helpers.ts +++ b/app/utils/helpers.ts @@ -25,6 +25,8 @@ import { getCredentialsForProofRequest, AnonCredsPredicateType, AnonCredsRequestedAttribute, + parseInvitationFromUrl, + findByReceivedInvitationId, } from '@adeya/ssi' import { CaptureBaseAttributeType } from '@hyperledger/aries-oca' import { TFunction } from 'i18next' @@ -860,6 +862,23 @@ export const receiveMessageFromDeepLink = async (url: string, agent: AdeyaAgent return message } +/** + * + * @param agent an Agent instance + * @param uri a URI containing a base64 encoded connection invite in the query parameter + * @returns boolean indicating if the connection was already established + */ +export const checkIfAlreadyConnected = async (agent: AdeyaAgent, invitationUrl: string) => { + const invitation = await parseInvitationFromUrl(agent, invitationUrl) + const outOfBandRecord = await findByReceivedInvitationId(agent, invitation.id) + + if (outOfBandRecord) { + return true + } + + return false +} + /** * * @param uri a URI containing a base64 encoded connection invite in the query parameter diff --git a/ios/AdeyaWallet.xcodeproj/project.pbxproj b/ios/AdeyaWallet.xcodeproj/project.pbxproj index bc480505c..dd399a1ca 100644 --- a/ios/AdeyaWallet.xcodeproj/project.pbxproj +++ b/ios/AdeyaWallet.xcodeproj/project.pbxproj @@ -507,7 +507,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; ENABLE_BITCODE = NO; @@ -517,7 +517,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -545,7 +545,7 @@ CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 18; + CURRENT_PROJECT_VERSION = 19; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; INFOPLIST_FILE = AdeyaWallet/Info.plist; @@ -554,7 +554,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.2; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", diff --git a/package.json b/package.json index 73401b292..394a153a3 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "prepare": "husky install" }, "dependencies": { - "@adeya/ssi": "0.0.1-alpha.14", + "@adeya/ssi": "0.0.1-alpha.16", "@formatjs/intl-datetimeformat": "^6.10.0", "@formatjs/intl-displaynames": "^6.5.0", "@formatjs/intl-getcanonicallocales": "^2.2.1", diff --git a/yarn.lock b/yarn.lock index f15031bd9..5735be6f8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@adeya/ssi@0.0.1-alpha.14": - version "0.0.1-alpha.14" - resolved "https://registry.yarnpkg.com/@adeya/ssi/-/ssi-0.0.1-alpha.14.tgz#09b0841747bbbb2573b4a051fc0fd7bd35c537f6" - integrity sha512-U9teolKydZ2C7kFGo735XF1at12ymy6f9xPo7bKXJyWk+b3bUZsJDg8raaYAlerMkN41zvwZn755UO+qb1fIzQ== +"@adeya/ssi@0.0.1-alpha.16": + version "0.0.1-alpha.16" + resolved "https://registry.yarnpkg.com/@adeya/ssi/-/ssi-0.0.1-alpha.16.tgz#a40624364a19fdfb86a31c3661a5126c652aabe6" + integrity sha512-inL7rl+vffPaAflYFw/tEiHSB3TK1bhmXPDAxzPxgjq7pER9Lur4v8Iklirmd2w/w40d/x10a+djVHUpxhCNdQ== dependencies: "@aries-framework/anoncreds" "0.4.2" "@aries-framework/anoncreds-rs" "0.4.2" From 4ef4e610d0b70e748fd55f22748bda748a459504 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Tue, 21 Nov 2023 11:15:14 +0530 Subject: [PATCH 03/16] fix: scanning qrcode multiple times (#143) Signed-off-by: Sai Ranjit Tummalapalli Signed-off-by: Poonam Ghewande --- app/screens/ExportWalletConfirmation.tsx | 16 +++++++++------- app/screens/ImportWalletConfirmation.tsx | 20 ++++++++++++++++---- app/screens/Onboarding.tsx | 4 ++++ app/screens/OrganizationDetails.tsx | 17 ++++++++++------- app/screens/Scan.tsx | 1 + ios/AdeyaWallet.xcodeproj/project.pbxproj | 4 ++-- package.json | 2 +- yarn.lock | 8 ++++---- 8 files changed, 47 insertions(+), 25 deletions(-) diff --git a/app/screens/ExportWalletConfirmation.tsx b/app/screens/ExportWalletConfirmation.tsx index a2c1ae192..56f48d658 100644 --- a/app/screens/ExportWalletConfirmation.tsx +++ b/app/screens/ExportWalletConfirmation.tsx @@ -130,9 +130,10 @@ function ExportWalletConfirmation() { setPhraseData(shuffledPhraseData) setArraySetPhraseData(Array(shuffledPhraseData.length).fill('')) }, []) + const exportWallet = async (seed: string) => { setMatchPhrase(true) - const encodeHash = seed.replaceAll(',', ' ') + const encodeHash = seed try { const documentDirectory: string = DownloadDirectoryPath @@ -171,7 +172,8 @@ function ExportWalletConfirmation() { } const exportWalletIOS = async (seed: string) => { setMatchPhrase(true) - const encodeHash = seed.replaceAll(',', ' ') + + const encodeHash = seed const { fs } = RNFetchBlob try { const documentDirectory = fs.dirs.DocumentDir @@ -251,11 +253,11 @@ function ExportWalletConfirmation() { try { const granted = await PermissionsAndroid.request(PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE, { title: 'Permission', - message: 'PCM needs to write to storage', + message: 'ADEYA Wallet needs to write to storage', buttonPositive: '', }) if (granted === PermissionsAndroid.RESULTS.GRANTED) { - exportWallet(sysPassPhrase) + await exportWallet(sysPassPhrase) } } catch (error) { Toast.show({ @@ -264,13 +266,13 @@ function ExportWalletConfirmation() { }) } } else { - exportWalletIOS(sysPassPhrase) + await exportWalletIOS(sysPassPhrase) } } const verifyPhrase = () => { - const addedPassPhraseData = arraySetPhraseData.join(',') - const displayedPassphrase = parms?.params?.phraseData.map(item => item).join(',') + const addedPassPhraseData = arraySetPhraseData.join('') + const displayedPassphrase = parms?.params?.phraseData.map(item => item).join('') if (displayedPassphrase.trim() !== '') { const sysPassPhrase = addedPassPhraseData.trim() const userPassphrase = displayedPassphrase.trim() diff --git a/app/screens/ImportWalletConfirmation.tsx b/app/screens/ImportWalletConfirmation.tsx index fa84e773b..00c58d238 100644 --- a/app/screens/ImportWalletConfirmation.tsx +++ b/app/screens/ImportWalletConfirmation.tsx @@ -115,7 +115,8 @@ const ImportWalletVerify: React.FC = ({ navigation }) = text1: `Please enter passphrase`, }) } - const encodeHash = seed + + const encodeHash = seed.replaceAll(' ', '').trim() const walletConfig = { id: credentials.id, @@ -130,11 +131,11 @@ const ImportWalletVerify: React.FC = ({ navigation }) = const agentConfig: InitConfig = { label: store.preferences.walletName, walletConfig, - logger: new ConsoleLogger(LogLevel.off), + logger: new ConsoleLogger(LogLevel.debug), autoUpdateStorageOnStartup: true, } - const walletImportCheck = await isWalletImportable(walletConfig, importConfig) + const walletImportCheck = await isWalletImportable({ ...walletConfig }, importConfig) if (!walletImportCheck) { Toast.show({ @@ -164,7 +165,7 @@ const ImportWalletVerify: React.FC = ({ navigation }) = } catch (e: unknown) { Toast.show({ type: ToastType.Error, - text1: `You've entered an invalid passphrase.`, + text1: 'Wallet import failed. Please try again', visibilityTime: 5000, position: 'bottom', }) @@ -192,10 +193,21 @@ const ImportWalletVerify: React.FC = ({ navigation }) = copyTo: 'documentDirectory', }) + if (!res.name?.endsWith('.wallet')) { + Toast.show({ + type: ToastType.Error, + text1: 'Please select a valid wallet file', + visibilityTime: 2000, + }) + navigation.goBack() + return + } + if (!res.fileCopyUri) { Toast.show({ type: ToastType.Error, }) + navigation.goBack() return } diff --git a/app/screens/Onboarding.tsx b/app/screens/Onboarding.tsx index 16762f200..095ad988b 100644 --- a/app/screens/Onboarding.tsx +++ b/app/screens/Onboarding.tsx @@ -124,6 +124,10 @@ const Onboarding: React.FC = ({ useFocusEffect( useCallback(() => { const onBackPress = () => { + if (navigation.canGoBack()) { + navigation.goBack() + return true + } BackHandler.exitApp() return true diff --git a/app/screens/OrganizationDetails.tsx b/app/screens/OrganizationDetails.tsx index 14b0a247f..b011bb36e 100644 --- a/app/screens/OrganizationDetails.tsx +++ b/app/screens/OrganizationDetails.tsx @@ -30,13 +30,15 @@ const OrganizationDetails: React.FC = () => { const { t } = useTranslation() const params = useRoute, string>>().params const { organizationDetailData, credentialDetailData } = useOrganizationDetailData(params?.orgSlug) - const [invitationUrl, setinvitationUrl] = useState('') - const [disableConnect, setdisableConnect] = useState(false) + const [invitationUrl, setInvitationUrl] = useState('') + const [disableConnect, setDisableConnect] = useState(false) const { records } = useConnections() const connections = records const isAlreadyConnected = useMemo(() => { - return connections?.some(connection => connection.theirLabel === params?.name) + return connections?.some( + connection => connection.theirLabel?.replace(/\s/g, '').trim() === params?.name.replace(/\s/g, '').trim(), + ) }, [connections, params]) const styles = StyleSheet.create({ @@ -187,21 +189,22 @@ const OrganizationDetails: React.FC = () => { let connectionInvitationUrl = '' if (agentInvitations.length === 1) { connectionInvitationUrl = agentInvitations[0]?.connectionInvitation - setinvitationUrl(connectionInvitationUrl) + setInvitationUrl(connectionInvitationUrl) } else if (agentInvitations.length > 1) { connectionInvitationUrl = agentInvitations[agentInvitations.length - 1]?.connectionInvitation - setinvitationUrl(connectionInvitationUrl) + setInvitationUrl(connectionInvitationUrl) } }, [organizationDetailData]) + const connectOrganization = async () => { - setdisableConnect(true) + setDisableConnect(true) try { if (!invitationUrl) { Toast.show({ type: ToastType.Error, text1: 'No connection invitation available', }) - setdisableConnect(false) + setDisableConnect(false) return } diff --git a/app/screens/Scan.tsx b/app/screens/Scan.tsx index e06f7670a..5a1c469de 100644 --- a/app/screens/Scan.tsx +++ b/app/screens/Scan.tsx @@ -52,6 +52,7 @@ const Scan: React.FC = ({ navigation, route }) => { type: ToastType.Warn, text1: t('Contacts.AlreadyConnected'), }) + navigation.goBack() return } diff --git a/ios/AdeyaWallet.xcodeproj/project.pbxproj b/ios/AdeyaWallet.xcodeproj/project.pbxproj index dd399a1ca..679a2a156 100644 --- a/ios/AdeyaWallet.xcodeproj/project.pbxproj +++ b/ios/AdeyaWallet.xcodeproj/project.pbxproj @@ -507,7 +507,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; ENABLE_BITCODE = NO; @@ -545,7 +545,7 @@ CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 19; + CURRENT_PROJECT_VERSION = 20; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; INFOPLIST_FILE = AdeyaWallet/Info.plist; diff --git a/package.json b/package.json index 394a153a3..784562b7b 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "prepare": "husky install" }, "dependencies": { - "@adeya/ssi": "0.0.1-alpha.16", + "@adeya/ssi": "0.0.1-alpha.17", "@formatjs/intl-datetimeformat": "^6.10.0", "@formatjs/intl-displaynames": "^6.5.0", "@formatjs/intl-getcanonicallocales": "^2.2.1", diff --git a/yarn.lock b/yarn.lock index 5735be6f8..f169db376 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7,10 +7,10 @@ resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== -"@adeya/ssi@0.0.1-alpha.16": - version "0.0.1-alpha.16" - resolved "https://registry.yarnpkg.com/@adeya/ssi/-/ssi-0.0.1-alpha.16.tgz#a40624364a19fdfb86a31c3661a5126c652aabe6" - integrity sha512-inL7rl+vffPaAflYFw/tEiHSB3TK1bhmXPDAxzPxgjq7pER9Lur4v8Iklirmd2w/w40d/x10a+djVHUpxhCNdQ== +"@adeya/ssi@0.0.1-alpha.17": + version "0.0.1-alpha.17" + resolved "https://registry.yarnpkg.com/@adeya/ssi/-/ssi-0.0.1-alpha.17.tgz#59c7548268ba78e0b3dbf9914e274be917fafff5" + integrity sha512-J39+SibqtqyhDgSMJA4MZFbmsPsMQ45XyuDjaABciNLEubJBKUfCyWr2CNO1+scR7ad1Rr8i6/OYohlNGDqgWw== dependencies: "@aries-framework/anoncreds" "0.4.2" "@aries-framework/anoncreds-rs" "0.4.2" From 454080a69e2adf3020b3ec4ac660a14193c9b17d Mon Sep 17 00:00:00 2001 From: poonam-ghewande <133869533+poonam-ghewande@users.noreply.github.com> Date: Wed, 22 Nov 2023 00:21:32 +0530 Subject: [PATCH 04/16] feat/social-share certificate (#144) Co-authored-by: Sai Ranjit Tummalapalli Signed-off-by: Poonam Ghewande --- app/api/api-constants.ts | 1 + app/screens/Connection.tsx | 2 +- app/screens/CredentialDetails.tsx | 53 +++++++++++++++++++++++++++++-- app/types/metadata.ts | 18 +++++++++++ app/types/share.ts | 8 +++++ app/utils/social-share.ts | 46 +++++++++++++++++++++++++++ package.json | 1 + yarn.lock | 5 +++ 8 files changed, 130 insertions(+), 4 deletions(-) create mode 100644 app/types/share.ts create mode 100644 app/utils/social-share.ts diff --git a/app/api/api-constants.ts b/app/api/api-constants.ts index 593f3b764..2c49afa32 100644 --- a/app/api/api-constants.ts +++ b/app/api/api-constants.ts @@ -1,3 +1,4 @@ export const ORG_PROFILE = '/orgs/public-profile' export const ORG_DETAILS = '/orgs/public-profiles/' +export const USER_CERTIFICATE = '/users/certificate' export const PAGE_SIZE = 20 diff --git a/app/screens/Connection.tsx b/app/screens/Connection.tsx index 3d3eb7140..b390f3168 100644 --- a/app/screens/Connection.tsx +++ b/app/screens/Connection.tsx @@ -4,6 +4,7 @@ import { StackScreenProps } from '@react-navigation/stack' import React, { useCallback, useEffect, useMemo, useReducer, useRef } from 'react' import { useTranslation } from 'react-i18next' import { AccessibilityInfo, Modal, ScrollView, StyleSheet, Text, View } from 'react-native' +import { heightPercentageToDP } from 'react-native-responsive-screen' import { SafeAreaView } from 'react-native-safe-area-context' import Button, { ButtonType } from '../components/buttons/Button' @@ -15,7 +16,6 @@ import { useNotifications } from '../hooks/notifications' import { Screens, TabStacks, DeliveryStackParams, Stacks } from '../types/navigators' import { useAppAgent } from '../utils/agent' import { testIdWithKey } from '../utils/testable' -import { heightPercentageToDP } from 'react-native-responsive-screen' type ConnectionProps = StackScreenProps diff --git a/app/screens/CredentialDetails.tsx b/app/screens/CredentialDetails.tsx index b60f9781f..a0c1cc191 100644 --- a/app/screens/CredentialDetails.tsx +++ b/app/screens/CredentialDetails.tsx @@ -9,9 +9,19 @@ import { BrandingOverlay } from '@hyperledger/aries-oca' import { BrandingOverlayType, CredentialOverlay } from '@hyperledger/aries-oca/build/legacy' import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { DeviceEventEmitter, Image, ImageBackground, StyleSheet, Text, View } from 'react-native' +import { + DeviceEventEmitter, + Image, + ImageBackground, + StyleSheet, + Text, + View, + TouchableOpacity, + ActivityIndicator, +} from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' import Toast from 'react-native-toast-message' +import Icon from 'react-native-vector-icons/AntDesign' import CredentialCard from '../components/misc/CredentialCard' import InfoBox, { InfoBoxType } from '../components/misc/InfoBox' @@ -35,6 +45,7 @@ import { } from '../utils/credential' import { formatTime, getCredentialConnectionLabel } from '../utils/helpers' import { buildFieldsFromAnonCredsCredential } from '../utils/oca' +import { useSocialShare } from '../utils/social-share' import { testIdWithKey } from '../utils/testable' type CredentialDetailsProps = StackScreenProps @@ -49,6 +60,20 @@ const CredentialDetails: React.FC = ({ navigation, route } const { credential } = route?.params + + const credentialId = credential.id + const schemaId = credential?.metadata?.data['_anoncreds/credential']?.schemaId + const attributes = credential?.credentialAttributes?.map(attribute => ({ + name: attribute.name, + value: attribute.value, + })) + + const shareData = { + credentialId, + schemaId, + attributes, + } + const { agent } = useAppAgent() const { t, i18n } = useTranslation() const { TextTheme, ColorPallet } = useTheme() @@ -60,6 +85,7 @@ const CredentialDetails: React.FC = ({ navigation, route const [isRevokedMessageHidden, setIsRevokedMessageHidden] = useState( (credential!.metadata.get(CredentialMetadata.customMetadata) as customMetadata)?.revoked_detail_dismissed ?? false, ) + const { socialShare, loading } = useSocialShare() const [overlay, setOverlay] = useState>({ bundle: undefined, @@ -69,7 +95,7 @@ const CredentialDetails: React.FC = ({ navigation, route }) const credentialConnectionLabel = getCredentialConnectionLabel(credential) - + const isPresentationFieldsEmpty = !overlay.brandingOverlay?.digest const styles = StyleSheet.create({ container: { backgroundColor: overlay.brandingOverlay?.primaryBackgroundColor, @@ -108,6 +134,14 @@ const CredentialDetails: React.FC = ({ navigation, route color: credentialTextColor(ColorPallet, overlay.brandingOverlay?.primaryBackgroundColor), flexShrink: 1, }, + shareContainer: { + flexDirection: 'row', + }, + shareIcon: { + position: 'absolute', + bottom: 10, + right: 20, + }, }) useEffect(() => { @@ -312,7 +346,20 @@ const CredentialDetails: React.FC = ({ navigation, route - + + + {loading ? ( + + ) : ( + + {!isPresentationFieldsEmpty && ( + socialShare(shareData)}> + + + )} + + )} + {isRevoked && !isRevokedMessageHidden ? ( {credential && } diff --git a/app/types/metadata.ts b/app/types/metadata.ts index 0f70e1d3c..f7e7ccb66 100644 --- a/app/types/metadata.ts +++ b/app/types/metadata.ts @@ -1,5 +1,23 @@ export enum CredentialMetadata { customMetadata = 'customMetadata', + metaData = 'metaData', +} + +export interface metaData { + data: { + '_anoncreds/credentialRequest': { + link_secret_blinding_data: { + v_prime: string + vr_prime: string | null + } + nonce: string + link_secret_name: string + } + '_anoncreds/credential': { + credentialDefinitionId: string + schemaId: string + } + } } export interface customMetadata { diff --git a/app/types/share.ts b/app/types/share.ts new file mode 100644 index 000000000..4f6a8fe8b --- /dev/null +++ b/app/types/share.ts @@ -0,0 +1,8 @@ +export interface CredentialData { + credentialId: string + schemaId: string + attributes: { + name: string + value: string + }[] +} diff --git a/app/utils/social-share.ts b/app/utils/social-share.ts new file mode 100644 index 000000000..69e3252ed --- /dev/null +++ b/app/utils/social-share.ts @@ -0,0 +1,46 @@ +import { useState } from 'react' +import { Config } from 'react-native-config' +import Share from 'react-native-share' +import Toast from 'react-native-toast-message' + +import { USER_CERTIFICATE } from '../api/api-constants' +import { ToastType } from '../components/toast/BaseToast' +import { CredentialData } from '../types/share' + +export const useSocialShare = () => { + const [loading, setLoading] = useState(false) + + const socialShare = async (credentialData: CredentialData) => { + setLoading(true) + try { + const url = `${Config.PUBLIC_ORG}${USER_CERTIFICATE}` + const headers = { + Accept: 'application/json', + 'Content-Type': 'application/json', + } + const options = { + method: 'POST', + headers, + body: JSON.stringify(credentialData), + } + + const response = await fetch(url, options) + + if (!response.ok) { + Toast.show({ + type: ToastType.Error, + text1: 'Failed to fetch social share content', + }) + } + + const data = await response.json() + const shareOptions = { message: data?.label, url: data?.data, mimeType: 'image/jpeg' } + await Share.open(shareOptions) + return data + } catch (error) { + setLoading(false) + } + } + + return { socialShare, loading } +} diff --git a/package.json b/package.json index 784562b7b..417b0a6d1 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "react-native-safe-area-context": "^4.7.1", "react-native-scalable-image": "^1.1.0", "react-native-screens": "^3.23.0", + "react-native-share": "^10.0.1", "react-native-splash-screen": "^3.3.0", "react-native-svg": "^13.10.0", "react-native-tcp-socket": "^6.0.6", diff --git a/yarn.lock b/yarn.lock index f169db376..a25fa9b53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8542,6 +8542,11 @@ react-native-securerandom@^0.1.1: dependencies: base64-js "*" +react-native-share@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/react-native-share/-/react-native-share-10.0.1.tgz#ea1121389754c4a01363711664f8424263f09d50" + integrity sha512-dEn4DTf3/qQnLuwXkZtuFkF3pq7a4LDqmOY+R/kcgeuKIBWheGzCxHXVD7XGm6qhwXXkiD0SOiDUrGjnXa96eg== + react-native-splash-screen@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/react-native-splash-screen/-/react-native-splash-screen-3.3.0.tgz" From 4fda0a1687d0103e81f8ab2cb69723b8ab093331 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Wed, 22 Nov 2023 12:59:11 +0530 Subject: [PATCH 05/16] feat: deeplink with universal link (#145) * feat: add agent link as deep link Signed-off-by: Sai Ranjit Tummalapalli * chore: bump iOS version Signed-off-by: Sai Ranjit Tummalapalli --------- Signed-off-by: Sai Ranjit Tummalapalli Signed-off-by: Poonam Ghewande --- .gitignore | 1 + android/app/build.gradle | 4 ++-- android/app/src/main/AndroidManifest.xml | 1 + app/screens/CredentialDetails.tsx | 6 +++--- app/screens/Home.tsx | 8 ++++---- app/utils/social-share.ts | 5 ++++- ios/AdeyaWallet.xcodeproj/project.pbxproj | 4 ++-- ios/AdeyaWallet/AdeyaWallet.entitlements | 4 ++++ ios/AdeyaWallet/AppDelegate.mm | 8 ++++++++ ios/AdeyaWallet/Info.plist | 12 ++++++++++-- ios/Podfile.lock | 6 ++++++ 11 files changed, 45 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index a0a74ecfc..2b2c9587e 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ local.properties .cxx/ *.keystore !debug.keystore +*.aab # node.js # diff --git a/android/app/build.gradle b/android/app/build.gradle index a56769148..947bf37e4 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -81,8 +81,8 @@ android { applicationId "id.credebl.adeya" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 4 - versionName "1.0.1" + versionCode 5 + versionName "1.0.2" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 3fd91aa87..bcc83a8b3 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -35,6 +35,7 @@ + diff --git a/app/screens/CredentialDetails.tsx b/app/screens/CredentialDetails.tsx index a0c1cc191..8d466e245 100644 --- a/app/screens/CredentialDetails.tsx +++ b/app/screens/CredentialDetails.tsx @@ -21,7 +21,7 @@ import { } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' import Toast from 'react-native-toast-message' -import Icon from 'react-native-vector-icons/AntDesign' +import Icon from 'react-native-vector-icons/MaterialCommunityIcons' import CredentialCard from '../components/misc/CredentialCard' import InfoBox, { InfoBoxType } from '../components/misc/InfoBox' @@ -139,7 +139,7 @@ const CredentialDetails: React.FC = ({ navigation, route }, shareIcon: { position: 'absolute', - bottom: 10, + bottom: 20, right: 20, }, }) @@ -354,7 +354,7 @@ const CredentialDetails: React.FC = ({ navigation, route {!isPresentationFieldsEmpty && ( socialShare(shareData)}> - + )} diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index 18d46ecfb..d8414dd33 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -2,7 +2,6 @@ import { StackScreenProps } from '@react-navigation/stack' import React, { ReactNode } from 'react' import { useTranslation } from 'react-i18next' import { FlatList, StyleSheet, View, Text, Dimensions, TouchableOpacity, Image } from 'react-native' -import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen' import ScanButton from '../components/common/ScanButton' import NotificationListItem, { NotificationType } from '../components/listItems/NotificationListItem' @@ -41,7 +40,8 @@ const Home: React.FC = ({ navigation }) => { alignSelf: 'center', alignItems: 'center', justifyContent: 'center', - height: 300, + marginTop: 20, + height: 250, width: 300, }, header: { @@ -61,8 +61,8 @@ const Home: React.FC = ({ navigation }) => { right: 10, }, homeImage: { - width: wp('70%'), - height: hp('40%'), + height: '85%', + width: '80%', }, }) diff --git a/app/utils/social-share.ts b/app/utils/social-share.ts index 69e3252ed..9b67bd5b5 100644 --- a/app/utils/social-share.ts +++ b/app/utils/social-share.ts @@ -29,8 +29,11 @@ export const useSocialShare = () => { if (!response.ok) { Toast.show({ type: ToastType.Error, - text1: 'Failed to fetch social share content', + text1: 'Failed to fetch social share content for this credential.', }) + + setLoading(false) + return } const data = await response.json() diff --git a/ios/AdeyaWallet.xcodeproj/project.pbxproj b/ios/AdeyaWallet.xcodeproj/project.pbxproj index 679a2a156..5f5996030 100644 --- a/ios/AdeyaWallet.xcodeproj/project.pbxproj +++ b/ios/AdeyaWallet.xcodeproj/project.pbxproj @@ -507,7 +507,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; ENABLE_BITCODE = NO; @@ -545,7 +545,7 @@ CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 20; + CURRENT_PROJECT_VERSION = 21; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; INFOPLIST_FILE = AdeyaWallet/Info.plist; diff --git a/ios/AdeyaWallet/AdeyaWallet.entitlements b/ios/AdeyaWallet/AdeyaWallet.entitlements index 903def2af..408bbcb9a 100644 --- a/ios/AdeyaWallet/AdeyaWallet.entitlements +++ b/ios/AdeyaWallet/AdeyaWallet.entitlements @@ -4,5 +4,9 @@ aps-environment development + com.apple.developer.associated-domains + + applinks:agent.credebl.id + diff --git a/ios/AdeyaWallet/AppDelegate.mm b/ios/AdeyaWallet/AppDelegate.mm index 522ab231f..33336bbf4 100644 --- a/ios/AdeyaWallet/AppDelegate.mm +++ b/ios/AdeyaWallet/AppDelegate.mm @@ -32,4 +32,12 @@ - (BOOL)application:(UIApplication *)application { return [RCTLinkingManager application:application openURL:url options:options]; } + +- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity + restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler +{ + return [RCTLinkingManager application:application + continueUserActivity:userActivity + restorationHandler:restorationHandler]; +} @end diff --git a/ios/AdeyaWallet/Info.plist b/ios/AdeyaWallet/Info.plist index 54342ba97..fbee9bf40 100644 --- a/ios/AdeyaWallet/Info.plist +++ b/ios/AdeyaWallet/Info.plist @@ -32,6 +32,16 @@ didcomm + + CFBundleTypeRole + Editor + CFBundleURLName + id.credebl.adeya + CFBundleURLSchemes + + agent.credebl.id + + CFBundleVersion $(CURRENT_PROJECT_VERSION) @@ -85,8 +95,6 @@ UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown - ITSAppUsesNonExemptEncryption - UIViewControllerBasedStatusBarAppearance diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 5b0ce835c..03b2783da 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -701,6 +701,8 @@ PODS: - RNScreens (3.23.0): - React-Core - React-RCTImage + - RNShare (10.0.1): + - React-Core - RNSVG (13.10.0): - React-Core - RNVectorIcons (10.0.0): @@ -804,6 +806,7 @@ DEPENDENCIES: - RNPermissions (from `../node_modules/react-native-permissions`) - RNReanimated (from `../node_modules/react-native-reanimated`) - RNScreens (from `../node_modules/react-native-screens`) + - RNShare (from `../node_modules/react-native-share`) - RNSVG (from `../node_modules/react-native-svg`) - RNVectorIcons (from `../node_modules/react-native-vector-icons`) - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) @@ -970,6 +973,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native-reanimated" RNScreens: :path: "../node_modules/react-native-screens" + RNShare: + :path: "../node_modules/react-native-share" RNSVG: :path: "../node_modules/react-native-svg" RNVectorIcons: @@ -1068,6 +1073,7 @@ SPEC CHECKSUMS: RNPermissions: f5763a7aa5b1aae3b0c0546791b002e3048042bd RNReanimated: 53ca20eee770c41173703f5948cd8898aa08262c RNScreens: 6a8a3c6b808aa48dca1780df7b73ea524f602c63 + RNShare: bed7c4fbe615f3d977f22feb0902af9a790c1660 RNSVG: 80584470ff1ffc7994923ea135a3e5ad825546b9 RNVectorIcons: 8b5bb0fa61d54cd2020af4f24a51841ce365c7e9 SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 From 0c6d4146c213da906505a4c8d1ab90401083c378 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Thu, 23 Nov 2023 15:39:10 +0530 Subject: [PATCH 06/16] fix deep link issue (#147) * feat: add agent link as deep link Signed-off-by: Sai Ranjit Tummalapalli * chore: bump iOS version Signed-off-by: Sai Ranjit Tummalapalli * chore: bump versions Signed-off-by: Sai Ranjit Tummalapalli --------- Signed-off-by: Sai Ranjit Tummalapalli Signed-off-by: Poonam Ghewande --- android/app/build.gradle | 4 +- android/app/src/main/AndroidManifest.xml | 107 +++++++++++++--------- ios/AdeyaWallet.xcodeproj/project.pbxproj | 8 +- 3 files changed, 72 insertions(+), 47 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 947bf37e4..47870bb6d 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -81,8 +81,8 @@ android { applicationId "id.credebl.adeya" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 5 - versionName "1.0.2" + versionCode 6 + versionName "1.0.3" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index bcc83a8b3..2569b1c0f 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,43 +1,68 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/AdeyaWallet.xcodeproj/project.pbxproj b/ios/AdeyaWallet.xcodeproj/project.pbxproj index 5f5996030..6e4b4a942 100644 --- a/ios/AdeyaWallet.xcodeproj/project.pbxproj +++ b/ios/AdeyaWallet.xcodeproj/project.pbxproj @@ -507,7 +507,7 @@ ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; - CURRENT_PROJECT_VERSION = 21; + CURRENT_PROJECT_VERSION = 22; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; ENABLE_BITCODE = NO; @@ -517,7 +517,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.2; + MARKETING_VERSION = 1.0.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", @@ -545,7 +545,7 @@ CODE_SIGN_ENTITLEMENTS = AdeyaWallet/AdeyaWallet.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 21; + CURRENT_PROJECT_VERSION = 22; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = Z5W7KRPGHZ; INFOPLIST_FILE = AdeyaWallet/Info.plist; @@ -554,7 +554,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.2; + MARKETING_VERSION = 1.0.3; OTHER_LDFLAGS = ( "$(inherited)", "-ObjC", From 426bfdf893dfb591109f3340f37a917d8dd5ae55 Mon Sep 17 00:00:00 2001 From: Sai Ranjit Tummalapalli Date: Fri, 24 Nov 2023 12:11:32 +0530 Subject: [PATCH 07/16] feat: add remote verification templates (#148) * feat: add remote verification templates Signed-off-by: Sai Ranjit Tummalapalli * chore: bump versions Signed-off-by: Sai Ranjit Tummalapalli * fix: verifier capability for previous version Signed-off-by: Sai Ranjit Tummalapalli --------- Signed-off-by: Sai Ranjit Tummalapalli Signed-off-by: Poonam Ghewande --- .env.sample | 3 + android/app/build.gradle | 4 +- app/contexts/configuration.tsx | 3 +- app/contexts/store.tsx | 2 +- app/defaultConfiguration.ts | 5 +- app/hooks/proof-request-templates.ts | 32 ++++- app/screens/CredentialOfferAccept.tsx | 2 +- app/screens/ListCredentials.tsx | 2 +- app/screens/ProofRequestDetails.tsx | 85 +++++++++----- app/screens/ProofRequesting.tsx | 15 ++- app/screens/Settings.tsx | 56 ++++----- app/utils/proofBundle.ts | 136 ++++++++++++++++++++++ ios/AdeyaWallet.xcodeproj/project.pbxproj | 8 +- verifier/index.ts | 2 +- verifier/request-templates.ts | 85 +------------- 15 files changed, 282 insertions(+), 158 deletions(-) create mode 100644 app/utils/proofBundle.ts diff --git a/.env.sample b/.env.sample index de40b4611..bdf4bd320 100644 --- a/.env.sample +++ b/.env.sample @@ -9,3 +9,6 @@ OCA_URL=https://raw.githubusercontent.com/credebl/credebl-aries-oca-bundles/rele #BASE_URL PUBLIC_ORG=https://example.com + +#PROOF_TEMPLATE_URL +PROOF_TEMPLATE_URL= \ No newline at end of file diff --git a/android/app/build.gradle b/android/app/build.gradle index 47870bb6d..ccec00614 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -81,8 +81,8 @@ android { applicationId "id.credebl.adeya" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 6 - versionName "1.0.3" + versionCode 8 + versionName "1.0.5" missingDimensionStrategy 'react-native-camera', 'general' } diff --git a/app/contexts/configuration.tsx b/app/contexts/configuration.tsx index 56d23c92b..8628a2941 100644 --- a/app/contexts/configuration.tsx +++ b/app/contexts/configuration.tsx @@ -29,6 +29,7 @@ export interface ConfigurationContext { credentialEmptyList: React.FC developer: React.FC OCABundleResolver: OCABundleResolverType + proofTemplateBaseUrl?: string scan: React.FC useBiometry: React.FC record: React.FC @@ -39,7 +40,7 @@ export interface ConfigurationContext { useCustomNotifications: () => { total: number; notifications: any } connectionTimerDelay?: number autoRedirectConnectionToHome?: boolean - proofRequestTemplates?: Array + proofRequestTemplates?: () => Array enableTours?: boolean enableWalletNaming?: boolean enableBackupWallet?: boolean diff --git a/app/contexts/store.tsx b/app/contexts/store.tsx index 766d44684..c0d4d101b 100644 --- a/app/contexts/store.tsx +++ b/app/contexts/store.tsx @@ -34,7 +34,7 @@ export const defaultState: State = { developerModeEnabled: false, biometryPreferencesUpdated: false, useBiometry: false, - useVerifierCapability: false, + useVerifierCapability: true, useConnectionInviterCapability: false, useDevVerifierTemplates: false, walletName: generateRandomWalletName(), diff --git a/app/defaultConfiguration.ts b/app/defaultConfiguration.ts index 9aff1f8c7..14d512031 100644 --- a/app/defaultConfiguration.ts +++ b/app/defaultConfiguration.ts @@ -2,7 +2,7 @@ import { BrandingOverlayType, RemoteOCABundleResolver } from '@hyperledger/aries import { Config } from 'react-native-config' import defaultIndyLedgers from '../configs/ledgers/indy' -import { defaultProofRequestTemplates } from '../verifier' +import { useProofRequestTemplates } from '../verifier' import EmptyList from './components/misc/EmptyList' import Record from './components/record/Record' @@ -30,6 +30,7 @@ export const defaultConfiguration: ConfigurationContext = { brandingOverlayType: BrandingOverlayType.Branding10, preLoad: true, }), + proofTemplateBaseUrl: Config.PROOF_TEMPLATE_URL ?? '', scan: Scan, useBiometry: UseBiometry, record: Record, @@ -45,7 +46,7 @@ export const defaultConfiguration: ConfigurationContext = { pageTitle: '', }, useCustomNotifications: useNotifications, - proofRequestTemplates: defaultProofRequestTemplates, + proofRequestTemplates: useProofRequestTemplates, enableTours: false, enableWalletNaming: true, enableBackupWallet: true, diff --git a/app/hooks/proof-request-templates.ts b/app/hooks/proof-request-templates.ts index 9e0edc716..eca7cf742 100644 --- a/app/hooks/proof-request-templates.ts +++ b/app/hooks/proof-request-templates.ts @@ -1,14 +1,36 @@ -import { useMemo } from 'react' +import { useEffect, useState } from 'react' import { ProofRequestTemplate } from '../../verifier' import { useConfiguration } from '../contexts/configuration' +import { applyTemplateMarkers, useRemoteProofBundleResolver } from '../utils/proofBundle' export const useTemplates = (): Array => { - const { proofRequestTemplates } = useConfiguration() - return proofRequestTemplates || [] + const [proofRequestTemplates, setProofRequestTemplates] = useState([]) + const { proofTemplateBaseUrl } = useConfiguration() + const resolver = useRemoteProofBundleResolver(proofTemplateBaseUrl) + const acceptDevCredentials = false + useEffect(() => { + resolver.resolve(acceptDevCredentials).then(templates => { + if (templates) { + setProofRequestTemplates(applyTemplateMarkers(templates)) + } + }) + }, []) + return proofRequestTemplates } export const useTemplate = (templateId: string): ProofRequestTemplate | undefined => { - const { proofRequestTemplates } = useConfiguration() - return useMemo(() => proofRequestTemplates?.find(template => template.id === templateId), [templateId]) + const [proofRequestTemplate, setProofRequestTemplate] = useState(undefined) + const { proofTemplateBaseUrl } = useConfiguration() + const resolver = useRemoteProofBundleResolver(proofTemplateBaseUrl) + const acceptDevCredentials = false + + useEffect(() => { + resolver.resolveById(templateId, acceptDevCredentials).then(template => { + if (template) { + setProofRequestTemplate(applyTemplateMarkers(template)) + } + }) + }, []) + return proofRequestTemplate } diff --git a/app/screens/CredentialOfferAccept.tsx b/app/screens/CredentialOfferAccept.tsx index 26c6c91db..b91e6dd50 100644 --- a/app/screens/CredentialOfferAccept.tsx +++ b/app/screens/CredentialOfferAccept.tsx @@ -147,7 +147,7 @@ const CredentialOfferAccept: React.FC = ({ visible, - {credentialDeliveryStatus === DeliveryStatus.Pending && ( + {credentialDeliveryStatus === DeliveryStatus.Pending && credential.state === CredentialState.RequestSent && ( From 90141dc90b715b798fc5428254ef837bbf1ad10b Mon Sep 17 00:00:00 2001 From: Poonam Ghewande Date: Thu, 14 Dec 2023 18:28:18 +0530 Subject: [PATCH 15/16] chore:modified bottom tab bar ui Signed-off-by: Poonam Ghewande --- app/assets/img/Connect-active.svg | 22 +++++++++ app/assets/img/Connect.svg | 8 ++++ app/assets/img/Rectangle.png | Bin 0 -> 23560 bytes app/assets/img/active-explore-icon.svg | 10 ++-- app/assets/img/credential-active.svg | 3 ++ app/assets/img/credential.svg | 5 ++ app/assets/img/explore-icon.svg | 8 ++-- app/assets/img/home.svg | 3 ++ app/assets/img/homeinactivesvg.svg | 5 ++ app/localization/en/index.ts | 3 +- app/navigators/TabStack.tsx | 63 +++++++++++++++++++------ app/screens/Home.tsx | 4 +- app/screens/ListCredentials.tsx | 4 +- app/screens/OrganizationList.tsx | 4 +- app/theme.ts | 31 +++++++++--- app/types/navigators.ts | 2 + 16 files changed, 136 insertions(+), 39 deletions(-) create mode 100644 app/assets/img/Connect-active.svg create mode 100644 app/assets/img/Connect.svg create mode 100644 app/assets/img/Rectangle.png create mode 100644 app/assets/img/credential-active.svg create mode 100644 app/assets/img/credential.svg create mode 100644 app/assets/img/home.svg create mode 100644 app/assets/img/homeinactivesvg.svg diff --git a/app/assets/img/Connect-active.svg b/app/assets/img/Connect-active.svg new file mode 100644 index 000000000..4e55d95f6 --- /dev/null +++ b/app/assets/img/Connect-active.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/assets/img/Connect.svg b/app/assets/img/Connect.svg new file mode 100644 index 000000000..2c90f4a1d --- /dev/null +++ b/app/assets/img/Connect.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/app/assets/img/Rectangle.png b/app/assets/img/Rectangle.png new file mode 100644 index 0000000000000000000000000000000000000000..f110974e8b8844864d1f8241f6eba36bccd9bbb6 GIT binary patch literal 23560 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 - - - - - + + + + diff --git a/app/assets/img/credential-active.svg b/app/assets/img/credential-active.svg new file mode 100644 index 000000000..73bb8beb5 --- /dev/null +++ b/app/assets/img/credential-active.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/img/credential.svg b/app/assets/img/credential.svg new file mode 100644 index 000000000..483b97cbd --- /dev/null +++ b/app/assets/img/credential.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/assets/img/explore-icon.svg b/app/assets/img/explore-icon.svg index 7282e45c0..80f83956b 100644 --- a/app/assets/img/explore-icon.svg +++ b/app/assets/img/explore-icon.svg @@ -1,5 +1,5 @@ - - - - + + + + diff --git a/app/assets/img/home.svg b/app/assets/img/home.svg new file mode 100644 index 000000000..ab335e872 --- /dev/null +++ b/app/assets/img/home.svg @@ -0,0 +1,3 @@ + + + diff --git a/app/assets/img/homeinactivesvg.svg b/app/assets/img/homeinactivesvg.svg new file mode 100644 index 000000000..4d70f2d5b --- /dev/null +++ b/app/assets/img/homeinactivesvg.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/app/localization/en/index.ts b/app/localization/en/index.ts index ef217349d..0183b5d44 100644 --- a/app/localization/en/index.ts +++ b/app/localization/en/index.ts @@ -492,7 +492,8 @@ const translation = { "Home": "Home", "Scan": "Scan", "Credentials": "Credentials", - "Explore":"Explore" + "Explore":"Explore", + "Connections": "Connections", }, "RootStack": { "Contacts": "Contacts", diff --git a/app/navigators/TabStack.tsx b/app/navigators/TabStack.tsx index 3ae65710c..116e4f44c 100644 --- a/app/navigators/TabStack.tsx +++ b/app/navigators/TabStack.tsx @@ -1,13 +1,12 @@ import { createBottomTabNavigator } from '@react-navigation/bottom-tabs' import React from 'react' import { useTranslation } from 'react-i18next' -import { Text, useWindowDimensions, View, StyleSheet } from 'react-native' +import { Text, useWindowDimensions, View, StyleSheet, ImageBackground } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' -import Icon from 'react-native-vector-icons/MaterialCommunityIcons' import { AttachTourStep } from '../components/tour/AttachTourStep' -import { useConfiguration } from '../contexts/configuration' import { useTheme } from '../contexts/theme' +import ListContacts from '../screens/ListContacts' import { Assets } from '../theme' import { TabStackParams, TabStacks } from '../types/navigators' import { isTablet, orientation, Orientation } from '../utils/helpers' @@ -19,8 +18,6 @@ import OrganizationStack from './OrganizationStack' const TabStack: React.FC = () => { const { width, height } = useWindowDimensions() - const { useCustomNotifications } = useConfiguration() - const { total } = useCustomNotifications() const { t } = useTranslation() const Tab = createBottomTabNavigator() const { ColorPallet, TabTheme } = useTheme() @@ -30,6 +27,7 @@ const TabStack: React.FC = () => { const styles = StyleSheet.create({ tabBarIcon: { flex: 1, + zIndex: 1, }, }) @@ -40,15 +38,26 @@ const TabStack: React.FC = () => { return 0 } - + const CustomTabBar = () => { + return ( + + + + ) + } return ( , tabBarActiveTintColor: TabTheme.tabBarActiveTintColor, tabBarInactiveTintColor: TabTheme.tabBarInactiveTintColor, header: () => null, @@ -58,9 +67,9 @@ const TabStack: React.FC = () => { component={HomeStack} options={{ tabBarIconStyle: styles.tabBarIcon, - tabBarIcon: ({ color, focused }) => ( + tabBarIcon: ({ focused }) => ( - + {focused ? : } {showLabels && ( { )} ), + tabBarShowLabel: false, - tabBarAccessibilityLabel: `${t('TabStack.Home')} (${ - total === 1 ? t('Home.OneNotification') : t('Home.CountNotifications', { count: total || 0 }) - })`, + tabBarAccessibilityLabel: `${t('TabStack.Home')} `, tabBarTestID: testIdWithKey(t('TabStack.Home')), - tabBarBadge: total || undefined, tabBarBadgeStyle: { marginLeft: leftMarginForDevice(width, height), backgroundColor: ColorPallet.semantic.error, @@ -108,15 +115,41 @@ const TabStack: React.FC = () => { tabBarTestID: testIdWithKey(t('TabStack.Explore')), }} /> + ( + + + {focused ? : } + {showLabels && ( + + {t('TabStack.Connections')} + + )} + + + ), + tabBarShowLabel: false, + tabBarAccessibilityLabel: t('TabStack.Credentials'), + tabBarTestID: testIdWithKey(t('TabStack.Credentials')), + }} + /> ( + tabBarIcon: ({ focused }) => ( - + {focused ? : } {showLabels && ( = ({ navigation }) => { }, fabContainer: { position: 'absolute', - bottom: 10, - right: 10, + bottom: 1, + alignSelf: 'center', }, homeImage: { height: '85%', diff --git a/app/screens/ListCredentials.tsx b/app/screens/ListCredentials.tsx index 265b9bbed..d848ea6e4 100644 --- a/app/screens/ListCredentials.tsx +++ b/app/screens/ListCredentials.tsx @@ -28,8 +28,8 @@ const styles = StyleSheet.create({ container: { flex: 1 }, scanContainer: { position: 'absolute', - bottom: 10, - right: 10, + bottom: 1, + alignSelf: 'center', }, }) diff --git a/app/screens/OrganizationList.tsx b/app/screens/OrganizationList.tsx index 33846920b..e514aff0f 100644 --- a/app/screens/OrganizationList.tsx +++ b/app/screens/OrganizationList.tsx @@ -81,8 +81,8 @@ const OrganizationList: React.FC = ({ navigation }) => { }, scanContainer: { position: 'absolute', - bottom: 10, - right: 10, + bottom: 1, + alignSelf: 'center', }, itemSeparator: { backgroundColor: ColorPallet.brand.primaryBackground, diff --git a/app/theme.ts b/app/theme.ts index 472b5f62b..75fab01b7 100644 --- a/app/theme.ts +++ b/app/theme.ts @@ -3,20 +3,25 @@ import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-nat import { SvgProps } from 'react-native-svg' import Arrow from './assets/icons/large-arrow.svg' +import ConnectionIconActive from './assets/img/Connect-active.svg' +import Connection from './assets/img/Connect.svg' import ExploreIconActive from './assets/img/active-explore-icon.svg' import AppLockout from './assets/img/app-lockout.svg' import BackupSuccess from './assets/img/backup-success.svg' import Biometrics from './assets/img/biometrics.svg' import ContactBook from './assets/img/contact-book.svg' +import CredentialIconActive from './assets/img/credential-active.svg' import CredentialDeclined from './assets/img/credential-declined.svg' +import Credential from './assets/img/credential.svg' import DeleteNotification from './assets/img/delete-notification.svg' import EmptyWallet from './assets/img/empty-wallet.svg' import ExploreIcon from './assets/img/explore-icon.svg' +import HomeIconActive from './assets/img/home.svg' +import HomeIcon from './assets/img/homeinactivesvg.svg' import IconInfoSentDark from './assets/img/icon-info-sent-dark.svg' import IconProofRequestDark from './assets/img/icon-proof-request-dark.svg' import Logo from './assets/img/logo-with-text.svg' import ProofRequestDeclined from './assets/img/proof-declined.svg' - export interface ISVGAssets { appLockout: React.FC biometrics: React.FC @@ -32,6 +37,12 @@ export interface ISVGAssets { IconInfoSentDark: React.FC ExploreIcon: React.FC ExploreIconActive: React.FC + HomeIcon: React.FC + HomeIconActive: React.FC + Connection: React.FC + ConnectionIconActive: React.FC + Credential: React.FC + CredentialIconActive: React.FC } export interface IFontAttributes { @@ -563,11 +574,11 @@ export const ListItems = StyleSheet.create({ export const TabTheme = { tabBarStyle: { - height: 60, - backgroundColor: '#D3E4FA', - shadowOffset: { width: 0, height: -3 }, - shadowRadius: 6, - shadowColor: ColorPallet.grayscale.black, + height: 80, + backgroundColor: '#ffff', + shadowOffset: { width: 0, height: -4 }, + shadowRadius: 8, + shadowColor: ColorPallet.grayscale.white, shadowOpacity: 0.1, borderTopWidth: 0, paddingBottom: 0, @@ -578,7 +589,7 @@ export const TabTheme = { alignItems: 'center', }, tabBarActiveTintColor: ColorPallet.brand.primary, - tabBarInactiveTintColor: ColorPallet.notification.infoText, + tabBarInactiveTintColor: '#000', tabBarTextStyle: { ...TextTheme.label, fontWeight: 'normal', @@ -848,6 +859,12 @@ export const Assets = { IconInfoSentDark: IconInfoSentDark, ExploreIcon: ExploreIcon, ExploreIconActive: ExploreIconActive, + HomeIcon: HomeIcon, + HomeIconActive: HomeIconActive, + Connection: Connection, + ConnectionIconActive: ConnectionIconActive, + Credential: Credential, + CredentialIconActive: CredentialIconActive, }, img: { logoPrimary: { diff --git a/app/types/navigators.ts b/app/types/navigators.ts index 5d0844b7f..e096c0d4f 100644 --- a/app/types/navigators.ts +++ b/app/types/navigators.ts @@ -68,6 +68,7 @@ export enum TabStacks { ConnectStack = 'Tab Connect Stack', CredentialStack = 'Tab Credential Stack', OrganizationStack = 'Tab OrganizationStack Stack', + ContactStack = ' Tab Contacts Stack', } export type RootStackParams = { @@ -85,6 +86,7 @@ export type TabStackParams = { [TabStacks.ConnectStack]: NavigatorScreenParams [TabStacks.CredentialStack]: NavigatorScreenParams [TabStacks.OrganizationStack]: NavigatorScreenParams + [TabStacks.ContactStack]: NavigatorScreenParams } export type AuthenticateStackParams = { From 4dabd59c436d12e85bb7690fbd7c4150ea984727 Mon Sep 17 00:00:00 2001 From: Poonam Ghewande Date: Fri, 15 Dec 2023 12:08:18 +0530 Subject: [PATCH 16/16] refactor:theme.ts Signed-off-by: Poonam Ghewande --- app/theme.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/theme.ts b/app/theme.ts index 75fab01b7..39b75e4b3 100644 --- a/app/theme.ts +++ b/app/theme.ts @@ -575,7 +575,7 @@ export const ListItems = StyleSheet.create({ export const TabTheme = { tabBarStyle: { height: 80, - backgroundColor: '#ffff', + backgroundColor: ColorPallet.grayscale.white, shadowOffset: { width: 0, height: -4 }, shadowRadius: 8, shadowColor: ColorPallet.grayscale.white, @@ -589,7 +589,7 @@ export const TabTheme = { alignItems: 'center', }, tabBarActiveTintColor: ColorPallet.brand.primary, - tabBarInactiveTintColor: '#000', + tabBarInactiveTintColor: ColorPallet.grayscale.black, tabBarTextStyle: { ...TextTheme.label, fontWeight: 'normal',