Browse Source

add an icon for each attendee to add them to their contact list

master
Trent Larson 4 days ago
parent
commit
4cd130244c
  1. 137
      src/components/MembersList.vue
  2. 1
      src/views/OnboardMeetingSetupView.vue

137
src/components/MembersList.vue

@ -7,27 +7,79 @@
<!-- Members List --> <!-- Members List -->
<div v-else class="space-y-4"> <div v-else class="space-y-4">
<div v-if="missingMyself" class="py-4"> <div v-if="missingMyself" class="py-4">
You are not yet admitted. The organizer will admit you. You are not yet admitted. The organizer will admit you.
</div> </div>
<div v-if="members.length > 0" class="flex justify-end">
<button
@click="fetchMembers"
class="w-8 h-8 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
title="Refresh members list"
>
<fa icon="rotate" :class="{ 'fa-spin': isLoading }" />
</button>
</div>
<div <div
v-for="member in decryptedMembers" v-for="member in decryptedMembers"
:key="member.member.memberId" :key="member.member.memberId"
class="p-4 bg-gray-50 rounded-lg" class="p-4 bg-gray-50 rounded-lg"
> >
<div class="flex items-center"> <div class="flex items-center justify-between">
<div class="flex">
<h3 class="text-lg font-medium">{{ member.name }}</h3> <h3 class="text-lg font-medium">{{ member.name }}</h3>
<span
v-if="showOrganizerTools && isOrganizer && member.did !== activeDid"
class="flex items-center"
>
<button <button
v-if="isOrganizer && member.did !== activeDid"
@click="toggleAdmission(member.member)" @click="toggleAdmission(member.member)"
class="ml-2 w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors" class="ml-2 w-6 h-6 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
:title="member.member.admitted ? 'Remove member' : 'Admit member'" :title="member.member.admitted ? 'Remove member' : 'Admit member'"
> >
<fa :icon="member.member.admitted ? 'minus' : 'plus'" class="text-sm" /> <fa :icon="member.member.admitted ? 'minus' : 'plus'" class="text-sm" />
</button> </button>
<button
@click="informAboutAdmission()"
class="ml-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-gray-800 transition-colors"
title="Admission info"
>
<fa icon="circle-info" class="text-base" />
</button>
</span>
</div>
<div
v-if="!isContactAlready(member.did) && member.did !== activeDid"
class="flex justify-end"
>
<button
@click="addAsContact(member)"
class="ml-1 w-8 h-8 flex items-center justify-center rounded-full bg-green-100 text-green-600 hover:bg-green-200 hover:text-green-800 transition-colors"
title="Add as contact"
>
<fa icon="circle-user" class="text-xl" />
</button>
<button
@click="informAboutAddingContact()"
class="ml-2 w-6 h-6 flex items-center justify-center rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-gray-800 transition-colors"
title="Contact info"
>
<fa icon="circle-info" class="text-base" />
</button>
</div>
</div> </div>
<p class="text-sm text-gray-600">{{ member.did }}</p> <p class="text-sm text-gray-600">{{ member.did }}</p>
</div> </div>
<div v-if="members.length > 0" class="flex justify-end mt-4">
<button
@click="fetchMembers"
class="w-8 h-8 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors"
title="Refresh members list"
>
<fa icon="rotate" :class="{ 'fa-spin': isLoading }" />
</button>
</div>
<p v-if="members.length === 0" class="text-gray-500 py-4"> <p v-if="members.length === 0" class="text-gray-500 py-4">
No members have joined this meeting yet No members have joined this meeting yet
@ -41,6 +93,7 @@
"Your password failed. Please go back and try again." "Your password failed. Please go back and try again."
}} }}
</p> </p>
</div> </div>
</div> </div>
</template> </template>
@ -48,13 +101,14 @@
<script lang="ts"> <script lang="ts">
import { Component, Vue, Prop } from "vue-facing-decorator"; import { Component, Vue, Prop } from "vue-facing-decorator";
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "@/db/index"; import { logConsoleAndDb, retrieveSettingsForActiveAccount, db } from "@/db/index";
import { import {
errorStringForLog, errorStringForLog,
getHeaders, getHeaders,
serverMessageForUser, serverMessageForUser,
} from "@/libs/endorserServer"; } from "@/libs/endorserServer";
import { decryptMessage } from "@/libs/crypto"; import { decryptMessage } from "@/libs/crypto";
import { Contact } from "@/db/tables/contacts";
interface Member { interface Member {
admitted: boolean; admitted: boolean;
@ -70,9 +124,15 @@ interface DecryptedMember {
@Component @Component
export default class MembersList extends Vue { export default class MembersList extends Vue {
$notify!: (
notification: { group: string; type: string; title: string; text: string },
timeout?: number,
) => void;
@Prop({ required: true }) password!: string; @Prop({ required: true }) password!: string;
@Prop({ default: "Your password failed. Please go back and try again." }) @Prop({ default: "Your password failed. Please go back and try again." })
decryptFailureMessage!: string; decryptFailureMessage!: string;
@Prop({ default: false }) showOrganizerTools!: boolean;
decryptedMembers: DecryptedMember[] = []; decryptedMembers: DecryptedMember[] = [];
missingPassword = false; missingPassword = false;
@ -82,6 +142,7 @@ export default class MembersList extends Vue {
members: Member[] = []; members: Member[] = [];
activeDid = ""; activeDid = "";
apiServer = ""; apiServer = "";
contacts: Array<Contact> = [];
async toggleAdmission(member: Member) { async toggleAdmission(member: Member) {
try { try {
@ -104,6 +165,7 @@ export default class MembersList extends Vue {
this.activeDid = settings.activeDid || ""; this.activeDid = settings.activeDid || "";
this.apiServer = settings.apiServer || ""; this.apiServer = settings.apiServer || "";
await this.fetchMembers(); await this.fetchMembers();
await this.loadContacts();
} }
async fetchMembers() { async fetchMembers() {
@ -170,5 +232,74 @@ export default class MembersList extends Vue {
this.missingMyself = true; this.missingMyself = true;
} }
} }
informAboutAdmission() {
this.$notify(
{
group: "alert",
type: "info",
title: "Admission info",
text: "This is to admit people to the meeting. A '+' symbol means they are not yet admitted and you can admit them. A '-' means you can remove them.",
},
10000,
);
}
informAboutAddingContact() {
this.$notify(
{
group: "alert",
type: "info",
title: "Contact info",
text: "This is to add people to your contacts. If you want to remove them, you must do that from the contacts screen.",
},
10000,
);
}
async loadContacts() {
this.contacts = await db.contacts.toArray();
}
isContactAlready(did: string): boolean {
return this.contacts.some(contact => contact.did === did);
}
async addAsContact(member: DecryptedMember) {
try {
const newContact = {
did: member.did,
name: member.name,
};
await db.contacts.add(newContact);
await this.loadContacts(); // Refresh contacts list
this.$notify(
{
group: "alert",
type: "success",
title: "Contact Added",
text: "They were added to your contacts.",
},
3000,
);
} catch (err) {
logConsoleAndDb("Error adding contact: " + errorStringForLog(err), true);
let message = "An error prevented adding this contact.";
if (err instanceof Error && err.message?.indexOf("already exists") > -1) {
message = "This person is already in your contact list.";
}
this.$notify(
{
group: "alert",
type: "danger",
title: "Contact Not Added",
text: message,
},
5000,
);
}
}
} }
</script> </script>

1
src/views/OnboardMeetingSetupView.vue

@ -209,6 +209,7 @@
<MembersList <MembersList
:password="currentMeeting.password || ''" :password="currentMeeting.password || ''"
decrypt-failure-message="Unable to decrypt some member information. Please check your password or have them reset theirs." decrypt-failure-message="Unable to decrypt some member information. Please check your password or have them reset theirs."
:show-organizer-tools="true"
@error="handleMembersError" @error="handleMembersError"
class="mt-8" class="mt-8"
/> />

Loading…
Cancel
Save