|
|
@ -63,12 +63,14 @@ |
|
|
|
|
|
|
|
<!-- Identity Details --> |
|
|
|
<div class="bg-slate-100 rounded-md overflow-hidden px-4 py-3 mb-4"> |
|
|
|
<h2 v-if="givenName" class="text-xl font-semibold mb-2"> |
|
|
|
{{ givenName }} |
|
|
|
<router-link :to="{ name: 'new-edit-account' }"> |
|
|
|
<fa icon="pen" class="text-xs text-blue-500 mb-1"></fa> |
|
|
|
</router-link> |
|
|
|
</h2> |
|
|
|
<div v-if="givenName"> |
|
|
|
<h2 class="text-xl font-semibold mb-2"> |
|
|
|
{{ givenName }} |
|
|
|
<router-link :to="{ name: 'new-edit-account' }"> |
|
|
|
<fa icon="pen" class="text-xs text-blue-500 mb-1"></fa> |
|
|
|
</router-link> |
|
|
|
</h2> |
|
|
|
</div> |
|
|
|
<span v-else> |
|
|
|
<router-link |
|
|
|
:to="{ name: 'new-edit-account' }" |
|
|
@ -77,6 +79,61 @@ |
|
|
|
Set Your Name |
|
|
|
</router-link> |
|
|
|
</span> |
|
|
|
<div class="flex justify-center mt-4"> |
|
|
|
<span v-if="profileImageUrl" class="flex justify-between"> |
|
|
|
<EntityIcon |
|
|
|
:icon-size="96" |
|
|
|
:profileImageUrl="profileImageUrl" |
|
|
|
class="inline-block align-text-bottom border border-slate-300 rounded" |
|
|
|
@click="showLargeIdenticonUrl = profileImageUrl" |
|
|
|
/> |
|
|
|
<fa |
|
|
|
icon="trash-can" |
|
|
|
@click="confirmDeleteImage" |
|
|
|
class="text-red-500 fa-fw ml-8 mt-8 w-12 h-12" |
|
|
|
/> |
|
|
|
</span> |
|
|
|
<span v-else> |
|
|
|
<fa |
|
|
|
icon="camera" |
|
|
|
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-2 py-2 rounded-md" |
|
|
|
@click="openPhotoDialog" |
|
|
|
/> |
|
|
|
</span> |
|
|
|
<GiftedPhotoDialog ref="photoDialog" /> |
|
|
|
</div> |
|
|
|
<div class="mt-4"> |
|
|
|
<div class="flex justify-center"> |
|
|
|
... and those without your image see this: |
|
|
|
</div> |
|
|
|
<div class="flex justify-center"> |
|
|
|
<EntityIcon |
|
|
|
:entityId="activeDid" |
|
|
|
:iconSize="64" |
|
|
|
class="inline-block align-middle border border-slate-300 rounded-md mr-1" |
|
|
|
@click="showLargeIdenticonId = activeDid" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div |
|
|
|
v-if="showLargeIdenticonId || showLargeIdenticonUrl" |
|
|
|
class="fixed z-[100] top-0 inset-x-0 w-full" |
|
|
|
> |
|
|
|
<div |
|
|
|
class="absolute inset-0 h-screen flex flex-col items-center justify-center bg-slate-900/50" |
|
|
|
> |
|
|
|
<EntityIcon |
|
|
|
:entityId="showLargeIdenticonId" |
|
|
|
:iconSize="512" |
|
|
|
:profileImageUrl="showLargeIdenticonUrl" |
|
|
|
class="flex w-11/12 max-w-sm mx-auto mb-3 overflow-hidden bg-white rounded-lg shadow-lg" |
|
|
|
@click=" |
|
|
|
showLargeIdenticonId = undefined; |
|
|
|
showLargeIdenticonUrl = undefined; |
|
|
|
" |
|
|
|
/> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div class="text-slate-500 text-sm font-bold">ID</div> |
|
|
|
<div class="text-sm text-slate-500 flex justify-start items-center mb-1"> |
|
|
@ -537,6 +594,7 @@ import { ref } from "vue"; |
|
|
|
import { Component, Vue } from "vue-facing-decorator"; |
|
|
|
import { useClipboard } from "@vueuse/core"; |
|
|
|
|
|
|
|
import GiftedPhotoDialog from "@/components/GiftedPhotoDialog.vue"; |
|
|
|
import QuickNav from "@/components/QuickNav.vue"; |
|
|
|
import TopMessage from "@/components/TopMessage.vue"; |
|
|
|
import { |
|
|
@ -555,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; |
|
|
@ -566,7 +626,7 @@ interface IAccount { |
|
|
|
const inputFileNameRef = ref<Blob>(); |
|
|
|
|
|
|
|
@Component({ |
|
|
|
components: { QuickNav, TopMessage }, |
|
|
|
components: {EntityIcon, GiftedPhotoDialog, QuickNav, TopMessage }, |
|
|
|
}) |
|
|
|
export default class AccountViewView extends Vue { |
|
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void; |
|
|
@ -586,10 +646,14 @@ export default class AccountViewView extends Vue { |
|
|
|
isRegistered = false; |
|
|
|
isSubscribed = false; |
|
|
|
notificationMaybeChanged = false; |
|
|
|
profileImageUrl?: string; |
|
|
|
publicHex = ""; |
|
|
|
publicBase64 = ""; |
|
|
|
showLargeIdenticonId?: string; |
|
|
|
showLargeIdenticonUrl?: string; |
|
|
|
webPushServer = ""; |
|
|
|
webPushServerInput = ""; |
|
|
|
|
|
|
|
limitsMessage = ""; |
|
|
|
loadingLimits = false; |
|
|
|
showContactGives = false; |
|
|
@ -657,6 +721,7 @@ export default class AccountViewView extends Vue { |
|
|
|
(settings?.firstName || "") + |
|
|
|
(settings?.lastName ? ` ${settings.lastName}` : ""); // pre v 0.1.3 |
|
|
|
this.isRegistered = !!settings?.isRegistered; |
|
|
|
this.profileImageUrl = settings?.profileImageUrl as string; |
|
|
|
this.showContactGives = !!settings?.showContactGivesInline; |
|
|
|
this.showShortcutBvc = !!settings?.showShortcutBvc; |
|
|
|
this.warnIfProdServer = !!settings?.warnIfProdServer; |
|
|
@ -1260,5 +1325,95 @@ export default class AccountViewView extends Vue { |
|
|
|
-1, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
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, |
|
|
|
"profile", |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
confirmDeleteImage() { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: "modal", |
|
|
|
type: "confirm", |
|
|
|
title: "Are you sure you want to delete your profile picture?", |
|
|
|
text: "", |
|
|
|
onYes: this.deleteImage, |
|
|
|
}, |
|
|
|
-1, |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
async deleteImage() { |
|
|
|
if (!this.profileImageUrl) { |
|
|
|
return; |
|
|
|
} |
|
|
|
try { |
|
|
|
const identity = await this.getIdentity(this.activeDid); |
|
|
|
if (!identity) { |
|
|
|
throw Error("No identity found."); |
|
|
|
} |
|
|
|
const token = await accessToken(identity); |
|
|
|
const response = await this.axios.delete( |
|
|
|
DEFAULT_IMAGE_API_SERVER + |
|
|
|
"/image/" + |
|
|
|
encodeURIComponent(this.profileImageUrl), |
|
|
|
{ |
|
|
|
headers: { |
|
|
|
Authorization: `Bearer ${token}`, |
|
|
|
}, |
|
|
|
}, |
|
|
|
); |
|
|
|
if (response.status === 204) { |
|
|
|
// don't bother with a notification |
|
|
|
// (either they'll simply continue or they're canceling and going back) |
|
|
|
} else { |
|
|
|
console.error("Non-success deleting image:", response); |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Error", |
|
|
|
text: "There was a problem deleting the image.", |
|
|
|
}, |
|
|
|
5000, |
|
|
|
); |
|
|
|
// keep the imageUrl in localStorage so the user can try again if they want |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
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 = undefined; |
|
|
|
|
|
|
|
// it already doesn't exist so we won't say anything to the user |
|
|
|
} else { |
|
|
|
this.$notify( |
|
|
|
{ |
|
|
|
group: "alert", |
|
|
|
type: "danger", |
|
|
|
title: "Error", |
|
|
|
text: "There was an error deleting the image.", |
|
|
|
}, |
|
|
|
5000, |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
</script> |
|
|
|