Commit Graph

5 Commits

Author SHA1 Message Date
31bc9f3c7b fix(push): include underlying error text in subscribe failure toast
Sellers running from an installed PWA still hit failures inside subscribe
(iOS 16.4+ PWA has its own quirks even when standalone), and a generic
"لطفاً چند لحظه بعد دوباره تلاش کنید" doesn't tell whoever is helping
them what actually broke. The catch handler now surfaces the underlying
Error.message (e.g. "timeout: pushManager.subscribe", "NotAllowedError")
in the toast description so field debugging works over screenshots.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 16:22:50 +00:00
c4e0f2f89f fix(push): timeout every await + iOS-not-PWA early exit so subscribe never wedges
The subscribe/disable flow awaited several browser APIs that on certain
devices — most reliably iOS Safari in a non-installed PWA — return a
Promise that never resolves or rejects (requestPermission, serviceWorker
.ready, pushManager.subscribe). When any of them hung, the try/finally
never ran, so `busy` stayed true forever: the dashboard nudge button
stuck at "..." and the settings toggle refused to flip.

Every awaited step is now wrapped in withTimeout(15s). Failures surface
as toasts instead of silent hangs (subscribe success, permission denied,
generic error). iOS Safari without the site installed to home screen —
where Web Push physically cannot work — short-circuits with an
add-to-home-screen instruction before we ever try, so we don't burn a
timeout waiting for a doomed subscribe().

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 15:33:04 +00:00
225161249e feat(chat+push): image attachments, day-labeled inbox with reply state, sub self-heal
- MessageInput: paperclip button (team-only, controlled by teamUserIds
  from ThreadDetail) uploads through the media service and hands the URL
  to the WebSocket; Enter now inserts a newline — sending is button-only
  since accidental Enter-sends were noisy for staff replies.
- MessageBubble: renders image messages inline (tap to open), shows the
  coworker's username at the top of team-side bubbles so staff can tell
  who typed what.
- MessageList / ThreadChatPage: any message from a store team member
  (owner or active staff) renders on the "our" side of the bubble even
  for a viewer who isn't the sender, using team_user_ids from
  ThreadDetail. Delete option stays gated to the actual sender.
- ThreadsPage: inbox now shows smart-relative timestamps
  (today HH:MM / دیروز HH:MM / N روز پیش / Persian date), plus a
  reply-state icon per row (red bell = customer waiting, blue double-check
  = you replied and they've seen it, nothing = you replied not yet seen).
  Image-only last messages preview as "📷 تصویر".
- Push notifications: on mount we re-POST the current PushSubscription so
  a browser-rotated sub or a backend row deleted after a 410 auto-heals.
- useWebSocket: SendMessagePayload and NewMessageEvent carry image_url so
  the server relay can round-trip images.
- API types: MessageList.imageUrl, LastMessage.imageUrl,
  ThreadList.lastMessageFromTeam/otherPartySeenLast, ThreadDetail.teamUserIds.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-19 13:22:44 +00:00
66b14cfa74 fix(notifications): read camelCase publicKey from VAPID endpoint
The backend view returns {"public_key": ...} but the global
CamelCaseJSONRenderer rewrites it to {"publicKey": ...} on the wire,
so destructuring `public_key` always resolved to undefined and the
subscribe path bailed silently. Browsers never registered with the
backend, no PushSubscription rows were ever created, and no push
notifications fired.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-17 12:06:35 +00:00
fef53fd003 feat(notifications): web push opt-in + unread nav badges
Surfaces three events without a full notification center:
- service worker (public/sw.js) gains push + notificationclick handlers
  (RTL, focuses an existing tab or opens the target URL).
- usePushNotifications hook: permission + VAPID subscribe/unsubscribe
  against the backend push endpoints.
- opt-in UI both ways: a one-time dismissible post-login banner
  (PushPermissionBanner, rendered in MyLayout) and a permanent toggle
  (PushSettingToggle) on buyer profile settings and seller store settings.
- useBadgeCounts polls /api/notif/v1/badges/; small red badges on the
  bottom-nav profile icon (chat + order updates), the profile sidebar
  (پیام‌ها / سفارش‌ها) and the seller dashboard sidebar. Visiting orders
  clears its badge.

Degrades gracefully: if the backend endpoints are absent the counts fall
back to zero and nothing throws.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 14:02:56 +03:30