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