|
@ -5,7 +5,7 @@ |
|
|
<!-- CONTENT --> |
|
|
<!-- CONTENT --> |
|
|
<section id="Content" class="p-2 pb-24 max-w-3xl mx-auto"> |
|
|
<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"> |
|
|
<h1 id="ViewHeading" class="text-4xl text-center font-light px-4 mb-8"> |
|
|
Time Safari |
|
|
{{ AppString.APP_NAME }} |
|
|
</h1> |
|
|
</h1> |
|
|
|
|
|
|
|
|
<!-- prompt to install notifications --> |
|
|
<!-- prompt to install notifications --> |
|
@ -79,89 +79,100 @@ |
|
|
<!-- !isCreatingIdentifier --> |
|
|
<!-- !isCreatingIdentifier --> |
|
|
<div |
|
|
<div |
|
|
v-if="!activeDid" |
|
|
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"> |
|
|
<p class="text-lg mb-3"> |
|
|
Want to connect with your contacts, or share contributions or |
|
|
Want to see info from your contacts, or share contributions? |
|
|
projects? |
|
|
|
|
|
</p> |
|
|
</p> |
|
|
<router-link |
|
|
<div class="flex justify-between"> |
|
|
:to="{ name: 'start' }" |
|
|
<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" |
|
|
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()" |
|
|
Create An Identifier |
|
|
> |
|
|
</router-link> |
|
|
Let me start the easiest (with a passkey). |
|
|
</div> |
|
|
</button> |
|
|
|
|
|
<router-link |
|
|
<div |
|
|
:to="{ name: 'start' }" |
|
|
v-else-if="!isRegistered" |
|
|
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" |
|
|
class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4" |
|
|
> |
|
|
> |
|
|
Give me all the options. |
|
|
<!-- activeDid && !isRegistered --> |
|
|
</router-link> |
|
|
Someone must register you before you can give kudos or make offers or |
|
|
</div> |
|
|
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 |
|
|
|
|
|
</router-link> |
|
|
|
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
<div v-else> |
|
|
<div v-else class="mb-4"> |
|
|
<!-- activeDid && isRegistered --> |
|
|
<!-- activeDid --> |
|
|
|
|
|
|
|
|
<!-- show the actions for recognizing a give --> |
|
|
|
|
|
<div class="mb-4"> |
|
|
|
|
|
<h2 class="text-xl font-bold">Record Something Given By:</h2> |
|
|
|
|
|
</div> |
|
|
|
|
|
|
|
|
|
|
|
<ul |
|
|
<div |
|
|
class="grid grid-cols-4 sm:grid-cols-5 md:grid-cols-6 gap-x-3 gap-y-5 text-center mb-5" |
|
|
v-if="!isRegistered" |
|
|
|
|
|
class="bg-amber-200 rounded-md overflow-hidden text-center px-4 py-3 mb-4" |
|
|
> |
|
|
> |
|
|
<li @click="openDialog()"> |
|
|
<!-- activeDid && !isRegistered --> |
|
|
<img |
|
|
Someone must register you before you can give kudos or make offers |
|
|
src="../assets/blank-square.svg" |
|
|
or create projects... basically before doing anything. |
|
|
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 |
|
|
<router-link |
|
|
v-if="allContacts.length >= 7" |
|
|
:to="{ name: 'contact-qr' }" |
|
|
:to="{ name: 'contact-gift' }" |
|
|
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" |
|
|
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 |
|
|
Show Them Your Identifier Info |
|
|
</router-link> |
|
|
</router-link> |
|
|
<button |
|
|
</div> |
|
|
@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 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... |
|
|
<li @click="openDialog()"> |
|
|
</button> |
|
|
<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 |
|
|
|
|
|
</router-link> |
|
|
|
|
|
<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> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
@ -309,6 +320,7 @@ import { IIdentifier } from "@veramo/core"; |
|
|
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 App from "../App.vue"; |
|
|
import EntityIcon from "@/components/EntityIcon.vue"; |
|
|
import EntityIcon from "@/components/EntityIcon.vue"; |
|
|
import GiftedDialog from "@/components/GiftedDialog.vue"; |
|
|
import GiftedDialog from "@/components/GiftedDialog.vue"; |
|
|
import GiftedPrompts from "@/components/GiftedPrompts.vue"; |
|
|
import GiftedPrompts from "@/components/GiftedPrompts.vue"; |
|
@ -316,7 +328,7 @@ import FeedFilters from "@/components/FeedFilters.vue"; |
|
|
import InfiniteScroll from "@/components/InfiniteScroll.vue"; |
|
|
import InfiniteScroll from "@/components/InfiniteScroll.vue"; |
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
import TopMessage from "@/components/TopMessage.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 { db, accountsDB } from "@/db/index"; |
|
|
import { Account } from "@/db/tables/accounts"; |
|
|
import { Account } from "@/db/tables/accounts"; |
|
|
import { Contact } from "@/db/tables/contacts"; |
|
|
import { Contact } from "@/db/tables/contacts"; |
|
@ -336,7 +348,7 @@ import { |
|
|
GiverReceiverInputInfo, |
|
|
GiverReceiverInputInfo, |
|
|
GiveSummaryRecord, |
|
|
GiveSummaryRecord, |
|
|
} from "@/libs/endorserServer"; |
|
|
} from "@/libs/endorserServer"; |
|
|
import { generateSaveAndActivateIdentity } from "@/libs/util"; |
|
|
import { registerSaveAndActivatePasskey } from "@/libs/util"; |
|
|
|
|
|
|
|
|
interface GiveRecordWithContactInfo extends GiveSummaryRecord { |
|
|
interface GiveRecordWithContactInfo extends GiveSummaryRecord { |
|
|
giver: { |
|
|
giver: { |
|
@ -354,6 +366,11 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Component({ |
|
|
@Component({ |
|
|
|
|
|
computed: { |
|
|
|
|
|
App() { |
|
|
|
|
|
return App; |
|
|
|
|
|
}, |
|
|
|
|
|
}, |
|
|
components: { |
|
|
components: { |
|
|
GiftedDialog, |
|
|
GiftedDialog, |
|
|
GiftedPrompts, |
|
|
GiftedPrompts, |
|
@ -367,6 +384,8 @@ interface GiveRecordWithContactInfo extends GiveSummaryRecord { |
|
|
export default class HomeView extends Vue { |
|
|
export default class HomeView extends Vue { |
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
|
|
|
|
|
|
|
|
|
|
|
AppString = AppString; |
|
|
|
|
|
|
|
|
activeDid = ""; |
|
|
activeDid = ""; |
|
|
allContacts: Array<Contact> = []; |
|
|
allContacts: Array<Contact> = []; |
|
|
allMyDids: Array<string> = []; |
|
|
allMyDids: Array<string> = []; |
|
@ -374,6 +393,7 @@ export default class HomeView extends Vue { |
|
|
feedData: GiveRecordWithContactInfo[] = []; |
|
|
feedData: GiveRecordWithContactInfo[] = []; |
|
|
feedPreviousOldestId?: string; |
|
|
feedPreviousOldestId?: string; |
|
|
feedLastViewedClaimId?: string; |
|
|
feedLastViewedClaimId?: string; |
|
|
|
|
|
givenName = ""; |
|
|
isAnyFeedFilterOn: boolean; |
|
|
isAnyFeedFilterOn: boolean; |
|
|
isCreatingIdentifier = false; |
|
|
isCreatingIdentifier = false; |
|
|
isFeedFilteredByVisible = false; |
|
|
isFeedFilteredByVisible = false; |
|
@ -397,15 +417,6 @@ export default class HomeView extends Vue { |
|
|
return identity; // may be null |
|
|
return identity; // may be null |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public async getHeaders(identity: IIdentifier) { |
|
|
|
|
|
const token = await accessToken(identity); |
|
|
|
|
|
const headers = { |
|
|
|
|
|
"Content-Type": "application/json", |
|
|
|
|
|
Authorization: "Bearer " + token, |
|
|
|
|
|
}; |
|
|
|
|
|
return headers; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
async mounted() { |
|
|
async mounted() { |
|
|
try { |
|
|
try { |
|
|
await accountsDB.open(); |
|
|
await accountsDB.open(); |
|
@ -418,6 +429,7 @@ export default class HomeView extends Vue { |
|
|
this.activeDid = settings?.activeDid || ""; |
|
|
this.activeDid = settings?.activeDid || ""; |
|
|
this.allContacts = await db.contacts.toArray(); |
|
|
this.allContacts = await db.contacts.toArray(); |
|
|
this.feedLastViewedClaimId = settings?.lastViewedClaimId; |
|
|
this.feedLastViewedClaimId = settings?.lastViewedClaimId; |
|
|
|
|
|
this.givenName = settings?.firstName || ""; |
|
|
this.isFeedFilteredByVisible = !!settings?.filterFeedByVisible; |
|
|
this.isFeedFilteredByVisible = !!settings?.filterFeedByVisible; |
|
|
this.isFeedFilteredByNearby = !!settings?.filterFeedByNearby; |
|
|
this.isFeedFilteredByNearby = !!settings?.filterFeedByNearby; |
|
|
this.isRegistered = !!settings?.isRegistered; |
|
|
this.isRegistered = !!settings?.isRegistered; |
|
@ -426,14 +438,7 @@ export default class HomeView extends Vue { |
|
|
|
|
|
|
|
|
this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings); |
|
|
this.isAnyFeedFilterOn = isAnyFeedFilterOn(settings); |
|
|
|
|
|
|
|
|
if (this.allMyDids.length === 0) { |
|
|
// someone may have have registered after sharing contact info, so recheck |
|
|
this.isCreatingIdentifier = true; |
|
|
|
|
|
this.activeDid = await generateSaveAndActivateIdentity(); |
|
|
|
|
|
this.allMyDids = [this.activeDid]; |
|
|
|
|
|
this.isCreatingIdentifier = false; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// someone may have have registered after sharing contact info |
|
|
|
|
|
if (!this.isRegistered && this.activeDid) { |
|
|
if (!this.isRegistered && this.activeDid) { |
|
|
const identity = await this.getIdentity(this.activeDid); |
|
|
const identity = await this.getIdentity(this.activeDid); |
|
|
try { |
|
|
try { |
|
@ -475,6 +480,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() { |
|
|
resultsAreFiltered() { |
|
|
return this.isFeedFilteredByVisible || this.isFeedFilteredByNearby; |
|
|
return this.isFeedFilteredByVisible || this.isFeedFilteredByNearby; |
|
|
} |
|
|
} |
|
@ -483,7 +497,7 @@ export default class HomeView extends Vue { |
|
|
return "Notification" in window; |
|
|
return "Notification" in window; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public async buildHeaders() { |
|
|
async buildHeaders() { |
|
|
const headers: HeadersInit = { |
|
|
const headers: HeadersInit = { |
|
|
"Content-Type": "application/json", |
|
|
"Content-Type": "application/json", |
|
|
}; |
|
|
}; |
|
@ -520,7 +534,7 @@ export default class HomeView extends Vue { |
|
|
* Data loader used by infinite scroller |
|
|
* Data loader used by infinite scroller |
|
|
* @param payload is the flag from the InfiniteScroll indicating if it should load |
|
|
* @param payload is the flag from the InfiniteScroll indicating if it should load |
|
|
**/ |
|
|
**/ |
|
|
public async loadMoreGives(payload: boolean) { |
|
|
async loadMoreGives(payload: boolean) { |
|
|
// Since feed now loads projects along the way, it takes longer |
|
|
// Since feed now loads projects along the way, it takes longer |
|
|
// and the InfiniteScroll component triggers a load before finished. |
|
|
// and the InfiniteScroll component triggers a load before finished. |
|
|
// One alternative is to totally separate the project link loading. |
|
|
// One alternative is to totally separate the project link loading. |
|
@ -542,7 +556,7 @@ export default class HomeView extends Vue { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public async updateAllFeed() { |
|
|
async updateAllFeed() { |
|
|
this.isFeedLoading = true; |
|
|
this.isFeedLoading = true; |
|
|
let endOfResults = true; |
|
|
let endOfResults = true; |
|
|
await this.retrieveGives(this.apiServer, this.feedPreviousOldestId) |
|
|
await this.retrieveGives(this.apiServer, this.feedPreviousOldestId) |
|
@ -650,7 +664,7 @@ export default class HomeView extends Vue { |
|
|
* @param beforeId the earliest ID (of previous searches) to search earlier |
|
|
* @param beforeId the earliest ID (of previous searches) to search earlier |
|
|
* @return claims in reverse chronological order |
|
|
* @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 beforeQuery = beforeId == null ? "" : "&beforeId=" + beforeId; |
|
|
const response = await fetch( |
|
|
const response = await fetch( |
|
|
endorserApiServer + |
|
|
endorserApiServer + |
|
|