Browse Source

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

pull/211/head
Trent Larson 1 week ago
parent
commit
9e1c267bc0
  1. 1
      .husky/pre-commit
  2. 53
      src/components/AdmitPendingMembersDialog.vue
  3. 70
      src/components/DeepLinkTest.vue
  4. 174
      src/components/MembersList.vue
  5. 73
      src/components/SetBulkVisibilityDialog.vue
  6. 1
      src/interfaces/index.ts
  7. 9
      src/interfaces/user.ts

1
.husky/pre-commit

@ -37,7 +37,6 @@ if [ "$git_status_before" != "$git_status_after" ]; then
echo echo
printf "Choose [c/a]: " printf "Choose [c/a]: "
# The `< /dev/tty` is necessary to make read work in git's non-interactive shell # The `< /dev/tty` is necessary to make read work in git's non-interactive shell
# The `|| choice="a"` is useful to set a default value to abort if read fails
read choice < /dev/tty read choice < /dev/tty
case $choice in case $choice in

53
src/components/AdmitPendingMembersDialog.vue

@ -16,7 +16,7 @@
<table <table
class="w-full border-collapse border border-slate-300 text-sm text-start" class="w-full border-collapse border border-slate-300 text-sm text-start"
> >
<thead v-if="pendingMembersData && pendingMembersData.length > 0"> <thead v-if="membersData && membersData.length > 0">
<tr class="bg-slate-100 font-medium"> <tr class="bg-slate-100 font-medium">
<th class="border border-slate-300 px-3 py-2"> <th class="border border-slate-300 px-3 py-2">
<label class="flex items-center gap-2"> <label class="flex items-center gap-2">
@ -33,7 +33,7 @@
</thead> </thead>
<tbody> <tbody>
<!-- Dynamic data from MembersList --> <!-- Dynamic data from MembersList -->
<tr v-if="!pendingMembersData || pendingMembersData.length === 0"> <tr v-if="!membersData || membersData.length === 0">
<td <td
class="border border-slate-300 px-3 py-2 text-center italic text-gray-500" class="border border-slate-300 px-3 py-2 text-center italic text-gray-500"
> >
@ -41,7 +41,7 @@
</td> </td>
</tr> </tr>
<tr <tr
v-for="member in pendingMembersData || []" v-for="member in membersData || []"
:key="member.member.memberId" :key="member.member.memberId"
> >
<td class="border border-slate-300 px-3 py-2"> <td class="border border-slate-300 px-3 py-2">
@ -85,7 +85,7 @@
<div class="space-y-2"> <div class="space-y-2">
<button <button
v-if="pendingMembersData && pendingMembersData.length > 0" v-if="membersData && membersData.length > 0"
:disabled="!hasSelectedMembers" :disabled="!hasSelectedMembers"
:class="[ :class="[
'block w-full text-center text-md font-bold uppercase px-2 py-2 rounded-md', 'block w-full text-center text-md font-bold uppercase px-2 py-2 rounded-md',
@ -95,7 +95,7 @@
]" ]"
@click="admitWithVisibility" @click="admitWithVisibility"
> >
Admit + Add Contact Admit + Add to Contacts
</button> </button>
<button <button
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md" class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
@ -114,23 +114,15 @@ import { Vue, Component, Prop } from "vue-facing-decorator";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { SOMEONE_UNNAMED } from "@/constants/entities"; import { SOMEONE_UNNAMED } from "@/constants/entities";
import { MemberData } from "@/interfaces";
import { setVisibilityUtil, getHeaders } from "@/libs/endorserServer"; import { setVisibilityUtil, getHeaders } from "@/libs/endorserServer";
import { createNotifyHelpers } from "@/utils/notify"; import { createNotifyHelpers } from "@/utils/notify";
interface PendingMemberData {
did: string;
name: string;
isContact: boolean;
member: {
memberId: string;
};
}
@Component({ @Component({
mixins: [PlatformServiceMixin], mixins: [PlatformServiceMixin],
emits: ["close"],
}) })
export default class AdmitPendingMembersDialog extends Vue { export default class AdmitPendingMembersDialog extends Vue {
@Prop({ default: () => [] }) pendingMembersData!: PendingMemberData[];
@Prop({ default: "" }) activeDid!: string; @Prop({ default: "" }) activeDid!: string;
@Prop({ default: "" }) apiServer!: string; @Prop({ default: "" }) apiServer!: string;
@ -144,6 +136,7 @@ export default class AdmitPendingMembersDialog extends Vue {
notify!: ReturnType<typeof createNotifyHelpers>; notify!: ReturnType<typeof createNotifyHelpers>;
// Component state // Component state
membersData: MemberData[] = [];
selectedMembers: string[] = []; selectedMembers: string[] = [];
visible = false; visible = false;
@ -156,30 +149,29 @@ export default class AdmitPendingMembersDialog extends Vue {
} }
get isAllSelected() { get isAllSelected() {
if (!this.pendingMembersData || this.pendingMembersData.length === 0) if (!this.membersData || this.membersData.length === 0) return false;
return false; return this.membersData.every((member) =>
return this.pendingMembersData.every((member) =>
this.selectedMembers.includes(member.did), this.selectedMembers.includes(member.did),
); );
} }
get isIndeterminate() { get isIndeterminate() {
if (!this.pendingMembersData || this.pendingMembersData.length === 0) if (!this.membersData || this.membersData.length === 0) return false;
return false; const selectedCount = this.membersData.filter((member) =>
const selectedCount = this.pendingMembersData.filter((member) =>
this.selectedMembers.includes(member.did), this.selectedMembers.includes(member.did),
).length; ).length;
return selectedCount > 0 && selectedCount < this.pendingMembersData.length; return selectedCount > 0 && selectedCount < this.membersData.length;
} }
created() { created() {
this.notify = createNotifyHelpers(this.$notify); this.notify = createNotifyHelpers(this.$notify);
} }
open() { open(members: MemberData[]) {
this.visible = true; this.visible = true;
this.membersData = members;
// Select all by default // Select all by default
this.selectedMembers = this.pendingMembersData.map((member) => member.did); this.selectedMembers = this.membersData.map((member) => member.did);
} }
close(notSelectedMemberDids: string[]) { close(notSelectedMemberDids: string[]) {
@ -188,21 +180,18 @@ export default class AdmitPendingMembersDialog extends Vue {
} }
cancel() { cancel() {
this.close(this.pendingMembersData.map((member) => member.did)); this.close(this.membersData.map((member) => member.did));
} }
toggleSelectAll() { toggleSelectAll() {
if (!this.pendingMembersData || this.pendingMembersData.length === 0) if (!this.membersData || this.membersData.length === 0) return;
return;
if (this.isAllSelected) { if (this.isAllSelected) {
// Deselect all // Deselect all
this.selectedMembers = []; this.selectedMembers = [];
} else { } else {
// Select all // Select all
this.selectedMembers = this.pendingMembersData.map( this.selectedMembers = this.membersData.map((member) => member.did);
(member) => member.did,
);
} }
} }
@ -221,10 +210,10 @@ export default class AdmitPendingMembersDialog extends Vue {
async admitWithVisibility() { async admitWithVisibility() {
try { try {
const selectedMembers = this.pendingMembersData.filter((member) => const selectedMembers = this.membersData.filter((member) =>
this.selectedMembers.includes(member.did), this.selectedMembers.includes(member.did),
); );
const notSelectedMembers = this.pendingMembersData.filter( const notSelectedMembers = this.membersData.filter(
(member) => !this.selectedMembers.includes(member.did), (member) => !this.selectedMembers.includes(member.did),
); );

70
src/components/DeepLinkTest.vue

@ -0,0 +1,70 @@
<template>
<div>
<p>Deep Link Test Component Loaded</p>
<p>Platform: {{ platform }}</p>
<p>Status: {{ status }}</p>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from "vue";
import { useRouter } from "vue-router";
const platform = process.env.VITE_PLATFORM;
const status = ref("Initializing...");
const router = useRouter();
onMounted(async () => {
console.log("[DeepLinkTest] Component mounted, platform:", platform);
alert(`[DeepLinkTest] Component mounted, platform: ${platform}`);
if (platform !== "capacitor") {
status.value = "Not Capacitor platform";
return;
}
try {
console.log("[DeepLinkTest] Importing Capacitor App...");
const { App } = await import("@capacitor/app");
console.log("[DeepLinkTest] Getting app info...");
const appInfo = await App.getInfo();
console.log("[DeepLinkTest] App info:", appInfo);
alert(`[DeepLinkTest] App version: ${appInfo.version}`);
console.log("[DeepLinkTest] Registering appUrlOpen listener...");
App.addListener("appUrlOpen", (data: { url: string }) => {
console.log("[DeepLinkTest] Deep link received:", data.url);
alert(`[DeepLinkTest] Deep link received: ${data.url}`);
// Simple URL parsing without DeepLinkHandler
try {
const url = new URL(data.url);
const path = url.pathname;
console.log("[DeepLinkTest] Parsed path:", path);
// Simple navigation test
if (path.startsWith("/claim/")) {
const claimId = path.replace("/claim/", "");
router.push(`/claim/${claimId}`);
alert(`[DeepLinkTest] Navigated to claim: ${claimId}`);
} else {
router.push(path);
alert(`[DeepLinkTest] Navigated to: ${path}`);
}
} catch (error) {
console.error("[DeepLinkTest] URL parsing error:", error);
alert(`[DeepLinkTest] URL parsing error: ${error}`);
}
});
status.value = "Deep link listener registered";
console.log("[DeepLinkTest] Setup complete");
alert("[DeepLinkTest] Setup complete");
} catch (error) {
console.error("[DeepLinkTest] Error:", error);
alert(`[DeepLinkTest] Error: ${error}`);
status.value = `Error: ${error}`;
}
});
</script>

174
src/components/MembersList.vue

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

73
src/components/SetBulkVisibilityDialog.vue

@ -3,15 +3,14 @@
<div class="dialog"> <div class="dialog">
<div class="text-slate-900 text-center"> <div class="text-slate-900 text-center">
<h3 class="text-lg font-semibold leading-[1.25] mb-2"> <h3 class="text-lg font-semibold leading-[1.25] mb-2">
Set Visibility to Meeting Members Add Members to Contacts
</h3> </h3>
<p class="text-sm mb-4"> <p class="text-sm mb-4">
Would you like to <b>make your activities visible</b> to the following Would you like to add these members to your contacts?
members? (This will also add them as contacts if they aren't already.)
</p> </p>
<!-- Custom table area - you can customize this --> <!-- Custom table area - you can customize this -->
<div v-if="shouldInitializeSelection" class="mb-4"> <div class="mb-4">
<table <table
class="w-full border-collapse border border-slate-300 text-sm text-start" class="w-full border-collapse border border-slate-300 text-sm text-start"
> >
@ -36,7 +35,7 @@
<td <td
class="border border-slate-300 px-3 py-2 text-center italic text-gray-500" class="border border-slate-300 px-3 py-2 text-center italic text-gray-500"
> >
No members need visibility settings No members are not in your contacts
</td> </td>
</tr> </tr>
<tr <tr
@ -80,15 +79,13 @@
]" ]"
@click="setVisibilityForSelectedMembers" @click="setVisibilityForSelectedMembers"
> >
Set Visibility Add to Contacts
</button> </button>
<button <button
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md" class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
@click="cancel" @click="cancel"
> >
{{ Maybe Later
membersData && membersData.length > 0 ? "Maybe Later" : "Cancel"
}}
</button> </button>
</div> </div>
</div> </div>
@ -101,24 +98,15 @@ import { Vue, Component, Prop } from "vue-facing-decorator";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { SOMEONE_UNNAMED } from "@/constants/entities"; import { SOMEONE_UNNAMED } from "@/constants/entities";
import { MemberData } from "@/interfaces";
import { setVisibilityUtil } from "@/libs/endorserServer"; import { setVisibilityUtil } from "@/libs/endorserServer";
import { createNotifyHelpers } from "@/utils/notify"; import { createNotifyHelpers } from "@/utils/notify";
interface MemberData {
did: string;
name: string;
isContact: boolean;
member: {
memberId: string;
};
}
@Component({ @Component({
mixins: [PlatformServiceMixin], mixins: [PlatformServiceMixin],
emits: ["close"],
}) })
export default class SetBulkVisibilityDialog extends Vue { export default class SetBulkVisibilityDialog extends Vue {
@Prop({ default: false }) visible!: boolean;
@Prop({ default: () => [] }) membersData!: MemberData[];
@Prop({ default: "" }) activeDid!: string; @Prop({ default: "" }) activeDid!: string;
@Prop({ default: "" }) apiServer!: string; @Prop({ default: "" }) apiServer!: string;
@ -132,8 +120,9 @@ export default class SetBulkVisibilityDialog extends Vue {
notify!: ReturnType<typeof createNotifyHelpers>; notify!: ReturnType<typeof createNotifyHelpers>;
// Component state // Component state
membersData: MemberData[] = [];
selectedMembers: string[] = []; selectedMembers: string[] = [];
selectionInitialized = false; visible = false;
// Constants // Constants
// In Vue templates, imported constants need to be explicitly made available to the template // In Vue templates, imported constants need to be explicitly made available to the template
@ -158,29 +147,24 @@ export default class SetBulkVisibilityDialog extends Vue {
return selectedCount > 0 && selectedCount < this.membersData.length; return selectedCount > 0 && selectedCount < this.membersData.length;
} }
get shouldInitializeSelection() {
// This method will initialize selection when the dialog opens
if (!this.selectionInitialized) {
this.initializeSelection();
this.selectionInitialized = true;
}
return true;
}
created() { created() {
this.notify = createNotifyHelpers(this.$notify); this.notify = createNotifyHelpers(this.$notify);
} }
initializeSelection() { open(members: MemberData[]) {
// Reset selection when dialog opens this.visible = true;
this.selectedMembers = []; this.membersData = members;
// Select all by default // Select all by default
this.selectedMembers = this.membersData.map((member) => member.did); this.selectedMembers = this.membersData.map((member) => member.did);
} }
resetSelection() { close(notSelectedMemberDids: string[]) {
this.selectedMembers = []; this.visible = false;
this.selectionInitialized = false; this.$emit("close", { notSelectedMemberDids: notSelectedMemberDids });
}
cancel() {
this.close(this.membersData.map((member) => member.did));
} }
toggleSelectAll() { toggleSelectAll() {
@ -248,11 +232,7 @@ export default class SetBulkVisibilityDialog extends Vue {
5000, 5000,
); );
// Emit success event this.close(notSelectedMembers.map((member) => member.did));
this.$emit("close", {
notSelectedMemberDids: notSelectedMembers.map((member) => member.did),
});
this.close();
} catch (error) { } catch (error) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.error("Error setting visibility:", error); console.error("Error setting visibility:", error);
@ -325,16 +305,5 @@ export default class SetBulkVisibilityDialog extends Vue {
5000, 5000,
); );
} }
close() {
this.resetSelection();
this.$emit("close", {
notSelectedMemberDids: this.membersData.map((member) => member.did),
});
}
cancel() {
this.close();
}
} }
</script> </script>

1
src/interfaces/index.ts

@ -27,6 +27,7 @@ export type {
export type { export type {
// From user.ts // From user.ts
UserInfo, UserInfo,
MemberData,
} from "./user"; } from "./user";
export * from "./limits"; export * from "./limits";

9
src/interfaces/user.ts

@ -6,3 +6,12 @@ export interface UserInfo {
profileImageUrl?: string; profileImageUrl?: string;
nextPublicEncKeyHash?: string; nextPublicEncKeyHash?: string;
} }
export interface MemberData {
did: string;
name: string;
isContact: boolean;
member: {
memberId: string;
};
}

Loading…
Cancel
Save