You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
5.7 KiB
215 lines
5.7 KiB
<template>
|
|
<QuickNav />
|
|
<TopMessage />
|
|
|
|
<!-- CONTENT -->
|
|
<section id="Content" class="p-2 pb-24 max-w-3xl mx-auto">
|
|
<!-- Breadcrumb -->
|
|
<div>
|
|
<!-- Back -->
|
|
<div class="text-lg text-center font-light relative px-7">
|
|
<button
|
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
|
aria-label="Go back"
|
|
@click="$router.back()"
|
|
>
|
|
<font-awesome icon="chevron-left" class="fa-fw" />
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Heading -->
|
|
<h1 id="ViewHeading" class="text-4xl text-center font-light">
|
|
Share Your Contact Info
|
|
</h1>
|
|
</div>
|
|
|
|
<div class="flex justify-center mt-8">
|
|
<button
|
|
class="block w-fit text-center text-lg 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 px-2 py-3 rounded-md disabled:opacity-50 disabled:cursor-not-allowed"
|
|
:disabled="isLoading"
|
|
aria-label="Copy contact information to clipboard"
|
|
@click="onClickShare()"
|
|
>
|
|
{{ isLoading ? "Copying..." : "Copy to Clipboard" }}
|
|
</button>
|
|
</div>
|
|
<div class="ml-12">
|
|
<div class="mt-8">Click to copy your info, then send it to them.</div>
|
|
<div>
|
|
They will paste it in the input box on the Contacts
|
|
<font-awesome icon="users" /> screen.
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
import { Router } from "vue-router";
|
|
import QuickNav from "../components/QuickNav.vue";
|
|
import TopMessage from "../components/TopMessage.vue";
|
|
import { NotificationIface } from "../constants/app";
|
|
import { retrieveFullyDecryptedAccount } from "../libs/util";
|
|
import { generateEndorserJwtUrlForAccount } from "../libs/endorserServer";
|
|
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|
import { Settings } from "@/db/tables/settings";
|
|
import { Account } from "@/db/tables/accounts";
|
|
|
|
// Constants for magic numbers
|
|
const NOTIFICATION_TIMEOUTS = {
|
|
COPY_SUCCESS: 5000,
|
|
SHARE_CONTACTS: 10000,
|
|
ERROR: 5000,
|
|
} as const;
|
|
|
|
const DELAYS = {
|
|
SHARE_CONTACTS_DELAY: 3000,
|
|
} as const;
|
|
|
|
@Component({
|
|
mixins: [PlatformServiceMixin],
|
|
components: { QuickNav, TopMessage },
|
|
})
|
|
export default class ShareMyContactInfoView extends Vue {
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
|
$router!: Router;
|
|
|
|
// Component state
|
|
isLoading = false;
|
|
|
|
/**
|
|
* Main share functionality - orchestrates the contact sharing process
|
|
*/
|
|
async onClickShare(): Promise<void> {
|
|
this.isLoading = true;
|
|
|
|
try {
|
|
const settings = await this.$settings();
|
|
const account = await this.retrieveAccount(settings);
|
|
|
|
if (!account) {
|
|
this.showAccountError();
|
|
return;
|
|
}
|
|
|
|
const message = await this.generateContactMessage(settings, account);
|
|
await this.copyToClipboard(message);
|
|
await this.showSuccessNotifications();
|
|
this.navigateToContacts();
|
|
} catch (error) {
|
|
await this.$logError(`Error sharing contact info: ${error}`);
|
|
this.showGenericError();
|
|
} finally {
|
|
this.isLoading = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Retrieve the fully decrypted account for the active DID
|
|
*/
|
|
private async retrieveAccount(
|
|
settings: Settings,
|
|
): Promise<Account | undefined> {
|
|
const activeDid = settings.activeDid || "";
|
|
if (!activeDid) {
|
|
return undefined;
|
|
}
|
|
return await retrieveFullyDecryptedAccount(activeDid);
|
|
}
|
|
|
|
/**
|
|
* Generate the contact message URL for sharing
|
|
*/
|
|
private async generateContactMessage(
|
|
settings: Settings,
|
|
account: Account,
|
|
): Promise<string> {
|
|
const givenName = settings.firstName || "";
|
|
const isRegistered = !!settings.isRegistered;
|
|
const profileImageUrl = settings.profileImageUrl || "";
|
|
|
|
return await generateEndorserJwtUrlForAccount(
|
|
account,
|
|
isRegistered,
|
|
givenName,
|
|
profileImageUrl,
|
|
true,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Copy the contact message to clipboard
|
|
*/
|
|
private async copyToClipboard(message: string): Promise<void> {
|
|
const { useClipboard } = await import("@vueuse/core");
|
|
await useClipboard().copy(message);
|
|
}
|
|
|
|
/**
|
|
* Show success notifications after copying
|
|
*/
|
|
private async showSuccessNotifications(): Promise<void> {
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "info",
|
|
title: "Copied",
|
|
text: "Your contact info was copied to the clipboard. Have them click on it, or paste it in the box on their 'Contacts' screen.",
|
|
},
|
|
NOTIFICATION_TIMEOUTS.COPY_SUCCESS,
|
|
);
|
|
|
|
const numContacts = await this.$contactCount();
|
|
if (numContacts > 0) {
|
|
setTimeout(() => {
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "success",
|
|
title: "Share Other Contacts",
|
|
text: "You may want to share some of your contacts with them. Select them below to copy and send.",
|
|
},
|
|
NOTIFICATION_TIMEOUTS.SHARE_CONTACTS,
|
|
);
|
|
}, DELAYS.SHARE_CONTACTS_DELAY);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Navigate to contacts page
|
|
*/
|
|
private navigateToContacts(): void {
|
|
this.$router.push({ name: "contacts" });
|
|
}
|
|
|
|
/**
|
|
* Show account not found error
|
|
*/
|
|
private showAccountError(): void {
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "error",
|
|
title: "Error",
|
|
text: "No account was found for the active DID.",
|
|
},
|
|
NOTIFICATION_TIMEOUTS.ERROR,
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Show generic error notification
|
|
*/
|
|
private showGenericError(): void {
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "error",
|
|
title: "Error",
|
|
text: "There was a problem sharing your contact information. Please try again.",
|
|
},
|
|
NOTIFICATION_TIMEOUTS.ERROR,
|
|
);
|
|
}
|
|
}
|
|
</script>
|
|
|