Commit Graph

3183 Commits

Author SHA1 Message Date
filemon edc4163852 Eliminate sidebar-look during route transitions by setting immediate center-top attention
On route change, immediately trigger a preliminary attention target at
viewport center-top before the 250ms DOM-mount delay. This ensures the
gaze system never falls to random or mouse-follow mode during the gap,
which previously caused Blobbi to drift leftward toward the sidebar.

The delayed reaction still fires at 250ms and replaces the preliminary
target with a precise DOM-measured position.
2026-04-12 19:36:57 -03:00
filemon 08cc77dbdc Fix Blobbi looking at sidebar during route transitions
Keep previous attention alive during the 250ms route-reaction delay
instead of clearing it immediately. This prevents the gaze system from
falling to random mode (which could point toward the sidebar) while
waiting for the new page's DOM to mount.

- Split cancelReaction into cancelPendingTimeouts (timeouts only) and
  full cancel (timeouts + attention); route changes use the former
- Add bypassCooldown option to triggerAttention so the delayed reaction
  can override the kept-alive attention without being blocked by cooldown
- Stabilize triggerAttention via uiAttentionRef instead of stale closure
2026-04-12 19:25:47 -03:00
filemon 4764202a44 Reset attention cooldown on clearAttention to fix rapid route changes
clearAttention() now resets lastAttentionTimeRef to 0 so that a
triggerAttention call immediately after a forced clear is not silently
rejected by the 1500ms cooldown guard.
2026-04-11 20:43:55 -03:00
filemon 3d951cdaea Simplify route reaction to single center-look with random duration
Remove right-sidebar detection and multi-target chaining. The generic
reaction now fires a single attention target at the top-center of the
main content area for a random 2-6 seconds.

- Remove findRightSidebarPosition() and all sidebar scan logic
- Replace fixed 1200ms duration with random 2000-6000ms
- Compute target position at reaction start via live DOM query
  and window.innerWidth/Height fallback (no stale closure)
- Cancel both pending timeouts and active attention on drag
- Remove viewport prop from hook options (no longer needed)
- Update docstrings to describe the simplified behavior
2026-04-11 20:23:05 -03:00
filemon aadd2908e2 Add generic route-transition reaction for Blobbi companion
On page navigation the companion now briefly pauses and scans the
layout areas that changed. Center content is always scanned first,
followed by the right sidebar if a non-placeholder sidebar is
detected in the DOM.

Implementation:
- useRouteReaction.ts: thin orchestration hook that watches pathname,
  determines changed areas, and chains triggerAttention calls via
  setTimeout. Cancels on new route change, drag, or unmount.
- useBlobbiCompanion.ts: wires the new hook with existing
  triggerAttention/clearAttention from useBlobbiAttention.

No changes to the attention system, state machine, gaze hook, motion
hook, or entry animation. The existing attending state and attend-ui
gaze mode handle all the visual behavior.

Includes an empty ROUTE_REACTIONS map for future per-route overrides.
2026-04-11 19:52:21 -03:00
Alex Gleason 357ba7d8c8 fix: migrate to SystemBars API for Android 16+ safe area inset support
Android 16 (API 36) enforces edge-to-edge rendering unconditionally,
breaking @capacitor/status-bar's setOverlaysWebView and setBackgroundColor.
Additionally, a Chromium bug (<140) causes env(safe-area-inset-*) to report
0 in some Android WebViews.

- Replace @capacitor/status-bar with SystemBars from @capacitor/core 8+
- Enable insetsHandling: 'css' in capacitor.config.ts so the SystemBars
  plugin injects --safe-area-inset-* CSS variables on Android
- Update all safe area CSS utilities and inline styles to use
  var(--safe-area-inset-*, env(safe-area-inset-*, 0px)) fallback pattern
- Remove @capacitor/status-bar dependency (no longer needed)
2026-04-11 14:47:15 -05:00
Alex Gleason 207ca6893a Add iCloud Keychain credential saving/restoring on iOS via @capgo/capacitor-autofill-save-password
- Use SecAddSharedWebCredential to prompt 'Save Password?' on signup
- Use ASAuthorizationPasswordProvider to restore credentials on login
- Add webcredentials:ditto.pub Associated Domains entitlement
- Deploy apple-app-site-association for domain validation
- Keep existing Chromium PasswordCredential flow as web fallback
- Add saveNsec() helper: native credential manager on iOS/Android,
  file download + bonus PasswordCredential on web
