From 46afd6b6c94b36ca39deb11ed4f33aa4c963a80a Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Mon, 28 Oct 2024 18:46:04 +0530 Subject: [PATCH 1/9] feat: Recently add credential show on home screen and handle navigation Signed-off-by: tusharbhayani --- app/assets/img/HistoryCardAcceptedIcon.svg | 9 +- app/assets/img/HistoryNewConnectionIcon.svg | 14 +- app/assets/img/HistoryProofRequestIcon.svg | 4 +- app/components/History/HistoryListItem.tsx | 45 ++- app/components/History/HistoryMenu.tsx | 13 +- app/components/buttons/HeaderButton.tsx | 27 +- app/localization/en/index.ts | 9 +- app/navigators/HomeStack.tsx | 13 +- app/screens/ContactDetails.tsx | 87 +++-- app/screens/HistoryPage.tsx | 3 +- app/screens/Home.tsx | 381 ++++++++++++++++---- app/screens/Scan.tsx | 39 +- app/types/navigators.ts | 4 + ios/.xcode.env | 12 +- ios/AdeyaWallet.xcodeproj/project.pbxproj | 2 + 15 files changed, 489 insertions(+), 173 deletions(-) diff --git a/app/assets/img/HistoryCardAcceptedIcon.svg b/app/assets/img/HistoryCardAcceptedIcon.svg index f147e8bbd..018297ae0 100644 --- a/app/assets/img/HistoryCardAcceptedIcon.svg +++ b/app/assets/img/HistoryCardAcceptedIcon.svg @@ -1,3 +1,8 @@ - - + + + + + + + diff --git a/app/assets/img/HistoryNewConnectionIcon.svg b/app/assets/img/HistoryNewConnectionIcon.svg index f4661f088..747fb86ff 100644 --- a/app/assets/img/HistoryNewConnectionIcon.svg +++ b/app/assets/img/HistoryNewConnectionIcon.svg @@ -1,10 +1,6 @@ - - - - - - - - - + + + + + diff --git a/app/assets/img/HistoryProofRequestIcon.svg b/app/assets/img/HistoryProofRequestIcon.svg index b99b068a5..8f794b411 100644 --- a/app/assets/img/HistoryProofRequestIcon.svg +++ b/app/assets/img/HistoryProofRequestIcon.svg @@ -1,3 +1,3 @@ - - + + diff --git a/app/components/History/HistoryListItem.tsx b/app/components/History/HistoryListItem.tsx index 050f6075e..ed62a7170 100644 --- a/app/components/History/HistoryListItem.tsx +++ b/app/components/History/HistoryListItem.tsx @@ -1,7 +1,7 @@ import moment from 'moment' import React from 'react' import { useTranslation } from 'react-i18next' -import { StyleSheet, Text, TouchableOpacity, View } from 'react-native' +import { StyleSheet, Text, View } from 'react-native' import { useTheme } from '../../contexts/theme' @@ -20,15 +20,16 @@ const styles = StyleSheet.create({ paddingVertical: 8, }, card: { - padding: 10, - flexDirection: 'row', - alignContent: 'center', + height: 40, + width: 40, alignSelf: 'center', - // backgroundColor:'green' + justifyContent: 'center', + alignContent: 'center', + alignItems: 'center', }, cardContent: { flexDirection: 'column', - marginHorizontal: 10, + marginLeft: 20, width: '80%', }, cardDescriptionContent: { @@ -136,10 +137,29 @@ const HistoryListItem: React.FC = ({ item }) => { } } + const formatDate = (dateString: string) => { + const now = moment() // Current date and time + const date = moment(dateString) // Message date and time + const diffDays = now.diff(date, 'days') + + if (diffDays === 0) { + // If it's today, show "x minutes/hours ago" + return moment(dateString).fromNow() + } else if (diffDays === 1) { + // If it's yesterday, show "Yesterday" + return 'Yesterday' + } else if (diffDays <= 2) { + // For messages up to 2 days old, show the full date (e.g., "Oct 10, 2024") + return moment(date).format('MMM D, YYYY') + } else { + // For older messages, show the full date + return moment(date).fromNow() + } + } const renderCardDate = (date?: Date) => { if (!date) return null - const dateFormate = moment(date).format('DD/MM/YYYY HH:mm:ss') - return {dateFormate} + + return {formatDate(date)} } const renderCard = (item: CustomRecord) => { @@ -158,14 +178,7 @@ const HistoryListItem: React.FC = ({ item }) => { ) } - return ( - { - //TODO: navigate to history details - }}> - {renderCard(item)} - - ) + return {renderCard(item)} } export default HistoryListItem diff --git a/app/components/History/HistoryMenu.tsx b/app/components/History/HistoryMenu.tsx index 50856872d..a19ab2778 100644 --- a/app/components/History/HistoryMenu.tsx +++ b/app/components/History/HistoryMenu.tsx @@ -3,11 +3,14 @@ import { StackNavigationProp } from '@react-navigation/stack' import React from 'react' import { useTranslation } from 'react-i18next' -import { RootStackParams, Screens, Stacks } from '../../types/navigators' +import { RootStackParams, Screens } from '../../types/navigators' import { testIdWithKey } from '../../utils/testable' import HeaderButton, { ButtonLocation } from '../buttons/HeaderButton' -const HistoryMenu: React.FC = () => { +const HistoryMenu: React.FC<{ type?: boolean; notificationCount?: number }> = ({ + type = false, + notificationCount = 0, +}) => { const navigation = useNavigation>() const { t } = useTranslation() @@ -16,8 +19,10 @@ const HistoryMenu: React.FC = () => { buttonLocation={ButtonLocation.Right} accessibilityLabel={t('Screens.Settings')} testID={testIdWithKey('Settings')} - onPress={() => navigation.navigate(Stacks.HistoryStack, { screen: Screens.HistoryPage })} - icon="history" + onPress={() => navigation.navigate(Screens.Notifications)} + icon={'bell'} + badgeShow={type} + notificationCount={notificationCount} /> ) } diff --git a/app/components/buttons/HeaderButton.tsx b/app/components/buttons/HeaderButton.tsx index bb892e023..5ca17edba 100644 --- a/app/components/buttons/HeaderButton.tsx +++ b/app/components/buttons/HeaderButton.tsx @@ -1,5 +1,5 @@ import React from 'react' -import { StyleSheet, TouchableOpacity, View, Text } from 'react-native' +import { StyleSheet, Text, TouchableOpacity, View } from 'react-native' import Icon from 'react-native-vector-icons/MaterialCommunityIcons' import { hitSlop } from '../../constants' @@ -19,6 +19,8 @@ interface HeaderButtonProps { onPress: () => void icon: string text?: string + badgeShow?: boolean + notificationCount?: number } const HeaderButton: React.FC = ({ @@ -28,6 +30,8 @@ const HeaderButton: React.FC = ({ accessibilityLabel, testID, onPress, + badgeShow, + notificationCount, }) => { const { ColorPallet, TextTheme } = useTheme() const style = StyleSheet.create({ @@ -44,6 +48,22 @@ const HeaderButton: React.FC = ({ color: ColorPallet.brand.headerText, marginRight: 4, }, + badge: { + position: 'absolute', + right: -5, // Adjust to position the badge on the top-right corner + top: -5, // Adjust to position the badge on the top-right corner + backgroundColor: 'red', + borderRadius: 10, // Makes the badge circular + width: 18, // Adjust size based on the badge + height: 18, // Adjust size based on the badge + justifyContent: 'center', + alignItems: 'center', + }, + badgeText: { + color: 'white', + fontSize: 12, + fontWeight: 'bold', + }, }) const layoutForButtonLocation = (buttonLocation: ButtonLocation) => { @@ -60,6 +80,11 @@ const HeaderButton: React.FC = ({ <> {text && {text}} + {badgeShow && notificationCount > 0 && ( + + {notificationCount} + + )} ) } diff --git a/app/localization/en/index.ts b/app/localization/en/index.ts index d569ae8f6..2a03a0aa1 100644 --- a/app/localization/en/index.ts +++ b/app/localization/en/index.ts @@ -39,6 +39,7 @@ const translation = { "No": "No", "History": "History", "SaveSettings": "Save Settings", + "Credentials":"Credentials" }, "Date": { "ShortFormat": "MMM D", @@ -341,7 +342,12 @@ const translation = { "YouHave": "You have", "Credential": "credential", "Credentials": "credentials", - "InYourWallet": "in your wallet" + "InYourWallet": "in your wallet", + "ConnectionsCount" : "Connections", + "CredentialsCount" : "Credentials", + "SharedCredentialsCount": "Shared credentials", + "ViewAll" : "View All", + "DontHaveCredentials":"You don’t have any recent credentials at the moment" }, "Scan": { "SuccessfullyAcceptedConnection": "Successfully Accepted Connection", @@ -717,6 +723,7 @@ const translation = { }, "DIDs":{ "Dids": "My DID", + "Did" :"DID" }, "GoogleDrive": { "Backup": "Backup to Google Drive", diff --git a/app/navigators/HomeStack.tsx b/app/navigators/HomeStack.tsx index 580bcb0cb..cdbd3c4b0 100644 --- a/app/navigators/HomeStack.tsx +++ b/app/navigators/HomeStack.tsx @@ -4,8 +4,9 @@ import { useTranslation } from 'react-i18next' import HistoryMenu from '../components/History/HistoryMenu' import SettingsMenu from '../components/buttons/SettingsMenu' -import { useStore } from '../contexts/store' +import { useConfiguration } from '../contexts/configuration' import { useTheme } from '../contexts/theme' +import CredentialDetailsW3C from '../screens/CredentialDetailsW3C' import Home from '../screens/Home' import ListNotifications from '../screens/ListNotifications' import { HomeStackParams, Screens } from '../types/navigators' @@ -16,8 +17,9 @@ const HomeStack: React.FC = () => { const Stack = createStackNavigator() const theme = useTheme() const { t } = useTranslation() - const [store] = useStore() const defaultStackOptions = createDefaultStackOptions(theme) + const { useCustomNotifications } = useConfiguration() + const { notifications } = useCustomNotifications() return ( @@ -26,7 +28,7 @@ const HomeStack: React.FC = () => { component={Home} options={() => ({ title: t('Screens.Home'), - headerRight: () => (store.preferences.useHistoryCapability ? : null), + headerRight: () => , headerLeft: () => , })} /> @@ -37,6 +39,11 @@ const HomeStack: React.FC = () => { title: t('Screens.Notifications'), })} /> + ) } diff --git a/app/screens/ContactDetails.tsx b/app/screens/ContactDetails.tsx index d30b6af23..0679d1c56 100644 --- a/app/screens/ContactDetails.tsx +++ b/app/screens/ContactDetails.tsx @@ -9,20 +9,19 @@ import { } from '@adeya/ssi' import { useNavigation } from '@react-navigation/core' import { StackNavigationProp, StackScreenProps } from '@react-navigation/stack' -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' -import { DeviceEventEmitter, StyleSheet, Text, TouchableOpacity, View } from 'react-native' +import { DeviceEventEmitter, Image, StyleSheet, Text, TouchableOpacity, View } from 'react-native' +import { widthPercentageToDP as wp } from 'react-native-responsive-screen' import { SafeAreaView } from 'react-native-safe-area-context' import Toast from 'react-native-toast-message' -import { saveHistory } from '../components/History/HistoryManager' -import { HistoryCardType, HistoryRecord } from '../components/History/types' import Button, { ButtonType } from '../components/buttons/Button' import CommonRemoveModal from '../components/modals/CommonRemoveModal' import { ToastType } from '../components/toast/BaseToast' import { EventTypes } from '../constants' -import { useStore } from '../contexts/store' import { useTheme } from '../contexts/theme' +import { ListItems } from '../theme' import { BifoldError } from '../types/error' import { ContactStackParams, Screens, TabStacks } from '../types/navigators' import { ModalUsage } from '../types/remove' @@ -42,7 +41,9 @@ const ContactDetails: React.FC = ({ route }) => { const [isCredentialsOfferRemoveModalDisplayed, setIsCredentialsOfferRemoveModalDisplayed] = useState(false) const [isProofRequestRemoveModalDisplayed, setIsProofRequestRemoveModalDisplayed] = useState(false) const connection = useConnectionById(connectionId) - const [store] = useStore() + const contactLabel = useMemo(() => getConnectionName(connection) ?? '', [connection]) + const contactDid = useMemo(() => connection?.theirDid ?? '', [connection]) + const contactLabelAbbr = useMemo(() => contactLabel?.charAt(0).toUpperCase(), [connection]) // FIXME: This should be exposed via a react hook that allows to filter credentials by connection id const connectionCredentials = [ ...useCredentialByState(CredentialState.CredentialReceived), @@ -66,8 +67,28 @@ const ContactDetails: React.FC = ({ route }) => { margin: 20, justifyContent: 'center', }, + avatarImage: { + width: 50, + height: 50, + }, + avatarPlaceholder: { + ...TextTheme.headingFour, + textAlign: 'center', + }, + avatarContainer: { + alignItems: 'center', + justifyContent: 'center', + alignSelf: 'center', + width: wp('25%'), + height: wp('25%'), + borderRadius: wp('12.5%'), + borderColor: ListItems.avatarCircle.borderColor, + borderWidth: 1, + marginRight: 16, + }, + labelText: { ...TextTheme.headingThree, marginTop: 20 }, + labelValueText: { ...TextTheme.normal, marginTop: 10 }, }) - const handleOnRemove = () => { switch (true) { case Boolean(connectionCredentialsOffer?.length): @@ -143,42 +164,6 @@ const ContactDetails: React.FC = ({ route }) => { const callCancelUnableToRemoveOffer = useCallback(() => handleOfferCancelUnableRemove(), []) const callCancelUnableToRemoveProofRequest = useCallback(() => handleProofRequestCancelUnableRemove(), []) - const contactLabel = useMemo(() => getConnectionName(connection) ?? '', [connection]) - - const logHistoryRecord = useCallback(async () => { - try { - if (!(agent && store.preferences.useHistoryCapability)) { - return - } - const type = HistoryCardType.Connection - if (!connection) { - return - } - - try { - // Prepare the history record object - const recordData: HistoryRecord = { - type: type, - message: type, - createdAt: connection?.createdAt, // Assuming `data` has `createdAt` field - correspondenceId: connectionId, - connection: contactLabel, - } - - // Save the history record asynchronously - await saveHistory(recordData, agent) - } catch (error) { - // error when save history - } - } catch (err: unknown) { - // error when agent and preferences not getting - } - }, [agent, store.preferences.useHistoryCapability, connection, connectionId]) - - useEffect(() => { - logHistoryRecord() - }, []) - const onDismissModalTouched = () => { navigation.getParent()?.navigate(TabStacks.HomeStack, { screen: Screens.Home }) } @@ -187,12 +172,24 @@ const ContactDetails: React.FC = ({ route }) => { {connection && ( - {contactLabel} - + + {connection.imageUrl ? ( + + + + ) : ( + {contactLabelAbbr} + )} + + {contactLabel} + {t('ContactDetails.DateOfConnection', { date: connection?.createdAt ? formatTime(connection.createdAt, { includeHour: true }) : '', })} + + {t('DIDs.Did')} + {contactDid} = () => { screenContainer: { height: '100%', backgroundColor: ColorPallet.brand.primaryBackground, - padding: 20, + paddingHorizontal: 20, + paddingVertical: 5, justifyContent: 'space-between', }, title: { diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index 54b9061f3..674f62f12 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -1,42 +1,125 @@ -import { useAdeyaAgent } from '@adeya/ssi' +import { + ConnectionType, + CredentialExchangeRecord, + CredentialState, + DidExchangeState, + getAllW3cCredentialRecords, + useAdeyaAgent, + useConnections, + useCredentialByState, + W3cCredentialRecord, +} from '@adeya/ssi' import { StackScreenProps } from '@react-navigation/stack' -import React, { ReactNode, useEffect } from 'react' +import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { FlatList, StyleSheet, View, Text, Dimensions, TouchableOpacity, Image } from 'react-native' +import { Dimensions, FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native' +import { widthPercentageToDP as wp } from 'react-native-responsive-screen' +import HistoryListItem from '../components/History/HistoryListItem' +import { getGenericRecordsByQuery } from '../components/History/HistoryManager' +import { CustomRecord, RecordType } from '../components/History/types' import ScanButton from '../components/common/ScanButton' -import NotificationListItem, { NotificationType } from '../components/listItems/NotificationListItem' -import NoNewUpdates from '../components/misc/NoNewUpdates' -import { AttachTourStep } from '../components/tour/AttachTourStep' +import { CredentialCard } from '../components/misc' import { useConfiguration } from '../contexts/configuration' +import { useStore } from '../contexts/store' import { useTheme } from '../contexts/theme' -import { HomeStackParams, Screens } from '../types/navigators' +import { ColorPallet, TextTheme } from '../theme' +import { HomeStackParams, Screens, Stacks, TabStacks } from '../types/navigators' import { AdeyaAgentModules } from '../utils/agent' +import { isW3CCredential } from '../utils/credential' import { getDefaultHolderDidDocument } from '../utils/helpers' const { width } = Dimensions.get('window') const offset = 25 const offsetPadding = 5 +interface EnhancedW3CRecord extends W3cCredentialRecord { + connectionLabel?: string +} type HomeProps = StackScreenProps const Home: React.FC = ({ navigation }) => { const { agent } = useAdeyaAgent() - const { useCustomNotifications } = useConfiguration() - const { notifications } = useCustomNotifications() + const [connectionCount, setConnectionCount] = useState(0) + const [credentialCount, setCredentialCount] = useState(0) const { t } = useTranslation() const { HomeTheme } = useTheme() + const [store] = useStore() + const [historyItems, setHistoryItems] = useState() + const { credentialListOptions: CredentialListOptions } = useConfiguration() + const credentials = [ + ...useCredentialByState(CredentialState.CredentialReceived), + ...useCredentialByState(CredentialState.Done), + ] + const [credentialList, setCredentialList] = useState<(CredentialExchangeRecord | EnhancedW3CRecord)[] | undefined>([]) + const { records: connectionRecords } = useConnections() useEffect(() => { if (!agent) return const setupDefaultDid = async () => { await getDefaultHolderDidDocument(agent) + + let connectionCount = await agent.connections.findAllByQuery({ + state: DidExchangeState.Completed, + }) + connectionCount = connectionCount.filter(r => !r.connectionTypes.includes(ConnectionType.Mediator)) + const credentialCount = await agent.credentials.findAllByQuery({ + state: CredentialState.Done, + }) + setConnectionCount(connectionCount.length) + setCredentialCount(credentialCount.length) } setupDefaultDid() }, [agent]) + const getHistory = async () => { + const allRecords = await getGenericRecordsByQuery(agent, { type: RecordType.HistoryRecord }) + allRecords.sort((objA, objB) => Number(objB.content.createdAt) - Number(objA.content.createdAt)) + + if (allRecords && allRecords.length) { + const top5Records = allRecords.slice(0, 5) + setHistoryItems(top5Records) + } + } + useEffect(() => { + getHistory() + }, []) + useEffect(() => { + const updateCredentials = async () => { + if (!agent) { + return + } + const w3cCredentialRecords = await getAllW3cCredentialRecords(agent) + + const updatedCredentials = credentials.map(credential => { + if (isW3CCredential(credential)) { + const credentialRecordId = credential.credentials[0].credentialRecordId + try { + const record = w3cCredentialRecords.find(record => record.id === credentialRecordId) + if (!credential?.connectionId) { + throw new Error('Connection Id notfound') + } + const connection = connectionRecords.find(connection => connection.id === credential?.connectionId) + const enhancedRecord = record as EnhancedW3CRecord + enhancedRecord.connectionLabel = connection?.theirLabel + return enhancedRecord + } catch (e: unknown) { + throw new Error(`${e}`) + } + } + return credential + }) + + return updatedCredentials + } + + updateCredentials().then(updatedCredentials => { + setCredentialList(updatedCredentials?.slice(-3, 3)) + }) + }, [credentials]) + const styles = StyleSheet.create({ container: { flex: 1, @@ -76,85 +159,231 @@ const Home: React.FC = ({ navigation }) => { height: '85%', width: '80%', }, + cardView: { flexDirection: 'row', justifyContent: 'center', marginVertical: 20, width: wp('100%') }, + cardContentView: { + backgroundColor: ColorPallet.brand.primary, + width: wp('45%'), + height: 80, + marginHorizontal: 5, + justifyContent: 'center', + alignItems: 'center', + borderRadius: 10, + shadowColor: '#000', + shadowOffset: { width: 0, height: 2 }, + shadowOpacity: 0.2, + shadowRadius: 3, + elevation: 3, + alignContent: 'center', + }, + countCardText: { + ...HomeTheme.notificationsHeader, + color: '#fff', + }, + cardText: { + color: '#fff', + fontSize: 12, + fontWeight: '900', + fontStyle: 'normal', + }, + viewText: { + ...HomeTheme.notificationsHeader, + fontSize: 14, + fontWeight: '500', + fontStyle: 'normal', + alignSelf: 'flex-end', + marginRight: 20, + }, + historyText: { + ...HomeTheme.welcomeHeader, + fontSize: 16, + fontWeight: 'bold', + fontStyle: 'normal', + marginLeft: 20, + }, + cardBottomBorder: { + borderBottomWidth: 0.5, + borderBottomColor: '#A0A4AB', + marginTop: 10, + }, + headerView: { + flexDirection: 'row', + justifyContent: 'space-between', + alignContent: 'center', + alignItems: 'center', + marginVertical: 10, + }, + title: { + marginTop: 16, + }, + welcomeText: { + ...HomeTheme.notificationsHeader, + fontSize: 24, + fontWeight: 'bold', + fontStyle: 'normal', + marginHorizontal: 20, + justifyContent: 'center', + alignContent: 'center', + alignSelf: 'center', + paddingVertical: 20, + }, + credentialsCardList: { flexGrow: 0, marginLeft: 15 }, + renderView: { + marginRight: 15, + marginTop: 15, + width: wp('85%'), + // marginBottom: index === credentials.length - 1 ? 45 : 0, + }, + noFavContainer: { + flexDirection: 'column', + justifyContent: 'center', + width: wp('95%'), + }, + noFav: { + fontWeight: '700', + fontSize: 24, + textAlign: 'center', + }, + favContainer: { + alignItems: 'center', + justifyContent: 'center', + width: '100%', + minHeight: 180, + }, }) - const DisplayListItemType = (item: any): ReactNode => { - let component: ReactNode = - if (item.type === 'CredentialRecord') { - let notificationType = NotificationType.CredentialOffer - if (item.revocationNotification) { - notificationType = NotificationType.Revocation - } - component = - } else if (item.type === 'CustomNotification') { - component = - } else { - component = - } - return component + const renderEmptyListComponent = () => { + return ( + + {t('ActivityHistory.NoHistory')} + + ) } + const renderHistoryListItem = (item: CustomRecord) => { + return + } + const renderHistoryView = () => { + return ( + + renderHistoryListItem(element.item)} + /> + + ) + } return ( - - {notifications?.length > 0 ? ( - - - {t('Home.Notifications')} - {notifications?.length ? ` (${notifications.length})` : ''} - - - ) : ( - - {t('Home.Notifications')} - {notifications?.length ? ` (${notifications.length})` : ''} - - )} - {notifications?.length > 1 ? ( + + + { + navigation.navigate(Stacks.ContactStack, { screen: Screens.Contacts, params: { navigation: navigation } }) + }}> + {`${connectionCount}`} + {t('Home.ConnectionsCount')} + + { + navigation.navigate(TabStacks.CredentialStack, { screen: Screens.Credentials }) + }}> + {`${credentialCount}`} + {t('Home.CredentialsCount')} + + + + {/* {credentialList && credentialList?.length >0 && */} + + {t('Global.Credentials')} + + {credentialList && credentialList?.length > 0 && ( navigation.navigate(Screens.Notifications)}> - {t('Home.SeeAll')} + onPress={() => { + navigation.navigate(TabStacks.CredentialStack, { screen: Screens.Credentials }) + }}> + {t('Home.ViewAll')} - ) : null} + )} - 0 ? true : false} - style={{ flexGrow: 0 }} - snapToOffsets={[ - 0, - ...Array(notifications?.length) - .fill(0) - .map((n: number, i: number) => i * (width - 2 * (offset - offsetPadding))) - .slice(1), - ]} - decelerationRate="fast" - ListEmptyComponent={() => ( - - - + {/* */} + {/* } */} + + 0 ? true : false} + style={styles.credentialsCardList} + snapToOffsets={[ + 0, + ...Array(credentialList?.length) + .fill(0) + .map((n: number, i: number) => i * (width - 2 * (offset - offsetPadding))) + .slice(1), + ]} + decelerationRate="fast" + ListEmptyComponent={() => ( + + {t('Home.DontHaveCredentials')} + )} + data={credentialList?.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf())} + keyExtractor={credential => credential.id} + renderItem={({ item: credential }) => ( + + {credential instanceof CredentialExchangeRecord ? ( + + navigation.navigate(Screens.CredentialDetails, { + credential: credential as CredentialExchangeRecord, + }) + } + /> + ) : ( + navigation.navigate(Screens.CredentialDetailsW3C, { credential: credential })} + /> + )} + + )} + /> + + + {/* {historyItems && historyItems.length && */} + + + {t('Global.History')} + {historyItems && historyItems.length && ( + { + navigation.navigate(Stacks.HistoryStack, { screen: Screens.HistoryPage }) + }}> + {t('Home.ViewAll')} + + )} + + {/* */} + + {/* } */} + + + {historyItems && historyItems.length && store.preferences.useHistoryCapability ? ( + renderHistoryView() + ) : ( + + )} - data={notifications} - keyExtractor={item => item.id} - renderItem={({ item, index }) => ( - - {DisplayListItemType(item)} - - )} - /> - - - + + {/* */} diff --git a/app/screens/Scan.tsx b/app/screens/Scan.tsx index e65dcdbb1..480061a5c 100644 --- a/app/screens/Scan.tsx +++ b/app/screens/Scan.tsx @@ -1,12 +1,15 @@ import type { BarCodeReadEvent } from 'react-native-camera' +import { ConnectionRecord } from '@adeya/ssi' import { StackScreenProps } from '@react-navigation/stack' -import React, { useState, useEffect } from 'react' +import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Platform } from 'react-native' import { check, Permission, PERMISSIONS, request, RESULTS } from 'react-native-permissions' import Toast from 'react-native-toast-message' +import { saveHistory } from '../components/History/HistoryManager' +import { HistoryCardType, HistoryRecord } from '../components/History/types' import NewQRView from '../components/misc/NewQRView' import QRScanner from '../components/misc/QRScanner' import CameraDisclosureModal from '../components/modals/CameraDisclosureModal' @@ -21,6 +24,7 @@ import { checkIfAlreadyConnected, connectFromInvitation, fetchUrlData, + getConnectionName, getJson, getUrl, isValidUrl, @@ -41,6 +45,36 @@ const Scan: React.FC = ({ navigation, route }) => { defaultToConnect = route.params['defaultToConnect'] } + const logHistoryRecord = async (connectionRecord: ConnectionRecord | undefined) => { + const contactLabel: string | void = await getConnectionName(connectionRecord) + + try { + if (!(agent && store.preferences.useHistoryCapability)) { + return + } + const type = HistoryCardType.Connection + if (!connectionRecord) { + return + } + + try { + // Prepare the history record object + const recordData: HistoryRecord = { + type: type, + message: type, + createdAt: connectionRecord?.createdAt, // Assuming `data` has `createdAt` field + correspondenceId: connectionRecord?.id, + connection: contactLabel, + } + // Save the history record asynchronously + await saveHistory(recordData, agent) + } catch (error) { + // error when save history + } + } catch (err: unknown) { + // error when agent and preferences not getting + } + } const handleInvitation = async (value: string): Promise => { try { setLoading(true) @@ -60,6 +94,7 @@ const Scan: React.FC = ({ navigation, route }) => { const { connectionRecord, outOfBandRecord } = await connectFromInvitation(agent, value) setLoading(false) + logHistoryRecord(connectionRecord) navigation.getParent()?.navigate(Stacks.ConnectionStack, { screen: Screens.Connection, params: { connectionId: connectionRecord?.id, outOfBandId: outOfBandRecord.id }, @@ -96,7 +131,7 @@ const Scan: React.FC = ({ navigation, route }) => { } const { connectionRecord, outOfBandRecord } = await connectFromInvitation(agent, urlData) - + logHistoryRecord(connectionRecord) setLoading(false) navigation.getParent()?.navigate(Stacks.ConnectionStack, { screen: Screens.Connection, diff --git a/app/types/navigators.ts b/app/types/navigators.ts index 6b32197fe..322e7d8b9 100644 --- a/app/types/navigators.ts +++ b/app/types/navigators.ts @@ -167,6 +167,10 @@ export type OrganizationStackParams = { export type HomeStackParams = { [Screens.Home]: undefined [Screens.Notifications]: undefined + [Screens.CredentialDetails]: { credential: CredentialExchangeRecord } + [Screens.CredentialDetailsW3C]: { credential: W3cCredentialRecord } + [Screens.HistoryPage]: undefined + [Screens.Credentials]: undefined } export type ConnectStackParams = { diff --git a/ios/.xcode.env b/ios/.xcode.env index 3d5782c71..34d2a70c0 100644 --- a/ios/.xcode.env +++ b/ios/.xcode.env @@ -1,11 +1 @@ -# This `.xcode.env` file is versioned and is used to source the environment -# used when running script phases inside Xcode. -# To customize your local environment, you can create an `.xcode.env.local` -# file that is not versioned. - -# NODE_BINARY variable contains the PATH to the node executable. -# -# Customize the NODE_BINARY variable here. -# For example, to use nvm with brew, add the following line -# . "$(brew --prefix nvm)/nvm.sh" --no-use -export NODE_BINARY=$(command -v node) +export NODE_BINARY=/opt/homebrew/bin/node diff --git a/ios/AdeyaWallet.xcodeproj/project.pbxproj b/ios/AdeyaWallet.xcodeproj/project.pbxproj index bda0cfce3..233418b3a 100644 --- a/ios/AdeyaWallet.xcodeproj/project.pbxproj +++ b/ios/AdeyaWallet.xcodeproj/project.pbxproj @@ -647,6 +647,7 @@ "$(inherited)", "-Wl", "-ld_classic", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; @@ -721,6 +722,7 @@ "$(inherited)", "-Wl", "-ld_classic", + "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; From 213d3cd3cc45a8d8bff66530391a6004027ace00 Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Tue, 29 Oct 2024 11:40:48 +0530 Subject: [PATCH 2/9] refactor: Remove unused code Signed-off-by: tusharbhayani --- app/components/History/HistoryListItem.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/app/components/History/HistoryListItem.tsx b/app/components/History/HistoryListItem.tsx index ed62a7170..a9580381f 100644 --- a/app/components/History/HistoryListItem.tsx +++ b/app/components/History/HistoryListItem.tsx @@ -32,10 +32,6 @@ const styles = StyleSheet.create({ marginLeft: 20, width: '80%', }, - cardDescriptionContent: { - // marginTop: 5, - // marginBottom: 10, - }, cardDate: { color: '#666666', }, From bb044fe61f3605500ef0ca6f5d28a0d5d9a11707 Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Tue, 29 Oct 2024 12:07:25 +0530 Subject: [PATCH 3/9] refactor: modified conditions Signed-off-by: tusharbhayani --- app/screens/Home.tsx | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index 674f62f12..b39738ab5 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -78,7 +78,8 @@ const Home: React.FC = ({ navigation }) => { const allRecords = await getGenericRecordsByQuery(agent, { type: RecordType.HistoryRecord }) allRecords.sort((objA, objB) => Number(objB.content.createdAt) - Number(objA.content.createdAt)) - if (allRecords && allRecords.length) { + const hasAllRecords = !!allRecords?.length + if (hasAllRecords) { const top5Records = allRecords.slice(0, 5) setHistoryItems(top5Records) } @@ -215,23 +216,11 @@ const Home: React.FC = ({ navigation }) => { title: { marginTop: 16, }, - welcomeText: { - ...HomeTheme.notificationsHeader, - fontSize: 24, - fontWeight: 'bold', - fontStyle: 'normal', - marginHorizontal: 20, - justifyContent: 'center', - alignContent: 'center', - alignSelf: 'center', - paddingVertical: 20, - }, credentialsCardList: { flexGrow: 0, marginLeft: 15 }, renderView: { marginRight: 15, marginTop: 15, width: wp('85%'), - // marginBottom: index === credentials.length - 1 ? 45 : 0, }, noFavContainer: { flexDirection: 'column', @@ -316,7 +305,7 @@ const Home: React.FC = ({ navigation }) => { 0 ? true : false} + scrollEnabled={!!credentialList?.length} style={styles.credentialsCardList} snapToOffsets={[ 0, @@ -361,7 +350,7 @@ const Home: React.FC = ({ navigation }) => { {t('Global.History')} - {historyItems && historyItems.length && ( + {historyItems && historyItems.length > 0 && ( { navigation.navigate(Stacks.HistoryStack, { screen: Screens.HistoryPage }) @@ -375,7 +364,7 @@ const Home: React.FC = ({ navigation }) => { {/* } */} - {historyItems && historyItems.length && store.preferences.useHistoryCapability ? ( + {historyItems && historyItems.length > 0 && store.preferences.useHistoryCapability ? ( renderHistoryView() ) : ( From 7a3be0b9bd9661b5f6f61bbcc5aa52bd8e7be2da Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Tue, 29 Oct 2024 17:15:21 +0530 Subject: [PATCH 4/9] refactor: Extract CredentialList component and remove duplicate list rendering Signed-off-by: tusharbhayani --- app/components/History/HistoryListItem.tsx | 2 +- .../listItems/CredentialsListitem.tsx | 83 +++++++++++++++++++ app/screens/Home.tsx | 67 ++++----------- app/screens/ListCredentials.tsx | 56 ++++--------- 4 files changed, 117 insertions(+), 91 deletions(-) create mode 100644 app/components/listItems/CredentialsListitem.tsx diff --git a/app/components/History/HistoryListItem.tsx b/app/components/History/HistoryListItem.tsx index a9580381f..4410981e0 100644 --- a/app/components/History/HistoryListItem.tsx +++ b/app/components/History/HistoryListItem.tsx @@ -133,7 +133,7 @@ const HistoryListItem: React.FC = ({ item }) => { } } - const formatDate = (dateString: string) => { + const formatDate = (dateString: Date) => { const now = moment() // Current date and time const date = moment(dateString) // Message date and time const diffDays = now.diff(date, 'days') diff --git a/app/components/listItems/CredentialsListitem.tsx b/app/components/listItems/CredentialsListitem.tsx new file mode 100644 index 000000000..12ff09a56 --- /dev/null +++ b/app/components/listItems/CredentialsListitem.tsx @@ -0,0 +1,83 @@ +import { CredentialExchangeRecord, W3cCredentialRecord } from '@adeya/ssi' +import React from 'react' +import { useTranslation } from 'react-i18next' +import { Dimensions, FlatList, StyleSheet, Text, View } from 'react-native' +import { widthPercentageToDP as wp } from 'react-native-responsive-screen' + +import { CredentialCard } from '../misc' + +interface EnhancedW3CRecord extends W3cCredentialRecord { + connectionLabel?: string +} +interface Props { + credentialList: (CredentialExchangeRecord | EnhancedW3CRecord)[] | undefined + isHorizontal?: boolean + onPress: (credential: CredentialExchangeRecord) => void +} + +const { width } = Dimensions.get('window') +const offset = 25 +const offsetPadding = 5 + +const CredentialsListItem: React.FC = ({ credentialList, isHorizontal = false, onPress }) => { + const { t } = useTranslation() + + const styles = StyleSheet.create({ + credentialsCardList: { flexGrow: 0, marginLeft: 15 }, + noFavContainer: { + flexDirection: 'column', + justifyContent: 'center', + width: wp('95%'), + }, + noFav: { + fontWeight: '700', + fontSize: 24, + textAlign: 'center', + }, + renderView: { + marginRight: 15, + marginTop: 15, + width: isHorizontal ? wp('85%') : 'auto', + }, + }) + + return ( + i * (width - 2 * (offset - offsetPadding))) + .slice(1), + ]} + decelerationRate="fast" + ListEmptyComponent={() => ( + + {t('Home.DontHaveCredentials')} + + )} + data={credentialList} + keyExtractor={credential => credential?.id} + renderItem={({ item: credential }) => ( + + {credential instanceof CredentialExchangeRecord ? ( + onPress(credential)} /> + ) : ( + onPress(credential)} + /> + )} + + )} + /> + ) +} + +export default CredentialsListItem diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index b39738ab5..de810ccf4 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -12,14 +12,14 @@ import { import { StackScreenProps } from '@react-navigation/stack' import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Dimensions, FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native' +import { FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native' import { widthPercentageToDP as wp } from 'react-native-responsive-screen' import HistoryListItem from '../components/History/HistoryListItem' import { getGenericRecordsByQuery } from '../components/History/HistoryManager' import { CustomRecord, RecordType } from '../components/History/types' import ScanButton from '../components/common/ScanButton' -import { CredentialCard } from '../components/misc' +import CredentialsListitem from '../components/listItems/CredentialsListitem' import { useConfiguration } from '../contexts/configuration' import { useStore } from '../contexts/store' import { useTheme } from '../contexts/theme' @@ -29,9 +29,7 @@ import { AdeyaAgentModules } from '../utils/agent' import { isW3CCredential } from '../utils/credential' import { getDefaultHolderDidDocument } from '../utils/helpers' -const { width } = Dimensions.get('window') const offset = 25 -const offsetPadding = 5 interface EnhancedW3CRecord extends W3cCredentialRecord { connectionLabel?: string @@ -299,54 +297,25 @@ const Home: React.FC = ({ navigation }) => { )} - {/* */} - {/* } */} + - i * (width - 2 * (offset - offsetPadding))) - .slice(1), - ]} - decelerationRate="fast" - ListEmptyComponent={() => ( - - {t('Home.DontHaveCredentials')} - - )} - data={credentialList?.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf())} - keyExtractor={credential => credential.id} - renderItem={({ item: credential }) => ( - - {credential instanceof CredentialExchangeRecord ? ( - - navigation.navigate(Screens.CredentialDetails, { - credential: credential as CredentialExchangeRecord, - }) - } - /> - ) : ( - navigation.navigate(Screens.CredentialDetailsW3C, { credential: credential })} - /> - )} - - )} - /> + {credentialList && ( + new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf(), + )} + isHorizontal + onPress={credential => { + credential instanceof CredentialExchangeRecord + ? navigation.navigate(Screens.CredentialDetails, { + credential: credential as CredentialExchangeRecord, + }) + : navigation.navigate(Screens.CredentialDetailsW3C, { credential: credential }) + }} + /> + )} - {/* {historyItems && historyItems.length && */} {t('Global.History')} diff --git a/app/screens/ListCredentials.tsx b/app/screens/ListCredentials.tsx index f66901d8d..a936f7cfd 100644 --- a/app/screens/ListCredentials.tsx +++ b/app/screens/ListCredentials.tsx @@ -1,22 +1,20 @@ import type { W3cCredentialRecord } from '@adeya/ssi' import { - useCredentialByState, CredentialExchangeRecord, CredentialState, - useConnections, getAllW3cCredentialRecords, + useConnections, + useCredentialByState, } from '@adeya/ssi' import { useNavigation } from '@react-navigation/core' import { StackNavigationProp } from '@react-navigation/stack' import React, { useEffect, useState } from 'react' -import { useTranslation } from 'react-i18next' -import { FlatList, StyleSheet, View } from 'react-native' +import { StyleSheet, View } from 'react-native' import ScanButton from '../components/common/ScanButton' -import CredentialCard from '../components/misc/CredentialCard' +import CredentialsListItem from '../components/listItems/CredentialsListitem' import { useConfiguration } from '../contexts/configuration' -import { useTheme } from '../contexts/theme' import { CredentialStackParams, Screens } from '../types/navigators' import { useAppAgent } from '../utils/agent' import { isW3CCredential } from '../utils/credential' @@ -34,9 +32,8 @@ const styles = StyleSheet.create({ }) const ListCredentials: React.FC = () => { - const { t } = useTranslation() const { agent } = useAppAgent() - const { credentialListOptions: CredentialListOptions, credentialEmptyList: CredentialEmptyList } = useConfiguration() + const { credentialListOptions: CredentialListOptions } = useConfiguration() const credentials = [ ...useCredentialByState(CredentialState.CredentialReceived), ...useCredentialByState(CredentialState.Done), @@ -45,7 +42,6 @@ const ListCredentials: React.FC = () => { const { records: connectionRecords } = useConnections() const navigation = useNavigation>() - const { ColorPallet } = useTheme() useEffect(() => { const updateCredentials = async () => { @@ -83,39 +79,17 @@ const ListCredentials: React.FC = () => { return ( - new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf())} - keyExtractor={credential => credential.id} - renderItem={({ item: credential, index }) => { - return ( - - {credential instanceof CredentialExchangeRecord ? ( - - navigation.navigate(Screens.CredentialDetails, { - credential: credential as CredentialExchangeRecord, - }) - } - /> - ) : ( - navigation.navigate(Screens.CredentialDetailsW3C, { credential: credential })} - /> - )} - - ) + new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf(), + )} + onPress={credential => { + credential instanceof CredentialExchangeRecord + ? navigation.navigate(Screens.CredentialDetails, { + credential: credential as CredentialExchangeRecord, + }) + : navigation.navigate(Screens.CredentialDetailsW3C, { credential: credential }) }} - ListEmptyComponent={() => } /> From 0ce80f8e58af36273db82396ddf8a040500c7311 Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Tue, 29 Oct 2024 18:39:10 +0530 Subject: [PATCH 5/9] refactor: remove duplicate list rendering Signed-off-by: tusharbhayani --- .../listItems/CredentialsListitem.tsx | 77 +++++++++++++-- app/screens/Home.tsx | 98 +++++++++---------- app/screens/ListCredentials.tsx | 62 +----------- 3 files changed, 116 insertions(+), 121 deletions(-) diff --git a/app/components/listItems/CredentialsListitem.tsx b/app/components/listItems/CredentialsListitem.tsx index 12ff09a56..04534eef6 100644 --- a/app/components/listItems/CredentialsListitem.tsx +++ b/app/components/listItems/CredentialsListitem.tsx @@ -1,28 +1,83 @@ -import { CredentialExchangeRecord, W3cCredentialRecord } from '@adeya/ssi' -import React from 'react' +import { + CredentialExchangeRecord, + CredentialState, + getAllW3cCredentialRecords, + useAdeyaAgent, + useConnections, + useCredentialByState, + W3cCredentialRecord, +} from '@adeya/ssi' +import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { Dimensions, FlatList, StyleSheet, Text, View } from 'react-native' import { widthPercentageToDP as wp } from 'react-native-responsive-screen' +import { useConfiguration } from '../../contexts/configuration' +import { AdeyaAgentModules } from '../../utils/agent' +import { isW3CCredential } from '../../utils/credential' import { CredentialCard } from '../misc' interface EnhancedW3CRecord extends W3cCredentialRecord { connectionLabel?: string } interface Props { - credentialList: (CredentialExchangeRecord | EnhancedW3CRecord)[] | undefined isHorizontal?: boolean onPress: (credential: CredentialExchangeRecord) => void + CredentialEmptyList?: string } const { width } = Dimensions.get('window') const offset = 25 const offsetPadding = 5 -const CredentialsListItem: React.FC = ({ credentialList, isHorizontal = false, onPress }) => { +const CredentialsListItem: React.FC = ({ isHorizontal = false, onPress }) => { const { t } = useTranslation() + const { agent } = useAdeyaAgent() + + const credentials = [ + ...useCredentialByState(CredentialState.CredentialReceived), + ...useCredentialByState(CredentialState.Done), + ] + const [credentialList, setCredentialList] = useState<(CredentialExchangeRecord | EnhancedW3CRecord)[] | undefined>([]) + const { records: connectionRecords } = useConnections() + const { credentialEmptyList: CredentialEmptyList } = useConfiguration() + + useEffect(() => { + const updateCredentials = async () => { + if (!agent) { + return + } + const w3cCredentialRecords = await getAllW3cCredentialRecords(agent) + + const updatedCredentials = credentials.map(credential => { + if (isW3CCredential(credential)) { + const credentialRecordId = credential.credentials[0].credentialRecordId + try { + const record = w3cCredentialRecords.find(record => record.id === credentialRecordId) + if (!credential?.connectionId) { + throw new Error('Connection Id notfound') + } + const connection = connectionRecords.find(connection => connection.id === credential?.connectionId) + const enhancedRecord = record as EnhancedW3CRecord + enhancedRecord.connectionLabel = connection?.theirLabel + return enhancedRecord + } catch (e: unknown) { + throw new Error(`${e}`) + } + } + return credential + }) + + return updatedCredentials + } + + updateCredentials().then(updatedCredentials => { + setCredentialList(updatedCredentials?.slice(-3, 3)) + }) + }, [credentials]) const styles = StyleSheet.create({ + credentialList: { flexGrow: 0 }, credentialsCardList: { flexGrow: 0, marginLeft: 15 }, noFavContainer: { flexDirection: 'column', @@ -46,7 +101,7 @@ const CredentialsListItem: React.FC = ({ credentialList, isHorizontal = f horizontal={isHorizontal} showsHorizontalScrollIndicator={false} scrollEnabled={!!credentialList?.length} - style={styles.credentialsCardList} + style={isHorizontal ? styles.credentialsCardList : styles.credentialList} snapToOffsets={[ 0, ...Array(credentialList?.length) @@ -56,11 +111,17 @@ const CredentialsListItem: React.FC = ({ credentialList, isHorizontal = f ]} decelerationRate="fast" ListEmptyComponent={() => ( - - {t('Home.DontHaveCredentials')} + + {!isHorizontal ? ( + + ) : ( + + {t('Home.DontHaveCredentials')} + + )} )} - data={credentialList} + data={credentialList?.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf())} keyExtractor={credential => credential?.id} renderItem={({ item: credential }) => ( diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index de810ccf4..2a94cba87 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -29,11 +29,10 @@ import { AdeyaAgentModules } from '../utils/agent' import { isW3CCredential } from '../utils/credential' import { getDefaultHolderDidDocument } from '../utils/helpers' -const offset = 25 - interface EnhancedW3CRecord extends W3cCredentialRecord { connectionLabel?: string } +const offset = 25 type HomeProps = StackScreenProps const Home: React.FC = ({ navigation }) => { @@ -45,6 +44,7 @@ const Home: React.FC = ({ navigation }) => { const [store] = useStore() const [historyItems, setHistoryItems] = useState() const { credentialListOptions: CredentialListOptions } = useConfiguration() + const credentials = [ ...useCredentialByState(CredentialState.CredentialReceived), ...useCredentialByState(CredentialState.Done), @@ -52,39 +52,6 @@ const Home: React.FC = ({ navigation }) => { const [credentialList, setCredentialList] = useState<(CredentialExchangeRecord | EnhancedW3CRecord)[] | undefined>([]) const { records: connectionRecords } = useConnections() - useEffect(() => { - if (!agent) return - - const setupDefaultDid = async () => { - await getDefaultHolderDidDocument(agent) - - let connectionCount = await agent.connections.findAllByQuery({ - state: DidExchangeState.Completed, - }) - connectionCount = connectionCount.filter(r => !r.connectionTypes.includes(ConnectionType.Mediator)) - const credentialCount = await agent.credentials.findAllByQuery({ - state: CredentialState.Done, - }) - setConnectionCount(connectionCount.length) - setCredentialCount(credentialCount.length) - } - - setupDefaultDid() - }, [agent]) - - const getHistory = async () => { - const allRecords = await getGenericRecordsByQuery(agent, { type: RecordType.HistoryRecord }) - allRecords.sort((objA, objB) => Number(objB.content.createdAt) - Number(objA.content.createdAt)) - - const hasAllRecords = !!allRecords?.length - if (hasAllRecords) { - const top5Records = allRecords.slice(0, 5) - setHistoryItems(top5Records) - } - } - useEffect(() => { - getHistory() - }, []) useEffect(() => { const updateCredentials = async () => { if (!agent) { @@ -118,6 +85,39 @@ const Home: React.FC = ({ navigation }) => { setCredentialList(updatedCredentials?.slice(-3, 3)) }) }, [credentials]) + useEffect(() => { + if (!agent) return + + const setupDefaultDid = async () => { + await getDefaultHolderDidDocument(agent) + + let connectionCount = await agent.connections.findAllByQuery({ + state: DidExchangeState.Completed, + }) + connectionCount = connectionCount.filter(r => !r.connectionTypes.includes(ConnectionType.Mediator)) + const credentialCount = await agent.credentials.findAllByQuery({ + state: CredentialState.Done, + }) + setConnectionCount(connectionCount.length) + setCredentialCount(credentialCount.length) + } + + setupDefaultDid() + }, [agent]) + + const getHistory = async () => { + const allRecords = await getGenericRecordsByQuery(agent, { type: RecordType.HistoryRecord }) + allRecords.sort((objA, objB) => Number(objB.content.createdAt) - Number(objA.content.createdAt)) + + const hasAllRecords = !!allRecords?.length + if (hasAllRecords) { + const top5Records = allRecords.slice(0, 5) + setHistoryItems(top5Records) + } + } + useEffect(() => { + getHistory() + }, []) const styles = StyleSheet.create({ container: { @@ -299,21 +299,16 @@ const Home: React.FC = ({ navigation }) => { - {credentialList && ( - new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf(), - )} - isHorizontal - onPress={credential => { - credential instanceof CredentialExchangeRecord - ? navigation.navigate(Screens.CredentialDetails, { - credential: credential as CredentialExchangeRecord, - }) - : navigation.navigate(Screens.CredentialDetailsW3C, { credential: credential }) - }} - /> - )} + { + credential instanceof CredentialExchangeRecord + ? navigation.navigate(Screens.CredentialDetails, { + credential: credential as CredentialExchangeRecord, + }) + : navigation.navigate(Screens.CredentialDetailsW3C, { credential: credential }) + }} + /> @@ -328,9 +323,7 @@ const Home: React.FC = ({ navigation }) => { )} - {/* */} - {/* } */} {historyItems && historyItems.length > 0 && store.preferences.useHistoryCapability ? ( @@ -342,7 +335,6 @@ const Home: React.FC = ({ navigation }) => { )} - {/* */} diff --git a/app/screens/ListCredentials.tsx b/app/screens/ListCredentials.tsx index a936f7cfd..4626be30a 100644 --- a/app/screens/ListCredentials.tsx +++ b/app/screens/ListCredentials.tsx @@ -1,27 +1,14 @@ -import type { W3cCredentialRecord } from '@adeya/ssi' - -import { - CredentialExchangeRecord, - CredentialState, - getAllW3cCredentialRecords, - useConnections, - useCredentialByState, -} from '@adeya/ssi' +import { CredentialExchangeRecord } from '@adeya/ssi' import { useNavigation } from '@react-navigation/core' import { StackNavigationProp } from '@react-navigation/stack' -import React, { useEffect, useState } from 'react' +import React from 'react' import { StyleSheet, View } from 'react-native' import ScanButton from '../components/common/ScanButton' import CredentialsListItem from '../components/listItems/CredentialsListitem' import { useConfiguration } from '../contexts/configuration' import { CredentialStackParams, Screens } from '../types/navigators' -import { useAppAgent } from '../utils/agent' -import { isW3CCredential } from '../utils/credential' -interface EnhancedW3CRecord extends W3cCredentialRecord { - connectionLabel?: string -} const styles = StyleSheet.create({ container: { flex: 1 }, scanContainer: { @@ -32,57 +19,12 @@ const styles = StyleSheet.create({ }) const ListCredentials: React.FC = () => { - const { agent } = useAppAgent() const { credentialListOptions: CredentialListOptions } = useConfiguration() - const credentials = [ - ...useCredentialByState(CredentialState.CredentialReceived), - ...useCredentialByState(CredentialState.Done), - ] - const [credentialList, setCredentialList] = useState<(CredentialExchangeRecord | EnhancedW3CRecord)[] | undefined>([]) - const { records: connectionRecords } = useConnections() - const navigation = useNavigation>() - useEffect(() => { - const updateCredentials = async () => { - if (!agent) { - return - } - - const w3cCredentialRecords = await getAllW3cCredentialRecords(agent) - - const updatedCredentials = credentials.map(credential => { - if (isW3CCredential(credential)) { - const credentialRecordId = credential.credentials[0].credentialRecordId - try { - const record = w3cCredentialRecords.find(record => record.id === credentialRecordId) - if (!credential?.connectionId) { - throw new Error('Connection Id notfound') - } - const connection = connectionRecords.find(connection => connection.id === credential?.connectionId) - const enhancedRecord = record as EnhancedW3CRecord - enhancedRecord.connectionLabel = connection?.theirLabel - return enhancedRecord - } catch (e: unknown) { - throw new Error(`${e}`) - } - } - return credential - }) - return updatedCredentials - } - - updateCredentials().then(updatedCredentials => { - setCredentialList(updatedCredentials) - }) - }, [credentials]) - return ( new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf(), - )} onPress={credential => { credential instanceof CredentialExchangeRecord ? navigation.navigate(Screens.CredentialDetails, { From f1046c545d25f8861afab86969f8884883bae018 Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Tue, 29 Oct 2024 18:51:08 +0530 Subject: [PATCH 6/9] refactor: remove duplicate Signed-off-by: tusharbhayani --- .../listItems/CredentialsListitem.tsx | 5 ++--- app/screens/Home.tsx | 22 +++++++++---------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/app/components/listItems/CredentialsListitem.tsx b/app/components/listItems/CredentialsListitem.tsx index 04534eef6..bc4229b4a 100644 --- a/app/components/listItems/CredentialsListitem.tsx +++ b/app/components/listItems/CredentialsListitem.tsx @@ -23,7 +23,6 @@ interface EnhancedW3CRecord extends W3cCredentialRecord { interface Props { isHorizontal?: boolean onPress: (credential: CredentialExchangeRecord) => void - CredentialEmptyList?: string } const { width } = Dimensions.get('window') @@ -110,7 +109,7 @@ const CredentialsListItem: React.FC = ({ isHorizontal = false, onPress }) .slice(1), ]} decelerationRate="fast" - ListEmptyComponent={() => ( + ListEmptyComponent={ {!isHorizontal ? ( @@ -120,7 +119,7 @@ const CredentialsListItem: React.FC = ({ isHorizontal = false, onPress }) )} - )} + } data={credentialList?.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf())} keyExtractor={credential => credential?.id} renderItem={({ item: credential }) => ( diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index 2a94cba87..1568b4090 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -53,24 +53,24 @@ const Home: React.FC = ({ navigation }) => { const { records: connectionRecords } = useConnections() useEffect(() => { - const updateCredentials = async () => { + const updateHomeScreenCredentials = async () => { if (!agent) { return } - const w3cCredentialRecords = await getAllW3cCredentialRecords(agent) + const w3cCredentialRecord = await getAllW3cCredentialRecords(agent) - const updatedCredentials = credentials.map(credential => { + const updatedCredential = credentials.map(credential => { if (isW3CCredential(credential)) { const credentialRecordId = credential.credentials[0].credentialRecordId try { - const record = w3cCredentialRecords.find(record => record.id === credentialRecordId) + const record = w3cCredentialRecord.find(record => record.id === credentialRecordId) if (!credential?.connectionId) { throw new Error('Connection Id notfound') } - const connection = connectionRecords.find(connection => connection.id === credential?.connectionId) - const enhancedRecord = record as EnhancedW3CRecord - enhancedRecord.connectionLabel = connection?.theirLabel - return enhancedRecord + const connections = connectionRecords.find(connection => connection.id === credential?.connectionId) + const enhancedRecords = record as EnhancedW3CRecord + enhancedRecords.connectionLabel = connections?.theirLabel + return enhancedRecords } catch (e: unknown) { throw new Error(`${e}`) } @@ -78,11 +78,11 @@ const Home: React.FC = ({ navigation }) => { return credential }) - return updatedCredentials + return updatedCredential } - updateCredentials().then(updatedCredentials => { - setCredentialList(updatedCredentials?.slice(-3, 3)) + updateHomeScreenCredentials().then(updatedCredential => { + setCredentialList(updatedCredential?.slice(-3, 3)) }) }, [credentials]) useEffect(() => { From cb84bf4501477dd249d5a1eb85aaf8dcd4f3bf24 Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Wed, 6 Nov 2024 18:43:48 +0530 Subject: [PATCH 7/9] refector: Added empty message view on home screen Signed-off-by: tusharbhayani --- .../listItems/CredentialsListitem.tsx | 28 ++++--------------- app/components/misc/EmptyList.tsx | 2 +- app/screens/Home.tsx | 4 +-- app/screens/ListCredentials.tsx | 2 +- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/app/components/listItems/CredentialsListitem.tsx b/app/components/listItems/CredentialsListitem.tsx index bc4229b4a..2ef6a91e8 100644 --- a/app/components/listItems/CredentialsListitem.tsx +++ b/app/components/listItems/CredentialsListitem.tsx @@ -9,7 +9,7 @@ import { } from '@adeya/ssi' import React, { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' -import { Dimensions, FlatList, StyleSheet, Text, View } from 'react-native' +import { Dimensions, FlatList, StyleSheet, View } from 'react-native' import { widthPercentageToDP as wp } from 'react-native-responsive-screen' import { useConfiguration } from '../../contexts/configuration' @@ -76,18 +76,8 @@ const CredentialsListItem: React.FC = ({ isHorizontal = false, onPress }) }, [credentials]) const styles = StyleSheet.create({ - credentialList: { flexGrow: 0 }, - credentialsCardList: { flexGrow: 0, marginLeft: 15 }, - noFavContainer: { - flexDirection: 'column', - justifyContent: 'center', - width: wp('95%'), - }, - noFav: { - fontWeight: '700', - fontSize: 24, - textAlign: 'center', - }, + credentialList: { width: wp('90%'), height: wp('40%') }, + credentialsCardList: { marginLeft: 15 }, renderView: { marginRight: 15, marginTop: 15, @@ -100,7 +90,7 @@ const CredentialsListItem: React.FC = ({ isHorizontal = false, onPress }) horizontal={isHorizontal} showsHorizontalScrollIndicator={false} scrollEnabled={!!credentialList?.length} - style={isHorizontal ? styles.credentialsCardList : styles.credentialList} + style={isHorizontal ? styles.credentialList : styles.credentialsCardList} snapToOffsets={[ 0, ...Array(credentialList?.length) @@ -110,14 +100,8 @@ const CredentialsListItem: React.FC = ({ isHorizontal = false, onPress }) ]} decelerationRate="fast" ListEmptyComponent={ - - {!isHorizontal ? ( - - ) : ( - - {t('Home.DontHaveCredentials')} - - )} + + } data={credentialList?.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf())} diff --git a/app/components/misc/EmptyList.tsx b/app/components/misc/EmptyList.tsx index dbbb08832..0c88ae735 100644 --- a/app/components/misc/EmptyList.tsx +++ b/app/components/misc/EmptyList.tsx @@ -14,7 +14,7 @@ const EmptyList: React.FC = ({ message }) => { const { ListItems, Assets, ColorPallet } = useTheme() return ( - + {message || t('Global.NoneYet!')} diff --git a/app/screens/Home.tsx b/app/screens/Home.tsx index 1568b4090..02ee3c13f 100644 --- a/app/screens/Home.tsx +++ b/app/screens/Home.tsx @@ -19,7 +19,7 @@ import HistoryListItem from '../components/History/HistoryListItem' import { getGenericRecordsByQuery } from '../components/History/HistoryManager' import { CustomRecord, RecordType } from '../components/History/types' import ScanButton from '../components/common/ScanButton' -import CredentialsListitem from '../components/listItems/CredentialsListitem' +import CredentialsListItem from '../components/listItems/CredentialsListItem' import { useConfiguration } from '../contexts/configuration' import { useStore } from '../contexts/store' import { useTheme } from '../contexts/theme' @@ -299,7 +299,7 @@ const Home: React.FC = ({ navigation }) => { - { credential instanceof CredentialExchangeRecord diff --git a/app/screens/ListCredentials.tsx b/app/screens/ListCredentials.tsx index 4626be30a..5bd6b2277 100644 --- a/app/screens/ListCredentials.tsx +++ b/app/screens/ListCredentials.tsx @@ -5,7 +5,7 @@ import React from 'react' import { StyleSheet, View } from 'react-native' import ScanButton from '../components/common/ScanButton' -import CredentialsListItem from '../components/listItems/CredentialsListitem' +import CredentialsListItem from '../components/listItems/CredentialsListItem' import { useConfiguration } from '../contexts/configuration' import { CredentialStackParams, Screens } from '../types/navigators' From 5640082ae36b9141d892212fbd554797ee855ec9 Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Mon, 11 Nov 2024 11:39:23 +0530 Subject: [PATCH 8/9] fix: Resolve invalid QR code issue Signed-off-by: tusharbhayani --- app/components/listItems/CredentialsListitem.tsx | 2 +- app/screens/Scan.tsx | 2 +- ios/.xcode.env | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/components/listItems/CredentialsListitem.tsx b/app/components/listItems/CredentialsListitem.tsx index 2ef6a91e8..2a475bacf 100644 --- a/app/components/listItems/CredentialsListitem.tsx +++ b/app/components/listItems/CredentialsListitem.tsx @@ -73,7 +73,7 @@ const CredentialsListItem: React.FC = ({ isHorizontal = false, onPress }) updateCredentials().then(updatedCredentials => { setCredentialList(updatedCredentials?.slice(-3, 3)) }) - }, [credentials]) + }, []) const styles = StyleSheet.create({ credentialList: { width: wp('90%'), height: wp('40%') }, diff --git a/app/screens/Scan.tsx b/app/screens/Scan.tsx index 480061a5c..9c2fbae6f 100644 --- a/app/screens/Scan.tsx +++ b/app/screens/Scan.tsx @@ -131,8 +131,8 @@ const Scan: React.FC = ({ navigation, route }) => { } const { connectionRecord, outOfBandRecord } = await connectFromInvitation(agent, urlData) - logHistoryRecord(connectionRecord) setLoading(false) + logHistoryRecord(connectionRecord) navigation.getParent()?.navigate(Stacks.ConnectionStack, { screen: Screens.Connection, params: { connectionId: connectionRecord?.id, outOfBandId: outOfBandRecord.id }, diff --git a/ios/.xcode.env b/ios/.xcode.env index 34d2a70c0..772b339b4 100644 --- a/ios/.xcode.env +++ b/ios/.xcode.env @@ -1 +1 @@ -export NODE_BINARY=/opt/homebrew/bin/node +export NODE_BINARY=$(command -v node) From 4045ed06ee1eeb3c4cdbacfd3bf5bec96445573b Mon Sep 17 00:00:00 2001 From: tusharbhayani Date: Mon, 11 Nov 2024 12:15:24 +0530 Subject: [PATCH 9/9] refector: revert code Signed-off-by: tusharbhayani --- ios/AdeyaWallet.xcodeproj/project.pbxproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/ios/AdeyaWallet.xcodeproj/project.pbxproj b/ios/AdeyaWallet.xcodeproj/project.pbxproj index 233418b3a..bda0cfce3 100644 --- a/ios/AdeyaWallet.xcodeproj/project.pbxproj +++ b/ios/AdeyaWallet.xcodeproj/project.pbxproj @@ -647,7 +647,6 @@ "$(inherited)", "-Wl", "-ld_classic", - "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos; @@ -722,7 +721,6 @@ "$(inherited)", "-Wl", "-ld_classic", - "-Wl -ld_classic ", ); REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; SDKROOT = iphoneos;