From d2d55849b9395e4d95970be4457e06b48471a96b Mon Sep 17 00:00:00 2001 From: thePR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> Date: Fri, 16 Jan 2026 09:55:10 -0500 Subject: [PATCH 001/103] Add trusted moderation stats to profile modal --- components/profile-modal.html | 19 +++ js/ui/profileModalController.js | 135 ++++++++++++++++++ ...profile-modal-moderation-settings.test.mjs | 3 + 3 files changed, 157 insertions(+) diff --git a/components/profile-modal.html b/components/profile-modal.html index 2cfedd74..50b317e0 100644 --- a/components/profile-modal.html +++ b/components/profile-modal.html @@ -286,6 +286,25 @@ Choose how many trusted reports or mutes it takes to blur thumbnails, block autoplay previews, or hide videos entirely. Lower numbers are stricter.

+
+

+ Trusted counts include your first-degree follows (F1) plus seeded moderators. +

+
+
+
Trusted contacts
+
0
+
+
+
Active trusted mute contributors
+
0
+
+
+
Active trusted report contributors
+
0
+
+
+
diff --git a/js/services/moderationService.js b/js/services/moderationService.js index 0cc17b01..31c0c563 100644 --- a/js/services/moderationService.js +++ b/js/services/moderationService.js @@ -497,6 +497,7 @@ export class ModerationService { this.trustedMuteLists = new Map(); this.trustedMutedAuthors = new Map(); this.trustedMuteSubscriptions = new Map(); + this.trustedSeedOnly = false; this.emitter = new SimpleEventEmitter((message, error) => { try { @@ -549,6 +550,35 @@ export class ModerationService { return merged; } + computeTrustedSeedOnly() { + const trustedContacts = + this.trustedContacts instanceof Set ? this.trustedContacts : new Set(); + const trustedSeedContacts = + this.trustedSeedContacts instanceof Set ? this.trustedSeedContacts : new Set(); + + if (!trustedSeedContacts.size || trustedContacts.size !== trustedSeedContacts.size) { + return false; + } + + for (const value of trustedContacts) { + if (!trustedSeedContacts.has(value)) { + return false; + } + } + + return true; + } + + updateTrustedSeedOnlyStatus() { + const nextValue = this.computeTrustedSeedOnly(); + if (nextValue === this.trustedSeedOnly) { + return; + } + + this.trustedSeedOnly = nextValue; + this.emit("trusted-seed-only", { seedOnly: nextValue }); + } + rebuildTrustedContacts(nextContacts = new Set(), { previous = null } = {}) { const sanitized = new Set(); @@ -580,6 +610,7 @@ export class ModerationService { const seededNext = this.mergeSeedsIntoSet(sanitized); this.trustedContacts = seededNext; + this.updateTrustedSeedOnlyStatus(); this.emit("contacts", { size: seededNext.size }); this.recomputeAllSummaries(); this.reconcileTrustedMuteSubscriptions(previousContacts, seededNext); @@ -681,6 +712,10 @@ export class ModerationService { this.rebuildTrustedContacts(this.viewerContacts, { previous: previousContacts }); } + isTrustedSeedOnly() { + return this.trustedSeedOnly === true; + } + logThresholdTransitions({ eventId = "", reportType = "", diff --git a/js/services/trustBootstrap.js b/js/services/trustBootstrap.js index 24ba4f67..4f029bb5 100644 --- a/js/services/trustBootstrap.js +++ b/js/services/trustBootstrap.js @@ -117,6 +117,9 @@ export async function bootstrapTrustedSeeds() { }); devLogger.info(`[trustBootstrap] Applying ${seeds.size} trusted seeds`, Array.from(seeds)); moderationService.setTrustedSeeds(seeds); + if (typeof moderationService.updateTrustedSeedOnlyStatus === "function") { + moderationService.updateTrustedSeedOnlyStatus(); + } if ( moderationService && typeof moderationService.recomputeAllSummaries === "function" diff --git a/js/ui/profileModalController.js b/js/ui/profileModalController.js index 25d5770e..d8b8d8b6 100644 --- a/js/ui/profileModalController.js +++ b/js/ui/profileModalController.js @@ -1421,6 +1421,8 @@ export class ProfileModalController { document.getElementById("profileModerationTrustedMuteCount") || null; this.moderationTrustedReportCount = document.getElementById("profileModerationTrustedReportCount") || null; + this.moderationSeedOnlyIndicator = + document.getElementById("profileModerationSeedOnlyIndicator") || null; this.moderationHideControlsGroup = this.moderationSettingsCard?.querySelector( "[data-role=\"trusted-hide-controls\"]", @@ -7140,6 +7142,7 @@ export class ProfileModalController { trustedContactsCount: 0, trustedMuteContributors: 0, trustedReportContributors: 0, + trustedSeedOnly: false, }; const service = this.moderationService; @@ -7147,6 +7150,12 @@ export class ProfileModalController { return summary; } + if (typeof service.isTrustedSeedOnly === "function") { + summary.trustedSeedOnly = service.isTrustedSeedOnly(); + } else if (typeof service.trustedSeedOnly === "boolean") { + summary.trustedSeedOnly = service.trustedSeedOnly; + } + const trustedContacts = service.trustedContacts instanceof Set ? service.trustedContacts @@ -7225,7 +7234,8 @@ export class ProfileModalController { if ( !(this.moderationTrustedContactsCount instanceof HTMLElement) && !(this.moderationTrustedMuteCount instanceof HTMLElement) && - !(this.moderationTrustedReportCount instanceof HTMLElement) + !(this.moderationTrustedReportCount instanceof HTMLElement) && + !(this.moderationSeedOnlyIndicator instanceof HTMLElement) ) { return; } @@ -7249,6 +7259,10 @@ export class ProfileModalController { summary.trustedReportContributors, ); } + + if (this.moderationSeedOnlyIndicator instanceof HTMLElement) { + this.moderationSeedOnlyIndicator.hidden = !summary.trustedSeedOnly; + } } async handleModerationSettingsSave() { From 2b446e8dddbb9a22bf6fde3c2773278120134eba Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 16 Jan 2026 15:44:22 +0000 Subject: [PATCH 005/103] Refactor upload modal for generic storage and read-only config - Updates Upload Modal UI labels to be provider-agnostic (e.g., 'Storage Credentials', 'Endpoint'). - Implements a read-only 'Summary View' for configured storage connections, collapsing the editable form by default. - Adds 'Manage Storage' button that navigates to the Profile Modal's storage pane. - Updates `initUploadModal.js` to inject the navigation callback. - Updates `r2Service.js` status messages to be generic. - Fixes unlock flow to ensure UI updates after storage unlock. --- components/upload-modal.html | 173 +++++++++++++++----------- js/ui/components/UploadModal.js | 179 +++++++++++++++++++-------- js/ui/initUploadModal.js | 5 + verification/upload_modal_static.png | Bin 0 -> 57531 bytes verification/verify_static_html.py | 45 +++++++ 5 files changed, 273 insertions(+), 129 deletions(-) create mode 100644 verification/upload_modal_static.png create mode 100644 verification/verify_static_html.py diff --git a/components/upload-modal.html b/components/upload-modal.html index 2ff25196..3033ac62 100644 --- a/components/upload-modal.html +++ b/components/upload-modal.html @@ -115,93 +115,118 @@ - Configure R2 Storage + Storage Configuration - +