- Single 'Continue' button triggers the appropriate save method per platform
2026-04-11 14:01:34 -05:00
Alex Gleason 37df5d0bd1 release: v2.6.3 v2.6.3 2026-04-10 23:27:38 -05:00
Alex Gleason 19906cf918 Merge branch 'fix/badge-image-aspect-ratio-hint' into 'main'
Show recommended 1:1 aspect ratio hint on badge image upload

Closes #212

See merge request soapbox-pub/ditto!178
2026-04-11 03:49:14 +00:00
Alex Gleason 874010c4fe Store nsec in browser password manager via Credential Management API
Progressive enhancement using PasswordCredential (Chromium-only).
On sign-up, the nsec is offered to the browser's password manager
alongside the existing file download. The prompt appears while the
user is looking at their key on the download step. On login, stored
credentials are retrieved for one-tap login on supported browsers.

Safari/Firefox/iOS silently skip — existing flows are unchanged.
2026-04-10 21:49:14 -05:00
Alex Gleason 126dce1dfc Surface account deletion as 'Delete Account' for App Store compliance
Add a 'Delete Account' pill button to the bottom of the Settings
page (Guideline 5.1.1v). Rename the Danger Zone heading in Advanced
Settings to match. Simplify the deletion dialog to a single screen:
plain-language warning, list of what gets deleted, type DELETE to
confirm, and Cancel/Delete buttons. Always broadcasts to all relays.

The underlying NIP-62 mechanism and components that render vanish
events to other users are unchanged.
2026-04-10 16:44:35 -05:00
Alex Gleason 105da53e2e Add NSCameraUsageDescription to Info.plist
File inputs with accept="image/*" present a camera option on iOS.
Without this usage description, WKWebView crashes or fails to show
the permission dialog when the user selects 'Take Photo'.
2026-04-10 16:10:35 -05:00
Alex Gleason 7bc4a632b0 Add XCode DEVELOPMENT_TEAM to project.pbxproj 2026-04-10 16:03:38 -05:00
Alex Gleason 0222248d76 Merge branch 'main' of gitlab.com:soapbox-pub/ditto 2026-04-10 15:49:15 -05:00
Alex Gleason a542dd3b36 Sanitize all event-sourced URLs and prevent CSS injection
Nostr events are untrusted user input. Any URL extracted from event tags
or metadata must be validated before use in any context — not just
navigable hrefs, but also img src, CSS url(), and style attributes.

Changes:
- Theme events (kind 16767/36767): validate background and font URLs
  through sanitizeUrl() at parse time in themeEvent.ts
- Badge definitions (kind 30009): validate image and thumb URLs through
  sanitizeUrl() at parse time in parseBadgeDefinition.ts
- Font family names: sanitize with an allowlist regex before
  interpolation into CSS declarations in fontLoader.ts
- Profile fields: replace weak startsWith('http://') checks with
  sanitizeUrl() in ProfileRightSidebar and ProfilePage
- Community descriptions: validate extracted URLs through sanitizeUrl()
  in CommunityContent.tsx
- AGENTS.md: mandate unconditional URL sanitization for all
  event-sourced URLs regardless of rendering context, document CSS
  injection prevention guidelines
2026-04-10 15:48:38 -05:00
Mary Kate Fain fc292a8654 Replace screenshots table with simpler Before/After format in MR template 2026-04-10 15:15:54 -05:00
Mary Kate Fain 9214bd823b Remove redundant Submission checklist from MR template 2026-04-10 15:14:54 -05:00
Mary Kate Fain 8f5b8264c9 Show recommended 1:1 aspect ratio hint on badge image upload 2026-04-10 15:13:18 -05:00
Alex Gleason 94f821d064 Merge branch 'contributor-quality-gates' into 'main'
Add contributor quality gates: CONTRIBUTING.md, MR template, and CI validation

