8 Commits

Author SHA1 Message Date
3fe344c3a0 Fix QA review findings: per-card responding state, preserve data on detach
C-01: ConnectionRequestCard now uses local isResponding state instead of
shared hook boolean, so accepting one card doesn't disable all others.

C-03: detach_umbral_contact no longer wipes person data (name, email,
phone, etc.) when a connection is severed. The person becomes a standard
contact with all data preserved, preventing data loss for pre-existing
contacts that were linked to connections.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 22:40:24 +08:00
9bcf5ace5d Fix search cold-cache gate, 429 handling, and datetime.now() violations
ConnectionSearch.tsx:
- Add loading guard for useSettings() — prevents cold cache from showing
  "enable connections" gate when settings haven't loaded yet
- Add 429 rate limit handling in search catch block — shows user-friendly
  message instead of silently showing "User not found"
- Import axios for isAxiosError type guard

connections.py:
- Fix 3x datetime.now() → datetime.now(timezone.utc) per hard rule
  (lines 187, 378, 565)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 20:02:52 +08:00
1e736eb333 Fix connection accept regression: revert disabled gates, add 409 handling
Previous fix (2139ea8) caused regression: staleTime:0 nuked cache on
every mount, isLoadingIncoming disabled buttons during cold-cache fetch.

Changes:
- Remove staleTime:0 (keep refetchOnMount:'always' for background refresh)
- Revert button gate to pendingRequestIds.has() only (no is_read gate)
- Remove isLoadingIncoming from disabled prop and spinner condition
- Add 409-as-success handling to ConnectionRequestCard (People tab)
- Use axios.isAxiosError() instead of err:any in all 3 catch blocks
- Add markRead() call to 409 branch so notifications clear properly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 18:23:47 +08:00
14a77f0f11 Fix stale UI after accept: await invalidations, dismiss toasts
- Await all query invalidations in respondMutation/cancelMutation
  onSuccess so UI has fresh data before mutation promise resolves
- Use deterministic toast IDs (connection-request-{id}) for Sonner
  toasts so they can be dismissed from any accept surface
- Dismiss stale connection toasts in respondMutation.onSuccess
- Fix handleCancel setting resolved before API call completes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 09:43:22 +08:00
73cef1df55 Add umbral name header, preferred name field, and link button for contacts
- Inject umbral_name into shared_fields for umbral contacts (always visible)
- Show @umbralname subtitle in detail panel header
- Add preferred_name to panel fields with synced label for umbral contacts
- Add Link button on standard contacts to tie to umbral user via connection request
- Migration 046: person_id FK on connection_requests with index
- Validate person_id ownership on send, re-validate + convert on accept

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 08:37:01 +08:00
820ff46efa Fix QA W-01/W-05/W-06/W-08: cancel requests, detach umbral, notifications
W-08: Add CHECK constraint on notifications.type (migration 044) with
defensive pre-check and matching __table_args__ on model.

W-05: Auto-detach umbral contact before Person delete — nulls out
connection's person_id so the connection survives deletion.

W-01: Add PUT /requests/{id}/cancel endpoint with atomic UPDATE,
silent notification cleanup, and audit logging. Frontend: direction-aware
ConnectionRequestCard, cancel mutation, pending requests section on
PeoplePage with incoming/outgoing subsections.

W-06: Convert useNotifications to context provider pattern — single
subscription shared via NotificationProvider in AppLayout. Adds
refreshNotifications convenience function.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 07:17:31 +08:00
e27beb7736 Fix toast notifications, require accept_connections for senders
- Rewrite NotificationToaster with max-ID watermark for reliable
  new-notification detection and faster unread count polling (15s)
- Block connection search and requests when sender has
  accept_connections disabled (backend + frontend gate)
- Remove duplicate sender_settings fetch in send_connection_request
- Show actionable error messages in toast respond failures

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 06:21:43 +08:00
3d22568b9c Add user connections, notification centre, and people integration
Implements the full User Connections & Notification Centre feature:

Phase 1 - Database: migrations 039-043 adding umbral_name to users,
profile/social fields to settings, notifications table, connection
request/user_connection tables, and linked_user_id to people.

Phase 2 - Notifications: backend CRUD router + service + 90-day purge,
frontend NotificationsPage with All/Unread filter, bell icon in sidebar
with unread badge polling every 60s.

Phase 3 - Settings: profile fields (phone, mobile, address, company,
job_title), social card with accept_connections toggle and per-field
sharing defaults, umbral name display with CopyableField.

Phase 4 - Connections: timing-safe user search, send/accept/reject flow
with atomic status updates, bidirectional UserConnection + Person records,
in-app + ntfy notifications, per-receiver pending cap, nginx rate limiting.

Phase 5 - People integration: batch-loaded shared profiles (N+1 prevention),
Ghost icon for umbral contacts, Umbral filter pill, split Add Person button,
shared field indicators (synced labels + Lock icons), disabled form inputs
for synced fields on umbral contacts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 02:10:16 +08:00