wip
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { type Chat, useChatsStore } from '@/stores/chats.ts'
|
import { type Chat, useChatsStore } from '@/stores/chats.ts'
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
import { useMessagesStore } from '@/stores/messages.ts'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
chat: Chat
|
chat: Chat
|
||||||
@@ -9,45 +10,36 @@ interface Props {
|
|||||||
const { chat } = defineProps<Props>()
|
const { chat } = defineProps<Props>()
|
||||||
const chatsStore = useChatsStore()
|
const chatsStore = useChatsStore()
|
||||||
|
|
||||||
const chatName = computed(() => {
|
const chatInfo = computed(() => {
|
||||||
return chatsStore.getChatInfo(chat).name
|
return chatsStore.getChatInfo(chat)
|
||||||
})
|
})
|
||||||
|
|
||||||
const lastMessage = computed(() => {
|
const convertDate = (dateString: string) => {
|
||||||
return chat.message?.message ?? ''
|
const date = new Date(dateString)
|
||||||
})
|
|
||||||
|
|
||||||
const avatarText = computed(() => {
|
|
||||||
return chatName.value.slice(0, 1).toUpperCase()
|
|
||||||
})
|
|
||||||
|
|
||||||
const lastMessageCreatedAt = computed(() => {
|
|
||||||
if (!chat.message) return ''
|
|
||||||
const date = new Date(chat.message.createdAt)
|
|
||||||
return date.toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
return date.toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
||||||
})
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VListItem :value="chat.id">
|
<VListItem :value="chat.id">
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<VAvatar color="primary" :text="avatarText" />
|
<VAvatar color="primary" :text="chatInfo?.name.slice(0, 1).toUpperCase()" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #title>
|
<template #title>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div class="font-medium truncate">
|
<div class="font-medium truncate">
|
||||||
{{ chatName }}
|
{{ chatInfo.name }}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-xs">
|
<div v-if="chat.message" class="text-xs">
|
||||||
{{ lastMessageCreatedAt }}
|
{{ convertDate(chat.message.createdAt) }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #subtitle>
|
<template v-if="chat.message" #subtitle>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<div>{{ lastMessage }}</div>
|
<div>{{ chat.message.message }}</div>
|
||||||
<div class="text-xs">
|
<div class="text-xs">
|
||||||
<!-- <VChip v-show="true" size="small" text="0" />-->
|
<!-- <VChip v-show="true" size="small" text="0" />-->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,23 +6,23 @@ import { useChatsStore } from '@/stores/chats.ts'
|
|||||||
const socketsStore = useSocketsStore()
|
const socketsStore = useSocketsStore()
|
||||||
const chatsStore = useChatsStore()
|
const chatsStore = useChatsStore()
|
||||||
|
|
||||||
const text = ref('')
|
const message = ref('')
|
||||||
|
|
||||||
const sendMessage = () => {
|
const sendMessage = () => {
|
||||||
if (text.value.trim()) {
|
if (message.value && chatsStore.selectedChat) {
|
||||||
socketsStore.send({
|
socketsStore.send({
|
||||||
type: SocketDataReq.CREATE_MESSAGE,
|
type: SocketDataReq.CREATE_MESSAGE,
|
||||||
data: {
|
data: {
|
||||||
chat_id: chatsStore.selected,
|
chatId: chatsStore.selectedChat.id,
|
||||||
text: text.value,
|
message: message.value.trim().slice(0, 200),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
text.value = ''
|
message.value = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEmptyText = computed(() => {
|
const isEmptyText = computed(() => {
|
||||||
return !text.value
|
return !message.value
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ const isEmptyText = computed(() => {
|
|||||||
prepend-inner-icon="mdi-emoticon-outline"
|
prepend-inner-icon="mdi-emoticon-outline"
|
||||||
append-inner-icon="mdi-paperclip"
|
append-inner-icon="mdi-paperclip"
|
||||||
bg-color="white"
|
bg-color="white"
|
||||||
v-model="text"
|
v-model="message"
|
||||||
placeholder="message"
|
placeholder="message"
|
||||||
density="default"
|
density="default"
|
||||||
@keyup.enter="sendMessage"
|
@keyup.enter="sendMessage"
|
||||||
|
|||||||
@@ -1,23 +1,27 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
|
|
||||||
const props = withDefaults(
|
interface Props {
|
||||||
defineProps<{ createdAt?: string; username?: string; onRightSide?: boolean; message?: string }>(),
|
createdAt?: string
|
||||||
{
|
username?: string
|
||||||
|
onRightSide?: boolean
|
||||||
|
message?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const { message, username, createdAt } = withDefaults(defineProps<Props>(), {
|
||||||
my: false,
|
my: false,
|
||||||
message: 'foobar',
|
message: 'foobar',
|
||||||
username: 'robot',
|
username: 'robot',
|
||||||
},
|
})
|
||||||
)
|
|
||||||
|
|
||||||
const createdAt = computed(() => {
|
const messageDate = computed(() => {
|
||||||
return props.createdAt
|
return createdAt
|
||||||
? new Date(props.createdAt).toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
? new Date(createdAt).toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
||||||
: new Date().toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
: new Date().toLocaleTimeString('ru-RU', { timeStyle: 'short' })
|
||||||
})
|
})
|
||||||
|
|
||||||
const avatarLetter = computed(() => {
|
const avatarLetter = computed(() => {
|
||||||
return props.username.slice(0, 1).toUpperCase()
|
return username.slice(0, 1).toUpperCase()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -35,15 +39,15 @@ const avatarLetter = computed(() => {
|
|||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
<!-- </div>-->
|
<!-- </div>-->
|
||||||
|
|
||||||
<div class="flex gap-2" :class="{ 'flex-row-reverse': props.onRightSide }">
|
<div class="flex gap-2" :class="{ 'flex-row-reverse': onRightSide }">
|
||||||
<!-- <v-avatar size="36" color="deep-purple-lighten-4">-->
|
<!-- <v-avatar size="36" color="deep-purple-lighten-4">-->
|
||||||
<!-- <span class="text-deep-purple-darken-2">{{ avatarLetter }}</span>-->
|
<!-- <span class="text-deep-purple-darken-2">{{ avatarLetter }}</span>-->
|
||||||
<!-- </v-avatar>-->
|
<!-- </v-avatar>-->
|
||||||
|
|
||||||
<!-- :class="props.my ? 'message-shaped-right' : 'message-shaped'"-->
|
<!-- :class="props.my ? 'message-shaped-right' : 'message-shaped'"-->
|
||||||
<div class="pa-4" :class="props.onRightSide ? 'bg-blue-400' : 'bg-white'">
|
<div class="flex gap-1 pa-4" :class="onRightSide ? 'bg-blue-400' : 'bg-white'">
|
||||||
<span class="">{{ props.message }}</span>
|
<span class="">{{ message }}</span>
|
||||||
<span class="">{{ createdAt }}</span>
|
<span class="text-xs self-end">{{ messageDate }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
name: string
|
name?: string
|
||||||
image?: string
|
image?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ const { name, image } = defineProps<Props>()
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<VToolbar theme="dark" class="px-2">
|
<VToolbar theme="dark" class="px-2">
|
||||||
<VAvatar text="A" color="primary" />
|
<VAvatar :text="name?.slice(0, 1).toUpperCase()" color="primary" />
|
||||||
<VToolbarTitle :text="name" />
|
<VToolbarTitle :text="name" />
|
||||||
</VToolbar>
|
</VToolbar>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { computed, nextTick, ref, useTemplateRef, watch } from 'vue'
|
|||||||
import { useScroll } from '@vueuse/core'
|
import { useScroll } from '@vueuse/core'
|
||||||
import type { User } from '@/stores/users.ts'
|
import type { User } from '@/stores/users.ts'
|
||||||
import { useChatsStore } from '@/stores/chats.ts'
|
import { useChatsStore } from '@/stores/chats.ts'
|
||||||
|
import { useMessagesStore } from '@/stores/messages.ts'
|
||||||
|
|
||||||
const chatsStore = useChatsStore()
|
const chatsStore = useChatsStore()
|
||||||
|
const messagesStore = useMessagesStore()
|
||||||
|
|
||||||
// const area = useTemplateRef('messageArea')
|
// const area = useTemplateRef('messageArea')
|
||||||
// const { y, arrivedState } = useScroll(area)
|
// const { y, arrivedState } = useScroll(area)
|
||||||
@@ -16,16 +18,23 @@ const chatsStore = useChatsStore()
|
|||||||
// if (area.value) y.value = area.value?.scrollHeight
|
// if (area.value) y.value = area.value?.scrollHeight
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
const chatInfo = computed(() => {
|
||||||
|
if (chatsStore.selectedChat) {
|
||||||
|
return chatsStore.getChatInfo(chatsStore.selectedChat)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
const messages = ref([])
|
const messages = ref([])
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-full flex-col overflow-hidden">
|
<div class="flex h-full flex-col overflow-hidden">
|
||||||
<div class="grow-0" v-if="chatsStore.selectedChat">
|
<div class="grow-0" v-if="chatsStore.selectedChat">
|
||||||
<slot name="toolbar" :toolBarData="chatsStore.getChatInfo(chatsStore.selectedChat)" />
|
<slot name="toolbar" :info="chatInfo" />
|
||||||
</div>
|
</div>
|
||||||
<div class="px-8 gap-2 grow flex flex-col-reverse overflow-y-auto" ref="messageArea">
|
<div class="px-8 gap-2 grow flex flex-col-reverse overflow-y-auto" ref="messageArea">
|
||||||
<slot :messages="messages" />
|
<slot :messages="messagesStore.messages" />
|
||||||
</div>
|
</div>
|
||||||
<div class="grow-0">
|
<div class="grow-0">
|
||||||
<slot name="input" />
|
<slot name="input" />
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import MessagesForm from '@/components/Messages/MessagesForm.vue'
|
|
||||||
import MessageToolbar from '@/components/Messages/MessageToolbar.vue'
|
import MessageToolbar from '@/components/Messages/MessageToolbar.vue'
|
||||||
import MessageInput from '@/components/Messages/MessageInput.vue'
|
import MessageInput from '@/components/Messages/MessageInput.vue'
|
||||||
import MessagesList from '@/components/Messages/MessagesList.vue'
|
|
||||||
import { useChatsStore } from '@/stores/chats.ts'
|
import { useChatsStore } from '@/stores/chats.ts'
|
||||||
|
import MessageItem from '@/components/Messages/MessageItem.vue'
|
||||||
|
import MessagesWrapper from '@/components/Messages/MessagesWrapper.vue'
|
||||||
|
|
||||||
const chatsStore = useChatsStore()
|
const chatsStore = useChatsStore()
|
||||||
</script>
|
</script>
|
||||||
@@ -11,17 +11,17 @@ const chatsStore = useChatsStore()
|
|||||||
<template>
|
<template>
|
||||||
<div class="h-full bg-gray-100">
|
<div class="h-full bg-gray-100">
|
||||||
<div class="flex flex-col h-full">
|
<div class="flex flex-col h-full">
|
||||||
<MessagesForm v-if="chatsStore.selected.length">
|
<MessagesWrapper v-if="chatsStore.selectedChat">
|
||||||
<template #toolbar="{ toolBarData }">
|
<template #toolbar="{ info }">
|
||||||
<MessageToolbar :name="toolBarData.name" />
|
<MessageToolbar v-bind="info" />
|
||||||
</template>
|
</template>
|
||||||
<template #default="{ messages }">
|
<template #default="{ messages }">
|
||||||
<MessagesList :messages />
|
<MessageItem v-for="message in messages" :key="message.id" v-bind="message" />
|
||||||
</template>
|
</template>
|
||||||
<template #input>
|
<template #input>
|
||||||
<MessageInput />
|
<MessageInput />
|
||||||
</template>
|
</template>
|
||||||
</MessagesForm>
|
</MessagesWrapper>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface Chat {
|
|||||||
name: string
|
name: string
|
||||||
users: User[]
|
users: User[]
|
||||||
message?: Message
|
message?: Message
|
||||||
|
image?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useChatsStore = defineStore('chats', () => {
|
export const useChatsStore = defineStore('chats', () => {
|
||||||
@@ -18,6 +19,8 @@ export const useChatsStore = defineStore('chats', () => {
|
|||||||
const selected = ref<string[]>([])
|
const selected = ref<string[]>([])
|
||||||
|
|
||||||
const selectedChat = computed(() => {
|
const selectedChat = computed(() => {
|
||||||
|
if (!selected.value.length) return
|
||||||
|
|
||||||
return chats.value.find((chat: Chat) => chat.id === selected.value[0])
|
return chats.value.find((chat: Chat) => chat.id === selected.value[0])
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -35,5 +38,9 @@ export const useChatsStore = defineStore('chats', () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getChatLastMessage(chat: Chat) {
|
||||||
|
return chats.value.find((el) => el.id === chat.id)
|
||||||
|
}
|
||||||
|
|
||||||
return { chats, selected, selectedChat, getChatInfo }
|
return { chats, selected, selectedChat, getChatInfo }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref } from 'vue'
|
import { computed, ref } from 'vue'
|
||||||
|
import { useChatsStore } from '@/stores/chats.ts'
|
||||||
|
|
||||||
export interface Message {
|
export interface Message {
|
||||||
id: number
|
id: number
|
||||||
@@ -9,7 +10,13 @@ export interface Message {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useMessagesStore = defineStore('messages', () => {
|
export const useMessagesStore = defineStore('messages', () => {
|
||||||
|
const chatsStore = useChatsStore()
|
||||||
const messages = ref<Message[]>([])
|
const messages = ref<Message[]>([])
|
||||||
|
const message = ref<Message>()
|
||||||
|
|
||||||
return { messages }
|
const lastMessage = computed(() => {
|
||||||
|
return message.value ?? chatsStore.selectedChat?.message ?? null
|
||||||
|
})
|
||||||
|
|
||||||
|
return { messages, lastMessage }
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ export const useSocketsStore = defineStore('sockets', () => {
|
|||||||
const idx = chatsStore.chats.findIndex((chat) => chat.id === data.id)
|
const idx = chatsStore.chats.findIndex((chat) => chat.id === data.id)
|
||||||
if (idx < 0) chatsStore.chats.push(data)
|
if (idx < 0) chatsStore.chats.push(data)
|
||||||
|
|
||||||
console.log(data.id)
|
|
||||||
menuStore.selected = ['chats']
|
menuStore.selected = ['chats']
|
||||||
chatsStore.selected = [data.id]
|
chatsStore.selected = [data.id]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ export interface User {
|
|||||||
id: number
|
id: number
|
||||||
email: string
|
email: string
|
||||||
name: string
|
name: string
|
||||||
|
image?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUsersStore = defineStore('users', () => {
|
export const useUsersStore = defineStore('users', () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user