See merge request soapbox-pub/ditto!177
2026-04-10 19:50:29 +00:00
Mary Kate 6d73e6d06b Add contributor quality gates: CONTRIBUTING.md, MR template, and CI validation 2026-04-10 19:50:28 +00:00
Alex Gleason bd724de1e8 Bump @unhead/addons and @unhead/react to ^2.1.13 to fix CVE-2026-39315
The vulnerability (GHSA-95h2-gj7x-gx9w) allows bypassing hasDangerousProtocol()
in useHeadSafe() via leading-zero padded HTML entities. Not currently reachable
in this codebase (we only use useSeoMeta), but closes the CVE in the dependency
tree.
2026-04-10 14:29:49 -05:00
Alex Gleason 9d899cfe87 Sanitize all user-supplied URLs from Nostr events to prevent javascript: XSS
Add a shared sanitizeUrl() utility that validates URLs are well-formed
https: before they reach href attributes, window.open(), or openUrl().

Apply sanitization across all components that render untrusted URLs:
- CalendarEventDetailPage: r-tag links
- ZapstoreAppContent: url and repository tags
- ZapstoreReleaseContent: asset url tags passed to openUrl()
- AppHandlerContent: web handler tags and metadata.website
- NsiteCard: source tag
- GitRepoCard: web tag URLs passed to openUrl()
- FileMetadataContent: url tag used in download href
- ProfilePage: metadata.website (tighten weak startsWith check)
- useUserStatus: r-tag URL

Document sanitizeUrl usage in AGENTS.md for future agent use.
2026-04-10 14:22:42 -05:00
Chad Curtis 72268dfde6 Merge branch 'feat/feed-blobbi-status-visuals' into 'main'
Reflect companion condition in feed Blobbi cards

See merge request soapbox-pub/ditto!169
2026-04-10 13:05:44 +00:00
Alex Gleason 7b63f6112c Clean up profile header: remove lightning address, NIP-05 check icon, and trailing slash from website URLs 2026-04-09 22:30:38 -05:00
Alex Gleason ce61d8d1a6 Restore right sidebar for profile pages, keep fields mobile-only 2026-04-09 22:00:50 -05:00
filemon c4a10b1303 Merge branch 'main' into feat/feed-blobbi-status-visuals 2026-04-09 15:27:28 -03:00
Chad Curtis 76c6846e91 Render BOLT11 lightning invoices in note content
Detect lnbc/lntb/lnbcrt/lntbs invoices (with optional lightning: prefix)
in note text and render them as interactive cards with a theme-aware QR
code, decoded amount, copy button, and Open in Wallet action.

- Add lightning-invoice token type to NoteContent tokenizer
- Create LightningInvoiceCard with tap-to-expand square QR, cqw-scaled
  amount text, and responsive layout
- Extract shared theme-aware QR color logic into src/lib/qrColors.ts
  (deduplicate from FollowQRDialog)
2026-04-09 08:02:26 -05:00
Alex Gleason ac1e82b52d release: v2.6.2 v2.6.2 2026-04-08 23:38:31 -05:00
Alex Gleason 437b8de652 Remove right sidebar content and show profile fields inline 2026-04-08 23:34:03 -05:00
Alex Gleason adadb6ed53 Fix native file downloads: save directly to Documents on iOS/Android 2026-04-08 22:54:46 -05:00
Alex Gleason f7c90a4a23 Remove trending hashtags section from logged-out homepage 2026-04-08 22:28:22 -05:00
Alex Gleason 82632bb76c Store nostr:login in secure storage on native platforms
Use capacitor-secure-storage-plugin to persist login credentials
(nsec keys) in iOS Keychain / Android KeyStore instead of plaintext
localStorage. Web behavior is unchanged. Existing native users are
auto-migrated on first launch: if secure storage is empty but
localStorage has data, it is moved over and the plaintext copy is
removed.

Also ignore ios/ directory in ESLint (Capacitor-generated files).
2026-04-08 22:20:48 -05:00
Alex Gleason 3a70d34e6d npm audit fix 2026-04-08 22:12:03 -05:00
Alex Gleason 221d3f4aff Merge branch 'mobile-search' 2026-04-08 22:11:38 -05:00
Alex Gleason 6a1a462ab0 Upgrade @nostrify/react to ^0.5.0 (async storage support)
Upgrade to the new version that includes the NLoginStorage interface
and storage/fallback props on NostrLoginProvider for pluggable async
storage backends (e.g. Capacitor Secure Storage).

- Add resolve.dedupe for react/react-dom to prevent dual-React issues
- Update NoteContent tests to use async findBy* queries since the
  provider now always awaits storage initialization
