From 5a9958cb4f9aa808f66f23e3a50c2c88174f0530 Mon Sep 17 00:00:00 2001 From: Trent Larson Date: Fri, 19 Apr 2024 11:39:01 -0600 Subject: [PATCH] show contact's or user's icon in more places --- src/components/EntityIcon.vue | 25 ++++-- src/components/GiftedPhotoDialog.vue | 8 +- src/db/tables/contacts.ts | 1 + src/views/AccountViewView.vue | 123 ++++++++++++++++++--------- src/views/ClaimView.vue | 3 +- src/views/ContactGiftingView.vue | 2 +- src/views/ContactQRScanShowView.vue | 31 +++++-- src/views/ContactsView.vue | 15 ++-- src/views/DiscoverView.vue | 2 - src/views/HomeView.vue | 2 +- src/views/ProjectViewView.vue | 2 +- src/views/ProjectsView.vue | 2 +- 12 files changed, 142 insertions(+), 74 deletions(-) diff --git a/src/components/EntityIcon.vue b/src/components/EntityIcon.vue index 373da04d..b14be845 100644 --- a/src/components/EntityIcon.vue +++ b/src/components/EntityIcon.vue @@ -5,20 +5,29 @@ import { createAvatar, StyleOptions } from "@dicebear/core"; import { avataaars } from "@dicebear/collection"; import { Vue, Component, Prop } from "vue-facing-decorator"; +import { Contact } from "@/db/tables/contacts"; @Component export default class EntityIcon extends Vue { - @Prop entityId = ""; + @Prop contact: Contact; + @Prop entityId = ""; // overridden by contact.did or profileImageUrl @Prop iconSize = 0; + @Prop profileImageUrl = ""; // overridden by contact.profileImageUrl generateIcon() { - const options: StyleOptions = { - seed: this.entityId || "", - size: this.iconSize, - }; - const avatar = createAvatar(avataaars, options); - const svgString = avatar.toString(); - return svgString; + const imageUrl = this.contact?.profileImageUrl || this.profileImageUrl; + if (imageUrl) { + return `avatar`; + } else { + const identifier = this.contact?.did || this.entityId; + const options: StyleOptions = { + seed: (identifier as string) || "", + size: this.iconSize, + }; + const avatar = createAvatar(avataaars, options); + const svgString = avatar.toString(); + return svgString; + } } } diff --git a/src/components/GiftedPhotoDialog.vue b/src/components/GiftedPhotoDialog.vue index 6c388fd7..80b5b051 100644 --- a/src/components/GiftedPhotoDialog.vue +++ b/src/components/GiftedPhotoDialog.vue @@ -133,7 +133,8 @@ export default class GiftedPhotoDialog extends Vue { activeDeviceNumber = 0; activeDid = ""; blob: Blob | null = null; - crop: boolean; + claimType = "GiveAction"; + crop = false; mirror = false; numDevices = 0; setImage: (arg: string) => void = () => {}; @@ -162,9 +163,10 @@ export default class GiftedPhotoDialog extends Vue { } } - open(setImageFn: (arg: string) => void, crop?: boolean) { + open(setImageFn: (arg: string) => void, crop?: boolean, claimType?: string) { this.visible = true; this.crop = !!crop; + this.claimType = claimType || "GiveAction"; const bottomNav = document.querySelector("#QuickNav") as HTMLElement; if (bottomNav) { bottomNav.style.display = "none"; @@ -329,7 +331,7 @@ export default class GiftedPhotoDialog extends Vue { return; } formData.append("image", this.blob, "snapshot.png"); // png is set in snapshot() - formData.append("claimType", "GiveAction"); + formData.append("claimType", this.claimType); try { const response = await axios.post( DEFAULT_IMAGE_API_SERVER + "/image", diff --git a/src/db/tables/contacts.ts b/src/db/tables/contacts.ts index 14ad6809..4ddc4dec 100644 --- a/src/db/tables/contacts.ts +++ b/src/db/tables/contacts.ts @@ -2,6 +2,7 @@ export interface Contact { did: string; name?: string; nextPubKeyHashB64?: string; // base64-encoded SHA256 hash of next public key + profileImageUrl?: string; publicKeyBase64?: string; seesMe?: boolean; registered?: boolean; diff --git a/src/views/AccountViewView.vue b/src/views/AccountViewView.vue index 43c5cd59..6879537e 100644 --- a/src/views/AccountViewView.vue +++ b/src/views/AccountViewView.vue @@ -63,36 +63,14 @@
-

- {{ givenName }} - - - -
- - - - - - - - - - -
-

+
+

+ {{ givenName }} + + + +

+
+
+ + + + + + + + +
+
+
+ ... and those without your image see this: +
+
+ +
+
+
+
+ +
+
ID
@@ -580,6 +613,8 @@ import { ImageRateLimits, } from "@/libs/endorserServer"; import { Buffer } from "buffer/"; +import EntityIcon from "@/components/EntityIcon.vue"; +import {Contact} from "@/db/tables/contacts"; interface IAccount { did: string; @@ -591,7 +626,7 @@ interface IAccount { const inputFileNameRef = ref(); @Component({ - components: { GiftedPhotoDialog, QuickNav, TopMessage }, + components: {EntityIcon, GiftedPhotoDialog, QuickNav, TopMessage }, }) export default class AccountViewView extends Vue { $notify!: (notification: NotificationIface, timeout?: number) => void; @@ -614,6 +649,8 @@ export default class AccountViewView extends Vue { profileImageUrl?: string; publicHex = ""; publicBase64 = ""; + showLargeIdenticonId?: string; + showLargeIdenticonUrl?: string; webPushServer = ""; webPushServerInput = ""; @@ -1290,14 +1327,18 @@ export default class AccountViewView extends Vue { } openPhotoDialog() { - (this.$refs.photoDialog as GiftedPhotoDialog).open(async (imgUrl) => { - await db.open(); - db.settings.update(MASTER_SETTINGS_KEY, { - profileImageUrl: imgUrl, - }); - this.profileImageUrl = imgUrl; - //console.log("Got image URL:", imgUrl); - }, true); + (this.$refs.photoDialog as GiftedPhotoDialog).open( + async (imgUrl) => { + await db.open(); + db.settings.update(MASTER_SETTINGS_KEY, { + profileImageUrl: imgUrl, + }); + this.profileImageUrl = imgUrl; + //console.log("Got image URL:", imgUrl); + }, + true, + "profile", + ); } confirmDeleteImage() { @@ -1351,14 +1392,14 @@ export default class AccountViewView extends Vue { return; } - this.profileImageUrl = ""; + this.profileImageUrl = undefined; } catch (error) { console.error("Error deleting image:", error); // eslint-disable-next-line @typescript-eslint/no-explicit-any if ((error as any).response.status === 404) { console.log("The image was already deleted:", error); - this.profileImageUrl = ""; + this.profileImageUrl = undefined; // it already doesn't exist so we won't say anything to the user } else { diff --git a/src/views/ClaimView.vue b/src/views/ClaimView.vue index f6237166..e47e40ef 100644 --- a/src/views/ClaimView.vue +++ b/src/views/ClaimView.vue @@ -415,12 +415,11 @@ import { accessToken } from "@/libs/crypto"; import * as serverUtil from "@/libs/endorserServer"; import * as libsUtil from "@/libs/util"; import QuickNav from "@/components/QuickNav.vue"; -import EntityIcon from "@/components/EntityIcon.vue"; import { Account } from "@/db/tables/accounts"; import { GiverInputInfo } from "@/libs/endorserServer"; @Component({ - components: { EntityIcon, GiftedDialog, OfferDialog, QuickNav }, + components: { GiftedDialog, OfferDialog, QuickNav }, }) export default class ClaimView extends Vue { $notify!: (notification: NotificationIface, timeout?: number) => void; diff --git a/src/views/ContactGiftingView.vue b/src/views/ContactGiftingView.vue index d522729d..446abcc2 100644 --- a/src/views/ContactGiftingView.vue +++ b/src/views/ContactGiftingView.vue @@ -47,7 +47,7 @@

diff --git a/src/views/ContactQRScanShowView.vue b/src/views/ContactQRScanShowView.vue index 72cbc958..be3a0c79 100644 --- a/src/views/ContactQRScanShowView.vue +++ b/src/views/ContactQRScanShowView.vue @@ -44,7 +44,8 @@ :dotsOptions="{ type: 'square' }" class="flex justify-center" /> - Click QR to copy your contact URL to your clipboard. + Click that QR to copy your contact URL to your clipboard. +
Not scanning? Show it in pieces.

You have no identitifiers yet, so @@ -81,7 +82,7 @@ import { useClipboard } from "@vueuse/core"; import { NotificationIface } from "@/constants/app"; import { accountsDB, db } from "@/db/index"; import { MASTER_SETTINGS_KEY } from "@/db/tables/settings"; -import { deriveAddress, nextDerivationPath, SimpleSigner } from "@/libs/crypto"; +import {deriveAddress, getContactPayloadFromJwtUrl, nextDerivationPath, SimpleSigner} from "@/libs/crypto"; import QuickNav from "@/components/QuickNav.vue"; import { Account } from "@/db/tables/accounts"; import { @@ -153,6 +154,7 @@ export default class ContactQRScanShow extends Vue { (settings?.lastName ? ` ${settings.lastName}` : ""), // deprecated, pre v 0.1.3 publicEncKey, nextPublicEncKeyHash: nextPublicEncKeyHashBase64, + profileImageUrl: settings?.profileImageUrl, }, }; @@ -177,9 +179,24 @@ export default class ContactQRScanShow extends Vue { // Unfortunately, there are not typescript definitions for the qrcode-stream component yet. // eslint-disable-next-line @typescript-eslint/no-explicit-any onScanDetect(content: any) { - if (content[0]?.rawValue) { - localStorage.setItem("contactEndorserUrl", content[0].rawValue); - this.$router.push({ name: "contacts" }); + const url = content[0]?.rawValue; + if (url) { + try { + const fullData = getContactPayloadFromJwtUrl(url); + console.log("fullData", fullData); + localStorage.setItem("contactEndorserUrl", url); + this.$router.push({ name: "contacts" }); + } catch (e) { + this.$notify( + { + group: "alert", + type: "warning", + title: "Invalid Contact QR Code", + text: "The QR code isn't in the right format.", + }, + 5000, + ); + } } else { this.$notify( { @@ -188,7 +205,7 @@ export default class ContactQRScanShow extends Vue { title: "Invalid Contact QR Code", text: "No QR code detected with contact information.", }, - -1, + 5000, ); } } @@ -203,7 +220,7 @@ export default class ContactQRScanShow extends Vue { title: "Invalid Scan", text: "The scan was invalid.", }, - -1, + 5000, ); } diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index e5c699be..6f1119d4 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -94,17 +94,17 @@

+ @click="showLargeIdenticon = contact" + /> {{ contact.name || AppString.NO_CONTACT_NAME }}

@@ -348,7 +348,7 @@ export default class ContactsView extends Vue { showGiveNumbers = false; showGiveTotals = true; showGiveConfirmed = true; - showLargeIdenticon = ""; + showLargeIdenticon?: Contact; AppString = AppString; libsUtil = libsUtil; @@ -672,6 +672,7 @@ export default class ContactsView extends Vue { did: payload.iss, name: payload.own.name, nextPubKeyHashB64: payload.own.nextPublicEncKeyHash, + profileImageUrl: payload.own.profileImageUrl, publicKeyBase64: payload.own.publicEncKey, } as Contact); } diff --git a/src/views/DiscoverView.vue b/src/views/DiscoverView.vue index 9126cc5a..5256ca50 100644 --- a/src/views/DiscoverView.vue +++ b/src/views/DiscoverView.vue @@ -131,7 +131,6 @@ import { Component, Vue } from "vue-facing-decorator"; import QuickNav from "@/components/QuickNav.vue"; import InfiniteScroll from "@/components/InfiniteScroll.vue"; -import EntityIcon from "@/components/EntityIcon.vue"; import ProjectIcon from "@/components/ProjectIcon.vue"; import TopMessage from "@/components/TopMessage.vue"; import { NotificationIface } from "@/constants/app"; @@ -143,7 +142,6 @@ import { didInfo, PlanData } from "@/libs/endorserServer"; @Component({ components: { - EntityIcon, InfiniteScroll, ProjectIcon, QuickNav, diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue index 66511dbd..b9bc8cf9 100644 --- a/src/views/HomeView.vue +++ b/src/views/HomeView.vue @@ -138,7 +138,7 @@ @click="openDialog(contact)" > diff --git a/src/views/ProjectViewView.vue b/src/views/ProjectViewView.vue index b4602458..f5766d20 100644 --- a/src/views/ProjectViewView.vue +++ b/src/views/ProjectViewView.vue @@ -162,7 +162,7 @@ @click="openGiftDialog(contact)" > diff --git a/src/views/ProjectsView.vue b/src/views/ProjectsView.vue index 2639a5be..b4a7184e 100644 --- a/src/views/ProjectsView.vue +++ b/src/views/ProjectsView.vue @@ -98,7 +98,7 @@ :entityId="offer.recipientDid" :iconSize="48" class="inline-block align-middle border border-slate-300 rounded-md" - > + />