Trent Larson
4 months ago
3 changed files with 240 additions and 11 deletions
@ -0,0 +1,179 @@ |
|||
<template> |
|||
<QuickNav selected="Contacts"></QuickNav> |
|||
<section id="Content" class="p-6 pb-24 max-w-3xl mx-auto"> |
|||
<!-- Heading --> |
|||
<h1 id="ViewHeading" class="text-4xl text-center font-light pt-4 mb-8"> |
|||
Contact Import |
|||
</h1> |
|||
|
|||
<div v-if="sameCount > 0"> |
|||
{{ sameCount }} contact{{ sameCount == 1 ? "" : "s" }} are the same as |
|||
existing contacts. |
|||
</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="text-sm 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-1.5 rounded-l-md" |
|||
@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 } 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 GiftedDialog from "@/components/GiftedDialog.vue"; |
|||
import OfferDialog from "@/components/OfferDialog.vue"; |
|||
|
|||
@Component({ |
|||
components: { GiftedDialog, EntityIcon, OfferDialog, QuickNav }, |
|||
}) |
|||
export default class ContactImportView extends Vue { |
|||
$notify!: (notification: NotificationIface, timeout?: number) => void; |
|||
|
|||
AppString = AppString; |
|||
libsUtil = libsUtil; |
|||
R = R; |
|||
|
|||
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; |
|||
sameCount = 0; |
|||
|
|||
async created() { |
|||
// 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( |
|||
false, |
|||
); |
|||
|
|||
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++; |
|||
} |
|||
} else { |
|||
// automatically import new data |
|||
this.contactsSelected[i] = true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
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 = R.clone(this.contactsImporting[i]); // cloning to avoid the problem with a Proxy object |
|||
const existingContact = this.contactsExisting[contact.did]; |
|||
if (existingContact) { |
|||
await db.contacts.update(contact.did, contact); |
|||
updatedCount++; |
|||
} else { |
|||
await db.contacts.add(contact); |
|||
importedCount++; |
|||
} |
|||
} |
|||
} |
|||
this.importing = false; |
|||
|
|||
this.$notify( |
|||
{ |
|||
group: "alert", |
|||
type: "success", |
|||
title: "Import Success", |
|||
text: |
|||
`${importedCount} contact${importedCount == 1 ? "" : "s"} imported.` + |
|||
(updatedCount ? ` ${updatedCount} updated.` : ""), |
|||
}, |
|||
3000, |
|||
); |
|||
(this.$router as Router).push({ name: "contacts" }); |
|||
} |
|||
} |
|||
</script> |
Loading…
Reference in new issue