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: credential branding update #592

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
67b1be4
Disable credential header and footer
amanji Jan 9, 2023
783a4b7
Render a basic credential
amanji Jan 9, 2023
2415ed0
Reconfigure card flex layout to row
amanji Jan 10, 2023
f3567a1
Rename credential body components
amanji Jan 10, 2023
e9b7221
Add credential status type
amanji Jan 10, 2023
3a64dfb
Render card status variations with switch
amanji Jan 10, 2023
25a49cb
Revocation status is passed by type now
amanji Jan 10, 2023
90cc863
Add issuer for branding 2.0
amanji Jan 10, 2023
13f1311
Use US spelling of colour for consistency
amanji Jan 10, 2023
da3bd79
Adds credential name
amanji Jan 10, 2023
cc0332c
Update layout commmentary
amanji Jan 11, 2023
1884ccc
Add credential layout overlay 2.0
amanji Jan 11, 2023
86fe66a
Add some file formatting for legibility
amanji Jan 11, 2023
46272e7
Update oca overlay types
amanji Jan 11, 2023
b699923
Refactor card loading logic and variable naming
amanji Jan 11, 2023
9f97480
Default branding credential implemented
amanji Jan 11, 2023
723e0d4
More logic cleanup
amanji Jan 11, 2023
3e43d26
Add fallback logo
amanji Jan 11, 2023
f852d85
Fix credential fallback logo initial
amanji Jan 12, 2023
cf637db
Adds background image slice
amanji Jan 12, 2023
96cc2e0
Adds primary and secondary attributes
amanji Jan 17, 2023
9a37e0d
Merge branch 'main' into credential-branding-2.0
amanji Jan 17, 2023
c80d482
Move common functions into utility
amanji Jan 18, 2023
c1b15d3
Tweaks to credential card
amanji Jan 18, 2023
4ec4289
Refactor reused components into functions
amanji Jan 19, 2023
8cfb522
Update tests
amanji Jan 19, 2023
94d66ba
Credential branding overlay version aligment to Aries RFC
amanji Jan 19, 2023
ba9628f
Merge branch 'main' into credential-branding-2.0
amanji Jan 19, 2023
9357272
Merge branch 'main' into credential-branding-2.0
amanji Jan 24, 2023
bf3d354
Fix issue with credential fields not displaying
amanji Jan 24, 2023
ddb9f3f
Clean up and group overlay related state
amanji Jan 24, 2023
12c7af9
Merge branch 'main' into credential-branding-2.0
amanji Jan 25, 2023
091813c
Merge branch 'main' into credential-branding-2.0
amanji Jan 30, 2023
76557d6
Update OCA type naming
amanji Jan 30, 2023
870326c
Adds fallback card overlay type
amanji Jan 30, 2023
a8f0001
Favour component names over "render" functions
amanji Jan 30, 2023
ba2fd93
Use null coalescing operator over logical or
amanji Jan 30, 2023
079c251
Refactor and clean up OCA core
amanji Jan 30, 2023
3ee8b42
Fix default bundle path
amanji Jan 30, 2023
bee5760
Update state types
amanji Jan 30, 2023
c533875
Favour component names over "render" functions
amanji Jan 30, 2023
ab151f0
Adds fallback mechanism for credential branding
amanji Jan 31, 2023
5ed6501
Rename bundle resolver options type
amanji Jan 31, 2023
673b69c
Use consistent casing for types
amanji Jan 31, 2023
4939564
Ran prettier
amanji Jan 31, 2023
dcb9e4a
Merge branch 'main' into credential-branding-2.0
amanji Jan 31, 2023
f6460d0
Fixed built in bundles
amanji Feb 1, 2023
4400fd3
Merge branch 'credential-branding-2.0' of github.com:petridishdev/ari…
amanji Feb 1, 2023
8c7c843
fix: multi credential proof request
amanji Feb 1, 2023
ea12b9b
Merge branch 'main' into credential-branding-2.0
amanji Feb 2, 2023
a8cdd66
Merge branch 'main' into main
amanji Feb 2, 2023
f76b044
Merge remote-tracking branch 'upstream/main' into main
amanji Feb 2, 2023
ba87d87
Merge remote-tracking branch 'upstream/main' into main
amanji Feb 7, 2023
cea8fc6
Merge remote-tracking branch 'upstream/main' into main
amanji Feb 15, 2023
71b1a48
Merge branch 'main' into credential-branding-2.0
amanji Feb 15, 2023
7ffecfa
chore: fix linter errors
amanji Feb 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 53 additions & 53 deletions core/App/assets/oca-bundles.json

Large diffs are not rendered by default.

