diff --git a/android/gradlew b/android/gradlew old mode 100644 new mode 100755 diff --git a/package.json b/package.json index 5cb2f6c6..a9f78ac2 100644 --- a/package.json +++ b/package.json @@ -67,8 +67,9 @@ "@sphereon/ssi-sdk.vc-handler-ld-local": "0.26.0", "@sphereon/ssi-sdk.xstate-machine-persistence": "0.26.0", "@sphereon/ssi-types": "0.26.0", - "@sphereon/ui-components.core": "0.2.0", - "@sphereon/ui-components.ssi-react-native": "0.2.0", + "@sphereon/ui-components.core": "0.2.1-unstable.44", + "@sphereon/ui-components.ssi-react-native": "0.2.1-unstable.44", + "@sphereon/ui-components.credential-branding": "0.2.1-unstable.44", "@veramo/core": "4.2.0", "@veramo/credential-w3c": "4.2.0", "@veramo/data-store": "4.2.0", diff --git a/src/components/fields/SSIImageField/index.tsx b/src/components/fields/SSIImageField/index.tsx index 73e8d8af..ef49b524 100644 --- a/src/components/fields/SSIImageField/index.tsx +++ b/src/components/fields/SSIImageField/index.tsx @@ -8,10 +8,10 @@ import { SSIImageFieldHeaderContainerStyled as HeaderContainer, SSITextH5LightStyled as HeaderLabel, } from '../../../styles/components'; -import {ICredentialDetailsRow} from '../../../types'; +import {CredentialDetailsRow} from '@sphereon/ui-components.credential-branding'; export interface IProps { - item: ICredentialDetailsRow; + item: CredentialDetailsRow; index?: number; } diff --git a/src/components/fields/SSITextField/index.tsx b/src/components/fields/SSITextField/index.tsx index ff2c0fe4..d05b9b50 100644 --- a/src/components/fields/SSITextField/index.tsx +++ b/src/components/fields/SSITextField/index.tsx @@ -11,11 +11,11 @@ import { SSITextH5LightStyled as HeaderLabel, SSITextFieldStatusLabelContainerStyled as StatusLabelContainer, } from '../../../styles/components'; -import {ICredentialDetailsRow} from '../../../types'; import {SSIStatusLabel} from '@sphereon/ui-components.ssi-react-native'; +import {CredentialDetailsRow} from '@sphereon/ui-components.credential-branding'; export interface IProps { - item: ICredentialDetailsRow; + item: CredentialDetailsRow; index?: number; } diff --git a/src/components/views/SSICredentialDetailsView/index.tsx b/src/components/views/SSICredentialDetailsView/index.tsx index c150eb3f..1d8e2126 100644 --- a/src/components/views/SSICredentialDetailsView/index.tsx +++ b/src/components/views/SSICredentialDetailsView/index.tsx @@ -10,19 +10,19 @@ import { SSICredentialDetailsViewFooterLabelValueStyled as IssuedBy, SSICredentialDetailsViewFooterLabelCaptionStyled as IssuedByLabel, } from '../../../styles/components'; -import {ICredentialDetailsRow} from '../../../types'; +import {CredentialDetailsRow} from '@sphereon/ui-components.credential-branding'; import SSIImageField from '../../fields/SSIImageField'; import SSITextField from '../../fields/SSITextField'; export interface IProps { - credentialProperties: Array; + credentialProperties: Array; issuer?: string; } // TODO we are now using this for more than just credential information. Would be nice to refactor it to be a more general usage component const SSICredentialDetailsView: FC = (props: IProps): JSX.Element => { - const renderItem = (itemInfo: ListRenderItemInfo) => { + const renderItem = (itemInfo: ListRenderItemInfo) => { if (itemInfo.item.imageSize) { return ; } else { @@ -47,7 +47,7 @@ const SSICredentialDetailsView: FC = (props: IProps): JSX.Element => { // TODO has a ItemSeparatorComponent which is a bit nicer to use then the logic now with margins data={props.credentialProperties} renderItem={renderItem} - keyExtractor={(item: ICredentialDetailsRow) => item.id} + keyExtractor={(item: CredentialDetailsRow) => item.id} initialNumToRender={DETAILS_INITIAL_NUMBER_TO_RENDER} removeClippedSubviews ListFooterComponent={renderFooter} diff --git a/src/components/views/SSICredentialViewItem/index.tsx b/src/components/views/SSICredentialViewItem/index.tsx index 634754cf..6834b0c3 100644 --- a/src/components/views/SSICredentialViewItem/index.tsx +++ b/src/components/views/SSICredentialViewItem/index.tsx @@ -13,12 +13,12 @@ import { SSITextH4LightStyled as IssuerCaption, SSICredentialViewItemTitleCaptionStyled as TitleCaption, } from '../../../styles/components'; -import {ICredentialSummary} from '../../../types'; +import {CredentialSummary} from '@sphereon/ui-components.credential-branding'; import {toLocalDateString, toLocalDateTimeString} from '../../../utils/DateUtils'; import {View} from 'react-native'; // TODO fix to many properties -export interface Props extends ICredentialSummary { +export interface Props extends CredentialSummary { // TODO should only contain info this screen needs, ICredentialSummary is to much showTime?: boolean; } diff --git a/src/handlers/IntentHandler/index.ts b/src/handlers/IntentHandler/index.ts index a64802db..25681788 100644 --- a/src/handlers/IntentHandler/index.ts +++ b/src/handlers/IntentHandler/index.ts @@ -12,7 +12,7 @@ import store from '../../store'; import {storeVerifiableCredential} from '../../store/actions/credential.actions'; import {NavigationBarRoutesEnum, ScreenRoutesEnum, ToastTypeEnum} from '../../types'; import {parseDeepLink} from '../../utils'; -import {toNonPersistedCredentialSummary} from '../../utils/mappers/credential/CredentialMapper'; +import {toNonPersistedCredentialSummary} from '@sphereon/ui-components.credential-branding'; import {showToast} from '../../utils'; import LockingHandler from '../LockingHandler'; diff --git a/src/navigation/machines/oid4vciStateNavigation.tsx b/src/navigation/machines/oid4vciStateNavigation.tsx index 733bdb7e..ec190c8a 100644 --- a/src/navigation/machines/oid4vciStateNavigation.tsx +++ b/src/navigation/machines/oid4vciStateNavigation.tsx @@ -26,11 +26,11 @@ import { OID4VCIProviderProps, OID4VCIContext as OID4VCIContextType, } from '@sphereon/ssi-sdk.oid4vci-holder'; -import {toNonPersistedCredentialSummary} from '../../utils/mappers/credential/CredentialMapper'; import {translate} from '../../localization/Localization'; import RootNavigation from './../rootNavigation'; import {APP_ID} from '../../@config/constants'; import {MainRoutesEnum, NavigationBarRoutesEnum, PopupImagesEnum, ScreenRoutesEnum} from '../../types'; +import {toNonPersistedCredentialSummary} from '@sphereon/ui-components.credential-branding'; const debug: Debugger = Debug(`${APP_ID}:oid4vciStateNavigation`); diff --git a/src/screens/Onboarding/SSISummaryScreen/index.tsx b/src/screens/Onboarding/SSISummaryScreen/index.tsx index 9ef8ee85..cba316d8 100644 --- a/src/screens/Onboarding/SSISummaryScreen/index.tsx +++ b/src/screens/Onboarding/SSISummaryScreen/index.tsx @@ -9,8 +9,9 @@ import SSICredentialDetailsView from '../../../components/views/SSICredentialDet import SSITabView from '../../../components/views/SSITabView'; import {translate} from '../../../localization/Localization'; import {SSIBasicHorizontalCenterContainerStyled as Container} from '../../../styles/components'; -import {ICredentialDetailsRow, ITabViewRoute, ScreenRoutesEnum, StackParamList} from '../../../types'; +import {ITabViewRoute, ScreenRoutesEnum, StackParamList} from '../../../types'; import {backgroundColors} from '@sphereon/ui-components.core'; +import {CredentialDetailsRow} from '@sphereon/ui-components.credential-branding'; type Props = NativeStackScreenProps; @@ -35,7 +36,7 @@ const SSIOnboardingSummaryScreen: FC = (props: Props): JSX.Element => { return true; }); - const getProperties = (): Array => { + const getProperties = (): Array => { return [ { id: uuidv4(), diff --git a/src/screens/SSICredentialDetailsScreen/index.tsx b/src/screens/SSICredentialDetailsScreen/index.tsx index 43e866ec..eed0b8be 100644 --- a/src/screens/SSICredentialDetailsScreen/index.tsx +++ b/src/screens/SSICredentialDetailsScreen/index.tsx @@ -5,8 +5,7 @@ import {PrimaryButton, SecondaryButton, SSICredentialCardView} from '@sphereon/u import SSIActivityView from '../../components/views/SSIActivityView'; import SSICredentialDetailsView from '../../components/views/SSICredentialDetailsView'; import SSITabView from '../../components/views/SSITabView'; -import {getCredentialStatus} from '../../utils/CredentialUtils'; -import {getIssuerLogo} from '../../utils/mappers/credential/CredentialMapper'; +import {CredentialSummary, getCredentialStatus, getIssuerLogo} from '@sphereon/ui-components.credential-branding'; import {translate} from '../../localization/Localization'; import { SSICredentialDetailsScreenButtonContainer as ButtonContainer, @@ -16,7 +15,7 @@ import { SSICredentialDetailsScreenContentContainer as ContentContainer, SSIStatusBarDarkModeStyled as StatusBar, } from '../../styles/components'; -import {ICredentialSummary, ITabViewRoute, ScreenRoutesEnum, StackParamList} from '../../types'; +import {ITabViewRoute, ScreenRoutesEnum, StackParamList} from '../../types'; import {useBackHandler} from '@react-native-community/hooks'; type Props = NativeStackScreenProps; @@ -26,7 +25,7 @@ enum CredentialTabRoutesEnum { ACTIVITY = 'activity', } -const getCredentialCardLogo = (credential: ICredentialSummary): ImageAttributes | undefined => { +const getCredentialCardLogo = (credential: CredentialSummary): ImageAttributes | undefined => { if (credential.branding?.logo?.uri || credential.branding?.logo?.dataUri) { return credential.branding.logo; } diff --git a/src/screens/SSICredentialsOverviewScreen/index.tsx b/src/screens/SSICredentialsOverviewScreen/index.tsx index 48b0dec4..62df48dd 100644 --- a/src/screens/SSICredentialsOverviewScreen/index.tsx +++ b/src/screens/SSICredentialsOverviewScreen/index.tsx @@ -16,14 +16,15 @@ import { SSIRippleContainerStyled as ItemContainer, SSIStatusBarDarkModeStyled as StatusBar, } from '../../styles/components'; -import {ICredentialSummary, IUser, IUserIdentifier, MainRoutesEnum, RootState, ScreenRoutesEnum, StackParamList} from '../../types'; +import {IUser, IUserIdentifier, MainRoutesEnum, RootState, ScreenRoutesEnum, StackParamList} from '../../types'; import {getOriginalVerifiableCredential} from '../../utils/CredentialUtils'; import {backgroundColors, borderColors} from '@sphereon/ui-components.core'; +import {CredentialSummary} from '@sphereon/ui-components.credential-branding'; interface IProps extends NativeStackScreenProps { getVerifiableCredentials: () => void; deleteVerifiableCredential: (credentialHash: string) => void; - verifiableCredentials: Array; + verifiableCredentials: Array; activeUser: IUser; } @@ -59,7 +60,7 @@ class SSICredentialsOverviewScreen extends PureComponent { }); }; - onItemPress = async (credential: ICredentialSummary): Promise => { + onItemPress = async (credential: CredentialSummary): Promise => { getVerifiableCredential({hash: credential.hash}).then((vc: VerifiableCredential) => this.props.navigation.navigate(ScreenRoutesEnum.CREDENTIAL_DETAILS, { rawCredential: getOriginalVerifiableCredential(vc), @@ -69,7 +70,7 @@ class SSICredentialsOverviewScreen extends PureComponent { ); }; - renderItem = (itemInfo: ListRenderItemInfo): JSX.Element => { + renderItem = (itemInfo: ListRenderItemInfo): JSX.Element => { const {activeUser, verifiableCredentials} = this.props; const credentialItem = ( @@ -113,12 +114,13 @@ class SSICredentialsOverviewScreen extends PureComponent { }; render() { + console.log('this.props.verifiableCredentials:', this.props.verifiableCredentials); return ( itemInfo.hash} + keyExtractor={(itemInfo: CredentialSummary) => itemInfo.hash} renderItem={this.renderItem} closeOnRowOpen closeOnRowBeginSwipe diff --git a/src/screens/SSICredentialsRequiredScreen/index.tsx b/src/screens/SSICredentialsRequiredScreen/index.tsx index 5f39e5ac..1aa5f58a 100644 --- a/src/screens/SSICredentialsRequiredScreen/index.tsx +++ b/src/screens/SSICredentialsRequiredScreen/index.tsx @@ -20,10 +20,10 @@ import { SSIBasicContainerStyled as Container, SSIStatusBarDarkModeStyled as StatusBar, } from '../../styles/components'; -import {ICredentialSummary, ScreenRoutesEnum, StackParamList} from '../../types'; +import {ScreenRoutesEnum, StackParamList} from '../../types'; import {getMatchingUniqueVerifiableCredential, getOriginalVerifiableCredential} from '../../utils'; -import {toCredentialSummary} from '../../utils/mappers/credential/CredentialMapper'; import {JSONPath} from '@astronautlabs/jsonpath'; +import {CredentialSummary, toCredentialSummary} from '@sphereon/ui-components.credential-branding'; type Props = NativeStackScreenProps; @@ -174,7 +174,7 @@ const SSICredentialsRequiredScreen: FC = (props: Props): JSX.Element => { const credentialBranding: ICredentialBranding | undefined = credentialsBranding.find( (branding: ICredentialBranding): boolean => branding.vcHash === uniqueVC.hash, ); - const credentialSummary: ICredentialSummary = await toCredentialSummary(uniqueVC, credentialBranding?.localeBranding); + const credentialSummary: CredentialSummary = await toCredentialSummary(uniqueVC, credentialBranding?.localeBranding); const rawCredential: OriginalVerifiableCredential = getOriginalVerifiableCredential(uniqueVC.verifiableCredential); const isSelected: boolean = userSelectedCredentials .get(inputDescriptorId)! diff --git a/src/screens/Veramo.tsx b/src/screens/Veramo.tsx index 402475e2..4f80fbc7 100644 --- a/src/screens/Veramo.tsx +++ b/src/screens/Veramo.tsx @@ -8,7 +8,7 @@ import {connect} from 'react-redux'; import {createIdentifier, getIdentifiers} from '../services/identityService'; import {CredentialIssuanceStateEnum, RootState, ScreenRoutesEnum, StackParamList} from '../types'; -import {toNonPersistedCredentialSummary} from '../utils/mappers/credential/CredentialMapper'; +import {toNonPersistedCredentialSummary} from '@sphereon/ui-components.credential-branding'; interface IProps extends NativeStackScreenProps { contacts: Array; diff --git a/src/services/brandingService.ts b/src/services/brandingService.ts index 35433760..bc384675 100644 --- a/src/services/brandingService.ts +++ b/src/services/brandingService.ts @@ -1,12 +1,10 @@ -import {IBasicCredentialLocaleBranding, IBasicIssuerLocaleBranding, ICredentialBranding} from '@sphereon/ssi-sdk.data-store'; +import {ICredentialBranding} from '@sphereon/ssi-sdk.data-store'; import {IDeletionResult} from '@sphereon/ssi-sdk.issuance-branding'; import Debug, {Debugger} from 'debug'; import {APP_ID} from '../@config/constants'; import {ibAddCredentialBranding, ibRemoveCredentialBranding} from '../agent'; -import Localization from '../localization/Localization'; -import {IAddCredentialBrandingArgs, IRemoveCredentialBrandingArgs, ISelectAppLocaleBrandingArgs} from '../types'; -import {preloadImage} from '../utils/ImageUtils'; +import {IAddCredentialBrandingArgs, IRemoveCredentialBrandingArgs} from '../types'; const debug: Debugger = Debug(`${APP_ID}:brandingService`); @@ -19,31 +17,3 @@ export const removeCredentialBranding = async (args: IRemoveCredentialBrandingAr debug(`removeCredentialBranding(${JSON.stringify(args)})...`); return ibRemoveCredentialBranding(args); }; - -export const selectAppLocaleBranding = async ( - args: ISelectAppLocaleBrandingArgs, -): Promise => { - // We need to retrieve the locale of the app and select a matching branding or fallback on a branding without a locale - // We search for a first match that starts with the app locale - const appLocale: string = Localization.getLocale(); - const localeBranding: IBasicCredentialLocaleBranding | IBasicIssuerLocaleBranding | undefined = args.localeBranding?.find( - (branding: IBasicCredentialLocaleBranding | IBasicIssuerLocaleBranding) => - branding.locale?.startsWith(appLocale) || branding.locale === undefined, - ); - - const logo: string | undefined = localeBranding?.logo?.dataUri || localeBranding?.logo?.uri; - if (logo) { - preloadImage([{uri: logo}]).catch((): void => { - //ignore - }); - } - - const backgroundImage: string | undefined = localeBranding?.background?.image?.dataUri || localeBranding?.background?.image?.uri; - if (backgroundImage) { - preloadImage([{uri: backgroundImage}]).catch((): void => { - //ignore - }); - } - - return localeBranding; -}; diff --git a/src/store/actions/credential.actions.ts b/src/store/actions/credential.actions.ts index 020e8a75..ebf83cd8 100644 --- a/src/store/actions/credential.actions.ts +++ b/src/store/actions/credential.actions.ts @@ -11,7 +11,7 @@ import { getVerifiableCredentialsFromStorage, storeVerifiableCredential as storeCredential, } from '../../services/credentialService'; -import {ICredentialSummary, RootState, ToastTypeEnum} from '../../types'; +import {RootState, ToastTypeEnum} from '../../types'; import { CREATE_CREDENTIAL_FAILED, CREATE_CREDENTIAL_SUCCESS, @@ -24,7 +24,8 @@ import { STORE_CREDENTIAL_SUCCESS, } from '../../types/store/credential.action.types'; import {showToast} from '../../utils/ToastUtils'; -import {toCredentialSummary} from '../../utils/mappers/credential/CredentialMapper'; +import {CredentialSummary, toCredentialSummary} from '@sphereon/ui-components.credential-branding'; +import {getCredentialIssuerContact} from '../../utils'; export const getVerifiableCredentials = (): ThunkAction, RootState, unknown, Action> => { return async (dispatch: ThunkDispatch): Promise => { @@ -35,12 +36,12 @@ export const getVerifiableCredentials = (): ThunkAction, RootState vcHash: uniqueCredential.hash, })); const credentialsBranding: Array = await ibGetCredentialBranding({filter: vcHashes}); - const credentialSummaries: Array = await Promise.all( - credentials.map(async (uniqueVC: UniqueVerifiableCredential): Promise => { + const credentialSummaries: Array = await Promise.all( + credentials.map(async (uniqueVC: UniqueVerifiableCredential): Promise => { const credentialBranding: ICredentialBranding | undefined = credentialsBranding.find( (branding: ICredentialBranding): boolean => branding.vcHash === uniqueVC.hash, ); - return await toCredentialSummary(uniqueVC, credentialBranding?.localeBranding); + return await toCredentialSummary(uniqueVC, credentialBranding?.localeBranding, getCredentialIssuerContact(uniqueVC.verifiableCredential)); }), ); dispatch({type: GET_CREDENTIALS_SUCCESS, payload: [...credentialSummaries]}); @@ -54,11 +55,11 @@ export const storeVerifiableCredential = (vc: VerifiableCredential): ThunkAction dispatch({type: CREDENTIALS_LOADING}); const mappedVc: VerifiableCredential = CredentialMapper.toUniformCredential(vc as OriginalVerifiableCredential) as VerifiableCredential; storeCredential({vc: mappedVc}) - .then(async (hash: string): Promise => { + .then(async (hash: string): Promise => { const credentialBranding: Array = await ibGetCredentialBranding({filter: [{vcHash: hash}]}); return toCredentialSummary({verifiableCredential: mappedVc, hash}, credentialBranding?.[0]?.localeBranding); }) - .then((summary: ICredentialSummary): void => { + .then((summary: CredentialSummary): void => { dispatch({ type: STORE_CREDENTIAL_SUCCESS, payload: summary, @@ -83,7 +84,7 @@ export const dispatchVerifiableCredential = ( .then((credentialBranding: Array) => toCredentialSummary({verifiableCredential: mappedVc, hash: credentialHash}, credentialBranding?.[0]?.localeBranding), ) - .then((summary: ICredentialSummary): void => { + .then((summary: CredentialSummary): void => { dispatch({ type: STORE_CREDENTIAL_SUCCESS, payload: summary, @@ -126,7 +127,7 @@ export const createVerifiableCredential = (args: ICreateVerifiableCredentialArgs createCredential(args) .then((vc: VerifiableCredential): void => { storeCredential({vc}).then((hash: string) => - toCredentialSummary({verifiableCredential: vc, hash}).then((summary: ICredentialSummary) => + toCredentialSummary({verifiableCredential: vc, hash}).then((summary: CredentialSummary) => // TODO fix mismatch in types dispatch({ type: CREATE_CREDENTIAL_SUCCESS, diff --git a/src/store/reducers/credential.reducer.ts b/src/store/reducers/credential.reducer.ts index 032cd626..d0ec75e5 100644 --- a/src/store/reducers/credential.reducer.ts +++ b/src/store/reducers/credential.reducer.ts @@ -1,4 +1,3 @@ -import {ICredentialSummary} from '../../types'; import { CLEAR_CREDENTIALS, CREATE_CREDENTIAL_FAILED, @@ -13,6 +12,7 @@ import { STORE_CREDENTIAL_SUCCESS, } from '../../types/store/credential.action.types'; import {ICredentialState} from '../../types/store/credential.types'; +import {CredentialSummary} from '@sphereon/ui-components.credential-branding'; const initialState: ICredentialState = { loading: false, @@ -58,7 +58,7 @@ const credentialReducer = (state: ICredentialState = initialState, action: Crede case DELETE_CREDENTIAL_SUCCESS: { return { ...state, - verifiableCredentials: state.verifiableCredentials.filter((vc: ICredentialSummary) => vc.hash !== action.payload), + verifiableCredentials: state.verifiableCredentials.filter((vc: CredentialSummary) => vc.hash !== action.payload), loading: false, }; } diff --git a/src/types/component/index.ts b/src/types/component/index.ts index 80155074..977aa67c 100644 --- a/src/types/component/index.ts +++ b/src/types/component/index.ts @@ -40,8 +40,6 @@ export interface IHeaderMenuButton extends IButton { fontColor?: ColorValue; } -export type LabelStatus = CredentialStatus | IssuerStatus; - export interface ITabRoute { key: string; title: string; diff --git a/src/types/credential/index.ts b/src/types/credential/index.ts index b812027a..f5c8d951 100644 --- a/src/types/credential/index.ts +++ b/src/types/credential/index.ts @@ -1,44 +1,11 @@ -import {IBasicCredentialLocaleBranding} from '@sphereon/ssi-sdk.data-store'; import {OriginalVerifiableCredential} from '@sphereon/ssi-types'; -import {CredentialStatus, ImageSize} from '@sphereon/ui-components.core'; -import {LabelStatus} from '../component'; +import {CredentialSummary} from '@sphereon/ui-components.credential-branding'; export enum CredentialIssuanceStateEnum { OFFER = 'offer', } -// TODO create proper interface for credential summary / info -export interface ICredentialSummary { - hash: string; - id?: string; // The id of the credential (optional according to VCDM) - title: string; - issuer: IIssuerSummary; - credentialStatus: CredentialStatus; - issueDate: number; - expirationDate: number; - properties: ICredentialDetailsRow[]; - branding?: IBasicCredentialLocaleBranding; -} - -// TODO create proper interface for credential summary / info -export interface IIssuerSummary { - name: string; - alias: string; - image?: string; - url?: string; -} - -// TODO interface should be replaced by proper interface for credential details -export interface ICredentialDetailsRow { - id: string; - label: string; - value: any; - isEditable?: boolean; - status?: LabelStatus; - imageSize?: ImageSize; -} - export interface ICredentialTypeSelection { id: string; credentialType: string; @@ -49,7 +16,7 @@ export interface ICredentialTypeSelection { export interface ICredentialSelection { hash: string; id?: string; - credential: ICredentialSummary; + credential: CredentialSummary; rawCredential: OriginalVerifiableCredential; isSelected: boolean; } diff --git a/src/types/navigation/index.ts b/src/types/navigation/index.ts index e291987e..f1120b79 100644 --- a/src/types/navigation/index.ts +++ b/src/types/navigation/index.ts @@ -3,9 +3,10 @@ import {NonPersistedIdentity, Party} from '@sphereon/ssi-sdk.data-store'; import {OriginalVerifiableCredential} from '@sphereon/ssi-types'; import {VerifiableCredential} from '@veramo/core'; import {IButton, PopupBadgesEnum, PopupImagesEnum} from '../component'; -import {ICredentialSelection, ICredentialSummary, ICredentialTypeSelection} from '../credential'; +import {ICredentialSelection, ICredentialTypeSelection} from '../credential'; import {OnboardingMachineContext, OnboardingMachineInterpreter, OnboardingPersonalData} from '../machines/onboarding'; import {SiopV2MachineInterpreter} from '../machines/siopV2'; +import {CredentialSummary} from '@sphereon/ui-components.credential-branding'; export type StackParamList = { CredentialsOverview: Record; @@ -101,7 +102,7 @@ export interface ICredentialsRequiredProps { } export interface ICredentialDetailsProps { - credential: ICredentialSummary; + credential: CredentialSummary; primaryAction?: IButton; secondaryAction?: IButton; showActivity?: boolean; diff --git a/src/types/service/brandingService/index.ts b/src/types/service/brandingService/index.ts index 51892379..db5fdf9b 100644 --- a/src/types/service/brandingService/index.ts +++ b/src/types/service/brandingService/index.ts @@ -9,7 +9,3 @@ export interface IAddCredentialBrandingArgs { export interface IRemoveCredentialBrandingArgs { filter: FindCredentialBrandingArgs; } - -export interface ISelectAppLocaleBrandingArgs { - localeBranding?: Array; -} diff --git a/src/types/store/credential.action.types.ts b/src/types/store/credential.action.types.ts index a7c0fa88..15441021 100644 --- a/src/types/store/credential.action.types.ts +++ b/src/types/store/credential.action.types.ts @@ -1,4 +1,4 @@ -import {ICredentialSummary} from '../credential'; +import {CredentialSummary} from '@sphereon/ui-components.credential-branding'; export const CREDENTIALS_LOADING = '[CREDENTIAL] CREDENTIALS_LOADING'; export type CREDENTIALS_LOADING = typeof CREDENTIALS_LOADING; @@ -26,7 +26,7 @@ interface ICredentialsLoading { } interface IGetCredentialsSuccessAction { - payload: Array; + payload: Array; type: GET_CREDENTIALS_SUCCESS; } @@ -35,7 +35,7 @@ interface IGetCredentialsFailedAction { } interface IStoreCredentialsSuccessAction { - payload: ICredentialSummary; + payload: CredentialSummary; type: STORE_CREDENTIAL_SUCCESS; } @@ -53,7 +53,7 @@ interface IDeleteCredentialsFailedAction { } interface ICreateCredentialsSuccessAction { - payload: ICredentialSummary; + payload: CredentialSummary; type: CREATE_CREDENTIAL_SUCCESS; } diff --git a/src/types/store/credential.types.ts b/src/types/store/credential.types.ts index b2e6dee3..292beb4d 100644 --- a/src/types/store/credential.types.ts +++ b/src/types/store/credential.types.ts @@ -1,6 +1,6 @@ -import {ICredentialSummary} from '../credential'; +import {CredentialSummary} from '@sphereon/ui-components.credential-branding'; export interface ICredentialState { loading: boolean; - verifiableCredentials: Array; + verifiableCredentials: Array; } diff --git a/src/utils/CredentialUtils.ts b/src/utils/CredentialUtils.ts index 63fb940c..bff719cc 100644 --- a/src/utils/CredentialUtils.ts +++ b/src/utils/CredentialUtils.ts @@ -1,10 +1,8 @@ import {Party, Identity} from '@sphereon/ssi-sdk.data-store'; import {CredentialMapper, ICredential, OriginalVerifiableCredential, IVerifiableCredential} from '@sphereon/ssi-types'; import {UniqueVerifiableCredential, VerifiableCredential} from '@veramo/core'; -import {CredentialStatus} from '@sphereon/ui-components.core'; import store from '../store'; -import {ICredentialSummary, IUser, IUserIdentifier} from '../types'; -import {makeEpochMilli} from './DateUtils'; +import {IUser, IUserIdentifier} from '../types'; /** * Return the type(s) of a VC minus the VerifiableCredential type which should always be present @@ -46,47 +44,6 @@ export const getOriginalVerifiableCredential = (vc: VerifiableCredential | ICred return CredentialMapper.toWrappedVerifiableCredential(vc as OriginalVerifiableCredential).original; }; -export const getCredentialStatus = (credential: ICredential | VerifiableCredential | ICredentialSummary): CredentialStatus => { - if (isRevoked()) { - return CredentialStatus.REVOKED; - } else if (isExpired(credential.expirationDate)) { - return CredentialStatus.EXPIRED; - } - - return CredentialStatus.VALID; -}; - -/** - * @return - * true means the credential is revoked. - * false means the credential is not revoked. - */ -export const isRevoked = (): boolean => { - return false; - // TODO implement - // { - // id: 'https://revocation-sphereon.sphereon.io/services/credentials/wallet-dev#2022021400', - // type: 'RevocationList2022021401Status', - // revocationListIndex: '2022021402', - // revocationListCredential: 'https://revocation-sphereon.sphereon.io/services/credentials/wallet-dev#2022021400', - // } -}; - -/** - * @param value The number of milliseconds between 1 January 1970 00:00:00 UTC and the given date or a formatted date required by Date(...) - * @return - * true means the credential is expired. - * false means the credential is not expired. - */ -export const isExpired = (value?: string | number): boolean => { - if (!value) { - return false; - } - let expirationDate: number = typeof value === 'string' ? new Date(value).valueOf() : value; - expirationDate = makeEpochMilli(expirationDate); - return expirationDate < Date.now(); -}; - export const translateCorrelationIdToName = (correlationId: string): string => { const contacts: Array = store.getState().contact.contacts; const activeUser: IUser | undefined = store.getState().user.activeUser; @@ -104,3 +61,9 @@ export const translateCorrelationIdToName = (correlationId: string): string => { return correlationId; }; + +export const getCredentialIssuerContact = (vc: VerifiableCredential | ICredential): Party | undefined => { + const contacts: Array = store.getState().contact.contacts; + const issuer: string = typeof vc.issuer === 'string' ? vc.issuer : vc.issuer?.id ?? vc.issuer?.name; + return contacts.find((contact: Party) => contact.identities.some((identity: Identity): boolean => identity.identifier.correlationId === issuer)); +}; diff --git a/src/utils/mappers/credential/CredentialMapper.ts b/src/utils/mappers/credential/CredentialMapper.ts deleted file mode 100644 index 9b81a402..00000000 --- a/src/utils/mappers/credential/CredentialMapper.ts +++ /dev/null @@ -1,200 +0,0 @@ -import {v4 as uuidv4} from 'uuid'; -import {UniqueVerifiableCredential, VerifiableCredential} from '@veramo/core'; -import {computeEntryHash} from '@veramo/utils'; -import {IBasicCredentialLocaleBranding, Party} from '@sphereon/ssi-sdk.data-store'; -import {ICredential} from '@sphereon/ssi-types'; -import {CredentialStatus} from '@sphereon/ui-components.core'; -import {selectAppLocaleBranding} from '../../../services/brandingService'; -import {ICredentialDetailsRow, ICredentialSummary} from '../../../types'; -import {getCredentialStatus, translateCorrelationIdToName} from '../../CredentialUtils'; -import {EPOCH_MILLISECONDS} from '../../DateUtils'; -import {getImageSize, isImage} from '../../ImageUtils'; -import {mapLanguageValues} from '@sphereon/ssi-types'; - -const toCredentialDetailsRow = async (object: Record): Promise => { - let rows: ICredentialDetailsRow[] = []; - const mappedValues = mapLanguageValues(object); - // eslint-disable-next-line prefer-const - for (let [key, value] of Object.entries(mappedValues)) { - // TODO fix hacking together the image - if (key.toLowerCase().includes('image')) { - const image = typeof value === 'string' ? value : value.id; - rows.push({ - id: uuidv4(), - label: 'image', - value: image, - imageSize: (await isImage(image)) ? await getImageSize(image) : undefined, - }); - continue; - } else if (key === 'type') { - rows.push({ - id: uuidv4(), - label: key, - value: value, - }); - continue; - } - - if (typeof value === 'object') { - rows.push({ - id: uuidv4(), - label: key, - value: undefined, - }); - rows = rows.concat(await toCredentialDetailsRow(value)); - } else if (typeof value === 'number' || typeof value === 'boolean') { - console.log(`==>${key}:${value}`); - let label: string = key; - rows.push({ - id: uuidv4(), - label, // TODO Human readable mapping - value, - }); - } else { - console.log(`==>${key}:${value}`); - if (key === '0' || value === undefined) { - continue; - } - - let label: string = key; - if (key === 'id' && value.startsWith('did:')) { - label = 'subject'; - } - - if (value.startsWith('did:')) { - console.log(`did: ${value}`); - value = translateCorrelationIdToName(value); - } - - rows.push({ - id: uuidv4(), - label, // TODO Human readable mapping - value, - imageSize: (await isImage(value)) ? await getImageSize(value) : undefined, - }); - } - } - - return rows; -}; - -/** - * To be used whenever we need to show a credential summary on VCs we have not persisted - * @param verifiableCredential - * @param branding The branding for the credential - * @param contact Optional contact for issuer name - */ -export const toNonPersistedCredentialSummary = ( - verifiableCredential: ICredential | VerifiableCredential, - branding?: Array, - contact?: Party, -): Promise => { - return toCredentialSummary( - { - verifiableCredential: verifiableCredential as VerifiableCredential, - hash: verifiableCredential.id ?? computeEntryHash(verifiableCredential as VerifiableCredential), - }, - branding, - contact, - ); -}; - -/** - * Map persisted (Unique) VCs to the summaries we display - * @param hash The hash of the unique verifiable credential - * @param verifiableCredential The VC itself - * @param branding The branding for the credential - * @param contact Optional contact for issuer name - */ -export const toCredentialSummary = async ( - {hash, verifiableCredential}: UniqueVerifiableCredential, - branding?: Array, - contact?: Party, -): Promise => { - const expirationDate: number = verifiableCredential.expirationDate - ? new Date(verifiableCredential.expirationDate).valueOf() / EPOCH_MILLISECONDS - : 0; - const issueDate: number = new Date(verifiableCredential.issuanceDate).valueOf() / EPOCH_MILLISECONDS; - const credentialStatus: CredentialStatus = getCredentialStatus(verifiableCredential); - const title = verifiableCredential.name - ? verifiableCredential.name - : !verifiableCredential.type - ? 'unknown' - : typeof verifiableCredential.type === 'string' - ? verifiableCredential.type - : verifiableCredential.type.filter((value: string): boolean => value !== 'VerifiableCredential')[0]; - const properties: Array = await toCredentialDetailsRow(verifiableCredential.credentialSubject); - const localeBranding: IBasicCredentialLocaleBranding | undefined = await selectAppLocaleBranding({localeBranding: branding}); - const logo: string | undefined = getIssuerLogo(verifiableCredential, localeBranding); - const url = typeof verifiableCredential.issuer !== 'string' ? verifiableCredential.issuer.url : undefined; - const name: string = - typeof verifiableCredential.issuer === 'string' - ? verifiableCredential.issuer - : verifiableCredential.issuer?.name ?? verifiableCredential.issuer?.id; - - let issuerAlias: string | undefined = undefined; - if (typeof verifiableCredential?.issuer === 'object' && verifiableCredential.issuer?.name) { - // if the name is part of the VC itself, always use that - // todo: Probably still wise to allow a user to override it - issuerAlias = verifiableCredential.issuer.name; - } - if (!issuerAlias) { - issuerAlias = contact - ? contact.contact.displayName - : typeof verifiableCredential.issuer === 'string' - ? translateCorrelationIdToName(verifiableCredential.issuer) - : translateCorrelationIdToName(verifiableCredential.issuer?.id); - } - if (!issuerAlias) { - throw Error(`Could not deduce issuer alias`); - } - return { - hash, - id: verifiableCredential.id, - title: localeBranding?.alias || title, - credentialStatus, - issueDate, - expirationDate, - properties, - branding: localeBranding, - issuer: { - name, - alias: issuerAlias.length > 50 ? `${issuerAlias.substring(0, 50)}...` : issuerAlias, - image: logo, - url, - }, - }; -}; - -export function getIssuerLogo(verifiableCredential: VerifiableCredential | ICredentialSummary, localeBranding?: IBasicCredentialLocaleBranding) { - let logo: string | undefined; - - if (localeBranding?.logo) { - logo = localeBranding.logo.dataUri ?? localeBranding.logo.uri; - } - if (!logo && typeof verifiableCredential.issuer === 'object') { - if ('logo' in verifiableCredential.issuer && verifiableCredential.issuer.logo) { - logo = getImageFromObjectOrString(verifiableCredential.issuer.logo); - } else if (verifiableCredential.issuer.image) { - logo = getImageFromObjectOrString(verifiableCredential.issuer.image); - } - } - return logo; -} - -export function getImageFromObjectOrString(image?: string | object): string | undefined { - if (!image) { - return undefined; - } else if (typeof image === 'string' && image.length > 0) { - return image; - } else if (typeof image === 'object') { - if ('id' in image && typeof image.id === 'string' && image.id.includes('://')) { - return image.id; - } else if ('url' in image && typeof image.url === 'string' && image.url.includes('://')) { - return image.url; - } else if ('uri' in image && typeof image.uri === 'string' && image.uri.includes('://')) { - return image.uri; - } - } - return undefined; -} diff --git a/yarn.lock b/yarn.lock index 6b3b0829..2f8ef417 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3544,7 +3544,7 @@ debug "^4.3.4" typeorm "^0.3.20" -"@sphereon/ssi-sdk.core@0.25.0", "@sphereon/ssi-sdk.core@0.26.0": +"@sphereon/ssi-sdk.core@0.25.0", "@sphereon/ssi-sdk.core@0.26.0", "@sphereon/ssi-sdk.core@0.26.1-next.6": version "0.26.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-sdk.core/-/ssi-sdk.core-0.26.0.tgz#caa966b161862a14f97af526fbedc98b925d13e8" integrity sha512-H5/JLnf6+YsPNWribe6Meyi+sM+vmLoOyGeZGHoNVkxj7rsnw81kA47PUUbzTbxWwRgBQqt1c5Kz8cL+TZr8Bg== @@ -3556,7 +3556,7 @@ image-size "^2.0.0-beta.2" uint8arrays "3.1.1" -"@sphereon/ssi-sdk.data-store@0.26.0": +"@sphereon/ssi-sdk.data-store@0.26.0", "@sphereon/ssi-sdk.data-store@0.26.1-next.6": version "0.26.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-sdk.data-store/-/ssi-sdk.data-store-0.26.0.tgz#50fccafe296fa99991ecb1b5a0a64002bf47f24e" integrity sha512-QVr1TCSo0NUtnz3S9FZzcqkwNYGB/iISCtJPj9/thGsIxFDvuErRrF/xc6Cl8EncjkWZvffuZyWxQeoSPjQ8BQ== @@ -3756,7 +3756,7 @@ uuid "^9.0.1" xstate "^4.38.3" -"@sphereon/ssi-types@0.22.0", "@sphereon/ssi-types@0.25.0", "@sphereon/ssi-types@0.25.1-unstable.87", "@sphereon/ssi-types@0.26.0", "@sphereon/ssi-types@^0.9.0": +"@sphereon/ssi-types@0.22.0", "@sphereon/ssi-types@0.25.0", "@sphereon/ssi-types@0.25.1-unstable.87", "@sphereon/ssi-types@0.26.0", "@sphereon/ssi-types@0.26.1-next.6", "@sphereon/ssi-types@^0.9.0": version "0.26.0" resolved "https://registry.yarnpkg.com/@sphereon/ssi-types/-/ssi-types-0.26.0.tgz#7bfb5e0bebc951e1e6978187ff177ec6f8d42df9" integrity sha512-r4JQIN7rnPunEv0HvCFC1ZCc9qlWcegYvhJbMJqSvyFE6VhmT5NNdH9jNV9QetgMa0yo5r3k+TnHNv3nH58Dmg== @@ -3766,23 +3766,37 @@ events "^3.3.0" jwt-decode "^3.1.2" -"@sphereon/ui-components.core@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@sphereon/ui-components.core/-/ui-components.core-0.2.0.tgz#a206d9a5366bd109b99dee16ee2efaad7b89e681" - integrity sha512-CdT4x7cJhaZuKHLaB7u+sG3mAkmtRn9b1MegpmNBep7S8dDqkoz0BrunDIQDToYjHUGoiKtN431QqEZk9smwNg== +"@sphereon/ui-components.core@0.2.1-unstable.44", "@sphereon/ui-components.core@0.2.1-unstable.44+5dc2b62": + version "0.2.1-unstable.44" + resolved "https://registry.yarnpkg.com/@sphereon/ui-components.core/-/ui-components.core-0.2.1-unstable.44.tgz#6a5610f45f93c3f327cb1e04d82f0f8a8a25bda2" + integrity sha512-v0kF4597cAO5IHrUcM0aDWqWOjHug7dYCaN9ezkPN6j2UWNNyJZhp7AflDeQsG4nD0YMIkqhoAyU4fzy/yJfZg== dependencies: i18n-js "^3.8.0" lodash.memoize "^4.1.2" styled-components "^5.3.3" -"@sphereon/ui-components.ssi-react-native@0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@sphereon/ui-components.ssi-react-native/-/ui-components.ssi-react-native-0.2.0.tgz#6477f694ced234a52de0f5b2333295d6ecf1fee0" - integrity sha512-sXaHaKxbJ7LDVpJvbvVek1j5MdbszyY3QDv1PJneGpNrrCaOeqpnlJLF/m/vv/G7ywY3wT2uPZQrZlQqNz53Cw== +"@sphereon/ui-components.credential-branding@0.2.1-unstable.44": + version "0.2.1-unstable.44" + resolved "https://registry.yarnpkg.com/@sphereon/ui-components.credential-branding/-/ui-components.credential-branding-0.2.1-unstable.44.tgz#1936d575970d2467f1db514fbff55bfa33b6b7d9" + integrity sha512-xFPqY+6LUzVwDuAFQ9+IlnUieGDCi+p9rX/U1pfPon8iDtWZbssPbaWL2l3bmmsaGQe3affiyDBEtYR5RDfsTA== + dependencies: + "@sphereon/ssi-sdk.core" "0.26.1-next.6" + "@sphereon/ssi-sdk.data-store" "0.26.1-next.6" + "@sphereon/ssi-types" "0.26.1-next.6" + "@sphereon/ui-components.core" "0.2.1-unstable.44+5dc2b62" + "@veramo/core" "4.2.0" + "@veramo/utils" "4.2.0" + i18n-js "^3.8.0" + uuid "^9.0.1" + +"@sphereon/ui-components.ssi-react-native@0.2.1-unstable.44": + version "0.2.1-unstable.44" + resolved "https://registry.yarnpkg.com/@sphereon/ui-components.ssi-react-native/-/ui-components.ssi-react-native-0.2.1-unstable.44.tgz#577db4fb843c72125de6f33fc9dcfeee339dfdc0" + integrity sha512-1NacHrjZ9mX7ttZY4j034g7Jgf5aVOI5+F4rGsVZgRt00jnZpuzQwEC0Wbzfzd4ng/RCUYb4ZdpCghpItWTt1A== dependencies: "@react-native-community/blur" "^4.4.0" "@react-native-masked-view/masked-view" "0.3.1" - "@sphereon/ui-components.core" "0.2.0" + "@sphereon/ui-components.core" "0.2.1-unstable.44+5dc2b62" expo-linear-gradient "~12.7.2" react-native-fast-image "^8.6.3" react-native-size-matters "^0.4.0"