@ -5,7 +5,7 @@
<!-- CONTENT -- >
< section id = "Content" class = "p-2 pb-24 max-w-3xl mx-auto" >
< h1 id = "ViewHeading" class = "text-4xl text-center font-light px-4 mb-8" >
Time Safari
{ { AppString . APP_NAME } }
< / h1 >
<!-- prompt to install notifications -- >
@ -79,89 +79,100 @@
<!-- ! isCreatingIdentifier -- >
< div
v - if = "!activeDid"
class = "bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
class = "bg-amber-200 rounded-md text-center px-4 py-3 mb-4"
>
< p class = "text-lg mb-3" >
Want to connect with your contacts , or share contributions or
projects ?
To recognize giving , have someone register you :
< / p >
< router -link
: to = "{ name: 'start' }"
class = "block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
>
Create An Identifier
< / r o u t e r - l i n k >
< / div >
< div
v - else - if = "!isRegistered"
class = "bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
>
<!-- activeDid && ! isRegistered -- >
Someone must register you before you can give kudos or make offers or
create projects ... basically before doing anything .
< router -link
: to = "{ name: 'contact-qr' }"
class = "block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
>
Show Them Your Identifier Info
< / r o u t e r - l i n k >
< div class = "flex justify-center" >
<!-- < button - - >
<!-- class = "block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md" -- >
<!-- @ click = "generateIdentifier()" -- >
<!-- > -- >
<!-- Let me start the easiest ( with a passkey ) . -- >
<!-- < / button > -- >
< router -link
: to = "{ name: 'contact-qr' }"
class = "block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
>
Share your contact info .
< / r o u t e r - l i n k >
< / div >
< / div >
< div v-else >
<!-- activeDid && isRegistered -- >
<!-- show the actions for recognizing a give -- >
< div class = "mb-4" >
< h2 class = "text-xl font-bold" > Record Something Given By : < / h2 >
< / div >
< div v -else class = "mb-4" >
<!-- activeDid -- >
< ul
class = "grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5"
< div
v - if = "!isRegistered"
class = "bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4"
>
< li @click ="openDialog()" >
< img
src = "../assets/blank-square.svg"
class = "mx-auto border border-slate-300 rounded-md mb-1"
/ >
< h3
class = "text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
Unnamed / Unknown
< / h3 >
< / li >
< li
v - for = "contact in allContacts.slice(0, 7)"
: key = "contact.did"
@ click = "openDialog(contact)"
>
< EntityIcon
: contact = "contact"
: iconSize = "64"
class = "mx-auto border border-slate-300 rounded-md mb-1 cursor-pointer"
/ >
< h3
class = "text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
{ { contact . name || contact . did } }
< / h3 >
< / li >
< / ul >
< div class = "flex justify-between" >
<!-- activeDid && ! isRegistered -- >
Someone must register you before you can give kudos or make offers
or create projects ... basically before doing anything .
< router -link
v - if = "allContacts.length >= 7"
: to = "{ name: 'contact-gift' }"
class = "block text-center text-md font-bold bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
: to = "{ name: 'contact-qr' }"
class = "block text-center text-md font-bold bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white mt-2 px-2 py-3 rounded-md"
>
Choose From All Contacts
Show Them Your Identifier Info
< / r o u t e r - l i n k >
< button
@ click = "openGiftedPrompts()"
class = "block text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
< / div >
< div v-else >
<!-- activeDid && isRegistered -- >
<!-- show the actions for recognizing a give -- >
< div class = "mb-4" >
< h2 class = "text-xl font-bold" > Record Something Given By : < / h2 >
< / div >
< ul
class = "grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5"
>
Ideas ...
< / button >
< li @click ="openDialog()" >
< img
src = "../assets/blank-square.svg"
class = "mx-auto border border-slate-300 rounded-md mb-1"
/ >
< h3
class = "text-xs italic font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
Unnamed / Unknown
< / h3 >
< / li >
< li
v - for = "contact in allContacts.slice(0, 7)"
: key = "contact.did"
@ click = "openDialog(contact)"
>
< EntityIcon
: contact = "contact"
: iconSize = "64"
class = "mx-auto border border-slate-300 rounded-md mb-1 cursor-pointer"
/ >
< h3
class = "text-xs font-medium text-ellipsis whitespace-nowrap overflow-hidden"
>
{ { contact . name || contact . did } }
< / h3 >
< / li >
< / ul >
< div class = "flex justify-between" >
< router -link
v - if = "allContacts.length >= 7"
: to = "{ name: 'contact-gift' }"
class = "block text-center text-md font-bold bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-3 rounded-md"
>
Choose From All Contacts
< / r o u t e r - l i n k >
< button
@ click = "openGiftedPrompts()"
class = "block text-center text-md bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-4 py-2 rounded-md"
>
Ideas ...
< / button >
< / div >
< / div >
< / div >
< / div >
@ -305,10 +316,10 @@
< script lang = "ts" >
import { UAParser } from "ua-parser-js" ;
import { IIdentifier } from "@veramo/core" ;
import { Component , Vue } from "vue-facing-decorator" ;
import { Router } from "vue-router" ;
import App from "../App.vue" ;
import EntityIcon from "@/components/EntityIcon.vue" ;
import GiftedDialog from "@/components/GiftedDialog.vue" ;
import GiftedPrompts from "@/components/GiftedPrompts.vue" ;
@ -316,9 +327,8 @@ import FeedFilters from "@/components/FeedFilters.vue";
import InfiniteScroll from "@/components/InfiniteScroll.vue" ;
import QuickNav from "@/components/QuickNav.vue" ;
import TopMessage from "@/components/TopMessage.vue" ;
import { NotificationIface } from "@/constants/app" ;
import { AppString , NotificationIface } from "@/constants/app" ;
import { db , accountsDB } from "@/db/index" ;
import { Account } from "@/db/tables/accounts" ;
import { Contact } from "@/db/tables/contacts" ;
import {
BoundingBox ,
@ -326,17 +336,17 @@ import {
MASTER_SETTINGS_KEY ,
Settings ,
} from "@/db/tables/settings" ;
import { accessToken } from "@/libs/crypto" ;
import {
contactForDid ,
containsNonHiddenDid ,
didInfoForContact ,
fetchEndorserRateLimits ,
getHeaders ,
getPlanFromCache ,
GiverReceiverInputInfo ,
GiveSummaryRecord ,
} from "@/libs/endorserServer" ;
import { generateSaveAndActivateIdentit y } from "@/libs/util" ;
import { registerSaveAndActivatePasske y } from "@/libs/util" ;
interface GiveRecordWithContactInfo extends GiveSummaryRecord {
giver : {
@ -354,6 +364,11 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord {
}
@ Component ( {
computed : {
App ( ) {
return App ;
} ,
} ,
components : {
GiftedDialog ,
GiftedPrompts ,
@ -367,6 +382,8 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord {
export default class HomeView extends Vue {
$notify ! : ( notification : NotificationIface , timeout ? : number ) => void ;
AppString = AppString ;
activeDid = "" ;
allContacts : Array < Contact > = [ ] ;
allMyDids : Array < string > = [ ] ;
@ -374,6 +391,7 @@ export default class HomeView extends Vue {
feedData : GiveRecordWithContactInfo [ ] = [ ] ;
feedPreviousOldestId ? : string ;
feedLastViewedClaimId ? : string ;
givenName = "" ;
isAnyFeedFilterOn : boolean ;
isCreatingIdentifier = false ;
isFeedFilteredByVisible = false ;
@ -387,25 +405,6 @@ export default class HomeView extends Vue {
showShortcutBvc = false ;
userAgentInfo = new UAParser ( ) ; / / s e e h t t p s : / / d o c s . u a p a r s e r . j s . o r g / v 2 / a p i / u a - p a r s e r - j s / g e t - o s . h t m l
public async getIdentity ( activeDid : string ) : Promise < IIdentifier | null > {
await accountsDB . open ( ) ;
const account = ( await accountsDB . accounts
. where ( "did" )
. equals ( activeDid )
. first ( ) ) as Account ;
const identity = JSON . parse ( account ? . identity || "null" ) ;
return identity ; / / m a y b e n u l l
}
public async getHeaders ( identity : IIdentifier ) {
const token = await accessToken ( identity ) ;
const headers = {
"Content-Type" : "application/json" ,
Authorization : "Bearer " + token ,
} ;
return headers ;
}
async mounted ( ) {
try {
await accountsDB . open ( ) ;
@ -418,6 +417,7 @@ export default class HomeView extends Vue {
this . activeDid = settings ? . activeDid || "" ;
this . allContacts = await db . contacts . toArray ( ) ;
this . feedLastViewedClaimId = settings ? . lastViewedClaimId ;
this . givenName = settings ? . firstName || "" ;
this . isFeedFilteredByVisible = ! ! settings ? . filterFeedByVisible ;
this . isFeedFilteredByNearby = ! ! settings ? . filterFeedByNearby ;
this . isRegistered = ! ! settings ? . isRegistered ;
@ -426,21 +426,13 @@ export default class HomeView extends Vue {
this . isAnyFeedFilterOn = isAnyFeedFilterOn ( settings ) ;
if ( this . allMyDids . length === 0 ) {
this . isCreatingIdentifier = true ;
this . activeDid = await generateSaveAndActivateIdentity ( ) ;
this . allMyDids = [ this . activeDid ] ;
this . isCreatingIdentifier = false ;
}
/ / s o m e o n e m a y h a v e h a v e r e g i s t e r e d a f t e r s h a r i n g c o n t a c t i n f o
/ / s o m e o n e m a y h a v e h a v e r e g i s t e r e d a f t e r s h a r i n g c o n t a c t i n f o , s o r e c h e c k
if ( ! this . isRegistered && this . activeDid ) {
const identity = await this . getIdentity ( this . activeDid ) ;
try {
const resp = await fetchEndorserRateLimits (
this . apiServer ,
this . axios ,
identity as IIdentifier ,
this . activeDid ,
) ;
if ( resp . status === 200 ) {
/ / w e j u s t n e e d e d t o k n o w t h a t t h e y ' r e r e g i s t e r e d
@ -475,6 +467,15 @@ export default class HomeView extends Vue {
}
}
async generateIdentifier ( ) {
this . isCreatingIdentifier = true ;
const account = await registerSaveAndActivatePasskey (
AppString . APP_NAME + ( this . givenName ? " - " + this . givenName : "" ) ,
) ;
this . activeDid = account . did ;
this . allMyDids = this . allMyDids . concat ( this . activeDid ) ;
this . isCreatingIdentifier = false ;
}
resultsAreFiltered ( ) {
return this . isFeedFilteredByVisible || this . isFeedFilteredByNearby ;
}
@ -483,26 +484,6 @@ export default class HomeView extends Vue {
return "Notification" in window ;
}
public async buildHeaders ( ) {
const headers : HeadersInit = {
"Content-Type" : "application/json" ,
} ;
const identity = await this . getIdentity ( this . activeDid ) ;
if ( this . activeDid ) {
if ( identity ) {
headers [ "Authorization" ] = "Bearer " + ( await accessToken ( identity ) ) ;
} else {
throw new Error (
"An ID is chosen but there are no keys for it so it cannot be used to talk with the service. Switch your ID." ,
) ;
}
} else {
/ / i t ' s O K w i t h o u t a u t h . . . w e j u s t w o n ' t g e t a n y i d e n t i f i e r s
}
return headers ;
}
/ / o n l y c a l l e d w h e n a s e t t i n g w a s c h a n g e d
async reloadFeedOnChange ( ) {
await db . open ( ) ;
@ -520,7 +501,7 @@ export default class HomeView extends Vue {
* Data loader used by infinite scroller
* @ param payload is the flag from the InfiniteScroll indicating if it should load
* * /
public async loadMoreGives ( payload : boolean ) {
async loadMoreGives ( payload : boolean ) {
/ / S i n c e f e e d n o w l o a d s p r o j e c t s a l o n g t h e w a y , i t t a k e s l o n g e r
/ / a n d t h e I n f i n i t e S c r o l l c o m p o n e n t t r i g g e r s a l o a d b e f o r e f i n i s h e d .
/ / O n e a l t e r n a t i v e i s t o t o t a l l y s e p a r a t e t h e p r o j e c t l i n k l o a d i n g .
@ -542,7 +523,7 @@ export default class HomeView extends Vue {
}
}
public async updateAllFeed ( ) {
async updateAllFeed ( ) {
this . isFeedLoading = true ;
let endOfResults = true ;
await this . retrieveGives ( this . apiServer , this . feedPreviousOldestId )
@ -550,7 +531,6 @@ export default class HomeView extends Vue {
if ( results . data . length > 0 ) {
endOfResults = false ;
/ / i n c l u d e t h e d e s c r i p t i o n s o f t h e g i v e r a n d r e c e i v e r
const identity = await this . getIdentity ( this . activeDid ) ;
for ( const record : GiveSummaryRecord of results . data ) {
/ / s i m i l a r c o d e i s i n e n d o r s e r - m o b i l e u t i l i t y . t s
/ / c l a i m . c l a i m h a p p e n f o r s o m e c l a i m s w r a p p e d i n a V e r i f i a b l e C r e d e n t i a l
@ -567,9 +547,9 @@ export default class HomeView extends Vue {
/ / W e s h o u l d d i s p l a y i t i m m e d i a t e l y a n d t h e n g e t t h e p l a n l a t e r .
const plan = await getPlanFromCache (
record . fulfillsPlanHandleId ,
identity ,
this . axios ,
this . apiServer ,
this . activeDid ,
) ;
/ / c h e c k i f t h e r e c o r d s h o u l d b e f i l t e r e d o u t
@ -650,7 +630,7 @@ export default class HomeView extends Vue {
* @ param beforeId the earliest ID ( of previous searches ) to search earlier
* @ return claims in reverse chronological order
* /
public async retrieveGives ( endorserApiServer : string , beforeId ? : string ) {
async retrieveGives ( endorserApiServer : string , beforeId ? : string ) {
const beforeQuery = beforeId == null ? "" : "&beforeId=" + beforeId ;
const response = await fetch (
endorserApiServer +
@ -658,7 +638,7 @@ export default class HomeView extends Vue {
beforeQuery ,
{
method : "GET" ,
headers : await this . buildHeaders ( ) ,
headers : await getHeaders ( this . activeDid ) ,
} ,
) ;