2026-04-08 22:08:56 -05:00
Alex Gleason 5ee8bc1cc0 Improve mobile search UX: lock scroll, hide bottom nav, dismiss accessory bar, and fix close behavior 2026-04-08 22:04:26 -05:00
Alex Gleason 76d53859cf Simplify webxdc to always open in fullscreen panel 2026-04-08 20:47:46 -05:00
Alex Gleason e482afbd3f Fix sandbox origin isolation and Android build issues 2026-04-08 20:47:42 -05:00
Alex Gleason 11ff27efe2 Enable iOS swipe-back navigation and fix bottom nav layout 2026-04-08 20:47:37 -05:00
Alex Gleason 8f6f678132 Add safe area padding and fix fullscreen sandbox on iOS 2026-04-08 20:47:32 -05:00
Alex Gleason f25139103c Add native SandboxPlugin for iOS and Android 2026-04-08 20:47:28 -05:00
Alex Gleason 0028b506e7 Fix webxdc bridge: serve script via resolveFile instead of injectedScripts
SandboxFrame's virtual script serving intercepted /webxdc.js and served
the empty placeholder content before resolveFile was ever called. The
dynamically generated bridge script (which embeds selfAddr etc.) was
never reaching the iframe.

Move bridge serving and HTML injection into resolveFileWithBridge so
the content is served from bridgeScriptRef after onReady populates it.
2026-04-08 16:55:01 -05:00
Alex Gleason 926c27d51c Fix webxdc race condition: await onReady before sending init
The sandbox frame was sending init immediately and calling onReady
concurrently, so fetch requests arrived before the archive was
downloaded and unzipped. Now onReady is awaited before init is sent,
matching the original Webxdc behavior.
2026-04-08 16:50:44 -05:00
Alex Gleason c4454ee2a1 Refactor iframe.diy usage into unified SandboxFrame component
Extract duplicated sandbox protocol logic from NsitePreviewDialog and
Webxdc into a single SandboxFrame component. Shared utilities (MIME
types, base64, HTML injection, JSON-RPC types) move to src/lib/sandbox/.

Add configurable sandboxDomain to AppConfig so the iframe.diy domain
can be overridden via ditto.json, preparing for native Capacitor
implementations.

Strip unused console/navigation/error RPC from previewInjectedScript,
leaving only the /index.html path normalization.
2026-04-08 16:41:23 -05:00
Chad Curtis e56737f776 Fix blobbi discovery: query by author instead of relying on profile.has[]
The Blobbi collection was previously discovered via the profile's has[] tag
list, meaning any blobbi whose d-tag was missing from that secondary index
would be invisible to the user despite existing on the relay.

Now useBlobbisCollection() without args queries all kind 31124 events by
author + ecosystem namespace tag — the user authored these events, so that
is the source of truth. The profile.has[] list is still used for selection
ordering preference, but no longer gates discovery.

The dList parameter remains available for targeted fetches (e.g. the
companion layer only needs one specific blobbi).
2026-04-08 11:02:03 -05:00
Chad Curtis feb6c1a9f6 Add drop shadow and solid gradient to overflow tab arrows 2026-04-08 10:27:17 -05:00
Chad Curtis 6f8d225597 Increase overflow tab arrow stroke to 4 and boost contrast 2026-04-08 10:22:04 -05:00
Chad Curtis 9ecd99a6a1 Add 'Write a letter' option to profile more menu
Adds a Mail-icon menu item in the profile more menu for other users'
profiles. Navigates to /letters/compose?to={npub} so the recipient is
pre-filled, matching the same flow used by the notification reply button.
2026-04-08 04:01:11 -05:00
Chad Curtis 287097627d Hide delivery method when push disabled; fix persistent description
Only show the delivery method radio group when push notifications are
enabled. Update the persistent option description to explain it is for
devices that don't support push notifications (e.g. GrapheneOS).
2026-04-08 00:20:20 -05:00
Chad Curtis 3ee491a63b Add push vs persistent notification delivery option for Android
Default to push mode (no foreground service). Persistent mode with
the always-on background polling service is opt-in via the new
Delivery Method section in notification settings.

- Add notificationStyle ('push' | 'persistent') to EncryptedSettings
- Show radio group in NotificationSettings on native platforms
- Pass notificationStyle through Capacitor plugin to SharedPreferences
- DittoNotificationPlugin starts/stops foreground service on style change
- MainActivity only starts service on launch when style is persistent
- Re-enable unread polling on native when push mode is active
2026-04-07 10:54:30 -05:00