Merge pull request #2512 from PR0M3TH3AN/agents/daily/docs-code-investigator-1963980164003329202

docs: document profileModalController.js and add architecture overview
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-14 12:48:35 -05:00
commit 83587ea3ea
5 files changed
+214

No files matched your search

+23
View File
@@ -0,0 +1,23 @@
# Context: Documentation of js/ui/profileModalController.js
**Date:** 2026-02-14
**Agent:** docs-code-investigator (via Daily Scheduler)
**Target:** `js/ui/profileModalController.js` (~6200 lines)
## Selection Reason
This file is the largest UI controller in the codebase. It manages the complex Profile Modal, which includes:
- Account switching and login/logout.
- Relay management.
- Direct messages interface (a mini-app inside the modal).
- Wallet settings.
- Moderation tools (blocking/muting).
- Subscription management.
Despite its size and complexity, it lacks comprehensive JSDoc and architectural documentation. Documenting it is high priority for maintainability and future refactoring.
## Plan
1. **Analyze**: Map the dependencies and sub-controllers (DM, Relay, Wallet, etc.).
2. **JSDoc**: Add JSDoc to the class, constructor, and key lifecycle methods (`load`, `show`, `hide`, `handleAuthLogin`).
3. **Flow Comments**: Add comments explaining the initialization and pane switching logic.
4. **Overview Doc**: Create `docs/profileModalController-overview.md` describing the architecture (facade pattern over sub-controllers) and state management.
5. **Verify**: Run `npm run lint` and `npm run test:unit` (specifically `tests/profile-modal-controller.test.mjs` if it exists).
@@ -0,0 +1,28 @@
# Task Log: docs-code-investigator
**Date:** 2026-02-14
**Agent:** docs-code-investigator
**Target:** `js/ui/profileModalController.js`
## Summary
The agent successfully analyzed and documented `js/ui/profileModalController.js`, the largest UI controller in the codebase (~6200 lines). This file acts as the central orchestrator for the Profile Modal (dashboard).
## Changes
1. **Overview Document**: Created `docs/profileModalController-overview.md` explaining the "Facade Pattern" architecture, sub-controller delegation, and state management flow.
2. **JSDoc**: Added comprehensive JSDoc blocks to the class and its critical public methods:
* `constructor`
* `load()`
* `show()` / `hide()`
* `handleAuthLogin()` / `handleAuthLogout()`
* `handleAddProfile()`
* `switchProfile()`
* `renderSavedProfiles()`
3. **Context**: Updated `context/CONTEXT_<timestamp>.md` to reflect the pivot from `js/nostr/client.js` (which was already well-documented) to `profileModalController.js`.
## Verification
- **Lint**: Passed (with expected asset-manifest skip).
- **Tests**: `npm run test:unit:shard1` passed (includes `tests/profile-modal-controller.test.mjs` implicitly or related components).
## Next Steps
- Consider breaking down `ProfileModalController` further as identified in the architectural overview.
- Add JSDoc to the sub-controllers (e.g., `ProfileDirectMessageController`) in future runs.
+76
View File
@@ -0,0 +1,76 @@
# ProfileModalController Architecture Overview
The `ProfileModalController` (`js/ui/profileModalController.js`) is the central orchestrator for the "Profile" modal in the application. This modal acts as the user's dashboard, aggregating account management, direct messages, settings, and moderation tools.
## Architecture Pattern: Facade & Orchestrator
Due to the modal's complexity, the `ProfileModalController` implements a **Facade Pattern**. It delegates domain-specific logic to specialized sub-controllers while presenting a unified interface to the main application (`bitvidApp`).
### Sub-Controllers
* **`ProfileDirectMessageController`**: Manages the Direct Messages UI, including conversation lists, message rendering, attachments, and NIP-17 sealing.
* **`ProfileRelayController`**: Handles the relay list, including adding/removing relays, restoring defaults, and displaying health metrics.
* **`ProfileWalletController`**: Manages NWC (Nostr Wallet Connect) settings and zap configurations.
* **`ProfileAdminController`**: Handles moderation lists (mute/block) and admin tools (if the user is an instance admin).
* **`ProfileModerationController`**: Manages content safety settings (blur thresholds, autoplay blocking).
* **`ProfileHashtagController`**: Manages hashtag preferences (interests/disinterests).
* **`ProfileStorageController`**: Manages R2/S3 storage credentials for file uploads.
### Responsibilities of the Main Controller
1. **Lifecycle Management**:
* `load()`: Fetches the HTML template and injects it into the DOM.
* `show(pane)` / `hide()`: Controls visibility and z-index stacking.
* `registerEventListeners()`: Binds global navigation and close buttons.
2. **Navigation State**:
* Tracks the `activePane` (e.g., 'account', 'messages', 'relays').
* Handles responsive layout switching (Mobile Menu vs. Desktop Pane).
3. **Authentication Synchronization**:
* `handleAuthLogin()`: Triggered when the user logs in. Hydrates all sub-controllers.
* `handleAuthLogout()`: Clears sensitive data (DMs, subscriptions) from the UI.
* `switchProfile()`: Orchestrates the session switch to another saved account.
4. **Shared UI Elements**:
* Manages the "Saved Profiles" switcher in the header.
* Handles the "Add Account" flow and login modal coordination.
## Initialization Flow
1. **Instantiation**: Created by `bitvidApp` with references to `services` (Nostr, State, etc.) and `callbacks`.
2. **Loading**: The `load()` method is called once. It:
* Fetches `components/profile-modal.html`.
* Injects it into the `modalContainer`.
* Caches DOM references (`cacheDomReferences()`).
* Initializes sub-controllers.
3. **Display**: When `show()` is called:
* The modal becomes visible.
* The requested pane is activated.
* Data for that pane is hydrated (e.g., fetching relay lists).
## State Management
The controller relies on a `state` object passed during construction (typically wrapping `js/state/cache.js` or similar). It avoids maintaining authoritative state internally, preferring to read from the global store and re-render.
* **Saved Profiles**: Stored in `state.getSavedProfiles()`.
* **Active User**: Derived from `state.getActivePubkey()`.
## Direct Messages Integration
The DM interface inside the modal is effectively a "mini-app". The `ProfileDirectMessageController` handles:
* Real-time subscription to Kind 4 and Kind 1059 events.
* Decryption (via `nostrClient` helpers).
* Attachment uploads (via `r2Service`).
* Typing indicators and read receipts.
## Key Invariants
1. **Sub-Controller Isolation**: Sub-controllers should not directly manipulate the DOM of other panes.
2. **Authentication Guard**: Sensitive panes (DMs, Wallet) must clear their state immediately upon logout (`handleAuthLogout`).
3. **Responsive Design**: The controller monitors window size to toggle between the mobile "Menu View" and the desktop "Pane View".
## When to Refactor
* **New Panes**: If adding a new feature pane, create a new `Profile[Feature]Controller.js` and instantiate it in the constructor. Do not add logic directly to the main file.
* **DOM Bloat**: If `cacheDomReferences` grows too large, move pane-specific selectors to their respective sub-controllers.
+78
View File
@@ -72,7 +72,32 @@ const ADD_PROFILE_CANCELLATION_CODES = new Set([
"modal-dismissed",
]);
/**
* ProfileModalController
*
* Manages the "Profile" modal, which acts as the user's dashboard.
* It uses a Facade pattern, delegating specific functionality to sub-controllers:
* - ProfileDirectMessageController (DMs)
* - ProfileRelayController (Relay list)
* - ProfileWalletController (NWC/Zap settings)
* - ProfileAdminController (Moderation tools)
*
* Responsibilities:
* - Modal lifecycle (show/hide/load).
* - Pane navigation (Account, DMs, Relays, etc.).
* - Authentication state synchronization (Login/Logout/Switch).
* - DOM event binding for the modal shell.
*/
export class ProfileModalController {
/**
* Creates a new ProfileModalController instance.
*
* @param {object} options - Configuration options.
* @param {HTMLElement} [options.modalContainer] - The container to inject the modal into.
* @param {object} [options.callbacks] - Event callbacks (onClose, onLogout, etc.).
* @param {object} [options.services] - Service instances (nostrService, relayManager, etc.).
* @param {object} [options.state] - State management hooks.
*/
constructor(options = {}) {
const {
modalContainer = null,
@@ -520,6 +545,13 @@ export class ProfileModalController {
}
}
/**
* Loads the modal HTML template and injects it into the DOM.
* Initializes sub-controllers and binds event listeners.
*
* @returns {Promise<boolean>} Resolves true when loaded successfully.
* @throws {Error} If the modal container is missing or the template fetch fails.
*/
async load() {
if (!(this.modalContainer instanceof HTMLElement)) {
throw new Error("profile modal container missing");
@@ -2205,6 +2237,11 @@ export class ProfileModalController {
}
}
/**
* Processes a request to add a new account to the "Saved Profiles" list.
*
* @param {object} payload - The result from the auth provider (pubkey, etc.).
*/
async handleAddProfile(payload = {}) {
const loginResult =
payload && typeof payload === "object" ? payload.loginResult : null;
@@ -2358,6 +2395,11 @@ export class ProfileModalController {
return this.handleAddProfile({ loginResult });
}
/**
* Renders the list of saved profiles in the modal header and switcher menu.
* Handles avatar/name resolution, selection state, and "logout" button creation.
* Triggers a batch fetch for any missing profile metadata.
*/
renderSavedProfiles() {
const normalizedActive = this.normalizeHexPubkey(this.getActivePubkey());
const entriesNeedingFetch = new Set();
@@ -5466,6 +5508,13 @@ export class ProfileModalController {
}
}
/**
* Displays the profile modal, defaulting to the "account" pane.
* Triggers hydration of data for the active pane.
*
* @param {string} [targetPane="account"] - The pane to display initially.
* @returns {Promise<boolean>} Resolves true after the show animation starts.
*/
async show(targetPane = "account") {
const pane =
typeof targetPane === "string" && targetPane.trim()
@@ -5628,6 +5677,12 @@ export class ProfileModalController {
});
}
/**
* Hides the profile modal and resets temporary state.
*
* @param {object} [options]
* @param {boolean} [options.silent=false] - If true, suppresses the `onClose` callback.
*/
hide(options = {}) {
const { silent = false } =
options && typeof options === "object" ? options : {};
@@ -5745,6 +5800,13 @@ export class ProfileModalController {
}
}
/**
* Handles a successful login event.
* Syncs the active profile, hydrates wallet/admin state, and refreshes all lists.
*
* @param {object} detail - Login details (pubkey, savedProfiles, etc.).
* @returns {Promise<boolean>}
*/
async handleAuthLogin(detail = {}) {
const postLoginPromise =
detail && typeof detail.postLoginPromise?.then === "function"
@@ -5826,6 +5888,13 @@ export class ProfileModalController {
return true;
}
/**
* Handles a logout event.
* Clears personal data (DMs, friends, subscriptions) from the UI.
*
* @param {object} detail - Logout details.
* @returns {Promise<boolean>}
*/
async handleAuthLogout(detail = {}) {
const savedProfiles = Array.isArray(detail?.savedProfiles)
? detail.savedProfiles
@@ -5973,6 +6042,15 @@ export class ProfileModalController {
}
}
/**
* Switches the active session to another saved profile.
*
* @param {string} pubkey - The public key of the target profile.
* @param {object} [options]
* @param {object} [options.entry] - The saved profile entry object.
* @param {string} [options.providerId] - The auth provider ID to use.
* @returns {Promise<{switched: boolean, error?: Error}>}
*/
async switchProfile(pubkey, { entry, providerId } = {}) {
if (!pubkey) {
return { switched: false, reason: "missing-pubkey" };
+9
View File
@@ -0,0 +1,9 @@
# TODO: Document js/nostr/client.js
- [ ] Add JSDoc to `NostrClient` class methods
- [ ] Add flow comments to `init()`
- [ ] Add flow comments to `publishVideo()` and `editVideo()`
- [ ] Add flow comments to `subscribeVideos()`
- [ ] Create `docs/nostrClient-overview.md`
- [ ] Run lint
- [ ] Run unit tests