Commit Graph
41 Commits
Author SHA1 Message Date
thePR0M3TH3ANandClaude Opus 4.8 cebd3dc1e7 [auth] Add "Lock this device" control to re-lock a kept-unlocked session (#51)
The opt-in "keep unlocked" cache had no user-facing way to undo it mid-session
(it cleared only on logout / clear-site-data). Add a "Lock this device" button to
the profile modal footer (desktop + mobile), shown only when the active account
has a cached key to forget (app.isSessionKeptUnlocked -> client.hasCachedUnlockedKey).

Clicking it runs app.lockKeptUnlockedSession(pubkey), which re-locks in one action
everything the signer unlocks:
  - forgets the cached key (both tiers) so future reloads re-prompt for the PIN,
  - drops the in-memory signer (logoutSigner) so the CURRENT session re-locks now
    (the next signing action prompts again via ensureEncryptionCapableSigner),
  - storageService.lock(pubkey), and
  - nwcSettingsService.clearCache() (re-hydrates from the signer next time).

Visibility refreshes on renderSavedProfiles + selectPane. Tests: 2 in
tests/profile-modal-controller.test.mjs (shown only when kept-unlocked; click locks
the active pubkey). Lint + build green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-01 20:16:00 -04:00
thePR0M3TH3ANandClaude Opus 4.8 1b79ab9d09 [auth] Re-unlock the signer on demand instead of blanket-erroring encryption ops (#57)
A persisted nsec session restores the pubkey + UI on reload but NOT the in-memory
signer (the private key is only passphrase-encrypted — TODO #36). So after a refresh,
adding/editing a hashtag on an nsec account dead-ended with "Connect a Nostr signer
that supports encryption before managing hashtag preferences" — even though the user
looks logged in. Same class blocks subscriptions/blocks/DMs/reactions.

New reusable gate app.ensureEncryptionCapableSigner({ pubkey, need, promptMessage }):
checks the active signer's capabilities; if it can't encrypt/sign AND a MATCHING
locked nsec key exists for the account, prompts once for the passphrase and re-unlocks
the signer app-wide via unlockStoredSessionActor. One unlock re-enables every signing
flow until the next reload. Never unlocks a different account's key (pubkey-mismatch
guard); "no-stored-key" leaves non-nsec cases (e.g. an unresponsive NIP-07 extension)
to the caller's own error. Branching lives in js/nostr/ensureSignerDecision.js (pure,
unit-testable without the app shell).

Wired hashtag add/edit through it (ProfileHashtagController.persistHashtagPreferences):
a cancelled prompt aborts silently, a bad passphrase shows its own toast, and publish
never runs until the signer is confirmed. Exposed to the modal as
services.ensureEncryptionCapableSigner.

This is the shared machinery for the whole reloaded-nsec cluster (#51/#54/#56/#57);
subscriptions/blocks/DM-send/reactions are mechanical fast-follows against the same
gate (tracked in TODO #57).

Tests: tests/ensure-signer-decision.test.mjs (9 — capability + recoverable/fail
branching, never-wrong-key), + 2 wiring tests (gate blocks publish until ok) in
tests/profile-modal-controller.test.mjs. Lint + build green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-01 19:11:19 -04:00
thePR0M3TH3ANandClaude Opus 4.8 87af650b38 [profile] Refresh the profile modal on account switch (no manual reload)
Switching accounts with the profile modal open (e.g. NIP-07 -> a saved nsec) left
the PREVIOUS account's DMs, subscriptions, hashtags and lists on screen until the
user refreshed. Two problems:

1. handleAuthLogin ran its identity-reset steps unguarded, so if any one threw
   (renderSavedProfiles, a warm-list call, etc.) the steps after it were skipped —
   including handleActiveDmIdentityChanged, which is what clears the previous
   account's conversation. Result: stale DMs after a switch. Each reset step now
   runs in isolation via runIdentityResetStep, and the DM reset runs first.

2. The switch-with-modal-open refresh called populateOpenProfilePanels(), which
   only paints from current service state and omits lazily-loaded panes (messages,
   watch history). It also fired before the new identity's lists had loaded, with
   no reliable refresh afterward. Replaced with refreshOpenPanesForActiveIdentity(),
   which re-runs the ACTIVE pane through selectPane() — the same load+populate path
   as opening it fresh — so DMs reload, subscriptions/hashtags refetch, etc. It runs
   immediately, again once profile/wallet data settles, and once more from the auth
   coordinator when the new identity's blocks/subscriptions/hashtags finish loading
   (backfill for slow sync). Non-active panes stay lazy and repopulate on select.

Per-user list caches (blocks/subscriptions/hashtags are already keyed per pubkey)
mean the active pane shows cached data instantly on switch, then updates when the
new identity's fetch settles — no manual refresh.

Tests (tests/profile-modal-controller.test.mjs +3): a throwing reset step is
isolated; a throwing DM reset no longer skips the saved-profiles reset; and
refreshOpenPanesForActiveIdentity re-runs the active pane only when the modal is
open. Existing profile-modal / switch-coordinator / stored-nsec-switch suites green.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-01 16:31:01 -04:00
thePR0M3TH3ANandClaude Opus 4.8 cf4ce5c3b8 [admin] Add sub-tab toolbar + Blocked-videos tool to the admin pane (#25 follow-up)
The admin pane stacked every list (Moderators / Whitelist / Blacklist) in one
scroll, and the per-event block list had no admin UI (block-only via the ⋯ menu,
no view/unblock). This reorganizes the pane into a sub-tab toolbar and adds the
missing Blocked-videos tool.

- Sub-tabs: a role="tablist" toolbar in #profilePaneAdmin switches between
  Whitelist (default), Blacklist, Blocked videos, and Moderators. Moderators is
  super-admin-only — its tab button is hidden otherwise. selectAdminSubtab()
  shows one section at a time (ARIA + data-state), falling back to the default
  when a hidden tab is requested.
- Blocked videos: view the per-event admin block list, unblock per row (confirm
  → removeFromEventBlacklist), and add by nevent/hex id. Rows show the video
  title+author when it's in the local cache, else the shortened id. Reuses the
  existing accessControl event-blacklist API + onEventBlacklistChange (live
  re-render) and refreshes the grids on change. No backend changes.
- Row-building/render logic in js/ui/profileModal/blockedVideosSection.js
  (keeps ProfileAdminController under its size cap; unit-testable without a DOM).

Spec correction: profile-modal-controller.test's admin assertion updated for the
sub-tab layout (Moderators section now hidden until its tab is active; its tab
button is shown to a super admin; selecting it reveals the section).

Tests: tests/profile-blocked-videos.test.mjs (7 — rows dedupe/enrich/fallback,
render, unblock wiring); profile-modal-controller admin test updated.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-01 10:56:38 -04:00
google-labs-jules[bot]andPR0M3TH3AN 106f7640c4 fix(exploreData): trigger refresh immediately on visibility change
- `handleVisibility` only scheduled intervals but didn't trigger an immediate refresh when returning to the tab.
- This caused `tests/unit/services/exploreDataService.test.mjs` to fail (assertion `0 !== 1`).
- Added explicit calls to `refreshWatchHistoryTagCounts` and `refreshTagIdf` when `document.hidden` becomes false.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-18 15:10:29 +00:00
google-labs-jules[bot]andPR0M3TH3AN ee339e7d5f chore(ai): CI health fixes (flaky tests)
Refactored `tests/profile-modal-controller.test.mjs` to improve maintainability and reliability.
- Removed redundant loop wrapper.
- Fixed duplicate mock entries.
- Verified test isolation with `beforeEach` JSDOM setup.
- Generated completion log.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-17 20:17:52 +00:00
google-labs-jules[bot]andPR0M3TH3AN 4059d52847 fix(ui): profile modal controller instantiation order
- Moved controller instantiation (DM, Relay, Hashtag) to after services setup in `ProfileModalController.js`.
- Fixed `ProfileHashtagController` usage of uninitialized services.
- Updated `profile-modal-controller.test.mjs` mock to include `uiReady/dataReady` flags and use correct service access pattern.
- Resolved CI failures in `profile-modal-controller.test.mjs`.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-13 03:50:42 +00:00
google-labs-jules[bot]andPR0M3TH3AN 7300977a6d [decompose] extract DM and Relay controllers from ProfileModalController
Extracted ProfileDirectMessageController (split into Controller, Actions, Renderer, Helper) and ProfileRelayController from ProfileModalController to reduce file size and improve maintainability.

ProfileModalController.js size reduced from ~12000 lines to ~8750 lines.
Updated unit tests to reflect the new structure.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-11 21:15:06 +00:00
thePR0M3TH3AN b1f06695a6 Merge pull request #2339 from PR0M3TH3AN/suppress-nip17-relay-warning-3701543430430345832
Add config to suppress NIP-17 relay warnings
2026-02-10 23:06:50 -05:00
google-labs-jules[bot]andPR0M3TH3AN 5ca378bb96 Add ENABLE_NIP17_RELAY_WARNING config to suppress privacy warnings
Added a new configuration variable `ENABLE_NIP17_RELAY_WARNING` to `config/instance-config.js` (defaulting to `false`) to control the display of NIP-17 relay fallback warnings. Updated `js/ui/profileModalController.js` and `js/app.js` to respect this setting.

This change allows instance operators to disable "Privacy warning: direct messages are using your default relays because no NIP-17 relay list is available" and related notifications.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-11 04:05:36 +00:00
google-labs-jules[bot]andPR0M3TH3AN 48d33f45eb [decompose] Extract Wallet and Storage controllers from ProfileModalController
Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-11 03:30:40 +00:00
google-labs-jules[bot]andPR0M3TH3AN d45b115f07 Fix annoying DM relay warning and clarify settings UI
- Throttle the "Privacy warning: direct messages are using your default relays..." message so it only appears once per session/identity change.
- Rename "DM Relay Hints" to "NIP-17 Relay List" in the DM Settings modal to match the warning message and reduce user confusion.
- Add regression test for the warning throttling logic.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-04 16:16:15 +00:00
google-labs-jules[bot]andPR0M3TH3AN b3f37058a2 Defer profile modal data loading to improve responsiveness
Co-authored-by: PR0M3TH3AN <[email protected]>
2026-02-03 14:44:32 +00:00
thePR0M3TH3AN a8c2608a01 Merge branch 'unstable' into ai/deps-scan-913034820998740720 2026-01-30 09:20:22 -05:00
thePR0M3TH3AN b71346c06a Merge branch 'unstable' into ai/interop-tests-20250228-12917398042927663384 2026-01-30 09:19:49 -05:00
thePR0M3TH3AN 646de9014b Merge pull request #1960 from PR0M3TH3AN/ai/ci-health-20250221-16879002849481713
chore(ai): CI health fixes (flaky tests)
2026-01-30 09:16:57 -05:00
thePR0M3TH3AN 36e724a9de Merge pull request #1956 from PR0M3TH3AN/refactor-automated-pr-review-12750616021674214922
feat: enhance automated PR review script with batch processing
2026-01-30 09:15:04 -05:00
google-labs-jules[bot]andPR0M3TH3AN f456a6d605 fix(test): mock hashtagPreferencesService.load in profile modal test
Co-authored-by: PR0M3TH3AN <[email protected]>
2026-01-30 09:19:46 +00:00
google-labs-jules[bot]andPR0M3TH3AN 51c5845ae1 fix: resolve missing mock method in profile modal tests
Added the missing `load()` method to the `hashtagPreferences` mock in `tests/profile-modal-controller.test.mjs`. This resolves the `TypeError` encountered during the `handleAddHashtagPreference` test case, ensuring the test suite passes consistently in CI.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-01-30 07:48:19 +00:00
google-labs-jules[bot]andPR0M3TH3AN ced1f5456f fix(test): mock load method in profile-modal-controller test
- Add missing `load` method to `baseHashtagPreferences` mock in `tests/profile-modal-controller.test.mjs` to fix `TypeError: this.hashtagPreferencesService.load is not a function` in unit tests.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-01-30 02:34:39 +00:00
google-labs-jules[bot]andPR0M3TH3AN 2ba9b6c4a5 fix(test): mock hashtagPreferencesService.load in profile modal tests
- Updates `tests/profile-modal-controller.test.mjs` to include a mock for `load()` in `baseHashtagPreferences`.
- Resolves the `TypeError: this.hashtagPreferencesService.load is not a function` error seen in CI.
- Ensures unit tests pass for ProfileModalController interactions with the hashtag pane.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-01-30 00:27:28 +00:00
google-labs-jules[bot]andPR0M3TH3AN 3c9f9841fc fix(test): mock load method in hashtagPreferences service
- Add `load: async () => {}` stub to `baseHashtagPreferences` in `tests/profile-modal-controller.test.mjs` to prevent `TypeError` during `handleAddHashtagPreference` test.
- Fixes CI failure in `tests/profile-modal-controller.test.mjs` where `this.hashtagPreferencesService.load` was undefined.
- Verified with `npm run test:unit:shard3`.

Co-authored-by: PR0M3TH3AN <[email protected]>
2026-01-29 22:30:26 +00:00
google-labs-jules[bot] 4bf3605fd2 Fix: Ensure history pane refreshes on subsequent visits
This change ensures that the profile history pane refreshes its content when the user navigates back to it, addressing a bug where stale data could be displayed.

It implements an optimized strategy:
- On the first load, `ensureInitialLoad` is called, which handles the initial fetch internally.
- On subsequent visits (when the renderer is already initialized), `refresh({ force: true })` is called explicitly to update the list.

The unit test `history pane lazily initializes the watch history renderer` was updated to reflect this behavior, verifying that `ensureInitialLoad` is called once and `refresh` is called on subsequent opens.
2026-01-14 04:05:23 +00:00
thePR0M3TH3AN b48edbb055 Refactor inline styles to CSS rules 2026-01-13 21:13:32 -05:00
thePR0M3TH3AN e9ce494ccd Remove watch history metadata caching 2026-01-03 09:11:26 -05:00
thePR0M3TH3AN 6a1b9a86e2 Hide profile modal while add-profile login is active 2025-10-27 17:43:52 -04:00
thePR0M3TH3AN 9377d39be0 Fix add profile flow to prioritize login modal 2025-10-27 17:36:18 -04:00
thePR0M3TH3AN 3da281c197 Handle hashtag preference publishing from profile modal and menu 2025-10-25 18:48:15 -04:00
thePR0M3TH3AN b9a927199c Add hashtag management to profile modal 2025-10-25 16:39:26 -04:00
thePR0M3TH3AN 616fad1612 Revamp login modal auth provider cards 2025-10-21 18:37:41 -04:00
thePR0M3TH3AN 094559b618 Add per-profile settings store and mask wallet inputs 2025-10-21 17:46:41 -04:00
thePR0M3TH3AN 6d84d6be4e feat: show provider metadata in saved profiles 2025-10-21 14:44:14 -04:00
thePR0M3TH3AN e8d20cd8dd Add channel and copy actions for profile modal lists 2025-10-20 14:17:15 -04:00
thePR0M3TH3AN 3ae86c68a3 Refine profile modal layout and metadata toggle 2025-10-19 22:10:26 -04:00
thePR0M3TH3AN 7ac4abfa61 Improve admin list hydration and visibility 2025-10-19 21:40:23 -04:00
thePR0M3TH3AN 4f1c610d80 Refine profile modal responsive behavior 2025-10-19 21:09:13 -04:00
thePR0M3TH3AN e927c78124 Improve profile modal mobile navigation 2025-10-19 20:46:02 -04:00
thePR0M3TH3AN 812203c799 Use consistent short npub display 2025-10-18 09:48:16 -04:00
thePR0M3TH3AN 39d8cee81a Remove design system flag and update tests 2025-10-14 20:16:38 -04:00
thePR0M3TH3AN 5e8e4a0d12 Add modal accessibility integration coverage 2025-10-11 16:37:55 -04:00
thePR0M3TH3AN 9122a14493 Add jsdom-based tests for profile modal controller 2025-10-07 22:28:43 -04:00