Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add recent card #230

Open
wants to merge 9 commits into
base: develop
Choose a base branch
from
9 changes: 7 additions & 2 deletions app/assets/img/HistoryCardAcceptedIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 5 additions & 9 deletions app/assets/img/HistoryNewConnectionIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions app/assets/img/HistoryProofRequestIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 29 additions & 20 deletions app/components/History/HistoryListItem.tsx
Original file line number Diff line number Diff line change
@@ -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'

Expand All @@ -20,21 +20,18 @@ 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: {
// marginTop: 5,
// marginBottom: 10,
},
cardDate: {
color: '#666666',
},
Expand Down Expand Up @@ -136,10 +133,29 @@ const HistoryListItem: React.FC<Props> = ({ item }) => {
}
}

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')

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 <Text style={[TextTheme.caption, styles.cardDate]}>{dateFormate}</Text>

return <Text style={[TextTheme.caption, styles.cardDate]}>{formatDate(date)}</Text>
}

const renderCard = (item: CustomRecord) => {
Expand All @@ -158,14 +174,7 @@ const HistoryListItem: React.FC<Props> = ({ item }) => {
)
}

return (
<TouchableOpacity
onPress={() => {
//TODO: navigate to history details
}}>
{renderCard(item)}
</TouchableOpacity>
)
return <View>{renderCard(item)}</View>
}

export default HistoryListItem
13 changes: 9 additions & 4 deletions app/components/History/HistoryMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<StackNavigationProp<RootStackParams>>()
const { t } = useTranslation()

Expand All @@ -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}
/>
)
}
Expand Down
27 changes: 26 additions & 1 deletion app/components/buttons/HeaderButton.tsx
Original file line number Diff line number Diff line change
@@ -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'
Expand All @@ -19,6 +19,8 @@ interface HeaderButtonProps {
onPress: () => void
icon: string
text?: string
badgeShow?: boolean
notificationCount?: number
}

const HeaderButton: React.FC<HeaderButtonProps> = ({
Expand All @@ -28,6 +30,8 @@ const HeaderButton: React.FC<HeaderButtonProps> = ({
accessibilityLabel,
testID,
onPress,
badgeShow,
notificationCount,
}) => {
const { ColorPallet, TextTheme } = useTheme()
const style = StyleSheet.create({
Expand All @@ -44,6 +48,22 @@ const HeaderButton: React.FC<HeaderButtonProps> = ({
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) => {
Expand All @@ -60,6 +80,11 @@ const HeaderButton: React.FC<HeaderButtonProps> = ({
<>
{text && <Text style={[style.title]}>{text}</Text>}
<Icon name={icon} size={defaultIconSize} color={ColorPallet.brand.headerIcon} />
{badgeShow && notificationCount > 0 && (
<View style={style.badge}>
<Text style={style.badgeText}>{notificationCount}</Text>
</View>
)}
</>
)
}
Expand Down
127 changes: 127 additions & 0 deletions app/components/listItems/CredentialsListitem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
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, 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 {
isHorizontal?: boolean
onPress: (credential: CredentialExchangeRecord) => void
}

const { width } = Dimensions.get('window')
const offset = 25
const offsetPadding = 5

const CredentialsListItem: React.FC<Props> = ({ isHorizontal = false, onPress }) => {
const { t } = useTranslation()
const { agent } = useAdeyaAgent<AdeyaAgentModules>()

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))
})
}, [])

const styles = StyleSheet.create({
credentialList: { width: wp('90%'), height: wp('40%') },
credentialsCardList: { marginLeft: 15 },
renderView: {
marginRight: 15,
marginTop: 15,
width: isHorizontal ? wp('85%') : 'auto',
},
})

return (
<FlatList
horizontal={isHorizontal}
showsHorizontalScrollIndicator={false}
scrollEnabled={!!credentialList?.length}
style={isHorizontal ? styles.credentialList : styles.credentialsCardList}
snapToOffsets={[
0,
...Array(credentialList?.length)
.fill(0)
.map((n: number, i: number) => i * (width - 2 * (offset - offsetPadding)))
.slice(1),
]}
decelerationRate="fast"
ListEmptyComponent={
<View style={isHorizontal ? styles.credentialList : styles.credentialsCardList}>
<CredentialEmptyList message={t('Credentials.EmptyCredentailsList')} />
</View>
}
data={credentialList?.sort((a, b) => new Date(b.createdAt).valueOf() - new Date(a.createdAt).valueOf())}
keyExtractor={credential => credential?.id}
renderItem={({ item: credential }) => (
<View style={styles.renderView}>
{credential instanceof CredentialExchangeRecord ? (
<CredentialCard credential={credential} onPress={() => onPress(credential)} />
) : (
<CredentialCard
schemaId={credential?.credential?.type[1]}
connectionLabel={credential?.connectionLabel}
credential={credential}
onPress={() => onPress(credential)}
/>
)}
</View>
)}
/>
)
}

export default CredentialsListItem
2 changes: 1 addition & 1 deletion app/components/misc/EmptyList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const EmptyList: React.FC<EmptyListProps> = ({ message }) => {
const { ListItems, Assets, ColorPallet } = useTheme()

return (
<View style={{ paddingTop: 100, height: '100%', backgroundColor: ColorPallet.brand.primaryBackground }}>
<View style={{ height: '100%', backgroundColor: ColorPallet.brand.primaryBackground }}>
<Assets.svg.emptyWallet fill={ListItems.emptyList.color} height={100} />
<Text style={[ListItems.emptyList, { textAlign: 'center' }]} testID={testIdWithKey('NoneYet')}>
{message || t('Global.NoneYet!')}
Expand Down
Loading