forked from jsnbuchanan/crowd-funder-for-time-pwa
refactor: unify member dialogs into reusable BulkMembersDialog component
- Merge AdmitPendingMembersDialog and SetBulkVisibilityDialog into single BulkMembersDialog - Add dynamic props for dialog type, title, description, button text, and empty state - Support both 'admit' and 'visibility' modes with conditional behavior - Rename setVisibilityForSelectedMembers to addContactWithVisibility for clarity - Update success counting to track contacts added vs visibility set - Improve error messages to reflect primary action of adding contacts - Update MembersList to use unified dialog with role-based configuration - Remove unused libsUtil import from MembersList - Update comments and method names to reflect unified functionality - Rename closeMemberSelectionDialogCallback to closeBulkMembersDialogCallback This consolidation eliminates ~200 lines of duplicate code while maintaining all existing functionality and improving maintainability through a single source of truth for bulk member operations.
This commit is contained in:
@@ -3,18 +3,18 @@
|
|||||||
<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">
|
||||||
Admit Pending Members
|
{{ title }}
|
||||||
</h3>
|
</h3>
|
||||||
<p class="text-sm mb-4">
|
<p class="text-sm mb-4">
|
||||||
Would you like to admit these members to the meeting and add them to
|
{{ description }}
|
||||||
your contacts?
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<!-- Custom table area - you can customize this -->
|
<!-- Member Selection Table -->
|
||||||
<div 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"
|
||||||
>
|
>
|
||||||
|
<!-- Select All Header -->
|
||||||
<thead v-if="membersData && membersData.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">
|
||||||
@@ -31,14 +31,15 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<!-- Dynamic data from MembersList -->
|
<!-- Empty State -->
|
||||||
<tr v-if="!membersData || membersData.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"
|
||||||
>
|
>
|
||||||
No pending members to admit
|
{{ emptyStateText }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<!-- Member Rows -->
|
||||||
<tr
|
<tr
|
||||||
v-for="member in membersData || []"
|
v-for="member in membersData || []"
|
||||||
:key="member.member.memberId"
|
:key="member.member.memberId"
|
||||||
@@ -79,6 +80,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<!-- Select All Footer -->
|
||||||
<tfoot v-if="membersData && membersData.length > 0">
|
<tfoot 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">
|
||||||
@@ -97,20 +99,23 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Action Buttons -->
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
|
<!-- Main Action Button -->
|
||||||
<button
|
<button
|
||||||
v-if="membersData && membersData.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',
|
||||||
hasSelectedMembers
|
hasSelectedMembers
|
||||||
? 'bg-green-600 text-white cursor-pointer'
|
? 'bg-blue-600 text-white cursor-pointer'
|
||||||
: 'bg-slate-400 text-slate-200 cursor-not-allowed',
|
: 'bg-slate-400 text-slate-200 cursor-not-allowed',
|
||||||
]"
|
]"
|
||||||
@click="admitWithVisibility"
|
@click="handleMainAction"
|
||||||
>
|
>
|
||||||
Admit + Add to Contacts
|
{{ buttonText }}
|
||||||
</button>
|
</button>
|
||||||
|
<!-- Cancel 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"
|
||||||
@@ -136,9 +141,14 @@ import { createNotifyHelpers } from "@/utils/notify";
|
|||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
emits: ["close"],
|
emits: ["close"],
|
||||||
})
|
})
|
||||||
export default class AdmitPendingMembersDialog extends Vue {
|
export default class BulkMembersDialog extends Vue {
|
||||||
@Prop({ default: "" }) activeDid!: string;
|
@Prop({ default: "" }) activeDid!: string;
|
||||||
@Prop({ default: "" }) apiServer!: string;
|
@Prop({ default: "" }) apiServer!: string;
|
||||||
|
@Prop({ required: true }) dialogType!: "admit" | "visibility";
|
||||||
|
@Prop({ required: true }) title!: string;
|
||||||
|
@Prop({ required: true }) description!: string;
|
||||||
|
@Prop({ required: true }) buttonText!: string;
|
||||||
|
@Prop({ required: true }) emptyStateText!: string;
|
||||||
|
|
||||||
// Vue notification system
|
// Vue notification system
|
||||||
$notify!: (
|
$notify!: (
|
||||||
@@ -222,6 +232,14 @@ export default class AdmitPendingMembersDialog extends Vue {
|
|||||||
return this.selectedMembers.includes(memberDid);
|
return this.selectedMembers.includes(memberDid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async handleMainAction() {
|
||||||
|
if (this.dialogType === "admit") {
|
||||||
|
await this.admitWithVisibility();
|
||||||
|
} else {
|
||||||
|
await this.addContactWithVisibility();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async admitWithVisibility() {
|
async admitWithVisibility() {
|
||||||
try {
|
try {
|
||||||
const selectedMembers = this.membersData.filter((member) =>
|
const selectedMembers = this.membersData.filter((member) =>
|
||||||
@@ -282,6 +300,61 @@ export default class AdmitPendingMembersDialog extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async addContactWithVisibility() {
|
||||||
|
try {
|
||||||
|
const selectedMembers = this.membersData.filter((member) =>
|
||||||
|
this.selectedMembers.includes(member.did),
|
||||||
|
);
|
||||||
|
const notSelectedMembers = this.membersData.filter(
|
||||||
|
(member) => !this.selectedMembers.includes(member.did),
|
||||||
|
);
|
||||||
|
|
||||||
|
let contactsAddedCount = 0;
|
||||||
|
|
||||||
|
for (const member of selectedMembers) {
|
||||||
|
try {
|
||||||
|
// If they're not a contact yet, add them as a contact first
|
||||||
|
if (!member.isContact) {
|
||||||
|
await this.addAsContact(member);
|
||||||
|
contactsAddedCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set their seesMe to true
|
||||||
|
await this.updateContactVisibility(member.did, true);
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error(`Error processing member ${member.did}:`, error);
|
||||||
|
// Continue with other members even if one fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show success notification
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "success",
|
||||||
|
title: "Contacts Added Successfully",
|
||||||
|
text: `${contactsAddedCount} member${contactsAddedCount === 1 ? "" : "s"} added as contact${contactsAddedCount === 1 ? "" : "s"}.`,
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
|
||||||
|
this.close(notSelectedMembers.map((member) => member.did));
|
||||||
|
} catch (error) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.error("Error adding contacts:", error);
|
||||||
|
this.$notify(
|
||||||
|
{
|
||||||
|
group: "alert",
|
||||||
|
type: "danger",
|
||||||
|
title: "Error",
|
||||||
|
text: "Failed to add some members as contacts. Please try again.",
|
||||||
|
},
|
||||||
|
5000,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async admitMember(member: {
|
async admitMember(member: {
|
||||||
did: string;
|
did: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -348,12 +421,17 @@ export default class AdmitPendingMembersDialog extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
showContactInfo() {
|
showContactInfo() {
|
||||||
|
const message =
|
||||||
|
this.dialogType === "admit"
|
||||||
|
? "This user is already your contact, but they are not yet admitted to the meeting."
|
||||||
|
: "This user is already your contact, but your activities are not visible to them yet.";
|
||||||
|
|
||||||
this.$notify(
|
this.$notify(
|
||||||
{
|
{
|
||||||
group: "alert",
|
group: "alert",
|
||||||
type: "info",
|
type: "info",
|
||||||
title: "Contact Info",
|
title: "Contact Info",
|
||||||
text: "This user is already your contact, but they are not yet admitted to the meeting.",
|
text: message,
|
||||||
},
|
},
|
||||||
5000,
|
5000,
|
||||||
);
|
);
|
||||||
@@ -197,22 +197,25 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- This Admit component is for the organizer to admit pending members to the meeting -->
|
<!-- Bulk Members Dialog for both admitting and setting visibility -->
|
||||||
<AdmitPendingMembersDialog
|
<BulkMembersDialog
|
||||||
ref="admitPendingMembersDialog"
|
ref="bulkMembersDialog"
|
||||||
:active-did="activeDid"
|
:active-did="activeDid"
|
||||||
:api-server="apiServer"
|
:api-server="apiServer"
|
||||||
@close="closeMemberSelectionDialogCallback"
|
:dialog-type="isOrganizer ? 'admit' : 'visibility'"
|
||||||
/>
|
:title="isOrganizer ? 'Admit Pending Members' : 'Add Members to Contacts'"
|
||||||
<!--
|
:description="
|
||||||
This Bulk Visibility component is for non-organizer members
|
isOrganizer
|
||||||
to add other members to their contacts and set their visibility
|
? 'Would you like to admit these members to the meeting and add them to your contacts?'
|
||||||
-->
|
: 'Would you like to add these members to your contacts?'
|
||||||
<SetBulkVisibilityDialog
|
"
|
||||||
ref="setBulkVisibilityDialog"
|
:button-text="isOrganizer ? 'Admit + Add to Contacts' : 'Add to Contacts'"
|
||||||
:active-did="activeDid"
|
:empty-state-text="
|
||||||
:api-server="apiServer"
|
isOrganizer
|
||||||
@close="closeMemberSelectionDialogCallback"
|
? 'No pending members to admit'
|
||||||
|
: 'No members are not in your contacts'
|
||||||
|
"
|
||||||
|
@close="closeBulkMembersDialogCallback"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -235,11 +238,9 @@ import {
|
|||||||
import { decryptMessage } from "@/libs/crypto";
|
import { decryptMessage } from "@/libs/crypto";
|
||||||
import { Contact } from "@/db/tables/contacts";
|
import { Contact } from "@/db/tables/contacts";
|
||||||
import { MemberData } from "@/interfaces";
|
import { MemberData } from "@/interfaces";
|
||||||
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 AdmitPendingMembersDialog from "./AdmitPendingMembersDialog.vue";
|
import BulkMembersDialog from "./BulkMembersDialog.vue";
|
||||||
import SetBulkVisibilityDialog from "./SetBulkVisibilityDialog.vue";
|
|
||||||
|
|
||||||
interface Member {
|
interface Member {
|
||||||
admitted: boolean;
|
admitted: boolean;
|
||||||
@@ -256,8 +257,7 @@ interface DecryptedMember {
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {
|
components: {
|
||||||
AdmitPendingMembersDialog,
|
BulkMembersDialog,
|
||||||
SetBulkVisibilityDialog,
|
|
||||||
},
|
},
|
||||||
mixins: [PlatformServiceMixin],
|
mixins: [PlatformServiceMixin],
|
||||||
})
|
})
|
||||||
@@ -265,7 +265,6 @@ export default class MembersList extends Vue {
|
|||||||
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
$notify!: (notification: NotificationIface, timeout?: number) => void;
|
||||||
|
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
notify!: ReturnType<typeof createNotifyHelpers>;
|
||||||
libsUtil = libsUtil;
|
|
||||||
|
|
||||||
@Prop({ required: true }) password!: string;
|
@Prop({ required: true }) password!: string;
|
||||||
@Prop({ default: false }) showOrganizerTools!: boolean;
|
@Prop({ default: false }) showOrganizerTools!: boolean;
|
||||||
@@ -532,7 +531,8 @@ export default class MembersList extends Vue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show the admit pending members dialog if conditions are met
|
* Show the bulk members dialog if conditions are met
|
||||||
|
* (admit pending members for organizers, add to contacts for non-organizers)
|
||||||
*/
|
*/
|
||||||
async refreshData(bypassPromptIfAllWereIgnored = true) {
|
async refreshData(bypassPromptIfAllWereIgnored = true) {
|
||||||
// Force refresh both contacts and members
|
// Force refresh both contacts and members
|
||||||
@@ -547,7 +547,7 @@ export default class MembersList extends Vue {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (bypassPromptIfAllWereIgnored) {
|
if (bypassPromptIfAllWereIgnored) {
|
||||||
// only show if there are pending members that have not been ignored
|
// only show if there are 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),
|
||||||
);
|
);
|
||||||
@@ -558,19 +558,11 @@ export default class MembersList extends Vue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.stopAutoRefresh();
|
this.stopAutoRefresh();
|
||||||
if (this.isOrganizer) {
|
(this.$refs.bulkMembersDialog as BulkMembersDialog).open(pendingMembers);
|
||||||
(this.$refs.admitPendingMembersDialog as AdmitPendingMembersDialog).open(
|
|
||||||
pendingMembers,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
(this.$refs.setBulkVisibilityDialog as SetBulkVisibilityDialog).open(
|
|
||||||
pendingMembers,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Admit Pending Members Dialog methods
|
// Bulk Members Dialog methods
|
||||||
async closeMemberSelectionDialogCallback(
|
async closeBulkMembersDialogCallback(
|
||||||
result: { notSelectedMemberDids: string[] } | undefined,
|
result: { notSelectedMemberDids: string[] } | undefined,
|
||||||
) {
|
) {
|
||||||
this.previousMemberDidsIgnored = result?.notSelectedMemberDids || [];
|
this.previousMemberDidsIgnored = result?.notSelectedMemberDids || [];
|
||||||
|
|||||||
@@ -1,324 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div v-if="visible" class="dialog-overlay">
|
|
||||||
<div class="dialog">
|
|
||||||
<div class="text-slate-900 text-center">
|
|
||||||
<h3 class="text-lg font-semibold leading-[1.25] mb-2">
|
|
||||||
Add Members to Contacts
|
|
||||||
</h3>
|
|
||||||
<p class="text-sm mb-4">
|
|
||||||
Would you like to add these members to your contacts?
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<!-- Custom table area - you can customize this -->
|
|
||||||
<div class="mb-4">
|
|
||||||
<table
|
|
||||||
class="w-full border-collapse border border-slate-300 text-sm text-start"
|
|
||||||
>
|
|
||||||
<thead v-if="membersData && membersData.length > 0">
|
|
||||||
<tr class="bg-slate-100 font-medium">
|
|
||||||
<th class="border border-slate-300 px-3 py-2">
|
|
||||||
<label class="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="isAllSelected"
|
|
||||||
:indeterminate="isIndeterminate"
|
|
||||||
@change="toggleSelectAll"
|
|
||||||
/>
|
|
||||||
Select All
|
|
||||||
</label>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<!-- Dynamic data from MembersList -->
|
|
||||||
<tr v-if="!membersData || membersData.length === 0">
|
|
||||||
<td
|
|
||||||
class="border border-slate-300 px-3 py-2 text-center italic text-gray-500"
|
|
||||||
>
|
|
||||||
No members are not in your contacts
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr
|
|
||||||
v-for="member in membersData || []"
|
|
||||||
:key="member.member.memberId"
|
|
||||||
>
|
|
||||||
<td class="border border-slate-300 px-3 py-2">
|
|
||||||
<div class="flex items-center justify-between gap-2">
|
|
||||||
<label class="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="isMemberSelected(member.did)"
|
|
||||||
@change="toggleMemberSelection(member.did)"
|
|
||||||
/>
|
|
||||||
{{ member.name || SOMEONE_UNNAMED }}
|
|
||||||
</label>
|
|
||||||
|
|
||||||
<!-- Friend indicator - only show if they are already a contact -->
|
|
||||||
<font-awesome
|
|
||||||
v-if="member.isContact"
|
|
||||||
icon="user-circle"
|
|
||||||
class="fa-fw ms-auto text-slate-400 cursor-pointer hover:text-slate-600"
|
|
||||||
@click="showContactInfo"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
<tfoot v-if="membersData && membersData.length > 0">
|
|
||||||
<tr class="bg-slate-100 font-medium">
|
|
||||||
<th class="border border-slate-300 px-3 py-2">
|
|
||||||
<label class="flex items-center gap-2">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
:checked="isAllSelected"
|
|
||||||
:indeterminate="isIndeterminate"
|
|
||||||
@change="toggleSelectAll"
|
|
||||||
/>
|
|
||||||
Select All
|
|
||||||
</label>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="space-y-2">
|
|
||||||
<button
|
|
||||||
v-if="membersData && membersData.length > 0"
|
|
||||||
:disabled="!hasSelectedMembers"
|
|
||||||
:class="[
|
|
||||||
'block w-full text-center text-md font-bold uppercase px-2 py-2 rounded-md',
|
|
||||||
hasSelectedMembers
|
|
||||||
? 'bg-blue-600 text-white cursor-pointer'
|
|
||||||
: 'bg-slate-400 text-slate-200 cursor-not-allowed',
|
|
||||||
]"
|
|
||||||
@click="setVisibilityForSelectedMembers"
|
|
||||||
>
|
|
||||||
Add to Contacts
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="block w-full text-center text-md font-bold uppercase bg-slate-600 text-white px-2 py-2 rounded-md"
|
|
||||||
@click="cancel"
|
|
||||||
>
|
|
||||||
Maybe Later
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { Vue, Component, Prop } from "vue-facing-decorator";
|
|
||||||
|
|
||||||
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
|
|
||||||
import { SOMEONE_UNNAMED } from "@/constants/entities";
|
|
||||||
import { MemberData } from "@/interfaces";
|
|
||||||
import { setVisibilityUtil } from "@/libs/endorserServer";
|
|
||||||
import { createNotifyHelpers } from "@/utils/notify";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
mixins: [PlatformServiceMixin],
|
|
||||||
emits: ["close"],
|
|
||||||
})
|
|
||||||
export default class SetBulkVisibilityDialog extends Vue {
|
|
||||||
@Prop({ default: "" }) activeDid!: string;
|
|
||||||
@Prop({ default: "" }) apiServer!: string;
|
|
||||||
|
|
||||||
// Vue notification system
|
|
||||||
$notify!: (
|
|
||||||
notification: { group: string; type: string; title: string; text: string },
|
|
||||||
timeout?: number,
|
|
||||||
) => void;
|
|
||||||
|
|
||||||
// Notification system
|
|
||||||
notify!: ReturnType<typeof createNotifyHelpers>;
|
|
||||||
|
|
||||||
// Component state
|
|
||||||
membersData: MemberData[] = [];
|
|
||||||
selectedMembers: string[] = [];
|
|
||||||
visible = false;
|
|
||||||
|
|
||||||
// Constants
|
|
||||||
// In Vue templates, imported constants need to be explicitly made available to the template
|
|
||||||
readonly SOMEONE_UNNAMED = SOMEONE_UNNAMED;
|
|
||||||
|
|
||||||
get hasSelectedMembers() {
|
|
||||||
return this.selectedMembers.length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
get isAllSelected() {
|
|
||||||
if (!this.membersData || this.membersData.length === 0) return false;
|
|
||||||
return this.membersData.every((member) =>
|
|
||||||
this.selectedMembers.includes(member.did),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get isIndeterminate() {
|
|
||||||
if (!this.membersData || this.membersData.length === 0) return false;
|
|
||||||
const selectedCount = this.membersData.filter((member) =>
|
|
||||||
this.selectedMembers.includes(member.did),
|
|
||||||
).length;
|
|
||||||
return selectedCount > 0 && selectedCount < this.membersData.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
created() {
|
|
||||||
this.notify = createNotifyHelpers(this.$notify);
|
|
||||||
}
|
|
||||||
|
|
||||||
open(members: MemberData[]) {
|
|
||||||
this.visible = true;
|
|
||||||
this.membersData = members;
|
|
||||||
// Select all by default
|
|
||||||
this.selectedMembers = this.membersData.map((member) => member.did);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(notSelectedMemberDids: string[]) {
|
|
||||||
this.visible = false;
|
|
||||||
this.$emit("close", { notSelectedMemberDids: notSelectedMemberDids });
|
|
||||||
}
|
|
||||||
|
|
||||||
cancel() {
|
|
||||||
this.close(this.membersData.map((member) => member.did));
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleSelectAll() {
|
|
||||||
if (!this.membersData || this.membersData.length === 0) return;
|
|
||||||
|
|
||||||
if (this.isAllSelected) {
|
|
||||||
// Deselect all
|
|
||||||
this.selectedMembers = [];
|
|
||||||
} else {
|
|
||||||
// Select all
|
|
||||||
this.selectedMembers = this.membersData.map((member) => member.did);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleMemberSelection(memberDid: string) {
|
|
||||||
const index = this.selectedMembers.indexOf(memberDid);
|
|
||||||
if (index > -1) {
|
|
||||||
this.selectedMembers.splice(index, 1);
|
|
||||||
} else {
|
|
||||||
this.selectedMembers.push(memberDid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
isMemberSelected(memberDid: string) {
|
|
||||||
return this.selectedMembers.includes(memberDid);
|
|
||||||
}
|
|
||||||
|
|
||||||
async setVisibilityForSelectedMembers() {
|
|
||||||
try {
|
|
||||||
const selectedMembers = this.membersData.filter((member) =>
|
|
||||||
this.selectedMembers.includes(member.did),
|
|
||||||
);
|
|
||||||
const notSelectedMembers = this.membersData.filter(
|
|
||||||
(member) => !this.selectedMembers.includes(member.did),
|
|
||||||
);
|
|
||||||
|
|
||||||
let successCount = 0;
|
|
||||||
|
|
||||||
for (const member of selectedMembers) {
|
|
||||||
try {
|
|
||||||
// If they're not a contact yet, add them as a contact first
|
|
||||||
if (!member.isContact) {
|
|
||||||
await this.addAsContact(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set their seesMe to true
|
|
||||||
await this.updateContactVisibility(member.did, true);
|
|
||||||
|
|
||||||
successCount++;
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error(`Error processing member ${member.did}:`, error);
|
|
||||||
// Continue with other members even if one fails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show success notification
|
|
||||||
this.$notify(
|
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "success",
|
|
||||||
title: "Visibility Set Successfully",
|
|
||||||
text: `Visibility set for ${successCount} member${successCount === 1 ? "" : "s"}.`,
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.close(notSelectedMembers.map((member) => member.did));
|
|
||||||
} catch (error) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error("Error setting visibility:", error);
|
|
||||||
this.$notify(
|
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "danger",
|
|
||||||
title: "Error",
|
|
||||||
text: "Failed to set visibility for some members. Please try again.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async addAsContact(member: { did: string; name: string }) {
|
|
||||||
try {
|
|
||||||
const newContact = {
|
|
||||||
did: member.did,
|
|
||||||
name: member.name,
|
|
||||||
};
|
|
||||||
|
|
||||||
await this.$insertContact(newContact);
|
|
||||||
} catch (err) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error("Error adding contact:", err);
|
|
||||||
if (err instanceof Error && err.message?.indexOf("already exists") > -1) {
|
|
||||||
// Contact already exists, continue
|
|
||||||
} else {
|
|
||||||
throw err; // Re-throw if it's not a duplicate error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateContactVisibility(did: string, seesMe: boolean) {
|
|
||||||
try {
|
|
||||||
// Get the contact object
|
|
||||||
const contact = await this.$getContact(did);
|
|
||||||
if (!contact) {
|
|
||||||
throw new Error(`Contact not found for DID: ${did}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use the proper API to set visibility on the server
|
|
||||||
const result = await setVisibilityUtil(
|
|
||||||
this.activeDid,
|
|
||||||
this.apiServer,
|
|
||||||
this.axios,
|
|
||||||
contact,
|
|
||||||
seesMe,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!result.success) {
|
|
||||||
throw new Error(result.error || "Failed to set visibility");
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// eslint-disable-next-line no-console
|
|
||||||
console.error("Error updating contact visibility:", err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
showContactInfo() {
|
|
||||||
this.$notify(
|
|
||||||
{
|
|
||||||
group: "alert",
|
|
||||||
type: "info",
|
|
||||||
title: "Contact Info",
|
|
||||||
text: "This user is already your contact, but your activities are not visible to them yet.",
|
|
||||||
},
|
|
||||||
5000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
Reference in New Issue
Block a user