add a way to copy a list of contacts with a shorter URL

This commit is contained in:
2026-03-02 21:14:02 -07:00
parent 4f89869a87
commit 41149ad28a

View File

@@ -197,16 +197,30 @@
Import Contacts Import Contacts
</button> </button>
</ul> </ul>
<p v-else-if="contactsImporting.length > 0"> <div v-else-if="contactsImporting.length > 0">
All those contacts are already in your list with the same information. <p>
<button All those contacts are already in your list with the same information.
v-if="applyLabelsToExisting" </p>
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-sm text-white mt-2 px-2 py-1.5 rounded" <div class="mt-3 flex flex-col items-center gap-2">
@click="importContacts" <button
> data-testId="copyUnsignedImportLinkButton"
Apply Labels to Existing Contacts class="bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-sm text-white px-2 py-1.5 rounded w-fit"
</button> @click="copyUnsignedImportLink"
</p> >
Copy Unsigned Link for These Contacts
</button>
<button
class="bg-gradient-to-b from-blue-400 to-blue-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-sm text-white px-2 py-1.5 rounded w-fit"
:class="{
'opacity-50 cursor-not-allowed': !canApplyLabelsToExisting,
}"
@click="handleApplyLabelsToExistingClick"
>
Apply Labels to Existing Contacts
</button>
</div>
</div>
<div v-else> <div v-else>
There are no contacts in that import. If some were sent, try again to There are no contacts in that import. If some were sent, try again to
get the full text and paste it. (Note that iOS cuts off data in text get the full text and paste it. (Note that iOS cuts off data in text
@@ -291,7 +305,8 @@ import { RouteLocationNormalizedLoaded, Router } from "vue-router";
import QuickNav from "../components/QuickNav.vue"; import QuickNav from "../components/QuickNav.vue";
import EntityIcon from "../components/EntityIcon.vue"; import EntityIcon from "../components/EntityIcon.vue";
import OfferDialog from "../components/OfferDialog.vue"; import OfferDialog from "../components/OfferDialog.vue";
import { AppString, NotificationIface } from "../constants/app"; import { APP_SERVER, AppString, NotificationIface } from "../constants/app";
import { copyToClipboard } from "../services/ClipboardService";
import { import {
Contact, Contact,
ContactWithLabels, ContactWithLabels,
@@ -647,6 +662,52 @@ export default class ContactImportView extends Vue {
this.checkingImports = false; this.checkingImports = false;
} }
private buildUnsignedImportLink(): string {
const contactsForLink: Array<Contact> = this.contactsImporting.map((c) => {
const contact: Contact = {
did: c.did,
};
if (c.name) {
contact.name = c.name;
}
if (c.nextPubKeyHashB64) {
contact.nextPubKeyHashB64 = c.nextPubKeyHashB64;
}
if (c.profileImageUrl) {
contact.profileImageUrl = c.profileImageUrl;
}
if (c.publicKeyBase64) {
contact.publicKeyBase64 = c.publicKeyBase64;
}
if (typeof c.registered === "boolean") {
contact.registered = c.registered;
}
return contact;
});
const contactsParam = encodeURIComponent(JSON.stringify(contactsForLink));
return `${APP_SERVER}/deep-link/contact-import?contacts=${contactsParam}`;
}
async copyUnsignedImportLink() {
if (this.contactsImporting.length === 0) {
this.notify.error(
"No contacts are loaded to build a link.",
TIMEOUTS.SHORT,
);
return;
}
try {
const link = this.buildUnsignedImportLink();
await copyToClipboard(link);
this.notify.copied("unsigned contact import link", TIMEOUTS.STANDARD);
} catch (error) {
const fullError =
"Error copying unsigned import link: " + errorStringForLog(error);
this.$logAndConsole(fullError, true);
this.notify.error("Failed to copy link to clipboard.", TIMEOUTS.STANDARD);
}
}
/** /**
* Adds a new label to the selected labels list * Adds a new label to the selected labels list
*/ */
@@ -803,5 +864,20 @@ export default class ContactImportView extends Vue {
); );
this.$router.push({ name: "contacts" }); this.$router.push({ name: "contacts" });
} }
private get canApplyLabelsToExisting(): boolean {
return this.applyLabelsToExisting && this.selectedLabels.length > 0;
}
private async handleApplyLabelsToExistingClick() {
if (!this.canApplyLabelsToExisting) {
this.notify.warning(
`You must choose some labels and check the "Apply" checkbox to use this.`,
TIMEOUTS.LONG,
);
return;
}
await this.importContacts();
}
} }
</script> </script>