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