@qor/chat-layer

Composables

Composables для работы с чатом — подключение, сообщения, списки, присутствие.

Все composables авто-импортируются в компоненты. Shared composables (отмечены) — синглтоны через createSharedComposable.

useChatSocket (shared)

Управление Socket.IO подключением. Центральный хаб для связи с сервером.

const {
  status,        // Ref<'disconnected' | 'connecting' | 'connected' | 'error'>
  error,         // Ref<string | null>
  currentUserId, // Ref<string | null>
  isConnected,   // ComputedRef<boolean>
  hasSocket,     // ComputedRef<boolean>
  socket,        // ShallowRef<Socket | null>
  connect,       // (userId: string) => Promise<void>
  disconnect,    // () => Promise<void>
  registerHandlers, // (socket: Socket) => void
  setSocket,     // (socket: Socket) => void
} = useChatSocket()
socket — это shallowRef. Socket.IO клиент не должен глубоко проксироваться Vue — это вызовет ошибки при внутренних мутациях SDK.

Подключение к чату

// Обычно делается автоматически через chat-auth.client.ts плагин
await connect('user-uuid')

// Ручное отключение
await disconnect()

useChatList (shared)

Список диалогов, фильтрация и поиск.

const {
  conversations,      // Ref<ChatConversation[]> (readonly)
  sortedConversations, // ComputedRef — отсортированные + отфильтрованные
  totalUnread,         // ComputedRef<number>
  loading,             // Ref<boolean>
  filter,              // Ref<'all' | 'single' | 'group' | 'unread'>
  searchQuery,         // Ref<string>
  participantsMap,     // ComputedRef<Map<userId, { name, avatar? }>>
  loadConversations,   // () => Promise<void>
  getConversation,     // (chatId: string) => ChatConversation | undefined
  markAsRead,          // (chatId: string) => void
  updateConversation,  // (chatId: string, updates: Partial<ChatConversation>) => void
  leaveGroupChat,      // (groupId: string) => void
} = useChatList()

Сортировка

Диалоги сортируются по:

  1. Закреплённые (pinned) — наверху
  2. Время последнего сообщения — по убыванию

Фильтрация

// Показать только непрочитанные
filter.value = 'unread'

// Поиск по имени или тексту
searchQuery.value = 'Иван'

useChatMessages (per-chat)

Сообщения конкретного диалога. Не shared — каждый экземпляр привязан к chatId.

const chatId = computed(() => route.params.id as string)
const {
  messages,     // Ref<ChatMessage[]> (readonly)
  loading,      // Ref<boolean>
  hasMore,      // Ref<boolean>
  error,        // Ref<string | null>
  replyTo,      // Ref<ChatMessage | null>
  loadMessages, // () => Promise<void> — загрузить первую страницу
  loadMore,     // () => Promise<void> — подгрузить историю (cursor)
  sendMessage,  // (body, attachments?, files?) => ChatMessage | null
  editMessage,  // (messageId, newBody) => Promise<void>
  deleteMessage, // (messageId) => Promise<void>
  removeMessage, // (messageId) => void — полное удаление (retry)
  editReactions, // (messageId, reactions, toggledEmoji?) => Promise<void>
  isOwnMessage,  // (msg) => boolean
  reset,         // () => void — очистка при смене чата
} = useChatMessages(chatId)

Отправка сообщения

// Текст
sendMessage('Привет!')

// Текст с вложениями
sendMessage('Смотри фото', attachments, files)

// Ответ на сообщение
replyTo.value = someMessage
sendMessage('Согласен')

Процесс отправки:

  1. Создаётся оптимистичное сообщение (status: sending)
  2. Файлы загружаются через REST (/api/v1/chat/upload)
  3. Сообщение отправляется через Socket.IO с ack
  4. При ack — оптимистичное сообщение заменяется серверным

Пагинация

// Infinite scroll
await loadMore() // Подгрузить старые сообщения по cursor

useChatMessageStore (shared)

Глобальный кеш сообщений. Хранит все загруженные сообщения, синхронизируется с useChatMessages.

const {
  getMessages,        // (chatId) => ChatMessage[]
  setMessages,        // (chatId, messages) => void
  addMessage,         // (chatId, message) => void
  updateMessage,      // (chatId, messageId, updates) => void
  updateMessageStatus, // (chatId, messageId, status) => void
  deleteMessage,      // (chatId, messageId) => void
} = useChatMessageStore()

useChatPresence (shared)

Онлайн-статус и индикатор набора текста.

const {
  getPresence, // (userId) => { online: boolean, lastSeen?: string }
  setPresence, // (userId, data) => void
  getTyping,   // (chatId) => string[] — массив userId кто печатает
  setTyping,   // (chatId, userId, isTyping) => void
  sendTyping,  // (chatId) => void — отправить typing:start
  stopTyping,  // (chatId) => void — отправить typing:stop
} = useChatPresence()

useChatUserInfo (shared)

Кеш информации о пользователях из REST API.

const {
  getUser,    // (userId) => { nickname?, avatarurl? } | undefined
  fetchUsers, // (userIds: string[]) => Promise<void>
} = useChatUserInfo()

useChatNotifications (shared)

Уведомления о новых сообщениях. Работает с useHead для обновления счётчика в заголовке.

const {
  setActiveChat,    // (chatId | null) => void
  notifyNewMessage, // (chatId, message, senderName) => void
} = useChatNotifications()

useChatFetch (shared)

Обёртка над $fetch с учётом chatApiBase.

const {
  chatUrl,   // (path: string) => string — добавляет chatApiBase
  chatFetch, // <T>(url, opts?) => Promise<T>
} = useChatFetch()

useChatPageActions (per-page)

Обработчики действий на странице чата — reply, edit, forward, react, pin, mute и т.д.

const {
  replyTo,              // Ref<ChatMessage | null>
  editingMessage,       // Ref<ChatMessage | null>
  forwardOpen,          // Ref<boolean>
  forwardMessage,       // Ref<ChatMessage | null>
  handleSend,           // (body, attachments?, files?) => void
  handleReply,          // (message) => void
  handleEdit,           // (message) => void
  handleCopy,           // (message) => void
  handleForward,        // (message) => void
  handleForwardComplete, // ({ targetId, message }) => void
  handleReact,          // ({ messageId, emoji }) => void
  handleRetry,          // (message) => void
  handlePin,            // (message?) => void
  handleMute,           // () => void
  handleClearChat,      // () => void
  handleLeaveGroup,     // () => void
} = useChatPageActions(chatId, chatMessages)

Вспомогательные composables

ComposableТипОписание
useIsMobile()sharedОпределение мобильного viewport
useChatScroll()per-chatУправление скроллом сообщений
useChatMessageItems()per-chatГруппировка сообщений + разделители дат
useChatInputDraft()per-chatАвтосохранение черновиков
useChatInputFiles()per-chatУправление прикреплёнными файлами
useChatKeyboard()per-chatГорячие клавиши (Esc, Ctrl+F)
useDragDrop()per-chatDrag & drop файлов
useSwipeReply()per-chatСвайп для ответа (мобильные)