From db322436b7aff0482510cb8f3af1ed685c3d3c29 Mon Sep 17 00:00:00 2001 From: Dima Shirkov Date: Mon, 29 Sep 2025 06:40:40 +0300 Subject: [PATCH 1/2] feat: add button delete message --- src/js/app.js | 18 +++++++++++++-- src/js/creatingElements.js | 47 +++++++++++++++++++++++++++++++++----- src/js/handlers.js | 16 +++++++++++++ src/js/services.js | 35 +++++++++++++++++++--------- src/scss/style.scss | 18 +++++++++++++++ 5 files changed, 115 insertions(+), 19 deletions(-) create mode 100644 src/js/handlers.js diff --git a/src/js/app.js b/src/js/app.js index 2cf299d..c95f63f 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -4,7 +4,21 @@ import { toggleClass, toggleHide } from './helpers'; import { launchSSE, launchWS } from './services'; import { SUBSCRIPTION, WS } from './constants'; +export const removeMessage = (message) => { + console.log(message); + // const messages = document.querySelectorAll('.chat__message-item'); + // const targetMessage = [...messages].filter((i) => i.dataset.id === id); + // const message = targetMessage[0].querySelector('.chat__message'); +}; + +export const removeMessage1 = ({ id }) => { + const messages = document.querySelectorAll('.chat__message-item'); + const targetMessage = [...messages].filter((i) => i.dataset.id === id); + const message = targetMessage[0].querySelector('.chat__message'); +}; + document.addEventListener('DOMContentLoaded', () => { + // TODO: Переместить все handlers в отдельные файлы??? const submitNickname = (e) => { e.preventDefault(); const modal = document.querySelector('.modal'); @@ -98,7 +112,7 @@ document.addEventListener('DOMContentLoaded', () => { input.addEventListener('click', hideError); }; - function handleCreateMessage() { + const handleCreateMessage = () => { const form = document.forms['create-message']; const input = form.querySelector('.form__input'); const errorContainer = form.querySelector('.form__validator-container'); @@ -114,7 +128,7 @@ document.addEventListener('DOMContentLoaded', () => { form.addEventListener('submit', submitMessage); input.addEventListener('click', hideError); - } + }; try { if (document.forms.length === 0) throw new Error('отсутствуют формы на странице'); diff --git a/src/js/creatingElements.js b/src/js/creatingElements.js index 6b05444..71c43e0 100644 --- a/src/js/creatingElements.js +++ b/src/js/creatingElements.js @@ -1,3 +1,4 @@ +import { handleRemoveMessage } from './handlers'; import { formatDate } from './helpers'; import { GLOBAL_STATE } from './store'; @@ -11,7 +12,40 @@ export function createNewSubscriber(name) { subscriptions.appendChild(subscriber); } -export function createMessage({ message, date, client }) { +const createRemoveButton = ({ parent, id }) => { + const remove = document.createElement('button'); + remove.setAttribute('type', 'button'); + remove.classList.add('chat__message-remove'); + remove.dataset.id = id; + remove.textContent = 'Удалить'; + remove.addEventListener('click', handleRemoveMessage); + parent.appendChild(remove); +}; + +const createMessageText = ({ + deleted, message, id, parent, +}) => { + const text = document.createElement('p'); + text.classList.add('chat__message'); + if (deleted) { + text.classList.add('chat__message_italic'); + text.textContent = 'Сообщение удалено'; + } + if (!deleted) { + text.textContent = message; + createRemoveButton({ parent, id }); + } + + return text; +}; + +export const createMessage = ({ + id, + message, + client, + date, + deleted, +}) => { const chat = document.querySelector('.chat__messages'); const container = document.querySelector('.chat__container'); @@ -20,6 +54,7 @@ export function createMessage({ message, date, client }) { nickname.classList.add('chat__message-nickname'); itemMessage.classList.add('chat__message-item'); + itemMessage.dataset.id = id; if (client !== GLOBAL_STATE.userName) nickname.textContent = client; if (client === GLOBAL_STATE.userName) { @@ -29,20 +64,20 @@ export function createMessage({ message, date, client }) { const time = document.createElement('time'); time.classList.add('chat__message-time'); - const timeSendingMessage = new Date(date); + const timeSendingMessage = new Date(parseInt(date, 10)); time.textContent = ` ${formatDate(timeSendingMessage)}`; nickname.appendChild(time); - const text = document.createElement('p'); - text.classList.add('chat__message'); - text.textContent = message; + const text = createMessageText({ + deleted, message, id, parent: nickname, + }); itemMessage.appendChild(nickname); itemMessage.appendChild(text); chat.appendChild(itemMessage); container.scrollTop = container.scrollHeight; -} +}; export const changeNickname = ({ userName }) => { if (GLOBAL_STATE.allUsers.every((i) => i !== userName)) throw new Error('юзера с данным именем нет в общем списке юзеров'); diff --git a/src/js/handlers.js b/src/js/handlers.js new file mode 100644 index 0000000..36c7e28 --- /dev/null +++ b/src/js/handlers.js @@ -0,0 +1,16 @@ +import { WS } from './constants'; + +export const handleRemoveMessage = (e) => { + const { id } = e.target.dataset; + WS.send(JSON.stringify({ + type: 'delete', + data: { id }, + })); +}; + +export const handleRemoveMessage1 = (id) => { + WS.send(JSON.stringify({ + type: 'delete', + data: id, + })); +}; diff --git a/src/js/services.js b/src/js/services.js index 6ccd458..aea8bdd 100644 --- a/src/js/services.js +++ b/src/js/services.js @@ -1,3 +1,4 @@ +import { removeMessage } from './app'; import { SSE, SUBSCRIPTION, WS, } from './constants'; @@ -87,16 +88,28 @@ export const launchWS = () => { }); WS.addEventListener('message', (e) => { - const data = JSON.parse(e.data); - - data.chat.forEach((item) => { - const { message, client, date } = item; - - createMessage({ - message, - client, - date, - }); - }); + const eventSocket = JSON.parse(e.data); + + switch (eventSocket.type) { + case 'first-load': + Object.entries(eventSocket.data).forEach(([id, data]) => { + const { + message, client, date, deleted, + } = data; + createMessage({ + date, + id, + message, + client, + deleted, + }); + }); + break; + case 'delete': + removeMessage(eventSocket.data); + break; + default: + break; + } }); }; diff --git a/src/scss/style.scss b/src/scss/style.scss index 377db0b..ef517f3 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -51,6 +51,7 @@ // .chat__message-nickname &__message-nickname { + display: flex; color: $colorGrey3Cool; } @@ -59,10 +60,27 @@ color: $colorGrey3Cool; } + // .chat__message-remove + &__message-remove{ + text-decoration: underline; + font-size: 14px; + color: $colorBlue1; + cursor: pointer; + background: none; + font-weight: 400; + margin-left: auto; + text-underline-position: under; + } + // .chat__message &__message { color: $colorBlackCool; font-weight: 500; + + // .chat__message_italic + &_italic{ + font-style: italic; + } } } From ea955323073c316b7db1c91fa27f3739b80c7941 Mon Sep 17 00:00:00 2001 From: Dmitry Shirkov Date: Tue, 30 Sep 2025 08:48:42 +0300 Subject: [PATCH 2/2] feat: realized message deletion --- src/js/app.js | 21 +++++-------------- src/js/constants.js | 1 + src/js/creatingElements.js | 41 ++++++++++++++++++++++++-------------- src/js/services.js | 26 ++++++++++++++++++++---- src/scss/style.scss | 35 ++++++++++++++++++++++++++++++-- src/scss/variables.scss | 2 ++ 6 files changed, 89 insertions(+), 37 deletions(-) diff --git a/src/js/app.js b/src/js/app.js index c95f63f..c8f42ab 100644 --- a/src/js/app.js +++ b/src/js/app.js @@ -4,19 +4,6 @@ import { toggleClass, toggleHide } from './helpers'; import { launchSSE, launchWS } from './services'; import { SUBSCRIPTION, WS } from './constants'; -export const removeMessage = (message) => { - console.log(message); - // const messages = document.querySelectorAll('.chat__message-item'); - // const targetMessage = [...messages].filter((i) => i.dataset.id === id); - // const message = targetMessage[0].querySelector('.chat__message'); -}; - -export const removeMessage1 = ({ id }) => { - const messages = document.querySelectorAll('.chat__message-item'); - const targetMessage = [...messages].filter((i) => i.dataset.id === id); - const message = targetMessage[0].querySelector('.chat__message'); -}; - document.addEventListener('DOMContentLoaded', () => { // TODO: Переместить все handlers в отдельные файлы??? const submitNickname = (e) => { @@ -80,13 +67,15 @@ document.addEventListener('DOMContentLoaded', () => { return; } - const messageItem = { + const data = { message, client: GLOBAL_STATE.userName, date: new Date().getTime(), }; - - WS.send(JSON.stringify(messageItem)); + WS.send(JSON.stringify({ + type: 'add', + data, + })); e.target.reset(); }; diff --git a/src/js/constants.js b/src/js/constants.js index dedd360..4737e8c 100644 --- a/src/js/constants.js +++ b/src/js/constants.js @@ -1,6 +1,7 @@ import SubscriptionApi from '../api/SubscriptionApi'; export const SERVER_URL = 'http://localhost:7070/'; +// export const SERVER_URL = 'http://192.168.14.55:9000/'; export const SERVER_URL_WS = `${SERVER_URL}ws`; export const SERVER_URL_SSE = `${SERVER_URL}sse`; export const SSE = new EventSource(SERVER_URL_SSE); diff --git a/src/js/creatingElements.js b/src/js/creatingElements.js index 71c43e0..5321128 100644 --- a/src/js/creatingElements.js +++ b/src/js/creatingElements.js @@ -23,20 +23,21 @@ const createRemoveButton = ({ parent, id }) => { }; const createMessageText = ({ - deleted, message, id, parent, + deleted, message, id, parent, client, }) => { - const text = document.createElement('p'); - text.classList.add('chat__message'); + const paragraph = document.createElement('p'); + paragraph.classList.add('chat__message'); + if (deleted) { - text.classList.add('chat__message_italic'); - text.textContent = 'Сообщение удалено'; - } - if (!deleted) { - text.textContent = message; - createRemoveButton({ parent, id }); + paragraph.classList.add('chat__message_italic'); + paragraph.textContent = 'Сообщение удалено'; + return paragraph; } - return text; + if (client === GLOBAL_STATE.userName) createRemoveButton({ parent, id }); + paragraph.textContent = message; + + return paragraph; }; export const createMessage = ({ @@ -46,9 +47,6 @@ export const createMessage = ({ date, deleted, }) => { - const chat = document.querySelector('.chat__messages'); - const container = document.querySelector('.chat__container'); - const itemMessage = document.createElement('li'); const nickname = document.createElement('h3'); @@ -69,13 +67,26 @@ export const createMessage = ({ nickname.appendChild(time); const text = createMessageText({ - deleted, message, id, parent: nickname, + deleted, message, id, parent: nickname, client, }); itemMessage.appendChild(nickname); itemMessage.appendChild(text); + return itemMessage; +}; + +export const removeMessage = (message) => { + const [id, messageData] = Object.entries(message)[0]; + const newMessage = createMessage({ id, ...messageData }); + const messages = document.querySelectorAll('.chat__message-item'); + const deletedMessage = [...messages].find((item) => item.dataset.id === id); + deletedMessage.replaceWith(newMessage); +}; - chat.appendChild(itemMessage); +export const insertInChat = ({ element }) => { + const chat = document.querySelector('.chat__messages'); + const container = document.querySelector('.chat__container'); + chat.appendChild(element); container.scrollTop = container.scrollHeight; }; diff --git a/src/js/services.js b/src/js/services.js index aea8bdd..2ecfaeb 100644 --- a/src/js/services.js +++ b/src/js/services.js @@ -1,8 +1,9 @@ -import { removeMessage } from './app'; import { SSE, SUBSCRIPTION, WS, } from './constants'; -import { createMessage, createNewSubscriber, removeSubscriber } from './creatingElements'; +import { + createMessage, createNewSubscriber, insertInChat, removeSubscriber, removeMessage, +} from './creatingElements'; import { deleteCompanion, setCompanion } from './store'; export const launchSSE = () => { @@ -89,20 +90,37 @@ export const launchWS = () => { WS.addEventListener('message', (e) => { const eventSocket = JSON.parse(e.data); - + let element; switch (eventSocket.type) { case 'first-load': Object.entries(eventSocket.data).forEach(([id, data]) => { const { message, client, date, deleted, } = data; - createMessage({ + element = createMessage({ + date, + id, + message, + client, + deleted, + }); + + insertInChat({ element }); + }); + break; + case 'add': + Object.entries(eventSocket.data).forEach(([id, data]) => { + const { + message, client, date, deleted, + } = data; + element = createMessage({ date, id, message, client, deleted, }); + insertInChat({ element }); }); break; case 'delete': diff --git a/src/scss/style.scss b/src/scss/style.scss index ef517f3..f57f0b9 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -8,7 +8,7 @@ .chat { position: relative; box-sizing: border-box; - width: 908px; + width: 795px; height: 583px; padding: 64px; margin: auto; @@ -23,7 +23,36 @@ // .chat__container &__container { overflow-y: auto; - // max-height: 200px; + padding-right: 10px; + + &::-webkit-scrollbar-button { + width: 5px; + height: 0; + transform: translateX(-5px); + } + + &::-webkit-scrollbar-track { + background-color: $colorGrey4Cool; + } + + &::-webkit-scrollbar-thumb { + -webkit-border-radius: 0; + border-radius: 0; + background-color: $colorBlue1; + } + + &::-webkit-scrollbar-thumb:hover { + background-color: $colorBlue1; + } + + &::-webkit-resizer { + width: 4px; + height: 0; + } + + &::-webkit-scrollbar { + width: 4px; + } } // .chat__messages @@ -39,6 +68,7 @@ // .chat__message-item &__message-item { + min-width: 40%; width: fit-content; border-bottom: solid 1px $colorGrey3Cool; @@ -52,6 +82,7 @@ // .chat__message-nickname &__message-nickname { display: flex; + column-gap: 10px; color: $colorGrey3Cool; } diff --git a/src/scss/variables.scss b/src/scss/variables.scss index 127df81..917d110 100644 --- a/src/scss/variables.scss +++ b/src/scss/variables.scss @@ -2,6 +2,8 @@ $fontUbuntu: 'Ubuntu'; $colorUnwhite: #FBFBFB; $colorGrey2Cool: #364347; $colorGrey3Cool: #9AA0A8; +$colorGrey4Cool: #D7E1E7; +$colorTextGrey: #5D6878; $colorBlackCool: #0F1113; $colorBlue1: #285FAA; $colorError: #C71938;