Appearance
Chat Improvements Design
Date: 2026-02-25 Scope: 7 frontend tasks (no backend changes)
Tasks
| # | Task | Status |
|---|---|---|
| 21 | Lightbox with gallery for chat images | Planned |
| 22 | Loader when sending images | Planned |
| 23 | Security audit of chat input | Planned |
| 24 | Click avatar/name → seller profile | Planned |
| 26 | Avatars on message groups | Planned |
| 27 | Infinite scroll for message history | Planned |
| 28 | Input limits (2000 chars, 5 photos, 10MB) | Planned |
Skipped: #25 (search) — deferred.
21. Lightbox with Gallery
New file: features/chat-image/ui/ChatImageLightbox.vue
Behavior:
- Click image thumbnail in
MessageBubble→ fullscreen lightbox - Loads
image_url(full size) instead ofimage_thumbnail - Prev/next arrows navigate through all images in current conversation
- Keyboard:
←/→navigation,Escapeclose - Mobile: swipe via
@vueuse/coreuseSwipe - Counter: "3 / 12"
- Close button (X) in corner
Implementation:
- Props:
images: Array<{id, url, thumbnail}>,initialIndex: number,open: boolean MessageBubbleemits@image-clickwithmessage.id- Page
[id].vuecollects all image messages, computes index, opens lightbox - Built on
UModal(fullscreen) +NuxtImgfor optimized loading - Preload adjacent images for smooth navigation
22. Loader When Sending Images
Behavior:
- On file selection → instant "ghost" message in list (optimistic rendering)
- Ghost message: local blob preview + spinner overlay + semi-transparent overlay
- On success → replace with real server message
- On error → error icon + "Retry" button
Files: [id].vue, MessageBubble.vue, useChatImageUpload.ts
23. Security Audit
Checklist:
- Verify text renders via
{{ }}(notv-html) — no XSS - No
v-htmlfor user content anywhere in chat - Trim + length check before sending (mirrors backend validation)
- File input: client-side MIME type + size check before upload
- No URL/script injection in display_name, text fields
24. Click Avatar/Name → Profile
Behavior:
- Chat header: companion avatar + name wrapped in
NuxtLink to="/seller/{companion.id}" - Message avatars (companion only) — also clickable →
/seller/{id} - Own avatar: not clickable
Files: [id].vue (chat header)
26. Avatars on Message Groups
Behavior:
- Grouping: consecutive messages from same
sender_id= one group - Avatar shown on last message in group (bottom, like Telegram/iMessage)
- Companion: avatar left of bubble (28x28, round)
- Own messages: avatar right of bubble (28x28, round)
- No
avatar_url→UAvatarwith initials fallback
Implementation:
MessageBubblenew props:showAvatar,avatarUrl,displayName,isOwn- Grouping logic in
[id].vuevia computed — passesshowAvatarto each bubble - Avatar visible only on last message in a group of same-sender messages
27. Infinite Scroll
Behavior:
- Remove "Load earlier" button
useIntersectionObserver(VueUse) on sentinel element at top of message list- When sentinel visible +
hasMore === true→ callloadOlder() - Preserve scroll position after loading (prevent jump to new messages)
- Show spinner during loading
Files: [id].vue, useMessages.ts
28. Input Limits
Text:
- Character counter under input (appears at >1800 chars): "1850 / 2000"
maxlength=2000on textarea- Send button disabled at 0 or >2000 chars
Files (multi-photo):
- Input:
multiple+accept="image/jpeg,image/png,image/webp" - If >5 files selected → toast "Maximum 5 photos at once"
- If any file >10MB → toast "File too large (max 10 MB)"
- Multi-photo: selected N photos → N sequential upload requests, each as its own ghost message
Files: [id].vue, useChatImageUpload.ts
Files Changed Summary
| File | Changes |
|---|---|
features/chat-image/ui/ChatImageLightbox.vue | NEW — fullscreen image gallery |
entities/message/ui/MessageBubble.vue | Avatar props, image click emit, ghost message state |
pages/cabinet/messages/[id].vue | Lightbox, infinite scroll, avatars, limits, loader |
features/chat-image/composables/useChatImageUpload.ts | Multi-photo, validation |
features/chat-messaging/composables/useMessages.ts | Ghost messages for images |
i18n/locales/ru.json | New i18n keys for limits/errors |