From 12ae0ae839a0508c7435ab0748300e90812b97a2 Mon Sep 17 00:00:00 2001 From: Opti1337 Date: Sat, 23 May 2026 22:49:52 +0600 Subject: [PATCH] brutalism design --- new-client/package.json | 1 + new-client/src/pages/auth/login.vue | 2 +- new-client/src/pages/auth/register.vue | 2 +- .../src/shared/components/ui/Button.vue | 63 +++++-- new-client/src/shared/components/ui/Input.vue | 2 +- .../components/ui/NotificationBadge.vue | 25 +++ .../src/shared/components/ui/ToBottom.vue | 38 ++++ new-client/src/shared/composables/use-bem.ts | 68 ++++++++ new-client/src/shared/styles/tokens.scss | 1 + .../chat/composables/use-chat-history.ts | 42 +++++ .../chat/composables/use-chat-scroll.ts | 12 +- .../src/widgets/chat/composables/use-chat.ts | 49 ++---- new-client/src/widgets/chat/ui/Chat.vue | 3 + new-client/src/widgets/chat/ui/ChatInput.vue | 126 ++++++++++++- .../src/widgets/chat/ui/ChatMessageGroup.vue | 2 +- .../src/widgets/chat/ui/ChatMessages.vue | 54 +++--- new-client/yarn.lock | 165 ++++++++++-------- 17 files changed, 497 insertions(+), 158 deletions(-) create mode 100644 new-client/src/shared/components/ui/NotificationBadge.vue create mode 100644 new-client/src/shared/components/ui/ToBottom.vue create mode 100644 new-client/src/shared/composables/use-bem.ts create mode 100644 new-client/src/widgets/chat/composables/use-chat-history.ts diff --git a/new-client/package.json b/new-client/package.json index 3c14182..8844c1a 100644 --- a/new-client/package.json +++ b/new-client/package.json @@ -20,6 +20,7 @@ "@vueuse/core": "^14.3.0", "@zag-js/avatar": "^1.40.0", "@zag-js/collapsible": "^1.40.0", + "@zag-js/file-upload": "^1.41.0", "@zag-js/file-utils": "^1.40.0", "@zag-js/password-input": "^1.40.0", "@zag-js/toggle": "^1.40.0", diff --git a/new-client/src/pages/auth/login.vue b/new-client/src/pages/auth/login.vue index 638e7a3..3eb7b15 100644 --- a/new-client/src/pages/auth/login.vue +++ b/new-client/src/pages/auth/login.vue @@ -6,7 +6,7 @@ diff --git a/new-client/src/pages/auth/register.vue b/new-client/src/pages/auth/register.vue index 8eb2d35..7536d9a 100644 --- a/new-client/src/pages/auth/register.vue +++ b/new-client/src/pages/auth/register.vue @@ -7,7 +7,7 @@ diff --git a/new-client/src/shared/components/ui/Button.vue b/new-client/src/shared/components/ui/Button.vue index 8ffc735..f648ff3 100644 --- a/new-client/src/shared/components/ui/Button.vue +++ b/new-client/src/shared/components/ui/Button.vue @@ -1,37 +1,53 @@ diff --git a/new-client/src/shared/components/ui/Input.vue b/new-client/src/shared/components/ui/Input.vue index 187027d..daf85b8 100644 --- a/new-client/src/shared/components/ui/Input.vue +++ b/new-client/src/shared/components/ui/Input.vue @@ -9,7 +9,7 @@ {{ label }} - +

