diff --git a/src/App.vue b/src/App.vue index 146cc3b01..06888fa18 100644 --- a/src/App.vue +++ b/src/App.vue @@ -332,7 +332,7 @@ export default class App extends Vue { truncateLongWords(sentence: string) { return sentence .split(" ") - .map(word => (word.length > 30 ? word.slice(0, 30) + "..." : word)) + .map((word) => (word.length > 30 ? word.slice(0, 30) + "..." : word)) .join(" "); } diff --git a/src/db/tables/contacts.ts b/src/db/tables/contacts.ts index 9a438e51e..bcbd9dfc7 100644 --- a/src/db/tables/contacts.ts +++ b/src/db/tables/contacts.ts @@ -1,5 +1,12 @@ +export interface ContactMethod { + label: string; + type: string; // eg. "EMAIL", "SMS", "WHATSAPP" + value: string; +} + export interface Contact { did: string; + contactMethods?: Array<ContactMethod>; name?: string; nextPubKeyHashB64?: string; // base64-encoded SHA256 hash of next public key notes?: string; diff --git a/src/main.ts b/src/main.ts index 08525c5af..758f84466 100644 --- a/src/main.ts +++ b/src/main.ts @@ -22,6 +22,7 @@ import { faBurst, faCalendar, faCamera, + faCaretDown, faCheck, faChevronDown, faChevronLeft, @@ -98,6 +99,7 @@ library.add( faBurst, faCalendar, faCamera, + faCaretDown, faCheck, faChevronDown, faChevronLeft, diff --git a/src/views/ClaimView.vue b/src/views/ClaimView.vue index a84988cca..e33ef5ee6 100644 --- a/src/views/ClaimView.vue +++ b/src/views/ClaimView.vue @@ -346,12 +346,12 @@ <!-- Note that a similar section is found in ConfirmGiftView.vue --> <h2 - class="font-bold uppercase text-xl text-blue-500 mt-8 cursor-pointer" - @click="showVeriClaimDump = !showVeriClaimDump" - > - Details - <fa v-if="showVeriClaimDump" icon="chevron-up" /> - <fa v-else icon="chevron-right" /> + class="font-bold uppercase text-xl text-blue-500 mt-8 cursor-pointer" + @click="showVeriClaimDump = !showVeriClaimDump" + > + Details + <fa v-if="showVeriClaimDump" icon="chevron-up" /> + <fa v-else icon="chevron-right" /> </h2> <div v-if="showVeriClaimDump"> <div diff --git a/src/views/ContactEditView.vue b/src/views/ContactEditView.vue index 9616fac74..fd81be12f 100644 --- a/src/views/ContactEditView.vue +++ b/src/views/ContactEditView.vue @@ -44,8 +44,77 @@ ></textarea> </div> + <!-- Contact Methods --> + <div class="mt-4"> + <h2 class="text-lg font-medium text-gray-700">Contact Methods</h2> + <div + v-for="(method, index) in contactMethods" + :key="index" + class="flex mt-2" + > + <input + type="text" + v-model="method.label" + class="block w-1/4 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" + placeholder="Label" + /> + <input + type="text" + v-model="method.type" + class="block ml-2 w-1/4 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" + placeholder="Type" + /> + <div class="relative"> + <button + @click="toggleDropdown(index)" + class="px-2 py-1 bg-gray-200 rounded-md" + > + <fa icon="caret-down" class="fa-fw" /> + </button> + <div + v-if="dropdownIndex === index" + class="absolute bg-white border border-gray-300 rounded-md mt-1" + > + <div + @click="setMethodType(index, 'CELL')" + class="px-4 py-2 hover:bg-gray-100 cursor-pointer" + > + CELL + </div> + <div + @click="setMethodType(index, 'EMAIL')" + class="px-4 py-2 hover:bg-gray-100 cursor-pointer" + > + EMAIL + </div> + <div + @click="setMethodType(index, 'WHATSAPP')" + class="px-4 py-2 hover:bg-gray-100 cursor-pointer" + > + WHATSAPP + </div> + </div> + </div> + <input + type="text" + v-model="method.value" + class="block ml-2 w-1/2 border border-gray-300 rounded-md shadow-sm focus:ring-indigo-500 focus:border-indigo-500" + placeholder="Number, email, etc." + /> + <button @click="removeContactMethod(index)" class="ml-2 text-red-500"> + <fa icon="trash-can" class="fa-fw" /> + </button> + </div> + <button @click="addContactMethod" class="mt-2"> + <fa + icon="plus" + class="fa-fw px-2 py-2.5 bg-green-500 text-green-100 rounded-full" + /> + </button> + </div> + <!-- Save Button --> - <div class="mt-4 flex justify-between"> + <div class="mt-8 flex justify-between"> <button class="px-4 py-2 bg-blue-500 text-white rounded-md" @click="saveEdit" @@ -63,6 +132,7 @@ </template> <script lang="ts"> +import * as R from "ramda"; import { Component, Vue } from "vue-facing-decorator"; import { RouteLocation, Router } from "vue-router"; @@ -70,7 +140,7 @@ import QuickNav from "@/components/QuickNav.vue"; import TopMessage from "@/components/TopMessage.vue"; import { AppString, NotificationIface } from "@/constants/app"; import { db } from "@/db/index"; -import { Contact } from "@/db/tables/contacts"; +import { Contact, ContactMethod } from "@/db/tables/contacts"; @Component({ components: { @@ -88,6 +158,8 @@ export default class ContactEditView extends Vue { }; contactName = ""; contactNotes = ""; + contactMethods: Array<ContactMethod> = []; + dropdownIndex: number | null = null; AppString = AppString; @@ -98,6 +170,7 @@ export default class ContactEditView extends Vue { this.contact = contact; this.contactName = contact.name || ""; this.contactNotes = contact.notes || ""; + this.contactMethods = contact.contactMethods || []; } else { this.$notify({ group: "alert", @@ -110,16 +183,52 @@ export default class ContactEditView extends Vue { } } + addContactMethod() { + this.contactMethods.push({ label: "", type: "", value: "" }); + } + + removeContactMethod(index: number) { + this.contactMethods.splice(index, 1); + } + + toggleDropdown(index: number) { + this.dropdownIndex = this.dropdownIndex === index ? null : index; + } + + setMethodType(index: number, type: string) { + this.contactMethods[index].type = type; + this.dropdownIndex = null; + } + async saveEdit() { + // without this conversion, "Failed to execute 'put' on 'IDBObjectStore': [object Array] could not be cloned." + const contactMethodsObj = JSON.parse(JSON.stringify(this.contactMethods)); + const contactMethods = contactMethodsObj.map((method: ContactMethod) => + R.set(R.lensProp("type"), method.type.toUpperCase(), method), + ); + if (!R.equals(contactMethodsObj, contactMethods)) { + this.contactMethods = contactMethods; + this.$notify( + { + group: "alert", + type: "warning", + title: "Contact Methods Updated", + text: "Note that some methods have been updated, such as uppercasing 'email' to 'EMAIL'. Save again if the changes are acceptable.", + }, + 15000, + ); + return; + } await db.contacts.update(this.contact.did, { name: this.contactName, notes: this.contactNotes, + contactMethods: contactMethods, }); this.$notify({ group: "alert", type: "success", title: "Notes Saved", - text: "The contact notes have been updated successfully.", + text: "The contact info has been updated successfully.", }); (this.$router as Router).push({ path: "/did/" + encodeURIComponent(this.contact.did), diff --git a/src/views/ContactsView.vue b/src/views/ContactsView.vue index 18827e7d1..963fccce0 100644 --- a/src/views/ContactsView.vue +++ b/src/views/ContactsView.vue @@ -174,7 +174,9 @@ data-testId="contactCheckOne" /> - <h2 class="text-base font-semibold ml-2 w-1/3 truncate flex-shrink-0"> + <h2 + class="text-base font-semibold ml-2 w-1/3 truncate flex-shrink-0" + > {{ contactNameNonBreakingSpace(contact.name) }} </h2> @@ -191,8 +193,7 @@ <span class="ml-4 text-sm overflow-hidden">{{ shortDid(contact.did) - }}</span - > + }}</span> </div> <div class="ml-4 text-sm"> {{ contact.notes }}