- Prioritize Kind 10000 events with `d=user-blocks` over plain mute lists in `loadBlocks`.
- Add `isTaggedBlockListEvent` helper for differentiation.
- Add regression test case to `tests/user-blocks.test.mjs` verifying that tagged lists override newer plain lists.
This commit modifies `js/userBlocks.js` to correctly parse Kind 10000 events that have empty content but populated `p` tags (used by clients like Primal). Previously, these events were treated as empty block lists, causing the user's block list to be cleared.
Changes:
- Refactored `loadBlocks` to check for empty content and fallback to extracting pubkeys from tags using `extractPubkeysFromTags`.
- Updated `loadBlocks` to use a sequential promise strategy (`Promise.any` then `Promise.allSettled`) to ensure eventual consistency without race conditions or double-processing.
- Added extensive tests in `tests/user-blocks.test.mjs` covering tag-only lists, whitespace content, and concurrency scenarios.
- Refactored `js/userBlocks.js` to merge Kind 10000 and Kind 30002 events on load, preventing overwritten mutes.
- Updated `js/constants.js` to set `DEFAULT_TRUSTED_MUTE_HIDE_THRESHOLD` to 4, enabling soft-hide (blur) behavior for lower mute counts.
- Update `ModerationService` to recognize session actors and load mutes from local storage.
- Update `UserBlockListManager` to persist blocks locally for session actors without relay publication.
- Ensure `submitReport` falls back to the session actor's signer.
- Add `resolveStorage` helper to safely access localStorage.
- Added `seedLocalBaseline` to `js/userBlocks.js` to merge admin blacklist into local block list for session actors.
- Implemented `_saveLocal` and `_loadLocal` in `js/userBlocks.js` to persist local-only block lists to `localStorage`.
- Updated `loadBlocks` to prioritize local storage for session actors, bypassing relay fetching when local data exists.
- Modified `js/app.js` to call `seedLocalBaseline` with the admin blacklist during initialization if the user is anonymous.
- Ensured `UserBlockListManager` correctly resolves `localStorage` in various environments (browser/test).
- Verified changes with `tests/user-blocks-local-seed.test.mjs`.
Updates `js/app.js` to:
1. Treat Trusted Seed mutes as hard blocks (`isAuthorBlocked` returns `true`) for anonymous users ("session actors"), ensuring strict filtering.
2. Refresh the entire video grid (`refreshAllVideoGrids`) instead of just the UI (`refreshVisibleModerationUi`) when `trusted-mutes` events arrive for anonymous users, ensuring blocked content is removed dynamically once the lists load. This fixes a race condition where content might render before the block list is fully fetched.
Subscribes `js/app.js` to `moderationService` events (`trusted-mutes`, `user-blocks`, `summary`). This ensures that asynchronous updates to trusted seed block lists (Kind 10000) trigger an immediate UI refresh, correctly applying moderation (blur/hide) even for anonymous session actors who rely on these seeds. Previously, the initial render would complete before the async lists arrived, leaving blocked content visible until a page reload.
- Fixed `ModerationService.reconcileTrustedMuteSubscriptions` to always force re-subscription to trusted seeds, bypassing previous state comparisons.
- Enhanced `ModerationService` to filter trusted seeds against the global admin blacklist.
- Added blacklist change event support to `AccessControl` and `TrustBootstrap` to dynamically update seeds when the blacklist changes.
- Added detailed logging to `ModerationService`, `TrustBootstrap`, and `AccessControl` to trace trust seed initialization and application.
- Fixed `ModerationService.reconcileTrustedMuteSubscriptions` to always force re-subscription to trusted seeds, bypassing previous state comparisons.
- Enhanced `ModerationService` to filter trusted seeds against the global admin blacklist.
- Added blacklist change event support to `AccessControl` and `TrustBootstrap` to dynamically update seeds when the blacklist changes.
- Added detailed logging to `ModerationService` for tracking subscription lifecycle.
- Fixed `ModerationService.clearTrustedMuteTracking` to properly force re-subscription to trusted seeds by passing an empty previous state.
- Enhanced `ModerationService` to filter trusted seeds against the global admin blacklist.
- Added blacklist change event support to `AccessControl` and `TrustBootstrap` to dynamically update seeds when the blacklist changes.
- Replaced the button with a simple text link (`<a id="link-r2-help">`) inside the R2 credentials section.
- Added a click event listener in `UploadModal.js` to call `this.close()` when the link is clicked, allowing users to navigate to the setup guide without obstruction.
- Modified `js/ui/components/UploadModal.js` to hide the thumbnail browse button when in 'External Link' mode.
- Ensured the browse button is visible when switching back to 'Upload File' mode.
- Added logic to clear the thumbnail file input when resetting the form.
- Added file input and browse button for thumbnails in `upload-modal.html`.
- Updated `UploadModal.js` to handle thumbnail file selection and UI toggling.
- Updated `r2Service.js` to upload thumbnail file (if present) to R2 and update metadata.
- Ensured thumbnail keys are derived from video keys for consistent naming.
- Add new "Setup Guide" section to `docs/storage-r2.md` detailing the dual-credential requirement (S3 Keys + Cloudflare API Token) for full automation.
- Remove unsupported "manual/S3-only" workflow documentation to prevent user confusion.
- Update `js/services/r2Service.js` error message to explicitly prompt users for an API Token when the public domain is missing, instead of suggesting manual configuration.
- Refactored `SimilarContentCard.js` to use Flexbox layout.
- Updated `css/tailwind.source.css` to remove grid layout and use standard flex utilities.
- Improved thumbnail sizing (16:9, fixed width) and responsiveness.
- Added hover effects (background color, thumbnail zoom).
- Cleaned up typography and metadata hierarchy.
- Replaces split "Custom" vs "Cloudflare" forms with a single unified form
- Implements source switching between "Upload File" and "External Link"
- Consolidates NIP-71 advanced fields into a single collapsible section
- Moves R2 storage configuration to a dedicated, collapsible settings block
- Improves responsiveness and modernizes the UI layout
- Updates internal JS logic to handle unified form submission and validation
Integrates WebTorrent client-side seeding into the Cloudflare R2 upload path.
- Adds `uploadFile` to `r2Service` for generic file uploads.
- Updates `UploadModal` to offer an optional "Create torrent & seed" flow.
- Generates magnet links and .torrent files in the browser after R2 upload.
- Uploads the .torrent file to R2 and populates `xs` tag.
- Populates `ws` (Web Seed) with the R2 public URL.
- Adds UI progress indicators for hashing and torrent upload.
- Enforces a 2GB file size limit for browser seeding to prevent crashes.
- Adds a mandatory legal rights confirmation checkbox for R2 uploads.
- Rename "Legacy description" to "Video description"
- Group technical NIP-71 fields (Event Type, IMETA, Tracks, Segments) into a hidden "Advanced Options" section
- Automate "Summary" field to mirror "Video description" by default (with unlock toggle)
- Automate "Web seed" field to mirror "Hosted video URL"
- Automate "Content Warning" based on NSFW toggle status
- Maintain visibility of Hashtags in the main form flow
- Apply changes consistently to both Custom and Cloudflare R2 upload modes
- Fix cleanup handler registration for advanced toggle listener
- Rename "Legacy description" to "Video description"
- Group technical NIP-71 fields (Event Type, IMETA, Tracks, Segments) into a hidden "Advanced Options" section
- Automate "Summary" field to mirror "Video description" by default (with unlock toggle)
- Automate "Web seed" field to mirror "Hosted video URL"
- Automate "Content Warning" based on NSFW toggle status
- Maintain visibility of Hashtags in the main form flow
- Apply changes consistently to both Custom and Cloudflare R2 upload modes
- Add a confirmation dialog in `UploadModal.js` when a user uploads a video with only a magnet link to warn about potential data loss.
- Replace the hardcoded limit of 300 in `NostrClient.fetchVideos` with a configurable limit that respects `DEFAULT_VIDEO_REQUEST_LIMIT` and `MAX_VIDEO_REQUEST_LIMIT`, improving client-side scalability.
- Implements `hasDisinterestedTag` helper in `js/feedEngine/utils.js` to check for disinterested tags in both raw Nostr tags and NIP-71 metadata.
- Updates `createBlacklistFilterStage` in `js/feedEngine/stages.js` to filter out videos matching `context.runtime.tagPreferences` disinterests.
- Ensures existing blacklist logic remains undisturbed.
- Verified with new unit test `tests/feed-engine-tag-pref.test.mjs` and existing feed engine tests.
Removes the fixed height and overflow from the watch history container in the profile modal, allowing it to expand fully. Updates the profile modal controller to use the main modal panes container as the scroll root for infinite scrolling.
This change introduces `createWatchHistoryProfileHydrationStage` to the
watch history feed pipeline. The stage resolves author profiles for
hydrated videos by:
1. Checking the global `getProfileCacheEntry` for cached profiles.
2. Batching missing profiles into a single Kind 0 relay request.
3. Updating the global cache (`setProfileCacheEntry`) with fetched data.
4. Attaching the resolved profile to `item.metadata.profile`.
This ensures that channel images populate correctly in the watch history
UI (both sidebar and profile modal).
- Shorten npubs in history view cards.
- Add Refresh, Privacy Banner, and Info to Profile Modal Watch History.
- Wire up Profile Modal Watch History controls.
Replaces unreliable esm.sh imports with cdn.jsdelivr.net to prevent site crashes due to CORS and timeout errors.
Updates imports for `nostr-tools`, `buffer`, and `@aws-sdk/client-s3` in `js/nostrToolsBootstrap.js`, `js/bufferPolyfill.js`, and `js/storage/r2-s3.js`.
Adds jsdelivr as a prioritized remote source in `nostrToolsBootstrap` while retaining esm.sh as a fallback.
When relays fail to connect or do not send EOSE, the legacy `pool.list` shim
would hang indefinitely, blocking watch history hydration.
This change adds a timeout (default 7000ms) to ensure it returns whatever events
it has collected, preventing the UI from getting stuck.
This commit addresses issues with the watch history not updating dynamically and adds a manual refresh mechanism.
Changes:
- Added a "Refresh" button to the watch history view in `views/history.html` and `js/historyView.js`.
- Updated `watchHistoryService.js` to accept a `forceRefresh` option in `loadLatest`, which bypasses the cache freshness check and triggers a relay fetch.
- Updated `js/feedEngine/watchHistoryFeed.js` to propagate the `forceRefresh` flag from the runtime context to the service.
- Added user feedback (toast messages and logs) for "Clear Local History" and "Republish Now" actions.
- Updated `tests/watch-history.test.mjs` to align with the new `fetchHistory` signature.