304 changes: 12 additions & 292 deletions core/App/components/misc/CredentialCard.tsx
Original file line number Diff line number Diff line change
@@ -1,307 +1,27 @@
import { CredentialExchangeRecord, CredentialState } from '@aries-framework/core'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
Dimensions,
ImageBackground,
ImageSourcePropType,
StyleSheet,
Text,
View,
ViewStyle,
Image,
} from 'react-native'
import { TouchableOpacity } from 'react-native-gesture-handler'
import { CredentialExchangeRecord } from '@aries-framework/core'
import React from 'react'
import { ViewStyle } from 'react-native'

import { useConfiguration } from '../../contexts/configuration'
import { useTheme } from '../../contexts/theme'
import { GenericFn } from '../../types/fn'
import { OCACredentialBundle } from '../../types/oca'
import { formatTime } from '../../utils/helpers'
import { luminanceForHexColour } from '../../utils/luminance'
import { testIdWithKey } from '../../utils/testable'
import { CardOverlayType } from '../../types/oca'

import CredentialCard10 from './CredentialCard10'
import CredentialCard11 from './CredentialCard11'

interface CredentialCardProps {
credential: CredentialExchangeRecord
onPress?: GenericFn
style?: ViewStyle
}

const paddingVertical = 10
const paddingHorizontal = 10
const transparent = 'rgba(0,0,0,0)'
const borderRadius = 15
const borderPadding = 8
const { width } = Dimensions.get('window')

/**
* A card is defined as a 4x8 (height/rows x width/columns) grid.
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
| 2 | | | | | | | |
| 3 | | | | | | | |
| 4 | | | | | | | |

The card width is the full screen width, and the card height is half of the screen width

Variation 1:
Header: Small Logo (1x1) + Issuer Name (1x3) + Credential Name (1x4)
Body: Reserved for Future Use (2x4)
Footer: Issued or Expired Date (1x4)

| L | Issuer | Cred Name |
| Body |
| Body |
| Issued/Expired Date |

Variation 2:
Header: Large Logo (1x4) + Credential Name (1x4)
Body: Reserved for Future Use (2x4)
Footer: Issued or Expired Date (1x4)

| Logo | Cred Name |
| Body |
| Body |
| Issued/Expired Date |


Note: The small logo MUST be provided as 1x1 (height/width) ratio, while the large logo MUST be provided as 1x4 (height/width) ratio
*/
const cardHeight = width / 2 // a card height is half of the screen width
const cardHeaderHeight = cardHeight / 4 // a card has a total of 4 rows, and the header occupy 1 row

const toImageSource = (source: unknown): ImageSourcePropType => {
if (typeof source === 'string') {
return { uri: source as string }
}
return source as ImageSourcePropType
}
interface BundlePair {
bundle1?: OCACredentialBundle
bundle2?: OCACredentialBundle
}

