- 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>
- Product-page chat buttons pass the product id when starting a thread, so it's
shared into the conversation as a card the seller can see (and tap through).
- MessageBubble renders the product card (image, title, price → product page);
card-only messages no longer render an empty bubble.
- Inbox preview shows 🛍 <product> instead of "بدون پیام" for product-card
threads. ChatProductCard type + product on message/last-message types.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Seller dashboard threads (type="store") now fetch the store's inbox via
useGetStoreThreads(storeId) -> /threads/list/?store_id=; the profile threads page
(type="user") keeps the personal buyer inbox. Depends on the seller-team backend
(store-aware threads + store_id-by-username), so it ships on this branch, not main.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Content was drawing under the iOS status bar and home indicator because
the app uses viewport-fit=cover + black-translucent but the fixed/sticky
chrome didn't reserve the safe areas.
- bottom nav (MyLayout): move the 50px row into an inner box and let the
bar extend by env(safe-area-inset-bottom) so icons sit above the home
indicator (previously the fixed height clipped the inset padding).
- top headers gain env(safe-area-inset-top): HomePageTopBar, ExploreTopBar,
SearchTopBar, CartHeader, ChatHeader, and wrapper-padded HeaderWithSupport
/ ProfilePagesHeader (so their absolutely-positioned buttons shift too).
- chat input pads bottom by the inset; chat scroll area height accounts for
the taller header/input.
- _index.tsx no longer overrides the viewport meta (it was dropping
viewport-fit=cover on the home page); the global one in root.tsx wins.
env(safe-area-inset-*) is 0 on non-notch devices, so desktop/Android render
identically — only iOS gains the padding.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>