wip
This commit is contained in:
77
src/components/Messages/ChatMessageTextExample.vue
Normal file
77
src/components/Messages/ChatMessageTextExample.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex mb-4 ga-2">
|
||||
<v-avatar size="36" class="" color="blue-lighten-4">
|
||||
<span class="text-blue-darken-2">А</span>
|
||||
</v-avatar>
|
||||
<div class="message-wrapper">
|
||||
<div class="d-flex align-center ga-2">
|
||||
<span class="text-subtitle-2 font-weight-medium">Анна</span>
|
||||
<span class="text-caption text-disabled">10:30</span>
|
||||
</div>
|
||||
<v-card class="pa-4" rounded="lg" flat>
|
||||
<span>Привет! Как дела? Что нового?</span>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex mb-4 ga-2">
|
||||
<v-avatar size="36" color="deep-purple-lighten-4">
|
||||
<span class="text-deep-purple-darken-2">М</span>
|
||||
</v-avatar>
|
||||
<div class="message-wrapper">
|
||||
<div class="d-flex align-center ga-2">
|
||||
<span class="text-subtitle-2 font-weight-medium">Максим</span>
|
||||
<span class="text-caption text-disabled">10:30</span>
|
||||
</div>
|
||||
<v-card class="pa-4" rounded="lg" flat>
|
||||
<span>
|
||||
Всем привет! Смотрели новый фильм? Там такие спецэффекты, просто космос! Очень советую
|
||||
сходить в кино.
|
||||
</span>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex mb-4 ga-2 flex-row-reverse">
|
||||
<v-avatar size="36" color="primary">
|
||||
<span class="text-white">Я</span>
|
||||
</v-avatar>
|
||||
<div style="align-items: flex-end">
|
||||
<div class="d-flex align-center flex-row-reverse ga-2">
|
||||
<span class="text-caption text-disabled">10:35</span>
|
||||
<span class="text-subtitle-2 font-weight-medium">Вы</span>
|
||||
</div>
|
||||
<v-card class="pa-4" rounded="lg" flat color="primary">
|
||||
<span class="text-white"
|
||||
>Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum
|
||||
has been the industry's standard dummy text ever since the 1500s, when an unknown printer
|
||||
took a galley of type and scrambled it to make a type specimen book.</span
|
||||
>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex mb-4 ga-2">
|
||||
<v-avatar size="36" color="orange-lighten-4">
|
||||
<span class="text-orange-darken-2">К</span>
|
||||
</v-avatar>
|
||||
<div class="message-wrapper">
|
||||
<div class="d-flex align-center ga-2">
|
||||
<span class="text-subtitle-2 font-weight-medium">Катя</span>
|
||||
<span class="text-caption text-disabled">10:40</span>
|
||||
</div>
|
||||
<v-card class="message-bubble friend-message pa-4" rounded="lg" flat>
|
||||
<span>Смотрите какой милый котик</span>
|
||||
<v-img
|
||||
src="https://cdn.vuetifyjs.com/images/cards/kitchen.png"
|
||||
max-width="200"
|
||||
class="mt-2 rounded"
|
||||
></v-img>
|
||||
</v-card>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
51
src/components/Messages/MessageData.vue
Normal file
51
src/components/Messages/MessageData.vue
Normal file
@@ -0,0 +1,51 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{ createdAt?: string; username?: string; my?: boolean; text?: string }>(),
|
||||
{
|
||||
my: false,
|
||||
text: 'foobar',
|
||||
username: 'robot',
|
||||
},
|
||||
)
|
||||
|
||||
const createdAt = computed(() => {
|
||||
return props.createdAt
|
||||
? new Date(props.createdAt).toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
||||
: new Date().toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
||||
})
|
||||
|
||||
const avatarLetter = computed(() => {
|
||||
return props.username.slice(0, 1).toUpperCase()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<!-- <div class="d-flex ga-2" :class="{ 'flex-row-reverse': props.side === 'right' }">-->
|
||||
<!-- <v-avatar size="36" color="primary" :text="avatarLetter" />-->
|
||||
<!-- <div>-->
|
||||
<!-- <div class="d-flex align-center ga-2" :class="{ 'justify-end': props.side === 'right' }">-->
|
||||
<!-- <span class="text-subtitle-2 font-weight-medium">{{ props.username }}</span>-->
|
||||
<!-- <span class="text-caption text-disabled">{{ createdAt }}</span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <v-card class="pa-2" rounded="lg" flat :color="props.side === 'right' ? 'primary' : 'white'">-->
|
||||
<!-- <span>{{ props.text }}</span>-->
|
||||
<!-- </v-card>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<div class="flex gap-2 border" :class="{ 'flex-row-reverse': props.my }">
|
||||
<!-- <v-avatar size="36" color="deep-purple-lighten-4">-->
|
||||
<!-- <span class="text-deep-purple-darken-2">{{ avatarLetter }}</span>-->
|
||||
<!-- </v-avatar>-->
|
||||
|
||||
<!-- :class="props.my ? 'message-shaped-right' : 'message-shaped'"-->
|
||||
<div class="pa-4" :class="props.my ? 'bg-blue-400' : 'bg-white'">
|
||||
<span class="">{{ props.text }}</span>
|
||||
<span class="">{{ createdAt }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
40
src/components/Messages/MessageInput.vue
Normal file
40
src/components/Messages/MessageInput.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { SocketDataReq, useSocketsStore } from '@/stores/sockets.ts'
|
||||
import { useChatsStore } from '@/stores/chats.ts'
|
||||
|
||||
const socketsStore = useSocketsStore()
|
||||
const chatsStore = useChatsStore()
|
||||
|
||||
const text = ref('')
|
||||
|
||||
const sendMessage = () => {
|
||||
if (text.value.trim()) {
|
||||
socketsStore.send({
|
||||
type: SocketDataReq.CREATE_MESSAGE,
|
||||
data: {
|
||||
chat_id: chatsStore.selected,
|
||||
text: text.value,
|
||||
},
|
||||
})
|
||||
}
|
||||
text.value = ''
|
||||
}
|
||||
|
||||
const isEmptyText = computed(() => {
|
||||
return !text.value
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="d-flex ga-2 border pa-4">
|
||||
<input v-model="text" placeholder="message" @keyup.enter="sendMessage" />
|
||||
<button><span class="mdi mdi-emoticon-outline"></span></button>
|
||||
<button><span class="mdi mdi-paperclip"></span></button>
|
||||
<button :disabled="isEmptyText" @click="sendMessage">
|
||||
<span class="mdi mdi-send"></span>
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
10
src/components/Messages/MessageToolbar.vue
Normal file
10
src/components/Messages/MessageToolbar.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<VToolbar class="px-2">
|
||||
<VAvatar text="A" color="primary" />
|
||||
<VToolbarTitle text="Message title" />
|
||||
</VToolbar>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
33
src/components/Messages/MessagesForm.vue
Normal file
33
src/components/Messages/MessagesForm.vue
Normal file
@@ -0,0 +1,33 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, useTemplateRef, watch } from 'vue'
|
||||
import { useScroll } from '@vueuse/core'
|
||||
|
||||
const area = useTemplateRef('messageArea')
|
||||
|
||||
const { y, arrivedState } = useScroll(area)
|
||||
|
||||
const messages = computed(() => {
|
||||
// return [...messagesStore.messages]
|
||||
})
|
||||
|
||||
async function scrollToBottom() {
|
||||
await nextTick()
|
||||
if (area.value) y.value = area.value?.scrollHeight
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="flex h-full flex-col overflow-hidden">
|
||||
<div class="grow-0">
|
||||
<slot name="toolbar" />
|
||||
</div>
|
||||
<div class="px-8 gap-2 grow flex flex-col-reverse overflow-y-auto" ref="messageArea">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="grow-0">
|
||||
<slot name="input" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user