chats
This commit is contained in:
Vendored
+1
@@ -9,6 +9,7 @@ declare module 'vue' {
|
|||||||
export interface GlobalComponents {
|
export interface GlobalComponents {
|
||||||
Button: typeof import('primevue/button')['default']
|
Button: typeof import('primevue/button')['default']
|
||||||
Card: typeof import('primevue/card')['default']
|
Card: typeof import('primevue/card')['default']
|
||||||
|
ChatCard: typeof import('./src/components/ChatCard.vue')['default']
|
||||||
ChatComponent: typeof import('./src/components/ChatComponent.vue')['default']
|
ChatComponent: typeof import('./src/components/ChatComponent.vue')['default']
|
||||||
ChatSubscribeWebPush: typeof import('./src/components/ChatSubscribeWebPush.vue')['default']
|
ChatSubscribeWebPush: typeof import('./src/components/ChatSubscribeWebPush.vue')['default']
|
||||||
Checkbox: typeof import('primevue/checkbox')['default']
|
Checkbox: typeof import('primevue/checkbox')['default']
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export const getLocalDb = <T extends BaseDocument>(dbName: DB_NAMES) => new Pouc
|
|||||||
export const qrCodeDbRemote = getRemoteDb<QRCodeDocument>(DB_NAMES.QR)
|
export const qrCodeDbRemote = getRemoteDb<QRCodeDocument>(DB_NAMES.QR)
|
||||||
export const qrCodeDbLocal = getLocalDb<QRCodeDocument>(DB_NAMES.QR)
|
export const qrCodeDbLocal = getLocalDb<QRCodeDocument>(DB_NAMES.QR)
|
||||||
export const messagesDbLocal = getLocalDb<MessageDocument>(DB_NAMES.MESSAGES)
|
export const messagesDbLocal = getLocalDb<MessageDocument>(DB_NAMES.MESSAGES)
|
||||||
|
export const chatsDbLocal = getLocalDb<ChatDocument>(DB_NAMES.CHATS)
|
||||||
export const chatsDbRemote = getRemoteDb<ChatDocument>(DB_NAMES.CHATS)
|
export const chatsDbRemote = getRemoteDb<ChatDocument>(DB_NAMES.CHATS)
|
||||||
export const webPushSubscriptionsDbRemote = getRemoteDb<
|
export const webPushSubscriptionsDbRemote = getRemoteDb<
|
||||||
WebPushSubscriptionDocument<PushSubscriptionJSON>
|
WebPushSubscriptionDocument<PushSubscriptionJSON>
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<RouterLink :to="route" class="p-4 rounded-lg bg-white shadow-md" v-if="chatDoc.qr_code_uri">
|
||||||
|
<div class="flex items-center justify-between mb-2">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<h2 class="text-lg font-semibold">{{ chatDoc.name }}</h2>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-gray-500">
|
||||||
|
<span class="text-nowrap">{{ formatedCreatedAt }}</span>
|
||||||
|
</div>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue'
|
||||||
|
import type { ChatDocument } from '@hereconnect/types'
|
||||||
|
import { ROUTE_NAMES } from '@/constants/routes'
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
chatDoc: ChatDocument
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const formatDate = (date: string) => {
|
||||||
|
// Форматирование даты
|
||||||
|
const options: Intl.DateTimeFormatOptions = {
|
||||||
|
year: 'numeric',
|
||||||
|
month: 'long',
|
||||||
|
day: 'numeric',
|
||||||
|
hour: 'numeric',
|
||||||
|
minute: 'numeric',
|
||||||
|
}
|
||||||
|
return new Date(date).toLocaleDateString(['ru-RU'], options)
|
||||||
|
}
|
||||||
|
|
||||||
|
const route = computed(() => {
|
||||||
|
return {
|
||||||
|
name:
|
||||||
|
props.chatDoc.role === 'owner' ? ROUTE_NAMES.MANAGE_QR_CODE_CHAT : ROUTE_NAMES.PUBLIC_CHAT,
|
||||||
|
params: { qr_code_uri: props.chatDoc.qr_code_uri, chat: props.chatDoc.chat },
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const formatedCreatedAt = computed(() => formatDate(props.chatDoc.created_at))
|
||||||
|
</script>
|
||||||
@@ -52,8 +52,13 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, onMounted, onUnmounted, useTemplateRef, watch, nextTick } from 'vue'
|
import { ref, onMounted, onUnmounted, useTemplateRef, watch, nextTick } from 'vue'
|
||||||
import { messagesDbLocal as db, messagesDbRemote as remoteDb } from '@/api/dbs'
|
import {
|
||||||
import { type MessageDocument } from '@hereconnect/types'
|
chatsDbLocal,
|
||||||
|
chatsDbRemote,
|
||||||
|
messagesDbLocal as db,
|
||||||
|
messagesDbRemote as remoteDb,
|
||||||
|
} from '@/api/dbs'
|
||||||
|
import { type ChatDocument, type MessageDocument } from '@hereconnect/types'
|
||||||
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
import { getBrowserUuid } from '@/utils/getBrowserUuid'
|
||||||
import { getUserUuid } from '@/utils/getUserUuid'
|
import { getUserUuid } from '@/utils/getUserUuid'
|
||||||
|
|
||||||
@@ -70,7 +75,8 @@ const { qr_code_uri, chat, userRole } = defineProps<{
|
|||||||
const messages = ref<MessageDocument[]>([])
|
const messages = ref<MessageDocument[]>([])
|
||||||
const newMessage = ref('')
|
const newMessage = ref('')
|
||||||
let changesSubscription: PouchDB.Core.Changes<MessageDocument> | null = null
|
let changesSubscription: PouchDB.Core.Changes<MessageDocument> | null = null
|
||||||
let syncHandler: PouchDB.Replication.Sync<MessageDocument> | null = null
|
let syncMessages: PouchDB.Replication.Sync<MessageDocument> | null = null
|
||||||
|
// let syncChats: PouchDB.Replication.Sync<ChatDocument> | null = null
|
||||||
|
|
||||||
// Селектор для фильтрации сообщений
|
// Селектор для фильтрации сообщений
|
||||||
const messageSelector = {
|
const messageSelector = {
|
||||||
@@ -95,6 +101,7 @@ async function loadMessages() {
|
|||||||
// Отправка нового сообщения
|
// Отправка нового сообщения
|
||||||
async function sendMessage() {
|
async function sendMessage() {
|
||||||
const created_at = new Date().toISOString()
|
const created_at = new Date().toISOString()
|
||||||
|
const user_uuid = getUserUuid()
|
||||||
const message: MessageDocument = {
|
const message: MessageDocument = {
|
||||||
_id: `message:${qr_code_uri}:${chat}:${created_at}`,
|
_id: `message:${qr_code_uri}:${chat}:${created_at}`,
|
||||||
type: 'message',
|
type: 'message',
|
||||||
@@ -103,13 +110,22 @@ async function sendMessage() {
|
|||||||
from: userRole,
|
from: userRole,
|
||||||
body: newMessage.value.trim(),
|
body: newMessage.value.trim(),
|
||||||
browser_uuid: getBrowserUuid(),
|
browser_uuid: getBrowserUuid(),
|
||||||
user_uuid: getUserUuid(),
|
user_uuid,
|
||||||
created_at,
|
created_at,
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await db.put(message)
|
await db.put(message)
|
||||||
newMessage.value = ''
|
newMessage.value = ''
|
||||||
|
|
||||||
|
// await chatsDbLocal.upsert(`chat:${user_uuid}:${userRole}:${qr_code_uri}:${chat}`, (chatDoc) => {
|
||||||
|
// if (chatDoc.last_message_at && chatDoc.last_message_at < created_at) {
|
||||||
|
// chatDoc.last_message_at = created_at
|
||||||
|
// return chatDoc
|
||||||
|
// } else {
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
// })
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка отправки сообщения:', error)
|
console.error('Ошибка отправки сообщения:', error)
|
||||||
}
|
}
|
||||||
@@ -145,22 +161,35 @@ function watchChanges() {
|
|||||||
|
|
||||||
// Настройка синхронизации
|
// Настройка синхронизации
|
||||||
function setupSync() {
|
function setupSync() {
|
||||||
syncHandler = db.sync(remoteDb, {
|
syncMessages = db.sync(remoteDb, {
|
||||||
live: true,
|
live: true,
|
||||||
retry: true,
|
retry: true,
|
||||||
selector: messageSelector,
|
selector: messageSelector,
|
||||||
})
|
})
|
||||||
syncHandler.on('error', (err) => {
|
syncMessages.on('error', (err) => {
|
||||||
console.error('Ошибка синхронизации:', err)
|
console.error('Ошибка синхронизации:', err)
|
||||||
})
|
})
|
||||||
|
// syncChats = chatsDbLocal.sync(chatsDbRemote, {
|
||||||
|
// live: true,
|
||||||
|
// retry: true,
|
||||||
|
// selector: {
|
||||||
|
// type: 'chat',
|
||||||
|
// qr_code_uri,
|
||||||
|
// chat,
|
||||||
|
// },
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
// Остановка синхронизации
|
// Остановка синхронизации
|
||||||
function stopSync() {
|
function stopSync() {
|
||||||
if (syncHandler) {
|
if (syncMessages) {
|
||||||
syncHandler.cancel()
|
syncMessages.cancel()
|
||||||
syncHandler = null
|
syncMessages = null
|
||||||
}
|
}
|
||||||
|
// if (syncChats) {
|
||||||
|
// syncChats.cancel()
|
||||||
|
// syncChats = null
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Отписка от изменений
|
// Отписка от изменений
|
||||||
|
|||||||
@@ -80,9 +80,12 @@ async function sendMessage(content: string) {
|
|||||||
type: 'chat',
|
type: 'chat',
|
||||||
qr_code_uri: doc.uri,
|
qr_code_uri: doc.uri,
|
||||||
role: 'guest',
|
role: 'guest',
|
||||||
|
// unread_messages_count: 0,
|
||||||
chat,
|
chat,
|
||||||
created_at,
|
|
||||||
user_uuid,
|
user_uuid,
|
||||||
|
to_user_uuid: doc.user_uuid,
|
||||||
|
created_at,
|
||||||
|
// last_message_at: created_at,
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatOwnerDoc: ChatDocument = {
|
const chatOwnerDoc: ChatDocument = {
|
||||||
@@ -91,8 +94,11 @@ async function sendMessage(content: string) {
|
|||||||
qr_code_uri: doc.uri,
|
qr_code_uri: doc.uri,
|
||||||
role: 'owner',
|
role: 'owner',
|
||||||
chat,
|
chat,
|
||||||
created_at,
|
// unread_messages_count: 1,
|
||||||
user_uuid: doc.user_uuid,
|
user_uuid: doc.user_uuid,
|
||||||
|
to_user_uuid: user_uuid,
|
||||||
|
created_at,
|
||||||
|
// last_message_at: created_at,
|
||||||
}
|
}
|
||||||
|
|
||||||
const message: MessageDocument = {
|
const message: MessageDocument = {
|
||||||
@@ -109,6 +115,7 @@ async function sendMessage(content: string) {
|
|||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
chatsDbRemote.bulkDocs([chatGuestDoc, chatOwnerDoc]),
|
chatsDbRemote.bulkDocs([chatGuestDoc, chatOwnerDoc]),
|
||||||
|
|
||||||
// Сохраняем сообщение в локальную базу данных
|
// Сохраняем сообщение в локальную базу данных
|
||||||
db.put(message),
|
db.put(message),
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -12,11 +12,17 @@
|
|||||||
<template #default="{ data }">
|
<template #default="{ data }">
|
||||||
<div
|
<div
|
||||||
v-if="data.rows.length > 0"
|
v-if="data.rows.length > 0"
|
||||||
class="flex flex-col gap-5 my-4 grid grid-cols-1 sm:grid-cols-3 gap-4"
|
class="justify-self-start align-baseline-start flex flex-col gap-4"
|
||||||
/>
|
>
|
||||||
|
<ChatCard
|
||||||
|
v-for="{ doc: chatDoc } in data.rows.filter((row) => !!row.doc)"
|
||||||
|
:key="`${chatDoc!._id}`"
|
||||||
|
:chatDoc="chatDoc!"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-else class="place-self-center">
|
<div v-else class="place-self-center">
|
||||||
<h1 class="text-2xl font-bold mb-4 text-gray-500">Чаты в разработке</h1>
|
<h1 class="text-2xl font-bold mb-4 text-gray-500">У вас пока нет чатов</h1>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -38,11 +44,19 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
|
import { chatsDbRemote } from '@/api/dbs'
|
||||||
|
import { getUserUuid } from '@/utils/getUserUuid'
|
||||||
|
|
||||||
const query = useQuery({
|
const query = useQuery({
|
||||||
queryKey: ['qrCodes'],
|
queryKey: ['qrCodes'],
|
||||||
queryFn: async () => {
|
queryFn: () => {
|
||||||
return { rows: [] } as const
|
const user_uuid = getUserUuid()
|
||||||
|
return chatsDbRemote.allDocs({
|
||||||
|
startkey: `chat:${user_uuid}:`,
|
||||||
|
endkey: `chat:${user_uuid}:\uffff`,
|
||||||
|
limit: Number.MAX_SAFE_INTEGER,
|
||||||
|
include_docs: true,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import type { Role } from "./Role";
|
|||||||
* Используется для хранения чатов, отправленных через QR‑код.
|
* Используется для хранения чатов, отправленных через QR‑код.
|
||||||
*/
|
*/
|
||||||
export interface ChatDocument extends BaseDocument {
|
export interface ChatDocument extends BaseDocument {
|
||||||
|
/** Идентификатор документа */
|
||||||
|
_id: string;
|
||||||
|
|
||||||
/** Тип документа, всегда 'chat' */
|
/** Тип документа, всегда 'chat' */
|
||||||
type: "chat";
|
type: "chat";
|
||||||
|
|
||||||
@@ -20,4 +23,13 @@ export interface ChatDocument extends BaseDocument {
|
|||||||
|
|
||||||
/** Идентификатор пользователя, которому принадлежит чат */
|
/** Идентификатор пользователя, которому принадлежит чат */
|
||||||
user_uuid: string;
|
user_uuid: string;
|
||||||
|
|
||||||
|
// /** Количество непрочитанных сообщений в чате */
|
||||||
|
// unread_messages_count: number;
|
||||||
|
|
||||||
|
// /** Дата последнего сообщения в чате */
|
||||||
|
// last_message_at: string;
|
||||||
|
|
||||||
|
/** Идентификатор пользователя, которому адресован чат */
|
||||||
|
to_user_uuid: string;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user