This commit is contained in:
Vendored
+1
-1
@@ -10,8 +10,8 @@ declare module 'vue' {
|
|||||||
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']
|
ChatCard: typeof import('./src/components/ChatCard.vue')['default']
|
||||||
ChatComponent: typeof import('./src/components/ChatComponent.vue')['default']
|
|
||||||
ChatForm: typeof import('./src/components/chat/ChatForm.vue')['default']
|
ChatForm: typeof import('./src/components/chat/ChatForm.vue')['default']
|
||||||
|
ChatMessages: typeof import('./src/components/chat/ChatMessages.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']
|
||||||
CopyLinkButton: typeof import('@/components/link/LinkCopyButton.vue')['default']
|
CopyLinkButton: typeof import('@/components/link/LinkCopyButton.vue')['default']
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex gap-3 px-4 pt-0 flex-col-reverse pb-safe-or-4">
|
<div class="sm:max-w-prose mx-auto flex gap-3 px-4 pt-0 flex-col-reverse sm:pb-safe-or-4 w-full">
|
||||||
<!-- Форма отправки сообщений -->
|
<!-- Форма отправки сообщений -->
|
||||||
<form @submit.prevent="sendMessage" class="flex gap-2 items-center" ref="form">
|
<form @submit.prevent="sendMessage" class="flex gap-2 items-center" ref="form">
|
||||||
<InputText
|
<InputText
|
||||||
|
|||||||
+31
-33
@@ -1,34 +1,32 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex flex-col flex-grow place-self-stretch">
|
<div class="place-self-stretch flex flex-col">
|
||||||
<!-- Список сообщений -->
|
<div
|
||||||
<div class="flex-grow overflow-y-auto p-4" ref="messagesContainer">
|
v-for="message in messages"
|
||||||
<div
|
:key="message._id"
|
||||||
v-for="message in messages"
|
class="mb-4 max-w-md"
|
||||||
:key="message._id"
|
:class="{
|
||||||
class="mb-4 max-w-md"
|
'ml-auto text-right': message.from === userRole,
|
||||||
:class="{ 'ml-auto': message.from === userRole, 'mr-auto': message.from !== userRole }"
|
'mr-auto text-left': message.from !== userRole,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<Card
|
||||||
|
class="p-card-sm"
|
||||||
|
:class="{
|
||||||
|
'bg-blue-50 border-blue-300 text-blue-500': message.from === userRole,
|
||||||
|
'bg-gray-50 border-gray-300 text-gray-700': message.from !== userRole,
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<Card
|
<template #content>
|
||||||
class="p-card-sm"
|
{{ message.body }}
|
||||||
:class="{
|
</template>
|
||||||
'bg-blue-50 border-blue-300 text-blue-500': message.from === userRole,
|
</Card>
|
||||||
'bg-gray-50 border-gray-300 text-gray-700': message.from !== userRole,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<template #content>
|
|
||||||
{{ message.body }}
|
|
||||||
</template>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<slot
|
|
||||||
name="endOfMessages"
|
|
||||||
:count="messages.length"
|
|
||||||
:lastMessage="messages.length > 0 ? messages[messages.length - 1] : null"
|
|
||||||
></slot>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ChatForm :qr_code_uri :chat :userRole />
|
<slot
|
||||||
|
name="endOfMessages"
|
||||||
|
:count="messages.length"
|
||||||
|
:lastMessage="messages.length > 0 ? messages[messages.length - 1] : null"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -37,8 +35,6 @@ import { ref, onMounted, onUnmounted, useTemplateRef, watch, nextTick } from 'vu
|
|||||||
import { messagesDbLocal as db, messagesDbRemote as remoteDb } from '@/api/dbs'
|
import { messagesDbLocal as db, messagesDbRemote as remoteDb } from '@/api/dbs'
|
||||||
import { type MessageDocument } from '@hereconnect/types'
|
import { type MessageDocument } from '@hereconnect/types'
|
||||||
|
|
||||||
const messagesContainerRef = useTemplateRef('messagesContainer')
|
|
||||||
|
|
||||||
const { qr_code_uri, chat, userRole } = defineProps<{
|
const { qr_code_uri, chat, userRole } = defineProps<{
|
||||||
qr_code_uri: MessageDocument['qr_code_uri']
|
qr_code_uri: MessageDocument['qr_code_uri']
|
||||||
chat: MessageDocument['chat']
|
chat: MessageDocument['chat']
|
||||||
@@ -134,13 +130,15 @@ onMounted(async () => {
|
|||||||
await loadMessages()
|
await loadMessages()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'scrollToBottom'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => messages.value?.length,
|
() => messages.value?.length,
|
||||||
async () => {
|
async () => {
|
||||||
if (messagesContainerRef.value) {
|
await nextTick()
|
||||||
await nextTick()
|
emit('scrollToBottom')
|
||||||
messagesContainerRef.value.scrollTop = messagesContainerRef.value.scrollHeight
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1,8 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="grid h-full sm:grid-rows-[auto_1fr_auto] grid-rows-[1fr_auto_auto] overflow-hidden">
|
<div class="grid h-full sm:grid-rows-[auto_1fr_auto] grid-rows-[1fr_auto_auto] overflow-hidden">
|
||||||
<div class="flex overflow-auto pb-safe-or-4 sm:pt-safe w-full px-4 touch-pan-y">
|
<div
|
||||||
<div class="sm:max-w-prose grid grid-cols-1 mx-auto my-5">
|
class="flex overflow-auto pb-safe-or-4 sm:pt-safe px-4 touch-pan-y"
|
||||||
<slot />
|
ref="contentScrollContainer"
|
||||||
|
>
|
||||||
|
<div class="sm:max-w-prose grid grid-cols-1 mx-auto my-5 w-full">
|
||||||
|
<slot name="default" v-bind:scrollToBottom />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -25,11 +28,21 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useTemplateRef } from 'vue'
|
||||||
|
|
||||||
const slots = defineSlots<{
|
const slots = defineSlots<{
|
||||||
default?: () => void
|
default?: (slotBindings: { scrollToBottom: typeof scrollToBottom }) => void
|
||||||
footer?: () => void
|
footer?: () => void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
|
const contentScrollContainerRef = useTemplateRef('contentScrollContainer')
|
||||||
|
|
||||||
|
const scrollToBottom = () => {
|
||||||
|
if (contentScrollContainerRef.value) {
|
||||||
|
contentScrollContainerRef.value.scrollTop = contentScrollContainerRef.value.scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const layoutPageHead = useHead({
|
const layoutPageHead = useHead({
|
||||||
htmlAttrs: { class: 'overflow-hidden' },
|
htmlAttrs: { class: 'overflow-hidden' },
|
||||||
bodyAttrs: { class: 'overflow-hidden' },
|
bodyAttrs: { class: 'overflow-hidden' },
|
||||||
|
|||||||
@@ -1,32 +1,42 @@
|
|||||||
<template>
|
<template>
|
||||||
<LayoutWithTabs>
|
<LayoutWithTabs>
|
||||||
<MetaHead>
|
<template #default="{ scrollToBottom }">
|
||||||
<title>Чат — НаСвязи</title>
|
<MetaHead>
|
||||||
<meta
|
<title>Чат — НаСвязи</title>
|
||||||
name="description"
|
<meta
|
||||||
content="Чат QR-кода. Безопасная и конфиденциальная связь для решения всех вопросов"
|
name="description"
|
||||||
/>
|
content="Чат QR-кода. Безопасная и конфиденциальная связь для решения всех вопросов"
|
||||||
</MetaHead>
|
/>
|
||||||
|
</MetaHead>
|
||||||
|
|
||||||
<ChatComponent userRole="guest" :qr_code_uri="qr_code_uri" :chat="chat">
|
<ChatMessages
|
||||||
<template #endOfMessages="{ count, lastMessage }">
|
userRole="guest"
|
||||||
<!-- Уведомление, если гость отправил только одно сообщение -->
|
:qr_code_uri="qr_code_uri"
|
||||||
<div
|
:chat="chat"
|
||||||
v-if="count === 1 && lastMessage && lastMessage.from === 'guest'"
|
@scrollToBottom="scrollToBottom()"
|
||||||
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 mt-6"
|
>
|
||||||
>
|
<template #endOfMessages="{ count, lastMessage }">
|
||||||
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
|
<!-- Уведомление, если гость отправил только одно сообщение -->
|
||||||
<p class="text-gray-600 mt-2">
|
<div
|
||||||
Вы можете написать ещё одно сообщение или подождать ответа.
|
v-if="count === 1 && lastMessage && lastMessage.from === 'guest'"
|
||||||
</p>
|
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 my-6"
|
||||||
</div>
|
>
|
||||||
</template>
|
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
|
||||||
</ChatComponent>
|
<p class="text-gray-600 mt-2">
|
||||||
|
Вы можете написать ещё одно сообщение или подождать ответа.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</ChatMessages>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<ChatForm :qr_code_uri :chat userRole="guest" />
|
||||||
|
</template>
|
||||||
</LayoutWithTabs>
|
</LayoutWithTabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MessageDocument } from '@hereconnect/types'
|
import type { MessageDocument } from '@hereconnect/types'
|
||||||
import ChatComponent from '@/components/ChatComponent.vue'
|
|
||||||
defineProps<{ qr_code_uri: MessageDocument['qr_code_uri']; chat: MessageDocument['chat'] }>()
|
defineProps<{ qr_code_uri: MessageDocument['qr_code_uri']; chat: MessageDocument['chat'] }>()
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
<template #default="{ data }">
|
<template #default="{ data }">
|
||||||
<div
|
<div
|
||||||
v-if="data.length > 0"
|
v-if="data.length > 0"
|
||||||
class="justify-self-start align-baseline-start flex flex-col gap-4"
|
class="grid grid-cols-1 gap-5 place-self-center sm:place-self-start w-full"
|
||||||
>
|
>
|
||||||
<ChatCard
|
<ChatCard
|
||||||
v-for="row in data"
|
v-for="row in data"
|
||||||
|
|||||||
@@ -1,56 +1,49 @@
|
|||||||
<template>
|
<template>
|
||||||
<LayoutWithTabs>
|
<LayoutWithTabs>
|
||||||
<MetaHead>
|
<template #default="{ scrollToBottom }">
|
||||||
<title>Чат {{ chatDoc?.name ? `«${chatDoc.name}»` : '' }} — НаСвязи</title>
|
<MetaHead>
|
||||||
<meta
|
<title>Чат {{ chatDoc?.name ? `«${chatDoc.name}»` : '' }} — НаСвязи</title>
|
||||||
name="description"
|
<meta
|
||||||
content="Ваш QR-код успешно создан! Узнайте, как его использовать и настройте все доступные функции для удобного взаимодействия с окружающими."
|
name="description"
|
||||||
/>
|
content="Ваш QR-код успешно создан! Узнайте, как его использовать и настройте все доступные функции для удобного взаимодействия с окружающими."
|
||||||
</MetaHead>
|
/>
|
||||||
|
</MetaHead>
|
||||||
|
|
||||||
<div class="flex flex-col min-h-full">
|
<ChatMessages
|
||||||
<!-- Основной контент -->
|
userRole="owner"
|
||||||
<main class="flex-grow flex flex-col">
|
:qr_code_uri="qr_code_uri"
|
||||||
<section class="flex flex-col flex-grow max-w-prose w-full mx-auto">
|
:chat="chat"
|
||||||
<ChatComponent userRole="owner" :qr_code_uri="qr_code_uri" :chat="chat">
|
@scrollToBottom="scrollToBottom()"
|
||||||
<template #endOfMessages="{ count, lastMessage }">
|
>
|
||||||
<div
|
<template #endOfMessages="{ count, lastMessage }">
|
||||||
v-if="!count"
|
<div
|
||||||
class="flex flex-col items-center justify-center flex-grow text-center text-gray-500"
|
v-if="!count"
|
||||||
>
|
class="flex flex-col items-center justify-center flex-grow text-center text-gray-500"
|
||||||
<h1 class="text-2xl font-bold mb-4">Пока нет сообщений</h1>
|
>
|
||||||
<p class="text-gray-600">Вы можете начать общение, отправив первое сообщение.</p>
|
<h1 class="text-2xl font-bold mb-4">Пока нет сообщений</h1>
|
||||||
</div>
|
<p class="text-gray-600">Вы можете начать общение, отправив первое сообщение.</p>
|
||||||
<div
|
</div>
|
||||||
v-else-if="lastMessage && lastMessage.from === 'owner'"
|
<div
|
||||||
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 mt-6"
|
v-else-if="lastMessage && lastMessage.from === 'owner'"
|
||||||
>
|
class="flex flex-col items-center justify-center text-center bg-gray-50 border border-gray-200 rounded-lg shadow-md p-6 my-6"
|
||||||
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
|
>
|
||||||
<p class="text-gray-600 mt-2">
|
<p class="text-gray-700 font-medium text-lg">Ваше сообщение отправлено.</p>
|
||||||
Вы можете написать ещё одно сообщение или подождать ответа.
|
<p class="text-gray-600 mt-2">
|
||||||
</p>
|
Вы можете написать ещё одно сообщение или подождать ответа.
|
||||||
</div>
|
</p>
|
||||||
</template>
|
</div>
|
||||||
</ChatComponent>
|
</template>
|
||||||
</section>
|
</ChatMessages>
|
||||||
</main>
|
</template>
|
||||||
|
|
||||||
<!-- Футер -->
|
<template #footer>
|
||||||
<!-- <footer class="bg-gray-100 text-center py-4 border-t text-sm text-gray-700">-->
|
<ChatForm :qr_code_uri :chat userRole="owner" />
|
||||||
<!-- <p><a href="/create" class="underline underline-offset-2">Создайте свой QR‑код</a>.</p>-->
|
</template>
|
||||||
<!-- <p class="mt-1">-->
|
|
||||||
<!-- Сделайте НаСвязи удобнее. Будем рады вашей <a href="/donate" class="underline underline-offset-2"-->
|
|
||||||
<!-- >поддержке</a-->
|
|
||||||
<!-- >.-->
|
|
||||||
<!-- </p>-->
|
|
||||||
<!-- </footer>-->
|
|
||||||
</div>
|
|
||||||
</LayoutWithTabs>
|
</LayoutWithTabs>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { MessageDocument } from '@hereconnect/types'
|
import type { MessageDocument } from '@hereconnect/types'
|
||||||
import ChatComponent from '@/components/ChatComponent.vue'
|
|
||||||
import { getQrCodeDocument } from '@/api/qrCode'
|
import { getQrCodeDocument } from '@/api/qrCode'
|
||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user