{{ helper }} diff --git a/new-client/src/shared/components/ui/NotificationBadge.vue b/new-client/src/shared/components/ui/NotificationBadge.vue new file mode 100644 index 0000000..3e7ba77 --- /dev/null +++ b/new-client/src/shared/components/ui/NotificationBadge.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/new-client/src/shared/components/ui/ToBottom.vue b/new-client/src/shared/components/ui/ToBottom.vue new file mode 100644 index 0000000..eb7a12b --- /dev/null +++ b/new-client/src/shared/components/ui/ToBottom.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/new-client/src/shared/composables/use-bem.ts b/new-client/src/shared/composables/use-bem.ts new file mode 100644 index 0000000..e2951ef --- /dev/null +++ b/new-client/src/shared/composables/use-bem.ts @@ -0,0 +1,68 @@ +interface Options { + block: string + blockSuffix?: string + element?: string + modifier?: string +} + +function _bem(options: Options) { + const { block, blockSuffix, element, modifier } = options + + let cls = block + + if (blockSuffix) + cls += `-${blockSuffix}` + + if (element) + cls += `__${element}` + + if (modifier) + cls += `--${modifier}` + + return cls +} + +function isTruthy(value: unknown): boolean { + if (Array.isArray(value)) { + return value.length > 0 + } + else if (typeof value === 'object' && value !== null) { + return Object.keys(value).length > 0 + } + + return !!value +} + +export default (block: string) => { + const b = (blockSuffix?: string) => _bem({ block, blockSuffix }) + const e = (element: string) => _bem({ block, element }) + const m = (modifier: string) => _bem({ block, modifier }) + + const be = (blockSuffix: string, element: string) => _bem({ block, blockSuffix, element }) + const em = (element: string, modifier: string) => _bem({ block, element, modifier }) + const bm = (blockSuffix: string, modifier: string) => _bem({ block, blockSuffix, modifier }) + + const bem = (blockSuffix: string, element: string, modifier: string) => _bem({ block, blockSuffix, element, modifier }) + + const is = (name: string, state: unknown) => isTruthy(state) ? `is-${name}` : '' + const has = (name: string, state: unknown) => isTruthy(state) ? `has-${name}` : '' + + const exp = (condition: boolean, trueState: string, falseState: string = '') => condition ? trueState : falseState + + return { + b, + e, + m, + + be, + em, + bm, + + bem, + + is, + has, + + exp, + } +} diff --git a/new-client/src/shared/styles/tokens.scss b/new-client/src/shared/styles/tokens.scss index 3ebcfba..3478dc8 100644 --- a/new-client/src/shared/styles/tokens.scss +++ b/new-client/src/shared/styles/tokens.scss @@ -10,6 +10,7 @@ --grey-2: #c9c6bd; --grey-3: #8a8a82; + --shadow-sm: 3px 3px 0 0 var(--ink); --shadow: 6px 6px 0 0 var(--ink); --border-w: 2px; diff --git a/new-client/src/widgets/chat/composables/use-chat-history.ts b/new-client/src/widgets/chat/composables/use-chat-history.ts new file mode 100644 index 0000000..baf6c6c --- /dev/null +++ b/new-client/src/widgets/chat/composables/use-chat-history.ts @@ -0,0 +1,42 @@ +import type { ChatMessage } from '@shared/api/generated-chad-api.ts' +import { useEventBus } from '@shared/composables/use-event-bus.ts' +import { createGlobalState } from '@vueuse/core' +import { computed, shallowRef, triggerRef } from 'vue' +import { qChatMessages } from '../api/qChatMessages.ts' + +export interface MessageGroup { + senderUsername: string + messages: ChatMessage[] +} + +export const useChatHistory = createGlobalState(() => { + const eventBus = useEventBus() + + const { data: messages, hasNextPage: hasMoreMessages, fetchNextPage: loadMoreMessages, isFetching } = qChatMessages() + + const feedItems = shallowRef([]) + + eventBus.on('chat:new-message', (message: ChatMessage) => { + feedItems.value.push(message) + triggerRef(feedItems) + }) + + const messageGroups = computed(() => { + const all = [...(messages.value ?? []), ...feedItems.value] + return all.reduce((groups, msg) => { + const last = groups.at(-1) + if (last?.senderUsername === msg.senderUsername) + last.messages.push(msg) + else + groups.push({ senderUsername: msg.senderUsername, messages: [msg] }) + return groups + }, [] as MessageGroup[]) + }) + + return { + messageGroups, + hasMoreMessages, + loadMoreMessages, + isFetching, + } +}) diff --git a/new-client/src/widgets/chat/composables/use-chat-scroll.ts b/new-client/src/widgets/chat/composables/use-chat-scroll.ts index 490f057..78b3a80 100644 --- a/new-client/src/widgets/chat/composables/use-chat-scroll.ts +++ b/new-client/src/widgets/chat/composables/use-chat-scroll.ts @@ -15,9 +15,7 @@ export function useChatScroll(target: TemplateRef, options?: MaybeR end: false, }) - watch(arrivedState, () => { - console.log('arrived state', arrivedState) - }) + let lastScrollHeight = 0 watch(el, (el) => { if (!el) @@ -31,14 +29,22 @@ export function useChatScroll(target: TemplateRef, options?: MaybeR if (arrivedState.start) { scrollToStart(true) } + else { + const heightDiff = el.value!.scrollHeight - lastScrollHeight + + el.value!.scrollTop += heightDiff + } getArrivedState() + + lastScrollHeight = el.value!.scrollHeight }, { childList: true, subtree: true, }) useEventListener(el, 'scroll', () => { + console.log('scroll', el.value!.scrollTop) getArrivedState() }, { passive: true }) diff --git a/new-client/src/widgets/chat/composables/use-chat.ts b/new-client/src/widgets/chat/composables/use-chat.ts index 8657dfe..9252120 100644 --- a/new-client/src/widgets/chat/composables/use-chat.ts +++ b/new-client/src/widgets/chat/composables/use-chat.ts @@ -1,41 +1,18 @@ -import type { ChatMessage } from '@shared/api/generated-chad-api.ts' -import { useSignaling } from '@shared/composables/use-signaling' -import { createGlobalState } from '@vueuse/core' -import { computed, shallowRef, triggerRef, watch } from 'vue' -import { qChatMessages } from '../api/qChatMessages.ts' +import type { NewChatMessagePayload } from '@shared/api/generated-chad-api.ts' +import api from '@shared/api/client.ts' -export interface MessageGroup { - senderUsername: string - messages: ChatMessage[] -} +export function useChat() { + async function sendMessage(newMessage: NewChatMessagePayload) { + newMessage.text = newMessage.text.trim() -export const useChat = createGlobalState(() => { - const { data: messages, hasNextPage: hasMoreMessages, fetchNextPage, isFetching } = qChatMessages() - - const { socket, connected } = useSignaling() - const feedItems = shallowRef([]) - - watch(connected, async (isConnected) => { - if (!isConnected) + if (newMessage.attachments?.length === 0 && newMessage.text.length === 0) { return + } - socket.value!.on('chat:new-message', (message: ChatMessage) => { - feedItems.value.push(message) - triggerRef(feedItems) - }) - }) + await api.chad.chatSend(newMessage) + } - const messageGroups = computed(() => { - const all = [...(messages.value ?? []), ...feedItems.value] - return all.reduce((groups, msg) => { - const last = groups.at(-1) - if (last?.senderUsername === msg.senderUsername) - last.messages.push(msg) - else - groups.push({ senderUsername: msg.senderUsername, messages: [msg] }) - return groups - }, [] as MessageGroup[]) - }) - - return { messageGroups, hasMoreMessages, fetchNextPage, isFetching } -}) + return { + sendMessage, + } +} diff --git a/new-client/src/widgets/chat/ui/Chat.vue b/new-client/src/widgets/chat/ui/Chat.vue index 71b56c9..607121d 100644 --- a/new-client/src/widgets/chat/ui/Chat.vue +++ b/new-client/src/widgets/chat/ui/Chat.vue @@ -6,8 +6,11 @@ diff --git a/new-client/src/widgets/chat/ui/ChatMessageGroup.vue b/new-client/src/widgets/chat/ui/ChatMessageGroup.vue index afaa675..9f2370c 100644 --- a/new-client/src/widgets/chat/ui/ChatMessageGroup.vue +++ b/new-client/src/widgets/chat/ui/ChatMessageGroup.vue @@ -13,7 +13,7 @@