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.
233 lines
7.9 KiB
233 lines
7.9 KiB
<template>
|
|
<QuickNav selected="Contacts"></QuickNav>
|
|
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto">
|
|
<!-- Back -->
|
|
<div class="text-lg text-center font-light relative px-7">
|
|
<h1
|
|
class="text-lg text-center px-2 py-1 absolute -left-2 -top-1"
|
|
@click="$router.back()"
|
|
>
|
|
<fa icon="chevron-left" class="fa-fw"></fa>
|
|
</h1>
|
|
</div>
|
|
|
|
<!-- Heading -->
|
|
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8">
|
|
Contact Import
|
|
</h1>
|
|
|
|
<span class="flex justify-center">
|
|
<input type="checkbox" v-model="makeVisible" class="mr-2" />
|
|
Make my activity visible to these contacts.
|
|
</span>
|
|
<div v-if="sameCount > 0">
|
|
<span v-if="sameCount == 1"
|
|
>One contact is the same as an existing contact</span
|
|
>
|
|
<span v-else
|
|
>{{ sameCount }} contacts are the same as existing contacts</span
|
|
>
|
|
</div>
|
|
|
|
<!-- Results List -->
|
|
<ul v-if="contactsImporting.length > 0" class="border-t border-slate-300">
|
|
<li v-for="(contact, index) in contactsImporting" :key="contact.did">
|
|
<div
|
|
v-if="
|
|
!contactsExisting[contact.did] ||
|
|
!R.isEmpty(contactDifferences[contact.did])
|
|
"
|
|
class="grow overflow-hidden border-b border-slate-300 pt-2.5 pb-4"
|
|
>
|
|
<h2 class="text-base font-semibold">
|
|
<input type="checkbox" v-model="contactsSelected[index]" />
|
|
{{ contact.name || AppString.NO_CONTACT_NAME }}
|
|
-
|
|
<span v-if="contactsExisting[contact.did]" class="text-orange-500"
|
|
>Existing</span
|
|
>
|
|
<span v-else class="text-green-500">New</span>
|
|
</h2>
|
|
<div class="text-sm truncate">
|
|
{{ contact.did }}
|
|
</div>
|
|
<div v-if="contactDifferences[contact.did]">
|
|
<div>
|
|
<div class="grid grid-cols-3 gap-2">
|
|
<div class="font-bold">Field</div>
|
|
<div class="font-bold">Old Value</div>
|
|
<div class="font-bold">New Value</div>
|
|
</div>
|
|
<div
|
|
v-for="(value, contactField) in contactDifferences[contact.did]"
|
|
:key="contactField"
|
|
class="grid grid-cols-3 border"
|
|
>
|
|
<div class="border p-1">{{ contactField }}</div>
|
|
<div class="border p-1">{{ value.old }}</div>
|
|
<div class="border p-1">{{ value.new }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</li>
|
|
<fa icon="spinner" v-if="importing" class="animate-spin" />
|
|
<button
|
|
v-else
|
|
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"
|
|
@click="importContacts"
|
|
>
|
|
Import Selected Contacts
|
|
</button>
|
|
</ul>
|
|
<p v-else>There are no contacts to import.</p>
|
|
</section>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import * as R from "ramda";
|
|
import { Component, Vue } from "vue-facing-decorator";
|
|
import { Router } from "vue-router";
|
|
|
|
import { AppString, NotificationIface } from "@/constants/app";
|
|
import { db, retrieveSettingsForActiveAccount } from "@/db/index";
|
|
import { Contact } from "@/db/tables/contacts";
|
|
import * as libsUtil from "@/libs/util";
|
|
import QuickNav from "@/components/QuickNav.vue";
|
|
import EntityIcon from "@/components/EntityIcon.vue";
|
|
import OfferDialog from "@/components/OfferDialog.vue";
|
|
import { setVisibilityUtil } from "@/libs/endorserServer";
|
|
|
|
@Component({
|
|
components: { EntityIcon, OfferDialog, QuickNav },
|
|
})
|
|
export default class ContactImportView extends Vue {
|
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
|
|
|
AppString = AppString;
|
|
libsUtil = libsUtil;
|
|
R = R;
|
|
|
|
activeDid = "";
|
|
apiServer = "";
|
|
contactsExisting: Record<string, Contact> = {}; // user's contacts already in the system, keyed by DID
|
|
contactsImporting: Array<Contact> = []; // contacts from the import
|
|
contactsSelected: Array<boolean> = []; // whether each contact in contactsImporting is selected
|
|
contactDifferences: Record<
|
|
string,
|
|
Record<string, { new: string; old: string }>
|
|
> = {}; // for existing contacts, it shows the difference between imported and existing contacts for each key
|
|
importing = false;
|
|
makeVisible = true;
|
|
sameCount = 0;
|
|
|
|
async created() {
|
|
const settings = await retrieveSettingsForActiveAccount();
|
|
this.activeDid = settings.activeDid || "";
|
|
this.apiServer = settings.apiServer || "";
|
|
|
|
// Retrieve the imported contacts from the query parameter
|
|
const importedContacts =
|
|
((this.$route as Router).query["contacts"] as string) || "[]";
|
|
this.contactsImporting = JSON.parse(importedContacts);
|
|
this.contactsSelected = new Array(this.contactsImporting.length).fill(true);
|
|
|
|
await db.open();
|
|
const baseContacts = await db.contacts.toArray();
|
|
// set the existing contacts, keyed by DID, if they exist in contactsImporting
|
|
for (let i = 0; i < this.contactsImporting.length; i++) {
|
|
const contactIn = this.contactsImporting[i];
|
|
const existingContact = baseContacts.find(
|
|
(contact) => contact.did === contactIn.did,
|
|
);
|
|
if (existingContact) {
|
|
this.contactsExisting[contactIn.did] = existingContact;
|
|
|
|
const differences: Record<string, { new: string; old: string }> = {};
|
|
Object.keys(contactIn).forEach((key) => {
|
|
if (contactIn[key] !== existingContact[key]) {
|
|
differences[key] = {
|
|
old: existingContact[key],
|
|
new: contactIn[key],
|
|
};
|
|
}
|
|
});
|
|
this.contactDifferences[contactIn.did] = differences;
|
|
if (R.isEmpty(differences)) {
|
|
this.sameCount++;
|
|
}
|
|
|
|
// don't automatically import previous data
|
|
this.contactsSelected[i] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
async importContacts() {
|
|
this.importing = true;
|
|
let importedCount = 0,
|
|
updatedCount = 0;
|
|
for (let i = 0; i < this.contactsImporting.length; i++) {
|
|
if (this.contactsSelected[i]) {
|
|
const contact = this.contactsImporting[i];
|
|
const existingContact = this.contactsExisting[contact.did];
|
|
if (existingContact) {
|
|
await db.contacts.update(contact.did, contact);
|
|
updatedCount++;
|
|
} else {
|
|
// without explicit clone on the Proxy, we get: DataCloneError: Failed to execute 'add' on 'IDBObjectStore': #<Object> could not be cloned.
|
|
await db.contacts.add(R.clone(contact));
|
|
importedCount++;
|
|
}
|
|
}
|
|
}
|
|
if (this.makeVisible) {
|
|
const failedVisibileToContacts = [];
|
|
for (let i = 0; i < this.contactsImporting.length; i++) {
|
|
const contact = this.contactsImporting[i];
|
|
if (contact) {
|
|
const visResult = await setVisibilityUtil(
|
|
this.activeDid,
|
|
this.apiServer,
|
|
this.axios,
|
|
db,
|
|
contact,
|
|
true,
|
|
);
|
|
if (!visResult.success) {
|
|
failedVisibileToContacts.push(contact);
|
|
}
|
|
}
|
|
}
|
|
if (failedVisibileToContacts.length) {
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "danger",
|
|
title: "Visibility Error",
|
|
text: `Failed to set visibility for ${failedVisibileToContacts.length} contact${
|
|
failedVisibileToContacts.length == 1 ? "" : "s"
|
|
}. You must set them individually: ${failedVisibileToContacts.map((c) => c.name).join(", ")}`,
|
|
},
|
|
-1,
|
|
);
|
|
}
|
|
}
|
|
|
|
this.importing = false;
|
|
|
|
this.$notify(
|
|
{
|
|
group: "alert",
|
|
type: "success",
|
|
title: "Imported",
|
|
text:
|
|
`${importedCount} contact${importedCount == 1 ? "" : "s"} imported.` +
|
|
(updatedCount ? ` ${updatedCount} updated.` : ""),
|
|
},
|
|
3000,
|
|
);
|
|
(this.$router as Router).push({ name: "contacts" });
|
|
}
|
|
}
|
|
</script>
|
|
|