You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

174 lines
4.9 KiB

<template>
<div class="space-y-4">
<!-- Loading State -->
<div v-if="isLoading" class="flex justify-center items-center py-8">
<fa icon="spinner" class="fa-spin-pulse" />
</div>
<!-- Members List -->
<div v-else class="space-y-4">
<div v-if="missingMyself" class="py-4">
You are not yet admitted. The organizer will admit you.
</div>
<div
v-for="member in decryptedMembers"
:key="member.member.memberId"
class="p-4 bg-gray-50 rounded-lg"
>
<div class="flex items-center">
<h3 class="text-lg font-medium">{{ member.name }}</h3>
<button
v-if="isOrganizer && member.did !== activeDid"
@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"
:title="member.member.admitted ? 'Remove member' : 'Admit member'"
>
<fa :icon="member.member.admitted ? 'minus' : 'plus'" class="text-sm" />
</button>
</div>
<p class="text-sm text-gray-600">{{ member.did }}</p>
</div>
<p v-if="members.length === 0" class="text-gray-500 py-4">
No members have joined this meeting yet
</p>
<p
v-if="decryptedMembers.length < members.length"
class="text-center text-red-600 py-4"
>
{{
decryptFailureMessage ||
"Your password failed. Please go back and try again."
}}
</p>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-facing-decorator";
import { logConsoleAndDb, retrieveSettingsForActiveAccount } from "@/db/index";
import {
errorStringForLog,
getHeaders,
serverMessageForUser,
} from "@/libs/endorserServer";
import { decryptMessage } from "@/libs/crypto";
interface Member {
admitted: boolean;
content: string;
memberId: number;
}
interface DecryptedMember {
member: Member;
name: string;
did: string;
}
@Component
export default class MembersList extends Vue {
@Prop({ required: true }) password!: string;
@Prop({ default: "Your password failed. Please go back and try again." })
decryptFailureMessage!: string;
decryptedMembers: DecryptedMember[] = [];
missingPassword = false;
missingMyself = false;
isLoading = false;
isOrganizer = false;
members: Member[] = [];
activeDid = "";
apiServer = "";
async toggleAdmission(member: Member) {
try {
const headers = await getHeaders(this.activeDid);
await this.axios.put(
`${this.apiServer}/api/partner/groupOnboardMember/${member.memberId}`,
{ admitted: !member.admitted },
{ headers }
);
// Update local state
member.admitted = !member.admitted;
} catch (error) {
logConsoleAndDb("Error toggling admission: " + errorStringForLog(error), true);
this.$emit("error", serverMessageForUser(error) || "Failed to update member admission status.");
}
}
async created() {
const settings = await retrieveSettingsForActiveAccount();
this.activeDid = settings.activeDid || "";
this.apiServer = settings.apiServer || "";
await this.fetchMembers();
}
async fetchMembers() {
this.isLoading = true;
try {
const headers = await getHeaders(this.activeDid);
const response = await this.axios.get(
`${this.apiServer}/api/partner/groupOnboardMembers`,
{ headers },
);
if (response.data && response.data.data) {
this.members = response.data.data;
await this.decryptMemberContents();
}
} catch (error) {
logConsoleAndDb(
"Error fetching members: " + errorStringForLog(error),
true,
);
this.$emit(
"error",
serverMessageForUser(error) || "Failed to fetch members.",
);
} finally {
this.isLoading = false;
}
}
async decryptMemberContents() {
this.decryptedMembers = [];
if (!this.password) {
this.missingPassword = true;
return;
}
let isFirstEntry = true, foundMyself = false;
for (const member of this.members) {
try {
const decryptedContent = await decryptMessage(
member.content,
this.password,
);
const content = JSON.parse(decryptedContent);
this.decryptedMembers.push({
member: member,
name: content.name,
did: content.did,
});
if (isFirstEntry && content.did === this.activeDid) {
this.isOrganizer = true;
}
if (content.did === this.activeDid) {
foundMyself = true;
}
} catch (error) {
// do nothing, relying on the count of members to determine if there was an error
}
isFirstEntry = false;
}
if (!foundMyself) {
this.missingMyself = true;
}
}
}
</script>