Skip to content

Chat Improvements Design

Date: 2026-02-25 Scope: 7 frontend tasks (no backend changes)

Tasks

#TaskStatus
21Lightbox with gallery for chat imagesPlanned
22Loader when sending imagesPlanned
23Security audit of chat inputPlanned
24Click avatar/name → seller profilePlanned
26Avatars on message groupsPlanned
27Infinite scroll for message historyPlanned
28Input limits (2000 chars, 5 photos, 10MB)Planned

Skipped: #25 (search) — deferred.


New file: features/chat-image/ui/ChatImageLightbox.vue

Behavior:

  • Click image thumbnail in MessageBubble → fullscreen lightbox
  • Loads image_url (full size) instead of image_thumbnail
  • Prev/next arrows navigate through all images in current conversation
  • Keyboard: / navigation, Escape close
  • Mobile: swipe via @vueuse/core useSwipe
  • Counter: "3 / 12"
  • Close button (X) in corner

Implementation:

  • Props: images: Array<{id, url, thumbnail}>, initialIndex: number, open: boolean
  • MessageBubble emits @image-click with message.id
  • Page [id].vue collects all image messages, computes index, opens lightbox
  • Built on UModal (fullscreen) + NuxtImg for 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 {{ }} (not v-html) — no XSS
  • No v-html for 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_urlUAvatar with initials fallback

Implementation:

  • MessageBubble new props: showAvatar, avatarUrl, displayName, isOwn
  • Grouping logic in [id].vue via computed — passes showAvatar to 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 → call loadOlder()
  • 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=2000 on 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

FileChanges
features/chat-image/ui/ChatImageLightbox.vueNEW — fullscreen image gallery
entities/message/ui/MessageBubble.vueAvatar props, image click emit, ghost message state
pages/cabinet/messages/[id].vueLightbox, infinite scroll, avatars, limits, loader
features/chat-image/composables/useChatImageUpload.tsMulti-photo, validation
features/chat-messaging/composables/useMessages.tsGhost messages for images
i18n/locales/ru.jsonNew i18n keys for limits/errors