From acf7d611e8fd8a7b715cf661e41a7ffcc187af32 Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Thu, 9 Oct 2025 21:56:50 +0800 Subject: [PATCH 1/9] WIP: mockup for set visibility dialog --- src/App.vue | 103 ++++++++++++++++++++++++ src/views/OnboardMeetingMembersView.vue | 18 ++++- 2 files changed, 120 insertions(+), 1 deletion(-) diff --git a/src/App.vue b/src/App.vue index 13052500..245c5d5a 100644 --- a/src/App.vue +++ b/src/App.vue @@ -352,6 +352,109 @@ + +
+
+
+

+ Set Visibility to Meeting Members +

+

+ Would you like to make your activities visible to the + following members? (This will also add them as contacts if + they aren't already.) +

+ + +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ +
+
+ +
+ + +
+
+
+
diff --git a/src/views/OnboardMeetingMembersView.vue b/src/views/OnboardMeetingMembersView.vue index 9b3c83ce..6cb1bf8b 100644 --- a/src/views/OnboardMeetingMembersView.vue +++ b/src/views/OnboardMeetingMembersView.vue @@ -7,7 +7,7 @@

- Meeting Members + Meeting Members

@@ -77,6 +77,7 @@ import { } from "../libs/endorserServer"; import { generateSaveAndActivateIdentity } from "../libs/util"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; +import { NotificationIface } from "../constants/app"; @Component({ components: { @@ -97,6 +98,7 @@ export default class OnboardMeetingMembersView extends Vue { projectLink = ""; $route!: RouteLocationNormalizedLoaded; $router!: Router; + $notify!: (notification: NotificationIface, timeout?: number) => void; userNameDialog!: InstanceType; @@ -257,5 +259,19 @@ export default class OnboardMeetingMembersView extends Vue { handleError(message: string) { this.errorMessage = message; } + + showAddMembersNotification() { + this.$notify( + { + group: "modal", + type: "set-visibility-to-meeting-members", + onYes: async () => { + // Handle the "Add Selected" action - you can implement the actual logic here + console.log("User confirmed adding selected members as contacts"); + }, + }, + -1, + ); // -1 means no auto-dismiss, stays open until user acts + } } -- 2.30.2 From 461ee84d2a26009449211d1cb296236d1081cda4 Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Sun, 12 Oct 2025 23:30:03 +0800 Subject: [PATCH 2/9] WIP: meeting members adjustments --- src/App.vue | 2 +- src/components/MembersList.vue | 207 ++++++++++++------------ src/views/OnboardMeetingMembersView.vue | 16 +- src/views/OnboardMeetingSetupView.vue | 40 ++--- 4 files changed, 131 insertions(+), 134 deletions(-) diff --git a/src/App.vue b/src/App.vue index 245c5d5a..84d2b203 100644 --- a/src/App.vue +++ b/src/App.vue @@ -354,7 +354,7 @@
-
+
{{ decryptionErrorMessage() }}
@@ -23,131 +23,125 @@ to set it.
-
- +
  • - - • Click - - - - / - - - - to add/remove them to/from the meeting. + Click + + - -
  • -
    - - • Click + / + + + + to add/remove them to/from the meeting. + +
  • + Click - + to add them to your contacts. - -
  • + + -
    +
    + +
    -
    -
    -
    -

    - {{ member.name || unnamedMember }} -

    -
    - + +
    - -
    -
    - - - - + + +
    -
    -

    - {{ member.did }} -

    -
    -
    +

    + {{ member.did }} +

    + + + +
    +
    @@ -508,6 +502,21 @@ export default class MembersList extends Vue { this.notify.error(message, TIMEOUTS.LONG); } } + + showAddMembersNotification() { + this.$notify( + { + group: "modal", + type: "set-visibility-meeting-members", + title: "Set Visibility for Meeting Members", + onYes: async () => { + // Handle the "Add Selected" action - you can implement the actual logic here + console.log("User confirmed adding selected members as contacts"); + }, + }, + -1, + ); // -1 means no auto-dismiss, stays open until user acts + } } @@ -522,7 +531,7 @@ export default class MembersList extends Vue { .btn-add-contact { /* stylelint-disable-next-line at-rule-no-unknown */ - @apply ml-2 w-8 h-8 flex items-center justify-center rounded-full + @apply ml-2 w-5 h-5 flex items-center justify-center rounded-full bg-green-100 text-green-600 hover:bg-green-200 hover:text-green-800 transition-colors; } @@ -536,14 +545,14 @@ export default class MembersList extends Vue { .btn-admission { /* stylelint-disable-next-line at-rule-no-unknown */ - @apply mr-2 w-6 h-6 flex items-center justify-center rounded-full + @apply w-5 h-5 flex items-center justify-center rounded-full bg-blue-100 text-blue-600 hover:bg-blue-200 hover:text-blue-800 transition-colors; } .btn-info-admission { /* stylelint-disable-next-line at-rule-no-unknown */ - @apply mr-2 mb-2 w-6 h-6 flex items-center justify-center rounded-full + @apply w-6 h-6 flex items-center justify-center rounded-full bg-slate-100 text-slate-500 hover:bg-slate-200 hover:text-slate-800 transition-colors; } diff --git a/src/views/OnboardMeetingMembersView.vue b/src/views/OnboardMeetingMembersView.vue index 6cb1bf8b..9dfba3d9 100644 --- a/src/views/OnboardMeetingMembersView.vue +++ b/src/views/OnboardMeetingMembersView.vue @@ -7,7 +7,7 @@

    - Meeting Members + Meeting Members

    @@ -259,19 +259,5 @@ export default class OnboardMeetingMembersView extends Vue { handleError(message: string) { this.errorMessage = message; } - - showAddMembersNotification() { - this.$notify( - { - group: "modal", - type: "set-visibility-to-meeting-members", - onYes: async () => { - // Handle the "Add Selected" action - you can implement the actual logic here - console.log("User confirmed adding selected members as contacts"); - }, - }, - -1, - ); // -1 means no auto-dismiss, stays open until user acts - } } diff --git a/src/views/OnboardMeetingSetupView.vue b/src/views/OnboardMeetingSetupView.vue index 45b2d580..e70148f5 100644 --- a/src/views/OnboardMeetingSetupView.vue +++ b/src/views/OnboardMeetingSetupView.vue @@ -230,26 +230,28 @@ class="mt-8 p-4 border rounded-lg bg-white shadow" >
    -

    Meeting Members

    -
    -
    - • Page for Members - - - - - - +

    Meeting Members

    +
      +
    • + Page for Members: + + + + + + +
    • +
    Date: Mon, 13 Oct 2025 21:38:12 +0800 Subject: [PATCH 3/9] WIP: button and icon additions - Mirrored "Refresh" and "Visibility" buttons on top and bottom of member list - Added back info icons for list actions - When clicked, Person icon shows informative notification --- src/App.vue | 58 +++++++++++++++------- src/components/MembersList.vue | 90 +++++++++++++++++++++------------- 2 files changed, 95 insertions(+), 53 deletions(-) diff --git a/src/App.vue b/src/App.vue index 84d2b203..c4638fe0 100644 --- a/src/App.vue +++ b/src/App.vue @@ -389,46 +389,56 @@ -
    - -
    -
    -
    -

    - Set Visibility to Meeting Members -

    -

    - Would you like to make your activities visible to the - following members? (This will also add them as contacts if - they aren't already.) -

    - - -
    - - - - - - - - - - - - - - - -
    - -
    - No members need visibility settings -
    -
    - - - - -
    -
    -
    - -
    - - -
    -
    -
    -
    @@ -488,9 +364,6 @@ import { Vue, Component } from "vue-facing-decorator"; import { NotificationIface } from "./constants/app"; import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin"; import { logger } from "./utils/logger"; -// eslint-disable-next-line @typescript-eslint/no-unused-vars -import { SOMEONE_UNNAMED } from "@/constants/entities"; -import { setVisibilityUtil } from "./libs/endorserServer"; interface Settings { notifyingNewActivityTime?: string; @@ -505,12 +378,6 @@ export default class App extends Vue { $notify!: (notification: NotificationIface, timeout?: number) => void; stopAsking = false; - selectedMembers: string[] = []; - selectionInitialized = false; - - get hasSelectedMembers() { - return this.selectedMembers.length > 0; - } activeDid = ""; apiServer = ""; @@ -525,201 +392,6 @@ export default class App extends Vue { this.activeDid = activeIdentity.activeDid || ""; } - isAllSelected(notification: NotificationIface) { - const membersData = notification?.membersData || []; - if (!membersData || membersData.length === 0) return false; - return membersData.every((member) => - this.selectedMembers.includes(member.did), - ); - } - - isIndeterminate(notification: NotificationIface) { - const membersData = notification?.membersData || []; - if (!membersData || membersData.length === 0) return false; - const selectedCount = membersData.filter((member) => - this.selectedMembers.includes(member.did), - ).length; - return selectedCount > 0 && selectedCount < membersData.length; - } - - toggleSelectAll(notification: NotificationIface) { - const membersData = notification?.membersData || []; - if (!membersData || membersData.length === 0) return; - - if (this.isAllSelected(notification)) { - // Deselect all - this.selectedMembers = []; - } else { - // Select all - this.selectedMembers = 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); - } - - shouldInitializeSelection(notification: NotificationIface) { - // This method will initialize selection when the dialog opens - if ( - notification?.type === "set-visibility-meeting-members" && - !this.selectionInitialized - ) { - this.initializeSelection(notification); - this.selectionInitialized = true; - } - return true; - } - - initializeSelection(notification: NotificationIface) { - // Reset selection when dialog opens - this.selectedMembers = []; - // Select all by default - const membersData = notification?.membersData || []; - this.selectedMembers = membersData.map((member) => member.did); - } - - resetSelection() { - this.selectedMembers = []; - this.selectionInitialized = false; - } - - async setVisibilityForSelectedMembers(notification: NotificationIface) { - try { - const membersData = notification?.membersData || []; - const selectedMembers = 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, - ); - } 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; - } - } - - async closeDialog( - notification: NotificationIface, - closeFn: (id: string) => void, - ) { - this.resetSelection(); - - // Close the notification first - closeFn(notification.id); - - // Then call the callback after a short delay to ensure dialog is closed - setTimeout(async () => { - if (notification.callback) { - await notification.callback(); - } - }, 100); - } - - 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, - ); - } - async turnOffNotifications( notification: NotificationIface, ): Promise { diff --git a/src/components/MembersList.vue b/src/components/MembersList.vue index 9dedec28..a315cf24 100644 --- a/src/components/MembersList.vue +++ b/src/components/MembersList.vue @@ -180,6 +180,15 @@

    + + + -- 2.30.2 From e3598992e72e10b68a77f77e77eede6aeb9298cd Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Thu, 16 Oct 2025 17:49:59 +0800 Subject: [PATCH 7/9] feat: pause auto-refresh when SetVisibilityDialog is open - Pause auto-refresh when SetVisibilityDialog becomes visible - Resume auto-refresh when dialog is closed - Prevents background refresh interference during visibility settings - Fix type compatibility for visibilityDialogMembers data structure This ensures users can interact with the visibility dialog without the members list refreshing in the background, providing a better user experience for setting member visibility preferences. --- src/components/MembersList.vue | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/components/MembersList.vue b/src/components/MembersList.vue index a315cf24..6d15e0f2 100644 --- a/src/components/MembersList.vue +++ b/src/components/MembersList.vue @@ -447,9 +447,12 @@ export default class MembersList extends Vue { return !contact || !contact.seesMe; }) .map((member) => ({ - ...member, + did: member.did, + name: member.name, isContact: !!this.getContactFor(member.did), - contact: this.getContactFor(member.did), + member: { + memberId: member.member.memberId.toString(), + }, })); } @@ -595,6 +598,9 @@ export default class MembersList extends Vue { // Filter members to show only those who need visibility set const membersForVisibility = this.getMembersForVisibility(); + // Pause auto-refresh when dialog opens + this.stopAutoRefresh(); + // Open the dialog directly this.visibilityDialogMembers = membersForVisibility; this.showSetVisibilityDialog = true; @@ -648,6 +654,8 @@ export default class MembersList extends Vue { this.visibilityDialogMembers = []; // Refresh data when dialog is closed this.refreshData(); + // Resume auto-refresh when dialog is closed + this.startAutoRefresh(); } beforeDestroy() { -- 2.30.2 From 4de4fbecafc30fb2f7b8d0e1c18e2ee9f61c01df Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Fri, 17 Oct 2025 17:59:41 +0800 Subject: [PATCH 8/9] refactor: rename SetVisibilityDialog to SetBulkVisibilityDialog and remove unused code - Rename SetVisibilityDialog.vue to SetBulkVisibilityDialog.vue for better clarity - Update all component references in MembersList.vue (import, registration, template usage) - Update component class name from SetVisibilityDialog to SetBulkVisibilityDialog - Rename calling function name to showSetBulkVisibilityDialog to match class name change - Remove unused properties and created() method from App.vue This cleanup removes dead code and improves component naming consistency. --- src/App.vue | 13 ------------- src/components/MembersList.vue | 12 ++++++------ ...bilityDialog.vue => SetBulkVisibilityDialog.vue} | 2 +- 3 files changed, 7 insertions(+), 20 deletions(-) rename src/components/{SetVisibilityDialog.vue => SetBulkVisibilityDialog.vue} (99%) diff --git a/src/App.vue b/src/App.vue index 01e9914f..13052500 100644 --- a/src/App.vue +++ b/src/App.vue @@ -378,19 +378,6 @@ export default class App extends Vue { $notify!: (notification: NotificationIface, timeout?: number) => void; stopAsking = false; - activeDid = ""; - apiServer = ""; - - async created() { - // Initialize settings - const settings = await this.$accountSettings(); - this.apiServer = settings.apiServer || ""; - - // Get activeDid from active_identity table - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const activeIdentity = await (this as any).$getActiveIdentity(); - this.activeDid = activeIdentity.activeDid || ""; - } async turnOffNotifications( notification: NotificationIface, diff --git a/src/components/MembersList.vue b/src/components/MembersList.vue index 6d15e0f2..f39676ed 100644 --- a/src/components/MembersList.vue +++ b/src/components/MembersList.vue @@ -71,7 +71,7 @@ v-if="getMembersForVisibility().length > 0" 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="Set visibility for meeting members" - @click="showAddMembersNotification" + @click="showSetBulkVisibilityDialog" > Set Visibility @@ -168,7 +168,7 @@ v-if="getMembersForVisibility().length > 0" 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="Set visibility for meeting members" - @click="showAddMembersNotification" + @click="showSetBulkVisibilityDialog" > Set Visibility @@ -182,7 +182,7 @@ - [] }) membersData!: MemberData[]; @Prop({ default: "" }) activeDid!: string; -- 2.30.2 From 4f3a1b390dae0d655f6da45c1433e6ee6eb15a2b Mon Sep 17 00:00:00 2001 From: Jose Olarte III Date: Fri, 17 Oct 2025 19:17:47 +0800 Subject: [PATCH 9/9] feat: auto-show visibility dialog for meeting members - Show dialog on initial load if members need visibility settings - Show dialog during auto-refresh only when new members are added (not removed) - Show dialog on manual refresh if any members need visibility settings - Remove manual "Set Visibility" buttons from UI as dialog now appears automatically - Add logic to track previous visibility members and detect changes - Improve UX by proactively prompting users to set visibility for new meeting members The dialog now appears automatically in these scenarios: - Component initialization with members needing visibility - Auto-refresh when new members join the meeting - Manual refresh when members need visibility settings --- src/components/MembersList.vue | 87 +++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 22 deletions(-) diff --git a/src/components/MembersList.vue b/src/components/MembersList.vue index f39676ed..e26613bf 100644 --- a/src/components/MembersList.vue +++ b/src/components/MembersList.vue @@ -66,16 +66,6 @@ Refresh ({{ countdownTimer }}s) - -
      ({{ countdownTimer }}s) - -

      @@ -272,6 +252,9 @@ export default class MembersList extends Vue { autoRefreshInterval: NodeJS.Timeout | null = null; lastRefreshTime = 0; + // Track previous visibility members to detect changes + previousVisibilityMembers: string[] = []; + /** * Get the unnamed member constant */ @@ -296,12 +279,18 @@ export default class MembersList extends Vue { // Start auto-refresh this.startAutoRefresh(); + + // Check if we should show the visibility dialog on initial load + this.checkAndShowVisibilityDialog(); } async refreshData() { // Force refresh both contacts and members await this.loadContacts(); await this.fetchMembers(); + + // Check if we should show the visibility dialog after refresh + this.checkAndShowVisibilityDialog(); } async fetchMembers() { @@ -456,6 +445,55 @@ export default class MembersList extends Vue { })); } + /** + * Check if we should show the visibility dialog + * Returns true if there are members for visibility and either: + * - This is the first time (no previous members tracked), OR + * - New members have been added since last check (not removed) + */ + shouldShowVisibilityDialog(): boolean { + const currentMembers = this.getMembersForVisibility(); + + if (currentMembers.length === 0) { + return false; + } + + // If no previous members tracked, show dialog + if (this.previousVisibilityMembers.length === 0) { + return true; + } + + // Check if new members have been added (not just any change) + const currentMemberIds = currentMembers.map((m) => m.did); + const previousMemberIds = this.previousVisibilityMembers; + + // Find new members (members in current but not in previous) + const newMembers = currentMemberIds.filter( + (id) => !previousMemberIds.includes(id), + ); + + // Only show dialog if there are new members added + return newMembers.length > 0; + } + + /** + * Update the tracking of previous visibility members + */ + updatePreviousVisibilityMembers() { + const currentMembers = this.getMembersForVisibility(); + this.previousVisibilityMembers = currentMembers.map((m) => m.did); + } + + /** + * Show the visibility dialog if conditions are met + */ + checkAndShowVisibilityDialog() { + if (this.shouldShowVisibilityDialog()) { + this.showSetBulkVisibilityDialog(); + } + this.updatePreviousVisibilityMembers(); + } + checkWhetherContactBeforeAdmitting(decrMember: DecryptedMember) { const contact = this.getContactFor(decrMember.did); if (!decrMember.member.admitted && !contact) { @@ -616,7 +654,7 @@ export default class MembersList extends Vue { if (timeSinceLastRefresh >= 10) { // Time to refresh - this.fetchMembers(); + this.refreshData(); this.lastRefreshTime = now; this.countdownTimer = 10; } else { @@ -644,8 +682,13 @@ export default class MembersList extends Vue { } // Trigger immediate refresh and restart timer - this.fetchMembers(); + this.refreshData(); this.startAutoRefresh(); + + // Always show dialog on manual refresh if there are members for visibility + if (this.getMembersForVisibility().length > 0) { + this.showSetBulkVisibilityDialog(); + } } // Set Visibility Dialog methods -- 2.30.2