4 changed files with 175 additions and 110 deletions
@ -0,0 +1,107 @@ |
|||||
|
<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-for="member in decryptedMembers" :key="member.memberId" |
||||
|
class="p-4 bg-gray-50 rounded-lg"> |
||||
|
<h3 class="text-lg font-medium">{{ member.name }}</h3> |
||||
|
<p class="text-sm text-gray-600">{{ member.did }}</p> |
||||
|
</div> |
||||
|
|
||||
|
<p v-if="members.length === 0" class="text-center 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 { |
||||
|
memberId: number; |
||||
|
content: string; |
||||
|
} |
||||
|
|
||||
|
interface DecryptedMember { |
||||
|
memberId: number; |
||||
|
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; |
||||
|
isLoading = false; |
||||
|
members: Member[] = []; |
||||
|
activeDid = ''; |
||||
|
apiServer = ''; |
||||
|
|
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
for (const member of this.members) { |
||||
|
try { |
||||
|
const decryptedContent = await decryptMessage(member.content, this.password); |
||||
|
const content = JSON.parse(decryptedContent); |
||||
|
|
||||
|
this.decryptedMembers.push({ |
||||
|
memberId: member.memberId, |
||||
|
name: content.name, |
||||
|
did: content.did, |
||||
|
}); |
||||
|
} catch (error) { |
||||
|
// do nothing, relying on the count of members to determine if there was an error |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
Loading…
Reference in new issue