This commit is contained in:
2026-03-15 20:55:00 +03:00
parent 43366a5089
commit b84a76ac08
16 changed files with 91 additions and 103 deletions

8
package-lock.json generated
View File

@@ -17,7 +17,7 @@
"tailwindcss": "^4.2.1", "tailwindcss": "^4.2.1",
"tailwindcss-primeui": "^0.6.1", "tailwindcss-primeui": "^0.6.1",
"vue": "^3.5.28", "vue": "^3.5.28",
"vuetify": "^4.0.0" "vuetify": "^4.0.2"
}, },
"devDependencies": { "devDependencies": {
"@mdi/font": "^7.4.47", "@mdi/font": "^7.4.47",
@@ -5843,9 +5843,9 @@
} }
}, },
"node_modules/vuetify": { "node_modules/vuetify": {
"version": "4.0.0", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/vuetify/-/vuetify-4.0.0.tgz", "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-4.0.2.tgz",
"integrity": "sha512-TRyNWd2KlX1KXbKwuHYRfrX24yLHq85AdVKmokfy5llAgVx7MNW4oBPwFmYLeuuSrWvw5ITtDJ5VjdBIKD5WVw==", "integrity": "sha512-klgSGmfXoLajdTuuxreilzDQjp0ojzL2U5v6Z3ZbMYtpihPPXT9rkd/FxWL3dIGevnWdgaP2Kpwoz6aS/MISDA==",
"license": "MIT", "license": "MIT",
"funding": { "funding": {
"type": "github", "type": "github",

View File

@@ -24,7 +24,7 @@
"tailwindcss": "^4.2.1", "tailwindcss": "^4.2.1",
"tailwindcss-primeui": "^0.6.1", "tailwindcss-primeui": "^0.6.1",
"vue": "^3.5.28", "vue": "^3.5.28",
"vuetify": "^4.0.0" "vuetify": "^4.0.2"
}, },
"devDependencies": { "devDependencies": {
"@mdi/font": "^7.4.47", "@mdi/font": "^7.4.47",

View File

@@ -5,7 +5,12 @@ const menuStore = useMenuStore()
</script> </script>
<template> <template>
<VBtn icon="mdi-arrow-left" size="small" @click="menuStore.selected = 'chats'" /> <VBtn
icon="mdi-arrow-left"
color="default"
size="small"
@click="menuStore.selected = ['chats']"
/>
</template> </template>
<style scoped></style> <style scoped></style>

View File

@@ -1,6 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type Chat } from '@/stores/chats.ts' import { type Chat, useChatsStore } from '@/stores/chats.ts'
import { useAuthStore } from '@/stores/auth.ts'
import { computed } from 'vue' import { computed } from 'vue'
interface Props { interface Props {
@@ -8,18 +7,10 @@ interface Props {
} }
const { chat } = defineProps<Props>() const { chat } = defineProps<Props>()
const authStore = useAuthStore() const chatsStore = useChatsStore()
const chatName = computed(() => { const chatName = computed(() => {
switch (chat.typeId) { return chatsStore.getChatInfo(chat).name
case 1:
const otherUsers = chat.users.filter((user) => user.id !== authStore.me?.id)
return otherUsers[0]?.name ?? otherUsers[0]?.email ?? 'unknown'
case 2:
return chat.name
default:
return 'chat'
}
}) })
const lastMessage = computed(() => { const lastMessage = computed(() => {

View File

@@ -1,44 +1,34 @@
<script setup lang="ts"> <script setup lang="ts">
import { useChatsStore } from '@/stores/chats.ts' import { useChatsStore } from '@/stores/chats.ts'
import { SocketDataReq, useSocketsStore } from '@/stores/sockets.ts' import { SocketDataReq, useSocketsStore } from '@/stores/sockets.ts'
import { onMounted, ref, watch, watchEffect } from 'vue' import { watchEffect } from 'vue'
import { type SelectedMenu, useMenuStore } from '@/stores/menu.ts' import { useMenuStore } from '@/stores/menu.ts'
import ChatListElement from '@/components/Chats/ChatListElement.vue' import ChatListElement from '@/components/Chats/ChatListElement.vue'
const chatsStore = useChatsStore() const chatsStore = useChatsStore()
const socketsStore = useSocketsStore() const socketsStore = useSocketsStore()
const menuStore = useMenuStore() const menuStore = useMenuStore()
const selected = ref<string[]>()
const menu = ref<string[]>()
watchEffect(() => getMessages(chatsStore.selected)) watchEffect(() => getMessages(chatsStore.selected))
watch(selected, (val) => {
if (val) chatsStore.selected = val[0]
})
watch(menu, (val) => { function getMessages([selected]: string[]) {
if (val) menuStore.selected = val[0] as SelectedMenu if (selected) {
}) socketsStore.send({
type: SocketDataReq.GET_MESSAGES,
function getMessages(chatId?: string) { data: { chatId: selected },
if (!chatId) return })
socketsStore.send({ }
type: SocketDataReq.GET_MESSAGES,
data: { chatId: chatId },
})
} }
onMounted(() => console.log('CHAT LIST'))
</script> </script>
<template> <template>
<div class="flex flex-col h-full"> <div class="flex flex-col h-full">
<VToolbar class="px-2"> <VToolbar theme="dark" class="px-2">
<VMenu transition="slide-y-transition"> <VMenu transition="slide-y-transition">
<template v-slot:activator="{ props }"> <template v-slot:activator="{ props }">
<VBtn size="small" icon="mdi-menu" class="mr-2" v-bind="props" /> <VBtn size="small" icon="mdi-menu" class="mr-2" color="white" v-bind="props" />
</template> </template>
<VList class="top-1" density="compact" slim v-model:selected="menu"> <VList class="top-1" density="compact" slim v-model:selected="menuStore.selected">
<VListItem value="users" title="Contacts" prepend-gap="8" prepend-icon="mdi-account" /> <VListItem value="users" title="Contacts" prepend-gap="8" prepend-icon="mdi-account" />
<VListItem value="settings" title="Settings" prepend-gap="8" prepend-icon="mdi-cog" /> <VListItem value="settings" title="Settings" prepend-gap="8" prepend-icon="mdi-cog" />
<VDivider class="my-1" /> <VDivider class="my-1" />
@@ -46,10 +36,10 @@ onMounted(() => console.log('CHAT LIST'))
</VList> </VList>
</VMenu> </VMenu>
<VTextField bg-color="white" prepend-inner-icon="mdi-magnify" placeholder="search chat" /> <VTextField bg-color="black" prepend-inner-icon="mdi-magnify" placeholder="search chat" />
</VToolbar> </VToolbar>
<VList v-model:selected="selected" mandatory lines="one"> <VList theme="dark" v-model:selected="chatsStore.selected" mandatory lines="one">
<ChatListElement v-for="chat in chatsStore.chats" :key="chat.id" v-bind="{ chat }" /> <ChatListElement v-for="chat in chatsStore.chats" :key="chat.id" :chat />
</VList> </VList>
</div> </div>
</template> </template>

View File

@@ -8,7 +8,7 @@ import SettingsList from '@/components/Settings/SettingsList.vue'
const menuStore = useMenuStore() const menuStore = useMenuStore()
const component = computed(() => { const component = computed(() => {
switch (menuStore.selected) { switch (menuStore.selected[0]) {
case 'chats': case 'chats':
return ChatsList return ChatsList
case 'users': case 'users':

View File

@@ -1,13 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import type { User } from '@/stores/users.ts' interface Props {
name: string
image?: string
}
const {user} = defineProps<{ user: User }>() const { name, image } = defineProps<Props>()
</script> </script>
<template> <template>
<VToolbar class="px-2"> <VToolbar theme="dark" class="px-2">
<VAvatar text="A" color="primary" /> <VAvatar text="A" color="primary" />
<VToolbarTitle :text="user.name" /> <VToolbarTitle :text="name" />
</VToolbar> </VToolbar>
</template> </template>

View File

@@ -2,33 +2,27 @@
import { computed, nextTick, ref, useTemplateRef, watch } from 'vue' 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'
const area = useTemplateRef('messageArea') const chatsStore = useChatsStore()
// const area = useTemplateRef('messageArea')
// const { y, arrivedState } = useScroll(area) // const { y, arrivedState } = useScroll(area)
// const messages = computed(() => { // const messages = computed(() => {
// return [...messagesStore.messages] // return [...messagesStore.messages]
// }) // })
// async function scrollToBottom() { // async function scrollToBottom() {
// await nextTick() // await nextTick()
// if (area.value) y.value = area.value?.scrollHeight // if (area.value) y.value = area.value?.scrollHeight
// } // }
const user = ref<User>({
id: 1,
name: 'test',
email: 'test@test.ru',
})
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"> <div class="grow-0" v-if="chatsStore.selectedChat">
<slot name="toolbar" :user="user" /> <slot name="toolbar" :toolBarData="chatsStore.getChatInfo(chatsStore.selectedChat)" />
</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="messages" />

View File

@@ -7,29 +7,6 @@ const { messages } = defineProps<{ messages: Message[] }>()
<template> <template>
<MessageData>Hello world</MessageData> <MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData on-right-side>Hello world</MessageData>
<MessageData on-right-side>Hello world</MessageData>
<MessageData on-right-side>Hello world</MessageData>
<MessageData on-right-side>Hello world</MessageData>
<MessageData on-right-side>Hello world</MessageData>
<MessageData on-right-side>Hello world</MessageData>
<MessageData on-right-side>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
<MessageData>Hello world</MessageData>
</template> </template>
<style scoped></style> <style scoped></style>

View File

@@ -1,19 +1,19 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAuthStore } from '@/stores/auth.ts'
import MessagesForm from '@/components/Messages/MessagesForm.vue' 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 MessagesList from '@/components/Messages/MessagesList.vue'
import { useChatsStore } from '@/stores/chats.ts'
const authStore = useAuthStore() const chatsStore = useChatsStore()
</script> </script>
<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> <MessagesForm v-if="chatsStore.selected.length">
<template #toolbar="{ user }"> <template #toolbar="{ toolBarData }">
<MessageToolbar :user /> <MessageToolbar :name="toolBarData.name" />
</template> </template>
<template #default="{ messages }"> <template #default="{ messages }">
<MessagesList :messages /> <MessagesList :messages />

View File

@@ -23,20 +23,17 @@ function onStartChat(userId: number) {
socketsStore.send({ socketsStore.send({
type: SocketDataReq.CREATE_CHAT, type: SocketDataReq.CREATE_CHAT,
data: { data: { userId: userId },
userId: userId,
},
}) })
} }
</script> </script>
<template> <template>
<div class="flex flex-col h-full"> <div class="flex flex-col h-full">
<VToolbar class="px-2"> <VToolbar theme="dark" class="px-2">
<BackToChats class="mr-2" /> <BackToChats class="mr-2" />
<VSheet class="w-full">
<VTextField prepend-inner-icon="mdi-magnify" placeholder="search contacts" /> <VTextField bg-color="black" prepend-inner-icon="mdi-magnify" placeholder="search contacts" />
</VSheet>
</VToolbar> </VToolbar>
<VList> <VList>

View File

@@ -33,7 +33,12 @@ const avatarText = computed(() => {
</template> </template>
<template #append> <template #append>
<VBtn icon="mdi-message" color="default" slim size="small" @click="emit('click', user.id)" /> <VBtn
icon="mdi-message-outline"
variant="plain"
size="small"
@click="emit('click', user.id)"
/>
</template> </template>
</VListItem> </VListItem>
</template> </template>

View File

@@ -1,7 +1,8 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { computed, ref } from 'vue'
import type { User } from '@/stores/users.ts' import type { User } from '@/stores/users.ts'
import type { Message } from '@/stores/messages.ts' import type { Message } from '@/stores/messages.ts'
import { useAuthStore } from '@/stores/auth.ts'
export interface Chat { export interface Chat {
id: string id: string
@@ -12,8 +13,31 @@ export interface Chat {
} }
export const useChatsStore = defineStore('chats', () => { export const useChatsStore = defineStore('chats', () => {
const authStore = useAuthStore()
const chats = ref<Chat[]>([]) const chats = ref<Chat[]>([])
const selected = ref<string>() const selected = ref<string[]>([])
return { chats, selected } const selectedChat = computed(() => {
const chatId = selected.value[0]
return chats.value.find((chat: Chat) => {
return chat.id === chatId
})
})
function getChatInfo(chat: Chat) {
switch (chat.typeId) {
case 1:
const user = chat.users.find((user) => {
return user.id !== authStore.me?.id
})
return user ?? chat
case 2:
return chat
default:
return chat
}
}
return { chats, selected, selectedChat, getChatInfo }
}) })

View File

@@ -1,10 +1,10 @@
import { defineStore } from 'pinia' import { defineStore } from 'pinia'
import { ref } from 'vue' import { ref } from 'vue'
export type SelectedMenu = 'chats' | 'users' | 'settings' export type MenuItems = 'chats' | 'users' | 'settings'
export const useMenuStore = defineStore('menu', () => { export const useMenuStore = defineStore('menu', () => {
const selected = ref<SelectedMenu>('chats') const selected = ref<MenuItems[]>(['chats'])
return { selected } return { selected }
}) })

View File

@@ -76,8 +76,9 @@ 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)
menuStore.selected = 'chats' console.log(data.id)
chatsStore.selected = data.id menuStore.selected = ['chats']
chatsStore.selected = [data.id]
} }
break break
@@ -114,6 +115,7 @@ export const useSocketsStore = defineStore('sockets', () => {
} }
const send = (data: unknown) => { const send = (data: unknown) => {
console.log(COMMAND.SEND, data)
postMessage(COMMAND.SEND, data) postMessage(COMMAND.SEND, data)
} }

View File

@@ -21,7 +21,7 @@ onMounted(() => {
</LeftPane> </LeftPane>
</div> </div>
<div class="md:w-2/3 sm:w-1/2 w-1/2"> <div class="md:w-2/3 sm:w-1/2 w-1/2">
<RightPane></RightPane> <RightPane />
</div> </div>
</div> </div>
</template> </template>