const CredentialCard: React.FC<CredentialCardProps> = ({ credential, style = {}, onPress = undefined }) => {
const { t, i18n } = useTranslation()
const { ColorPallet, TextTheme } = useTheme()
const { OCABundle } = useConfiguration()

const [bundles, setBundles] = useState<BundlePair | undefined>(undefined)
const metaLayer = bundles?.bundle1?.getMetaOverlay(i18n.language) ?? bundles?.bundle2?.getMetaOverlay(i18n.language)
const overlay = bundles?.bundle1?.getCardLayoutOverlay() ?? bundles?.bundle2?.getCardLayoutOverlay()

const [isRevoked, setIsRevoked] = useState<boolean>(false)
const bundleLoaded = bundles?.bundle1 !== undefined || bundles?.bundle2 !== undefined

const credentialTextColor = (hex?: string) => {
const midpoint = 255 / 2
if ((luminanceForHexColour(hex ?? '') ?? 0) >= midpoint) {
return ColorPallet.grayscale.darkGrey
}
return ColorPallet.grayscale.white
}

const styles = StyleSheet.create({
container: {
backgroundColor: overlay?.imageSource ? transparent : overlay?.backgroundColor,
height: cardHeight,
borderRadius: borderRadius,
},
outerHeaderContainer: {
flexDirection: 'column',
backgroundColor: overlay?.header?.backgroundColor ?? transparent,
height: cardHeaderHeight + borderPadding,
borderTopLeftRadius: borderRadius,
borderTopRightRadius: borderRadius,
},
innerHeaderContainer: {
flexDirection: 'row',
height: cardHeaderHeight,
marginLeft: borderPadding,
marginRight: borderPadding,
marginTop: borderPadding,
marginBottom: borderPadding,
backgroundColor: overlay?.header?.backgroundColor ?? transparent,
},
bodyContainer: {
flexGrow: 1,
},
footerContainer: {
flexDirection: 'row',
backgroundColor: overlay?.footer?.backgroundColor ?? transparent,
paddingHorizontal,
paddingVertical,
borderBottomLeftRadius: borderRadius,
borderBottomRightRadius: borderRadius,
},
revokedFooter: {
backgroundColor: ColorPallet.notification.error,
flexGrow: 1,
marginHorizontal: -1 * paddingHorizontal,
marginVertical: -1 * paddingVertical,
paddingHorizontal: paddingHorizontal,
paddingVertical: paddingVertical,
borderBottomLeftRadius: borderRadius,
borderBottomRightRadius: borderRadius,
},
flexGrow: {
flexGrow: 1,
},
})

useEffect(() => {
if (!credential) {
return
}
if (credential.state !== CredentialState.OfferReceived) {
const indyCredentialFormat = credential.credentials.find((c) => c.credentialRecordType === 'indy')
if (!indyCredentialFormat) {
return
}
}
OCABundle.resolve(credential).then(async (bundle) => {
if (bundle !== undefined) {
setBundles({ bundle1: bundle, bundle2: undefined })
} else {
await OCABundle.resolveDefaultBundle(credential).then((defaultBundle) => {
setBundles({ bundle1: undefined, bundle2: defaultBundle })
})
}
})
}, [])

useEffect(() => {
setIsRevoked(credential.revocationNotification !== undefined)
}, [credential.revocationNotification])

const renderCredentialCardHeader = () => {
return (
<View style={[styles.outerHeaderContainer]}>
<View testID={testIdWithKey('CredentialCardHeader')} style={[styles.innerHeaderContainer]}>
{overlay?.header?.imageSource && (
<Image
source={toImageSource(overlay?.header?.imageSource)}
style={{
flex: !overlay?.header?.hideIssuer ? 1 : 4,
resizeMode: 'contain',
maxHeight: styles.outerHeaderContainer.height - borderPadding,
}}
/>
)}
{overlay?.header?.hideIssuer ? null : (
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={[
TextTheme.label,
{
color:
overlay?.header?.color ??
credentialTextColor(overlay?.header?.backgroundColor || overlay?.backgroundColor),
paddingHorizontal: 0.5 * paddingHorizontal,
flex: !overlay?.header?.imageSource ? 4 : 3,
textAlignVertical: 'center',
},
]}
testID={testIdWithKey('CredentialIssuer')}
maxFontSizeMultiplier={1}
>
{metaLayer?.issuerName}
</Text>
)}
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={[
TextTheme.label,
{
color:
overlay?.header?.color ??
credentialTextColor(overlay?.header?.backgroundColor || overlay?.backgroundColor),
textAlign: 'right',
paddingHorizontal: 0.5 * paddingHorizontal,
flex: 4,
textAlignVertical: 'center',
},
]}
testID={testIdWithKey('CredentialName')}
maxFontSizeMultiplier={1}
>
{metaLayer?.name}
</Text>
</View>
</View>
)
}

const renderCredentialCardBody = () => {
return <View style={styles.bodyContainer} testID={testIdWithKey('CredentialCardBody')}></View>
}

const renderCredentialCardFooter = (revoked = false) => {
return (
<View testID={testIdWithKey('CredentialCardFooter')} style={styles.footerContainer}>
{revoked ? (
<View style={styles.revokedFooter}>
<Text
style={[TextTheme.label, { color: ColorPallet.semantic.error }]}
testID={testIdWithKey('CredentialRevoked')}
>
{t('CredentialDetails.Revoked')}
</Text>
</View>
) : (
<Text
style={[
TextTheme.caption,
{
color:
overlay?.footer?.color ??
credentialTextColor(overlay?.footer?.backgroundColor || overlay?.backgroundColor),
},
]}
testID={testIdWithKey('CredentialIssued')}
maxFontSizeMultiplier={1}
>
{t('CredentialDetails.Issued')}: {formatTime(credential.createdAt)}
</Text>
)}
</View>
)
}

const renderCredentialCard = (revoked = false) => {
return (
<>
{renderCredentialCardHeader()}
{renderCredentialCardBody()}
{renderCredentialCardFooter(revoked)}
</>
)
}
const { OCABundleResolver } = useConfiguration()

return (
<TouchableOpacity
disabled={typeof onPress === 'undefined' ? true : false}
onPress={onPress}
style={[styles.container, style]}
testID={testIdWithKey('ShowCredentialDetails')}
>
{bundleLoaded === true ? (
<View style={styles.flexGrow} testID={testIdWithKey('CredentialCard')}>
{overlay?.imageSource ? (
<ImageBackground
source={toImageSource(overlay?.imageSource)}
style={styles.flexGrow}
imageStyle={{ borderRadius }}
>
{renderCredentialCard(isRevoked)}
</ImageBackground>
) : (
renderCredentialCard(isRevoked)
)}
</View>
) : null}
</TouchableOpacity>
return OCABundleResolver.cardOverlayType === CardOverlayType.CardLayout10 ? (
<CredentialCard10 credential={credential} style={style} onPress={onPress} />
) : (
<CredentialCard11 credential={credential} style={style} onPress={onPress} />
)
}

Expand Down
Loading