@ -26,8 +26,20 @@
didInfoForContact ( viewingDid , activeDid , contact , allMyDids )
didInfoForContact ( viewingDid , activeDid , contact , allMyDids )
. displayName
. displayName
} }
} }
< button
@ click = "
contactEdit = true ;
contactNewName = contact . name || '' ;
"
title = "Edit"
>
< fa icon = "pen" class = "text-sm text-blue-500 ml-2 mb-1" / >
< / button >
< / h2 >
< / h2 >
< button @ click = "showDidDetails = !showDidDetails" class = "ml-2 mr-2" >
< button
@ click = "showDidDetails = !showDidDetails"
class = "ml-2 mr-2 mt-4"
>
Details
Details
< fa v -if = " showDidDetails " icon = "chevron-up" class = "text-blue-400" / >
< fa v -if = " showDidDetails " icon = "chevron-up" class = "text-blue-400" / >
< fa v -else icon = "chevron-down" class = "text-blue-400" / >
< fa v -else icon = "chevron-down" class = "text-blue-400" / >
@ -49,8 +61,68 @@
/ >
/ >
< / span >
< / span >
< / div >
< / div >
< div class = "mt-4" >
< div class = "flex justify-between mt-4" >
< div class = "flex justify-center" > Auto - Generated Icon : < / div >
< div class = "flex items-center" >
< div v-if ="activeDid" class="flex justify-between" >
< div >
< button
v - if = "contact?.seesMe && contact.did !== activeDid"
class = "text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
@ click = "confirmSetVisibility(contact, false)"
title = "They can see you"
>
< fa icon = "eye" class = "fa-fw" / >
< / button >
< button
v - else - if = "!contact?.seesMe && contact?.did !== activeDid"
class = "text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
@ click = "confirmSetVisibility(contact, true)"
title = "They cannot see you"
>
< fa icon = "eye-slash" class = "fa-fw" / >
< / button >
<!-- otherwise it ' s this user so hide it -- >
< fa v -else icon = "eye" class = "text-white mx-2.5" / >
< button
class = "text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
@ click = "checkVisibility(contact)"
title = "Check Visibility"
v - if = "contact?.did !== activeDid"
>
< fa icon = "rotate" class = "fa-fw" / >
< / button >
<!-- otherwise it ' s this user so hide it -- >
< fa v -else icon = "rotate" class = "text-white mx-2.5" / >
< / div >
< button
@ click = "confirmRegister(contact)"
class = "text-sm uppercase bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
v - if = "contact?.did !== activeDid"
title = "Registration"
>
< fa
v - if = "contact?.registered"
icon = "person-circle-check"
class = "fa-fw"
/ >
< fa v -else icon = "person-circle-question" class = "fa-fw" / >
< / button >
<!-- otherwise it ' s this user so hide it -- >
< fa v -else icon = "rotate" class = "text-white ml-6 px-2.5" / >
< / div >
< button
@ click = "confirmDeleteContact(contact)"
class = "text-sm uppercase bg-gradient-to-b from-rose-500 to-rose-800 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white ml-6 mx-0.5 my-0.5 px-2 py-1.5 rounded-md"
title = "Delete"
>
< fa icon = "trash-can" class = "fa-fw" / >
< / button >
< / div >
< div v-if ="!contact?.profileImageUrl" >
< div > Auto - Generated Icon < / div >
< div class = "flex justify-center" >
< div class = "flex justify-center" >
< EntityIcon
< EntityIcon
: entityId = "viewingDid"
: entityId = "viewingDid"
@ -60,6 +132,7 @@
/ >
/ >
< / div >
< / div >
< / div >
< / div >
< / div >
< div
< div
v - if = "showLargeIdenticonId || showLargeIdenticonUrl"
v - if = "showLargeIdenticonId || showLargeIdenticonUrl"
class = "fixed z-[100] top-0 inset-x-0 w-full"
class = "fixed z-[100] top-0 inset-x-0 w-full"
@ -80,6 +153,32 @@
< / div >
< / div >
< / div >
< / div >
< / div >
< / div >
< div v-if ="contactEdit" class="dialog-overlay" >
< div class = "dialog" >
< h1 class = "text-xl font-bold text-center mb-4" > Edit Name < / h1 >
< input
type = "text"
class = "block w-full rounded border border-slate-400 mb-2 px-3 py-2"
placeholder = "Name"
v - model = "contactNewName"
/ >
< div class = "flex justify-between" >
< button
class = "text-sm bg-blue-600 text-white px-2 py-1.5 rounded -ml-1.5 border-l border-blue-400"
@ click = "onClickSaveName(contactNewName)"
>
< fa icon = "save" / >
< / button >
< span class = "inline-block w-2" / >
< button
class = "text-sm bg-blue-600 text-white px-2 py-1.5 rounded -ml-1.5 border-l border-blue-400"
@ click = "onClickCancelName()"
>
< fa icon = "ban" / >
< / button >
< / div >
< / div >
< / div >
<!-- Loading Animation -- >
<!-- Loading Animation -- >
< div
< div
@ -126,15 +225,16 @@
v - if = "!isLoading && claims.length === 0"
v - if = "!isLoading && claims.length === 0"
class = "flex justify-center mt-4"
class = "flex justify-center mt-4"
>
>
< span > They Are in No Claims Visible to You < / span >
< span > They are in no claims visible to you . < / span >
< / div >
< / div >
< / section >
< / section >
< / template >
< / template >
< script lang = "ts" >
< script lang = "ts" >
import { AxiosError } from "axios" ;
import * as yaml from "js-yaml" ;
import { Component , Vue } from "vue-facing-decorator" ;
import { Component , Vue } from "vue-facing-decorator" ;
import { Router } from "vue-router" ;
import { Router } from "vue-router" ;
import * as yaml from "js-yaml" ;
import QuickNav from "@/components/QuickNav.vue" ;
import QuickNav from "@/components/QuickNav.vue" ;
import InfiniteScroll from "@/components/InfiniteScroll.vue" ;
import InfiniteScroll from "@/components/InfiniteScroll.vue" ;
@ -152,6 +252,8 @@ import {
GenericVerifiableCredential ,
GenericVerifiableCredential ,
GiveVerifiableCredential ,
GiveVerifiableCredential ,
OfferVerifiableCredential ,
OfferVerifiableCredential ,
register ,
setVisibilityUtil ,
} from "@/libs/endorserServer" ;
} from "@/libs/endorserServer" ;
import * as libsUtil from "@/libs/util" ;
import * as libsUtil from "@/libs/util" ;
import EntityIcon from "@/components/EntityIcon.vue" ;
import EntityIcon from "@/components/EntityIcon.vue" ;
@ -174,7 +276,9 @@ export default class DIDView extends Vue {
allMyDids : Array < string > = [ ] ;
allMyDids : Array < string > = [ ] ;
apiServer = "" ;
apiServer = "" ;
claims : Array < GenericCredWrapper < GenericVerifiableCredential > > = [ ] ;
claims : Array < GenericCredWrapper < GenericVerifiableCredential > > = [ ] ;
contact ? : Contact ;
contact : Contact ;
contactEdit = false ;
contactNewName ? : string ;
contactYaml = "" ;
contactYaml = "" ;
hitEnd = false ;
hitEnd = false ;
isLoading = false ;
isLoading = false ;
@ -195,23 +299,29 @@ export default class DIDView extends Vue {
this . apiServer = ( settings ? . apiServer as string ) || "" ;
this . apiServer = ( settings ? . apiServer as string ) || "" ;
const pathParam = window . location . pathname . substring ( "/did/" . length ) ;
const pathParam = window . location . pathname . substring ( "/did/" . length ) ;
let theContact : Contact | undefined ;
if ( pathParam ) {
if ( pathParam ) {
this . viewingDid = decodeURIComponent ( pathParam ) ;
this . viewingDid = decodeURIComponent ( pathParam ) ;
this . contact = await db . contacts . get ( this . viewingDid ) ;
theContact = await db . contacts . get ( this . viewingDid ) ;
this . contactYaml = yaml . dump ( this . contact ) ;
}
await this . loadClaimsAbout ( ) ;
if ( theContact ) {
this . contact = theContact ;
} else {
} else {
this . $notify (
this . $notify (
{
{
group : "alert" ,
group : "alert" ,
type : "danger" ,
type : "danger" ,
title : "Error" ,
title : "Error" ,
text : "No claim ID was provided." ,
text : "No valid claim ID was provided." ,
} ,
} ,
- 1 ,
- 1 ,
) ;
) ;
return ;
}
}
this . contactYaml = yaml . dump ( this . contact ) ;
await this . loadClaimsAbout ( ) ;
await accountsDB . open ( ) ;
await accountsDB . open ( ) ;
const allAccounts = await accountsDB . accounts . toArray ( ) ;
const allAccounts = await accountsDB . accounts . toArray ( ) ;
this . allMyDids = allAccounts . map ( ( acc ) => acc . did ) ;
this . allMyDids = allAccounts . map ( ( acc ) => acc . did ) ;
@ -227,6 +337,128 @@ export default class DIDView extends Vue {
}
}
}
}
/ / p r o m p t w i t h c o n f i r m a t i o n i f t h e y w a n t t o d e l e t e a c o n t a c t
confirmDeleteContact ( contact : Contact ) {
this . $notify (
{
group : "modal" ,
type : "confirm" ,
title : "Delete" ,
text :
"Are you sure you want to remove " +
libsUtil . nameForContact ( contact , false ) +
" from your contact list?" ,
onYes : async ( ) => {
await this . deleteContact ( contact ) ;
} ,
} ,
- 1 ,
) ;
}
async deleteContact ( contact : Contact ) {
await db . open ( ) ;
await db . contacts . delete ( contact . did ) ;
this . $notify (
{
group : "alert" ,
type : "success" ,
title : "Deleted" ,
text : "Contact has been removed." ,
} ,
3000 ,
) ;
( this . $router as Router ) . push ( { name : "contacts" } ) ;
}
/ / c o n f i r m t o r e g i s t e r a n e w c o n t a c t
async confirmRegister ( contact : Contact ) {
this . $notify (
{
group : "modal" ,
type : "confirm" ,
title : "Register" ,
text :
"Are you sure you want to register " +
libsUtil . nameForContact ( this . contact , false ) +
( contact . registered
? " -- especially since they are already marked as registered"
: "" ) +
"?" ,
onYes : async ( ) => {
await this . register ( contact ) ;
} ,
} ,
- 1 ,
) ;
}
/ / n o t e t h a t t h i s i s a l s o i n C o n t a c t V i e w . v u e
async register ( contact : Contact ) {
this . $notify ( { group : "alert" , type : "toast" , title : "Sent..." } , 1000 ) ;
try {
const regResult = await register (
this . activeDid ,
this . apiServer ,
this . axios ,
contact ,
) ;
if ( regResult . success ) {
contact . registered = true ;
await db . contacts . update ( contact . did , { registered : true } ) ;
this . $notify (
{
group : "alert" ,
type : "success" ,
title : "Registration Success" ,
text :
( contact . name || "That unnamed person" ) + " has been registered." ,
} ,
5000 ,
) ;
} else {
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "Registration Error" ,
text :
( regResult . error as string ) ||
"Something went wrong during registration." ,
} ,
5000 ,
) ;
}
} catch ( error ) {
console . error ( "Error when registering:" , error ) ;
let userMessage = "There was an error. See logs for more info." ;
const serverError = error as AxiosError ;
if ( serverError ) {
if ( serverError . response ? . data ? . error ? . message ) {
userMessage = serverError . response . data . error . message ;
} else if ( serverError . message ) {
userMessage = serverError . message ; / / I n f o f o r t h e u s e r
} else {
userMessage = JSON . stringify ( serverError . toJSON ( ) ) ;
}
} else {
userMessage = error as string ;
}
/ / N o w s e t t h a t e r r o r f o r t h e u s e r t o s e e .
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "Registration Error" ,
text : userMessage ,
} ,
5000 ,
) ;
}
}
public async loadClaimsAbout ( ) {
public async loadClaimsAbout ( ) {
if ( ! this . viewingDid ) {
if ( ! this . viewingDid ) {
console . error ( "This should never be called without a DID." ) ;
console . error ( "This should never be called without a DID." ) ;
@ -323,5 +555,178 @@ export default class DIDView extends Vue {
claimDescription ( claim : GenericVerifiableCredential ) {
claimDescription ( claim : GenericVerifiableCredential ) {
return claim . claim . name || claim . claim . description || "" ;
return claim . claim . name || claim . claim . description || "" ;
}
}
private async onClickCancelName ( ) {
this . contactEdit = false ;
}
private async onClickSaveName ( newName : string ) {
this . contact . name = newName ;
return db . contacts
. update ( this . contact . did , { name : newName } )
. then ( ( ) => ( this . contactEdit = false ) ) ;
}
/ / n o t e t h a t t h i s i s a l s o i n C o n t a c t V i e w . v u e
async confirmSetVisibility ( contact : Contact , visibility : boolean ) {
const visibilityPrompt = visibility
? "Are you sure you want to make your activity visible to them?"
: "Are you sure you want to hide all your activity from them?" ;
this . $notify (
{
group : "modal" ,
type : "confirm" ,
title : "Set Visibility" ,
text : visibilityPrompt ,
onYes : async ( ) => {
const success = await this . setVisibility ( contact , visibility , true ) ;
if ( success ) {
contact . seesMe = visibility ; / / d i d n ' t w o r k i n s i d e s e t V i s i b i l i t y
}
} ,
} ,
- 1 ,
) ;
}
/ / n o t e t h a t t h i s i s a l s o i n C o n t a c t V i e w . v u e
async setVisibility (
contact : Contact ,
visibility : boolean ,
showSuccessAlert : boolean ,
) {
const result = await setVisibilityUtil (
this . activeDid ,
this . apiServer ,
this . axios ,
db ,
contact ,
visibility ,
) ;
if ( result . success ) {
/ / c o n t a c t . s e e s M e = v i s i b i l i t y ; / / w h y d o e s n ' t i t a f f e c t t h e U I f r o m h e r e ?
/ / c o n s o l e . l o g ( " S e t r e s u l t & s e e s M e " , r e s u l t , c o n t a c t . s e e s M e , c o n t a c t . d i d ) ;
if ( showSuccessAlert ) {
this . $notify (
{
group : "alert" ,
type : "success" ,
title : "Visibility Set" ,
text :
( contact . name || "That user" ) +
" can " +
( visibility ? "" : "not " ) +
"see your activity." ,
} ,
3000 ,
) ;
}
return true ;
} else {
console . error ( "Got strange result from setting visibility:" , result ) ;
const message =
( result . error as string ) || "Could not set visibility on the server." ;
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "Error Setting Visibility" ,
text : message ,
} ,
5000 ,
) ;
return false ;
}
}
/ / n o t e t h a t t h i s i s a l s o i n C o n t a c t V i e w . v u e
async checkVisibility ( contact : Contact ) {
const url =
this . apiServer +
"/api/report/canDidExplicitlySeeMe?did=" +
encodeURIComponent ( contact . did ) ;
const headers = await getHeaders ( this . activeDid ) ;
if ( ! headers [ "Authorization" ] ) {
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "No Identity" ,
text : "There is no identity to use to check visibility." ,
} ,
3000 ,
) ;
return ;
}
try {
const resp = await this . axios . get ( url , { headers } ) ;
if ( resp . status === 200 ) {
const visibility = resp . data ;
contact . seesMe = visibility ;
/ / c o n s o l e . l o g ( " V i s i c h e c k : " , v i s i b i l i t y , c o n t a c t . s e e s M e , c o n t a c t . d i d ) ;
await db . contacts . update ( contact . did , { seesMe : visibility } ) ;
this . $notify (
{
group : "alert" ,
type : "info" ,
title : "Visibility Refreshed" ,
text :
libsUtil . nameForContact ( contact , true ) +
" can " +
( visibility ? "" : "not " ) +
"see your activity." ,
} ,
3000 ,
) ;
} else {
console . error ( "Got bad server response checking visibility:" , resp ) ;
const message = resp . data . error ? . message || "Got bad server response." ;
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "Error Checking Visibility" ,
text : message ,
} ,
5000 ,
) ;
}
} catch ( err ) {
console . error ( "Caught error from request to check visibility:" , err ) ;
this . $notify (
{
group : "alert" ,
type : "danger" ,
title : "Error Checking Visibility" ,
text : "Check connectivity and try again." ,
} ,
3000 ,
) ;
}
}
}
}
< / script >
< / script >
< style >
. dialog - overlay {
position : fixed ;
top : 0 ;
left : 0 ;
right : 0 ;
bottom : 0 ;
background - color : rgba ( 0 , 0 , 0 , 0.5 ) ;
display : flex ;
justify - content : center ;
align - items : center ;
padding : 1.5 rem ;
}
. dialog {
background - color : white ;
padding : 1 rem ;
border - radius : 0.5 rem ;
width : 100 % ;
max - width : 500 px ;
}
< / style >