Skip to content

Commit

Permalink
Added Avatar display & editor.
Browse files Browse the repository at this point in the history
  • Loading branch information
martinheidegger committed Jan 23, 2020
1 parent bea61a6 commit 11a0368
Show file tree
Hide file tree
Showing 79 changed files with 339 additions and 99 deletions.
Binary file added assets/avatar/eyes/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/eyes/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/eyes/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/eyes/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/eyes/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/eyes/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/face/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/face/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/face/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/face/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/face/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/face/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/above/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/above/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/above/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/above/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/above/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/above/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/below/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/below/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/below/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/below/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/below/3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/hair/below/[email protected]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/mouth/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/avatar/mouth/[email protected]
Binary file added assets/avatar/mouth/2.png
Binary file added assets/avatar/mouth/[email protected]
Binary file added assets/avatar/mouth/3.png
Binary file added assets/avatar/mouth/[email protected]
Binary file added assets/avatar/nose/1.png
Binary file added assets/avatar/nose/[email protected]
Binary file added assets/avatar/nose/2.png
Binary file added assets/avatar/nose/[email protected]
Binary file added assets/avatar/nose/3.png
Binary file added assets/avatar/nose/[email protected]
Binary file added assets/avatar/unknown.png
Binary file added assets/avatar/[email protected]
Binary file removed assets/element/avatar/icon-bg.png
Diff not rendered.
Binary file removed assets/element/avatar/[email protected]
Diff not rendered.
Binary file removed assets/element/avatar/[email protected]
Diff not rendered.
Binary file removed assets/element/avatar/[email protected]
Diff not rendered.
Binary file added assets/icon/avatar/placeholder.png
Binary file added assets/icon/avatar/[email protected]
Binary file added assets/icon/avatar/[email protected]
Binary file added assets/icon/avatar/[email protected]
Binary file modified design/design.sketch
Binary file not shown.
63 changes: 60 additions & 3 deletions src/Asset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -151,18 +151,75 @@ export class Slice9 {
const images = new Cache<ImageAsset, ImageSourcePropType>(ImageAsset)

export const Asset = {
avatarEyes1 () {
return images.fetch('avatarEyes1', () => require('../assets/avatar/eyes/1.png'))
},
avatarEyes2 () {
return images.fetch('avatarEyes2', () => require('../assets/avatar/eyes/2.png'))
},
avatarEyes3 () {
return images.fetch('avatarEyes3', () => require('../assets/avatar/eyes/3.png'))
},
avatarFace1 () {
return images.fetch('avatarFace1', () => require('../assets/avatar/face/1.png'))
},
avatarFace2 () {
return images.fetch('avatarFace2', () => require('../assets/avatar/face/2.png'))
},
avatarFace3 () {
return images.fetch('avatarFace3', () => require('../assets/avatar/face/3.png'))
},
avatarHairAbove1 () {
return images.fetch('avatarHairAbove1', () => require('../assets/avatar/hair/above/1.png'))
},
avatarHairAbove2 () {
return images.fetch('avatarHairAbove2', () => require('../assets/avatar/hair/above/2.png'))
},
avatarHairAbove3 () {
return images.fetch('avatarHairAbove3', () => require('../assets/avatar/hair/above/3.png'))
},
avatarHairBelow1 () {
return images.fetch('avatarHairBelow1', () => require('../assets/avatar/hair/below/1.png'))
},
avatarHairBelow2 () {
return images.fetch('avatarHairBelow2', () => require('../assets/avatar/hair/below/2.png'))
},
avatarHairBelow3 () {
return images.fetch('avatarHairBelow3', () => require('../assets/avatar/hair/below/3.png'))
},
avatarMouth1 () {
return images.fetch('avatarMouth1', () => require('../assets/avatar/mouth/1.png'))
},
avatarMouth2 () {
return images.fetch('avatarMouth2', () => require('../assets/avatar/mouth/2.png'))
},
avatarMouth3 () {
return images.fetch('avatarMouth3', () => require('../assets/avatar/mouth/3.png'))
},
avatarNose1 () {
return images.fetch('avatarNose1', () => require('../assets/avatar/nose/1.png'))
},
avatarNose2 () {
return images.fetch('avatarNose2', () => require('../assets/avatar/nose/2.png'))
},
avatarNose3 () {
return images.fetch('avatarNose3', () => require('../assets/avatar/nose/3.png'))
},
avatarUnknown () {
return images.fetch('avatarUnknown', () => require('../assets/avatar/unknown.png'))
},
buttonAddHexagonal () {
return images.fetch('buttonAddHexagonal', () => require('../assets/button/add/hexagonal.png'))
},
buttonAddRound () {
return images.fetch('buttonAddRound', () => require('../assets/button/add/round.png'))
},
elementAvatarIconBg () {
return images.fetch('elementAvatarIconBg', () => require('../assets/element/avatar/icon-bg.png'))
},
elementCardVaultBackground () {
return images.fetch('elementCardVaultBackground', () => require('../assets/element/card/vault/background.png'))
},
iconAvatarPlaceholder () {
return images.fetch('iconAvatarPlaceholder', () => require('../assets/icon/avatar/placeholder.png'))
},
iconBackGrey () {
return images.fetch('iconBackGrey', () => require('../assets/icon/back/grey.png'))
},
Expand Down
6 changes: 6 additions & 0 deletions src/model/Consento.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ import { IHandshakeAcceptMessage, IAPI, ISuccessNotification } from '@consento/a
import { Receiver } from './Connection'
import { BaseModel, findParent } from 'mobx-keystone'

export interface IRelationEntry {
readonly humanId: string
readonly name: string
readonly avatarId: string
}

export interface ISubscription {
receiver: Receiver
action: (notification: ISuccessNotification, api: IAPI) => void
Expand Down
10 changes: 9 additions & 1 deletion src/model/Consentos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ export class ConsentoBecomeLockee extends Model({
_lock = observable.box<boolean>(false)

@computed get relationName (): string {
return this.relation.maybeCurrent?.name ?? ''
return this.relation.maybeCurrent?.name
}

@computed get relationAvatarId (): string {
return this.relation.maybeCurrent?.avatarId
}

@computed get relationHumanId (): string {
Expand Down Expand Up @@ -172,6 +176,10 @@ export class ConsentoUnlockVault extends ExtendedModel(RequestBase, {
return this.becomeUnlockee.current.relationHumanId
}

@computed get relationAvatarId (): string {
return this.becomeUnlockee.current.relationAvatarId
}

get vaultName (): string {
return this.becomeUnlockee.current.vaultName
}
Expand Down
10 changes: 9 additions & 1 deletion src/model/Relation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { model, tProp, Model, types, modelAction } from 'mobx-keystone'
import { IConnection } from '@consento/api'
import { Connection, fromIConnection } from './Connection'
import { humanModelId } from '../util/humanModelId'
import { randomAvatarId } from '../screens/components/Avatar'
import { ISortable } from '../util/compareNames'
import { IRelationEntry } from './Consento.types'

// eslint-disable-next-line @typescript-eslint/require-await
export function fromConnection (connection: IConnection): Relation {
Expand All @@ -15,8 +18,9 @@ export function fromConnection (connection: IConnection): Relation {
@model('consento/Relation')
export class Relation extends Model({
name: tProp(types.string, () => ''),
avatarId: tProp(types.maybeNull(types.string), () => null),
connection: tProp(types.model<Connection>(Connection))
}) {
}) implements ISortable, IRelationEntry {
@computed get displayName (): string {
if (this.name === '') {
return this.humanId
Expand All @@ -35,4 +39,8 @@ export class Relation extends Model({
@modelAction setName (name: string): void {
this.name = name
}

@modelAction setAvatarId (avatarId: string): void {
this.avatarId = avatarId
}
}
7 changes: 5 additions & 2 deletions src/model/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,9 @@ import { mobxPersist } from '../util/mobxPersist'
import { compareNames, ISortable } from '../util/compareNames'
import { VaultLockee } from './VaultData'
import { ISuccessNotification, IAPI } from '@consento/api'
import { ISubscriptionMap, Message, MessageType } from './Consento.types'
import { ISubscriptionMap, Message, MessageType, IRelationEntry } from './Consento.types'
import { Buffer } from 'buffer'
import { mapSubscriptions } from './mapSubscriptions'
import { IRelationEntry } from '../screens/components/RelationListEntry'

const ASSUMED_SAFETY_DELAY: number = 1000 // Lets count off a second for network overhead

Expand Down Expand Up @@ -58,6 +57,10 @@ export class Lockee implements IRelationEntry, ISortable {
this.vaultLockee = vaultLockee
}

get avatarId (): string {
return this.relation?.avatarId
}

get sortBy (): string {
return this.relation?.name ?? this.humanId
}
Expand Down
1 change: 1 addition & 0 deletions src/model/VaultStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export class VaultStore extends Model({
for (const dataKeyHex of toDelete) {
this.vaults.delete(dataKeyHex)
const disposer = this._disposersByKeyHex[dataKeyHex]
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete this._disposersByKeyHex[dataKeyHex]
disposer()
}
Expand Down
7 changes: 5 additions & 2 deletions src/screens/Consentos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { elementConsentosLockeeIdle } from '../styles/component/elementConsentos
import { map } from '../util/map'
import { ConsentoContext } from '../model/Consento'
import { useHumanSince } from '../util/useHumanSince'
import { Avatar } from './components/Avatar'

const cardMargin = screen02Consentos.b.place.top - screen02Consentos.a.place.bottom

Expand Down Expand Up @@ -75,8 +76,9 @@ const BecomeLockee = observer(({ consento }: { consento: ConsentoBecomeLockee })
</Svg>
<elementConsentosLockeeIdle.vaultIcon.Render />
<elementConsentosLockeeIdle.lastAccess.Render value={useHumanSince(consento.creationTime)} />
<elementConsentosLockeeIdle.relationName.Render value={consento.relationName} />
<elementConsentosLockeeIdle.relationName.Render value={consento.relationName !== '' ? consento.relationName : null} />
<elementConsentosLockeeIdle.relationID.Render value={consento.relationHumanId} />
<Avatar place={elementConsentosLockeeIdle.avatar.place} avatarId={consento.relationAvatarId} />
<elementConsentosLockeeIdle.question.Render />
<elementConsentosLockeeIdle.vaultName.Render value={consento.vaultName} />
<ConsentoState state={consento.state} onAccept={consento.handleAccept} onDelete={consento.handleDelete} style={elementConsentosLockeeIdle.state.place.style()} />
Expand All @@ -86,9 +88,10 @@ const BecomeLockee = observer(({ consento }: { consento: ConsentoBecomeLockee })
const UnlockVault = observer(({ consento }: { consento: ConsentoUnlockVault }) => {
return <View style={accessCardStyle}>
<elementConsentosBase.lastAccess.Render value={useHumanSince(consento.time)} />
<elementConsentosBase.relationName.Render value={consento.relationName} style={{ ...elementConsentosBase.relationName.place.size(), backgroundColor: '#00000000', position: 'relative' }} />
<elementConsentosBase.relationName.Render value={consento.relationName !== '' ? consento.relationName : null} />
<elementConsentosBase.relationID.Render value={consento.relationHumanId} />
<elementConsentosBase.actionRequested.Render />
<Avatar place={elementConsentosBase.avatar.place} avatarId={consento.relationAvatarId} />
<elementConsentosBase.vaultIcon.Render />
<elementConsentosBase.vaultName.Render value={consento.vaultName} />
<ConsentoState state={consento.state} onAccept={consento.handleAccept} onDelete={consento.handleDelete} style={elementConsentosAccessAccepted.state.place.style()} />
Expand Down
3 changes: 2 additions & 1 deletion src/screens/NewRelation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const NewRelation = withNavigation(({ navigation }: { navigation: TNaviga
...cutOutRect
}

// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
const cutOutG = `M0 0 L0 ${camSpace.height} L${camSpace.width} ${camSpace.height} L${camSpace.width} 0 z M${cutOut.left} ${cutOut.top} L${cutOut.left} ${cutOutRect.top + cutOutRect.height} L${cutOutRect.left + cutOutRect.width} ${cutOutRect.top + cutOutRect.height} L${cutOutRect.left + cutOutRect.width} ${cutOut.top} z`

function onCode (code: BarCodeScanningResult): void {
Expand All @@ -100,7 +101,7 @@ export const NewRelation = withNavigation(({ navigation }: { navigation: TNaviga
return <View style={{ width: '100%', height: '100%', display: 'flex', flexDirection: isHorz ? 'row' : 'column', backgroundColor: screen09ScanQRCode.backgroundColor }}>
<Text style={{ position: 'absolute', borderRadius: 20, top: inset.top, padding: 10, backgroundColor: '#fff', color: '#000', zIndex: 10 }}>{String(exists(outgoing) ? outgoing.state : '')}</Text>
<CameraContainer style={camSpace} onCode={onCode}>
<Svg viewBox={`0 0 ${camSpace.width} ${camSpace.height}`} style={{ ...camSpace, position: 'absolute' }}>
<Svg viewBox={`0 0 ${camSpace.width.toString()} ${camSpace.height.toString()}`} style={{ ...camSpace, position: 'absolute' }}>
<Path fill={screen09ScanQRCode.shadow.fill.color} d={cutOutG} fillRule='evenodd' />
</Svg>
<View style={cutOutContainer}>
Expand Down
41 changes: 40 additions & 1 deletion src/screens/Relation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import { InputField } from './components/InputField'
import { useForm } from '../util/useForm'
import { Relation as RelationModel } from '../model/Relation'
import { ConsentoContext } from '../model/Consento'
import { TouchableOpacity, TouchableWithoutFeedback } from 'react-native-gesture-handler'
import { exists } from '../util/exists'
import { Avatar, randomAvatarId } from './components/Avatar'

function confirmDelete (user: User, relation: RelationModel, navigation: TNavigation): void {
Alert.alert(
Expand All @@ -29,14 +32,20 @@ function confirmDelete (user: User, relation: RelationModel, navigation: TNaviga
)
}

const avatar = elementRelationName.elementAvatarGenerate.component

export const Relation = withNavigation(observer(({ navigation }: { navigation: TNavigation }): JSX.Element => {
const { relation } = useContext(RelationContext)
const { user } = useContext(ConsentoContext)
const { leave, save, useField } = useForm(
navigation,
fields => relation.setName(fields.name)
fields => {
relation.setName(fields.name ?? '')
relation.setAvatarId(fields.avatarId)
}
)
const name = useField('name', relation.name)
const avatarId = useField('avatarId', relation.avatarId)
return <View style={{ flex: 1 }}>
<TopNavigation
title={relation.displayName}
Expand All @@ -51,6 +60,36 @@ export const Relation = withNavigation(observer(({ navigation }: { navigation: T
defaultValue={relation.humanId}
onEdit={name.handleValue}
/>
<View style={{ width: '100%', display: 'flex', alignItems: 'center' }}>
<View style={{
position: 'relative',
marginTop: elementRelationName.elementAvatarGenerate.place.top - elementRelationName.relationName.place.bottom,
width: avatar.width,
height: avatar.height
}}>
{
exists(avatarId.value)
? <Avatar avatarId={avatarId.value} place={avatar.avatar.place} />
: <avatar.placeholder.Render />
}
<avatar.label.Render />
<TouchableOpacity style={{ width: '100%', height: '100%', top: 0, left: 0 }} onPress={() => avatarId.setValue(randomAvatarId())} />
{
exists(avatarId.value)
? <View style={{ position: 'absolute', ...avatar.crossToucharea.place.style() }}>
<TouchableOpacity style={{ width: '100%', height: '100%' }} onPress={() => avatarId.setValue(null)}>
{
avatar.crossIcon.img({
marginTop: avatar.crossIcon.place.left - avatar.crossToucharea.place.left,
marginBottom: avatar.crossIcon.place.top - avatar.crossToucharea.place.top
})
}
</TouchableOpacity>
</View>
: null
}
</View>
</View>
</BottomButtonView>
</View>
}))
72 changes: 72 additions & 0 deletions src/screens/components/Avatar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react'
import Svg, { Image, Circle } from 'react-native-svg'
import { Asset, ImageAsset } from '../../Asset'
import { Buffer } from 'buffer'
import { bufferToString } from '@consento/crypto/util/buffer'
import { Placement } from '../../styles/Component'
import { Color } from '../../styles/Color'
import { exists } from '../../util/exists'

const hairs = [
{ below: Asset.avatarHairBelow1, above: Asset.avatarHairAbove1 },
{ below: Asset.avatarHairBelow2, above: Asset.avatarHairAbove2 },
{ below: Asset.avatarHairBelow3, above: Asset.avatarHairAbove3 }
]

function matchingProperties <T> (item: any, reg: RegExp): T[] {
return Object.keys(item).filter(key => reg.test(key)).map(key => item[key])
}

const faces = matchingProperties <() => ImageAsset>(Asset, /^avatarFace/)
const noses = matchingProperties <() => ImageAsset>(Asset, /^avatarNose/)
const mouths = matchingProperties <() => ImageAsset>(Asset, /^avatarMouth/)
const eyes = matchingProperties <() => ImageAsset>(Asset, /^avatarEyes/)
const colors = matchingProperties <string>(Color, /^avatar/)

function oneOf (list: any[]): number {
const index = (list.length * Math.random()) | 0
return index % list.length
}

export function randomAvatarId (): string {
const buffer = Buffer.alloc(13)
buffer[0] = 1
buffer.writeUInt16LE(oneOf(hairs), 1)
buffer.writeUInt16LE(oneOf(faces), 3)
buffer.writeUInt16LE(oneOf(noses), 5)
buffer.writeUInt16LE(oneOf(mouths), 7)
buffer.writeUInt16LE(oneOf(eyes), 9)
buffer.writeUInt16LE(oneOf(colors), 11)
return bufferToString(buffer, 'hex')
}

const combinations = hairs.length * faces.length * noses.length * mouths.length * eyes.length * colors.length

console.log(`${combinations.toString()} possible Avatar combinations`)

export interface IAvatarProps {
avatarId: string
place: Placement
}

export const Avatar = ({ avatarId, place }: IAvatarProps): JSX.Element => {
if (!exists(avatarId)) {
return Asset.avatarUnknown().img({ ...place.style(), position: 'absolute' })
}
const buffer = Buffer.from(avatarId, 'hex')
const hair = hairs[buffer.readUInt16LE(1)]
const face = faces[buffer.readUInt16LE(3)]
const nose = noses[buffer.readUInt16LE(5)]
const mouth = mouths[buffer.readUInt16LE(7)]
const eye = eyes[buffer.readUInt16LE(9)]
const color = colors[buffer.readUInt16LE(11)]
return <Svg width={place.width} height={place.height} viewBox='0 0 200 200' style={{ position: 'absolute', left: place.left, top: place.top }}>
<Circle cx={100} cy={100} r={100} fill={color} />
<Image href={hair.below().source} />
<Image href={face().source} />
<Image href={eye().source} />
<Image href={nose().source} />
<Image href={mouth().source} />
<Image href={hair.above().source} />
</Svg>
}
3 changes: 2 additions & 1 deletion src/screens/components/Locks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ import { elementRelationSelectListDisplay } from '../../styles/component/element
import { VaultContext } from '../../model/VaultContext'
import { withNavigation, TNavigation } from '../navigation'
import { BottomButtonView } from './BottomButtonView'
import { RelationListEntry, IRelationListEntryProps, IRelationEntry } from './RelationListEntry'
import { RelationListEntry, IRelationListEntryProps } from './RelationListEntry'
import { Relation } from '../../model/Relation'
import { ConsentoContext } from '../../model/Consento'
import { Lockee } from '../../model/User'
import { IRelationEntry } from '../../model/Consento.types'

export interface ILocksProps {
navigation: TNavigation
Expand Down
Loading

0 comments on commit 11a0368

Please sign in to comment.