refactor: make the meeting member "set visibility" screen much like the organizer's "admit" screen

This commit is contained in:
2025-10-26 14:08:30 -06:00
parent 723a0095a0
commit 9e1c267bc0
7 changed files with 196 additions and 193 deletions

View File

@@ -1,4 +1,5 @@
<template>
<div>
<div class="space-y-4">
<!-- Loading State -->
<div
@@ -48,7 +49,7 @@
<button
class="text-sm bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
title="Refresh members list now"
@click="manualRefresh"
@click="refreshData(false)"
>
<font-awesome icon="rotate" :class="{ 'fa-spin': isLoading }" />
Refresh
@@ -163,7 +164,7 @@
<button
class="text-sm bg-gradient-to-b from-slate-400 to-slate-700 shadow-[inset_0_-1px_0_0_rgba(0,0,0,0.5)] text-white px-3 py-1.5 rounded-md"
title="Refresh members list now"
@click="manualRefresh"
@click="refreshData(false)"
>
<font-awesome icon="rotate" :class="{ 'fa-spin': isLoading }" />
Refresh
@@ -177,43 +178,44 @@
</div>
</div>
<!-- Admit Pending Members Dialog Component -->
<!-- This Admit component is for the organizer to admit pending members to the meeting -->
<AdmitPendingMembersDialog
ref="admitPendingMembersDialog"
:active-did="activeDid"
:api-server="apiServer"
:pending-members-data="pendingMembersData"
@close="closeAdmitPendingDialog"
@close="closeMemberSelectionDialogCallback"
/>
<!-- This Bulk Visibility component is for non-organizer members to add other members to their contacts and set their visibility -->
<SetBulkVisibilityDialog
:visible="visibleBulkVisibilityDialog"
ref="setBulkVisibilityDialog"
:active-did="activeDid"
:api-server="apiServer"
:members-data="pendingMembersData"
@close="closeSetBulkVisibilityDialog"
@close="closeMemberSelectionDialogCallback"
/>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
import {
errorStringForLog,
getHeaders,
register,
serverMessageForUser,
} from "../libs/endorserServer";
import { decryptMessage } from "../libs/crypto";
import { Contact } from "../db/tables/contacts";
import * as libsUtil from "../libs/util";
import { NotificationIface } from "../constants/app";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import { NotificationIface } from "@/constants/app";
import {
NOTIFY_ADD_CONTACT_FIRST,
NOTIFY_CONTINUE_WITHOUT_ADDING,
} from "@/constants/notifications";
import { SOMEONE_UNNAMED } from "@/constants/entities";
import {
errorStringForLog,
getHeaders,
register,
serverMessageForUser,
} from "@/libs/endorserServer";
import { decryptMessage } from "@/libs/crypto";
import { Contact } from "@/db/tables/contacts";
import { MemberData } from "@/interfaces";
import * as libsUtil from "@/libs/util";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import AdmitPendingMembersDialog from "./AdmitPendingMembersDialog.vue";
import SetBulkVisibilityDialog from "./SetBulkVisibilityDialog.vue";
@@ -263,16 +265,6 @@ export default class MembersList extends Vue {
activeDid = "";
apiServer = "";
// Admit Pending Members Dialog state
pendingMembersData: Array<{
did: string;
name: string;
isContact: boolean;
member: { memberId: string };
}> = [];
visibleBulkVisibilityDialog = false;
// Auto-refresh functionality
countdownTimer = 10;
autoRefreshInterval: NodeJS.Timeout | null = null;
@@ -302,16 +294,6 @@ export default class MembersList extends Vue {
this.refreshData();
}
async refreshData(showPendingEvenIfAllWereIgnored = false) {
// Force refresh both contacts and members
this.contacts = await this.$getAllContacts();
await this.fetchMembers();
// Check if we should show the admit pending members dialog first
this.checkAndShowAdmitPendingDialog(showPendingEvenIfAllWereIgnored);
}
async fetchMembers() {
try {
this.isLoading = true;
@@ -408,8 +390,22 @@ export default class MembersList extends Vue {
);
}
} else {
// non-organizers only get visible members from server
members = this.decryptedMembers;
// non-organizers only get visible members from server, plus themselves
// this is a stub for this user just in case they are waiting to get in
// which is especially useful so they can see their own DID
const currentUser: DecryptedMember = {
member: {
admitted: false,
content: "{}",
memberId: -1,
},
name: this.firstName,
did: this.activeDid,
isRegistered: false,
};
const otherMembersPlusUser = [ currentUser, ...this.decryptedMembers ];
members = otherMembersPlusUser;
}
// Sort members according to priority:
@@ -462,61 +458,51 @@ export default class MembersList extends Vue {
return this.contacts.find((contact) => contact.did === did);
}
getPendingMembers(): {
did: string;
name: string;
isContact: boolean;
member: { memberId: string };
}[] {
getPendingMembersToAdmit(): MemberData[] {
return this.decryptedMembers
.filter((member) => {
// Exclude the current user
if (member.did === this.activeDid) {
return false;
}
// Only include non-admitted members
return !member.member.admitted;
})
.map((member) => ({
did: member.did,
name: member.name,
isContact: !!this.getContactFor(member.did),
member: {
memberId: member.member.memberId.toString(),
},
}));
.filter((member) =>
member.did !== this.activeDid && !member.member.admitted
)
.map(this.convertDecryptedMemberToMemberData);
}
getNonContactMembers(): {
did: string;
name: string;
isContact: boolean;
member: { memberId: string };
}[] {
getNonContactMembers(): MemberData[] {
return this.decryptedMembers
.filter((member) => !this.getContactFor(member.did))
.map((member) => ({
did: member.did,
name: member.name,
isContact: false,
member: {
memberId: member.member.memberId.toString(),
},
}));
.filter((member) =>
member.did !== this.activeDid && !this.getContactFor(member.did)
)
.map(this.convertDecryptedMemberToMemberData);
}
convertDecryptedMemberToMemberData(
decryptedMember: DecryptedMember,
): MemberData {
return {
did: decryptedMember.did,
name: decryptedMember.name,
isContact: !!this.getContactFor(decryptedMember.did),
member: {
memberId: decryptedMember.member.memberId.toString(),
},
};
}
/**
* Show the admit pending members dialog if conditions are met
*/
checkAndShowAdmitPendingDialog(showPendingEvenIfAllWereIgnored = false) {
async refreshData(bypassPromptIfAllWereIgnored = true) {
// Force refresh both contacts and members
this.contacts = await this.$getAllContacts();
await this.fetchMembers();
const pendingMembers = this.isOrganizer
? this.getPendingMembers()
? this.getPendingMembersToAdmit()
: this.getNonContactMembers();
if (pendingMembers.length === 0) {
this.startAutoRefresh();
return;
}
if (!showPendingEvenIfAllWereIgnored) {
if (bypassPromptIfAllWereIgnored) {
// only show if there are pending members that have not been ignored
const pendingMembersNotIgnored = pendingMembers.filter(
(member) => !this.previousMemberDidsIgnored.includes(member.did),
@@ -528,31 +514,21 @@ export default class MembersList extends Vue {
}
}
this.stopAutoRefresh();
this.pendingMembersData = pendingMembers;
if (this.isOrganizer) {
(
this.$refs.admitPendingMembersDialog as AdmitPendingMembersDialog
).open();
).open(pendingMembers);
} else {
this.visibleBulkVisibilityDialog = true;
(
this.$refs.setBulkVisibilityDialog as SetBulkVisibilityDialog
).open(pendingMembers);
}
}
// Admit Pending Members Dialog methods
async closeAdmitPendingDialog(
async closeMemberSelectionDialogCallback(
result: { notSelectedMemberDids: string[] } | undefined,
) {
this.pendingMembersData = [];
this.previousMemberDidsIgnored = result?.notSelectedMemberDids || [];
await this.refreshData();
}
async closeSetBulkVisibilityDialog(
result: { notSelectedMemberDids: string[] } | undefined,
) {
this.visibleBulkVisibilityDialog = false;
this.pendingMembersData = [];
this.previousMemberDidsIgnored = result?.notSelectedMemberDids || [];
await this.refreshData();
@@ -697,6 +673,7 @@ export default class MembersList extends Vue {
}
startAutoRefresh() {
this.stopAutoRefresh();
this.lastRefreshTime = Date.now();
this.countdownTimer = 10;
@@ -726,17 +703,6 @@ export default class MembersList extends Vue {
}
}
async manualRefresh() {
// Clear existing auto-refresh interval
if (this.autoRefreshInterval) {
clearInterval(this.autoRefreshInterval);
this.autoRefreshInterval = null;
}
// Trigger immediate refresh
await this.refreshData(true);
}
beforeDestroy() {
this.stopAutoRefresh();
}