отправка сообщений с картинками

This commit is contained in:
uniknow 2024-05-23 10:54:26 +04:00
parent 36f113187a
commit 44c149c60f
7 changed files with 348 additions and 123 deletions

View file

@ -1,9 +0,0 @@
@font-face {
font-family: 'Comfortaa';
src: url('./fonts/Comfortaa-VariableFont_wght.ttf') format('truetype');
font-weight: 1 1000; /* Объявляем диапазон веса шрифта */
font-style: normal;
font-stretch: normal;
}

View file

@ -14,6 +14,14 @@
<style lang="scss"> <style lang="scss">
@font-face {
font-family: 'Comfortaa';
src: url('./fonts/Comfortaa-VariableFont_wght.ttf') format('truetype');
font-weight: 1 1000; /* Объявляем диапазон веса шрифта */
font-style: normal;
font-stretch: normal;
}
:root{ :root{
--bg: #101010; --bg: #101010;
--gradient: linear-gradient(to bottom, #3C53FF, #7734AA); --gradient: linear-gradient(to bottom, #3C53FF, #7734AA);
@ -34,7 +42,7 @@
background-color: var(--bg); background-color: var(--bg);
} }
* { *{
font-family: Comfortaa; font-family: Comfortaa;
margin: 0; margin: 0;
} }
@ -98,8 +106,11 @@
font-size: 20px; font-size: 20px;
font-weight: 100; font-weight: 100;
} }
textarea{
font-size: 20px;
}
.noise { .noiseGif {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -108,7 +119,7 @@
border-radius: 15px; border-radius: 15px;
} }
.noise::before { .noiseGif::before {
content: ''; content: '';
background-image: url(../noise.gif); background-image: url(../noise.gif);
opacity: 0.011; opacity: 0.011;
@ -122,5 +133,28 @@
border-radius: 15px; border-radius: 15px;
} }
.noisePic {
position: relative;
width: 100%;
height: 100%;
display: flex;
box-shadow: 4px 4px 11px rgba(#000000, 0.4);
border-radius: 15px;
}
.noisePic::before {
content: '';
background-image: url(../noise.png);
opacity: 0.811;
width: 100%;
height: 100%;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
border-radius: 15px;
}
</style> </style>
</html> </html>

View file

@ -6,11 +6,6 @@
credentials:'include' credentials:'include'
}) })
if(!response.ok)
{
console.log(response.status)
}
if(response.ok) if(response.ok)
{ {
let msgMassive = await response.json(); let msgMassive = await response.json();
@ -23,15 +18,26 @@
let answer = await getMessageById(chatId,msgMassive[i].answer_id) let answer = await getMessageById(chatId,msgMassive[i].answer_id)
msgMassive[i].answerMessage = answer.message msgMassive[i].answerMessage = answer.message
console.log(answer.message) //console.log(answer.message)
} }
} }
msgMassive.reverse(); msgMassive.reverse();
return msgMassive return msgMassive
} }
else if(response.status === 404){
let msg = [];
return msg
} }
else if(!response.ok)
console.log(response.status)
}
export async function getMessageById(chatId,msgId){ export async function getMessageById(chatId,msgId){
const response = await fetch(`http://localhost:8000/api/chat/message/${chatId}?message_id=${msgId}`,{ const response = await fetch(`http://localhost:8000/api/chat/message/${chatId}?message_id=${msgId}`,{
@ -44,7 +50,7 @@
return data; return data;
} }
else{ else{
console.log(response.status) return { message: "Сообщение не найдено" };
} }
} }
@ -103,3 +109,32 @@
console.log(response.status) console.log(response.status)
} }
} }
export async function pinMessage(chatId,messageId){
const response = await fetch(`http://localhost:8000/api/chat/pinn_message?chat_id=${chatId}&message_id=${messageId}`,{
method:"POST",
credentials:'include'
})
if(response.ok){
return await response.json();
}
else
console.log(response.status)
}
export async function unpinMessage(chatId, messageId){
const response = await fetch(`http://localhost:8000/api/chat/unpinn_message?chat_id=${chatId}&message_id=${messageId}`,{
method:"DELETE",
credentials:'include'
})
if(response.ok){
return await response.json();
}
else
console.log(response.status)
}

View file

@ -16,7 +16,7 @@ export async function handleLogin(username, password) {
if(response.status === 200) if(response.status === 200)
{ {
window.location.href = '/chatPage' window.location.href = '/chat'
return "" return ""
} }
else if(response.status === 401) else if(response.status === 401)

View file

@ -19,7 +19,6 @@
function dropDown() { function dropDown() {
isOpen = !isOpen; isOpen = !isOpen;
} }
</script> </script>
<header> <header>
@ -29,17 +28,19 @@
</div> </div>
<div class="userUser"> <div class="userUser">
<button class="buttonUser" <button
class="buttonUser"
style:border-bottom-right-radius={isOpen ? '0px' : '15px'} style:border-bottom-right-radius={isOpen ? '0px' : '15px'}
style:border-bottom-left-radius= {isOpen ? '0px' : '15px'} style:border-bottom-left-radius={isOpen ? '0px' : '15px'}
on:click={dropDown}> on:click={dropDown}
>
<h3>{Nickname}</h3> <h3>{Nickname}</h3>
<div><img class="userAvatar" src={userImage} alt="аватарка"/></div> <div><img class="userAvatar" src={userImage} alt="аватарка" /></div>
</button> </button>
{#if isOpen} {#if isOpen}
<div class="dropdown"> <div class="dropdown">
<button class="listItem" >Профиль</button> <button class="listItem">Профиль</button>
<button class="listItem">Настройки</button> <button class="listItem">Настройки</button>
<button on:click={handleLogout} class="listItem">Выйти</button> <button on:click={handleLogout} class="listItem">Выйти</button>
</div> </div>
@ -48,9 +49,8 @@
</header> </header>
<style lang="scss"> <style lang="scss">
.buttonUser, .buttonUser,
.dropdown{ .dropdown {
border: 1px solid transparent; border: 1px solid transparent;
background: background:
linear-gradient(#101010, #101010) padding-box, linear-gradient(#101010, #101010) padding-box,
@ -96,7 +96,7 @@
} }
.dropdown { .dropdown {
width: 230px; width: 228px;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: flex-start; align-items: flex-start;
@ -111,17 +111,14 @@
border-radius: 50%; border-radius: 50%;
} }
.dropdown > * {
.dropdown > *{
cursor: pointer; cursor: pointer;
} }
.listItem{ .listItem {
font-size: 16px; font-size: 16px;
} }
.avatarDiv { .avatarDiv {
width: 230px; width: 230px;
height: 100%; height: 100%;

View file

@ -1,5 +1,5 @@
<script> <script>
import { getLastMessages, MessagePicToUrl, getAllChats, getPinnedMsg, getMessageById } from '$lib/chat'; import { getLastMessages, MessagePicToUrl, getAllChats, getPinnedMsg, getMessageById, pinMessage, unpinMessage } from '$lib/chat';
import { UserCheck } from '$lib/userFunction' import { UserCheck } from '$lib/userFunction'
import Header from '../Header.svelte'; import Header from '../Header.svelte';
import createWebSocket from '$lib/websocket'; import createWebSocket from '$lib/websocket';
@ -8,6 +8,8 @@
import { dayjs } from 'svelte-time'; import { dayjs } from 'svelte-time';
import 'dayjs/locale/ru'; import 'dayjs/locale/ru';
import { fade } from 'svelte/transition'; import { fade } from 'svelte/transition';
import {page} from '$app/stores';
import { writable } from 'svelte/store';
dayjs.locale('ru'); dayjs.locale('ru');
console.clear(); console.clear();
@ -18,69 +20,80 @@
let messages = [] //массив сообщений, которые отображаются на сайте let messages = [] //массив сообщений, которые отображаются на сайте
let msgLoaded = 0 let msgLoaded = 0
let messageText = '' //сообщения для отправки let messageText = '' //сообщения для отправки
let selectedFile // картинка для отправки
let image_url = null let image_url = null
let messagePic //картинка для отправки let messagePic //картинка для отправки
let flag = "send" let flag = "send"
let answer = null let answer = null
let chats = [] //массив чатов, доступных юзеру let chats = [] //массив чатов, доступных юзеру
let pinnedMsg = [] let pinnedMsg = []
let chatId = null
onMount(async () => { onMount(async () => {
const userData = await UserCheck(); const userData = await UserCheck();
let userId = userData.id; userId = userData.id;
let chatId = 1;
if(userId === undefined) if(userId === undefined)
window.location.href = '/login' window.location.href = '/login'
const websocketUrl = `ws://localhost:8000/api/chat/ws/${chatId}?user_id=${userId}`;
pinnedMsg = await getPinnedMsg(chatId)
chats = await getAllChats() chats = await getAllChats()
messages = await getLastMessages(chatId,msgLoaded); console.log(chats)
async function handleChatUrlChange() {
// Проверяем, что текущий хэш соответствует шаблону для чата
if (window.location.hash.startsWith('#id=')) {
chatId = window.location.hash.slice(4);
console.log(chatId, " chatId")
const websocketUrl = `ws://localhost:8000/api/chat/ws/${chatId}?user_id=${userId}`
console.log(messages) console.log(messages)
messages = await getLastMessages(chatId,msgLoaded)
pinnedMsg = await getPinnedMsg(chatId)
console.log(pinnedMsg, " pinned")
console.log(messages, " сообщения")
socket = createWebSocket(websocketUrl, async (message) => { socket = createWebSocket(websocketUrl, async (message) => {
if(message.answer_id != null){ if(message.answer_id != null){
let answer = await getMessageById(chatId,message.answer_id) let answer = await getMessageById(chatId,message.answer_id)
message.answerMessage = answer.message message.answerMessage = answer.message
console.log(answer.message) //console.log(answer.message)
} }
messages = [...messages, message]; if(message.flag === "delete"){
}); let messageToDelete = messages.findIndex(msg => msg.id === message.id)
console.log(messageToDelete, "индекс сообщения, которое нужно делитнуть")
}); messages.splice(messageToDelete,1)
messages = messages
async function uploadImages() { console.log(messages," сообщения после удаления")
const fileInput = document.getElementById('file');
if(fileInput === null){
return null;
} }
else{ else{
const file = fileInput.files[0];
let urlpic; messages = [...messages, message]
if (file) {
urlpic = await MessagePicToUrl(file);
} else {
urlpic = null;
} }
fileInput.value = null; console.log(message, "пришло сообщение")
return urlpic; })
} }
} }
handleChatUrlChange();
window.addEventListener('hashchange', handleChatUrlChange);
});
async function sendMessage() { async function sendMessage() {
if(messageText != ""){
socket.send(JSON.stringify({flag: flag, socket.send(JSON.stringify({flag: flag,
message: messageText, message: messageText,
image_url: image_url, image_url: image_url,
answer: answer })); answer: answer }));
messageText = "" messageText = ""
imageFile = undefined
imageShow = false
image_url = null
answer = null
}
} }
@ -150,12 +163,65 @@ function helperDivShow(event, id) {
answer = pickedId answer = pickedId
} }
function pinFunc(event){ async function pinFunc(event){
let findPinMsg
if(pinnedMsg != null){
findPinMsg = pinnedMsg.find(msg => msg.id === pickedId)
console.log(findPinMsg)
if(findPinMsg != undefined){
console.log("удаляю сообщение")
unpinMessage(chatId,pickedId)
pinnedMsg = await getPinnedMsg(chatId)
console.log(pinnedMsg)
} else {
console.log("добавляю сообщение, потому что такого еще не было")
await pinMessage(chatId,pickedId)
pinnedMsg = await getPinnedMsg(chatId)
}} else {
console.log("добавляю сообщение, потому что сообщений нет")
await pinMessage(chatId,pickedId)
pinnedMsg = await getPinnedMsg(chatId)
console.log(pinmsg, " сообщения для закрепа", pinnedMsg, "массив закреп сообщений")
}}
function delFunc(event){
socket.send(JSON.stringify({"flag": "delete",
"user_id": userId,
"id": pickedId
}));
} }
function delFunc(event){ let imageFile = undefined
let imageShow = false;
$: console.log(imageFile, "картинка")
function triggerFileInput() {
imageFile.click();
}
function handleFileChange(event) {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
imageFile = e.target.result;
imageShow = true;
image_url = imageFile
};
reader.readAsDataURL(file);
}
}
function deleteImage(){
imageFile = undefined;
imageShow = false;
image_url = null
} }
</script> </script>
@ -173,8 +239,10 @@ function helperDivShow(event, id) {
<div class="chatsDiv"> <div class="chatsDiv">
{#each chats as chat} {#each chats as chat}
<div class="chatDivLeft"> <div class="chatDivLeft">
<a class="aChatDivLeft" href={`/chat#id=${chat.chat_id}`}>
<img class="chatImage" src="{chat.avatar_image}" alt="аватарка"> <img class="chatImage" src="{chat.avatar_image}" alt="аватарка">
<h2>{chat.chat_name}</h2> <h2>{chat.chat_name}</h2>
</a>
</div> </div>
{/each} {/each}
</div> </div>
@ -183,9 +251,10 @@ function helperDivShow(event, id) {
<div> <div>
<div class="gradient"> <div class="gradient">
<div class="chatDiv"> <div class="chatDiv">
{#if (messages.length === 0)} {#if ((chatId != null) && (messages.length === 0))}
<div class="noMsgDiv"> <div class="noMsgDiv">
<h2 class="noMsg1">В этом чате еще нет сообщений.</h2> <h2 class="noMsg1">В этом чате еще нет сообщений.</h2>
<h3 class="noMsg2">Крыски, начните обсуждать <br> прям за ее спиной</h3> <h3 class="noMsg2">Крыски, начните обсуждать <br> прям за ее спиной</h3>
@ -211,7 +280,7 @@ function helperDivShow(event, id) {
<div class="messageMessage"> <div class="messageMessage">
<p>{message.message}</p> <p>{message.message}</p>
{#if message.image_url != null} {#if message.image_url != null}
<img src="{message.image_url}" alt="пикча"> <img src="{message.image_url}" alt="пикча" class="messageImage">
{/if} {/if}
</div> </div>
@ -219,26 +288,41 @@ function helperDivShow(event, id) {
</div> </div>
{/each} {/each}
</div> </div>
<!-- {#if showDiv} -->
<!-- svelte-ignore a11y-no-static-element-interactions --> <!-- svelte-ignore a11y-no-static-element-interactions -->
<div id="helperDiv" class="helperDiv" on:contextmenu={(event) => event.preventDefault()}> <div id="helperDiv" class="helperDiv" on:contextmenu={(event) => event.preventDefault()}>
<button on:click={ansFunc} class="helpDiv"><img class="helpImg" src="./icon/answer.png" alt="ans"><p class="ans">Ответить</p></button> <button on:click={ansFunc} class="helpDiv"><img class="helpImg" src="./icon/answer.png" alt="ans"><p class="ans">Ответить</p></button>
<button on:click={pinFunc} class="helpDiv"><img class="helpImg" src="./icon/pin.png" alt="pin"><p class="pin">Закрепить</p></button> <button on:click={pinFunc} class="helpDiv"><img class="helpImg" src="./icon/pin.png" alt="pin"><p class="pin">Закрепить</p></button>
<button on:click={delFunc} class="helpDiv"><img class="helpImg" src="./icon/trash.png" alt="del"><p class="del">Удалить</p></button> <button on:click={delFunc} class="helpDiv"><img class="helpImg" src="./icon/trash.png" alt="del"><p class="del">Удалить</p></button>
</div> </div>
<!-- {/if} -->
{#if (chatId != null)}
<!-- svelte-ignore a11y-no-noninteractive-element-interactions -->
<div class="inputDiv"> <div class="inputDiv">
{#if imageShow == true}
<div class="imageDiv">
<img src="{imageFile}" alt="" class="imageFile">
<button on:click={deleteImage} class="imageDelete">x</button>
</div>
{/if}
<!-- svelte-ignore a11y-autofocus --> <!-- svelte-ignore a11y-autofocus -->
<div class="textMsg">
<textarea on:keydown={onEnterPress} <textarea on:keydown={onEnterPress}
autofocus placeholder="Введите сообщение" rows="{rows}" bind:value={messageText} autofocus placeholder="Введите сообщение" rows="{rows}" bind:value={messageText}
on:input={onInput} name="msg" id="msg"></textarea> on:input={onInput} name="msg" id="msg"></textarea>
<img class="logoForImage" src="logoForImage.svg" alt=""> <!-- svelte-ignore a11y-click-events-have-key-events -->
<img class="logoForImage" src="logoForImage.svg" alt=""
on:click={() => document.getElementById('fileInput').click()}>
</div> </div>
<input type="file" accept=".png, .jpeg, .jpg, .gif" id="fileInput"
bind:this={imageFile} on:change={handleFileChange} style="display: none;">
</div>
{/if}
</div> </div>
</div> </div>
@ -250,12 +334,15 @@ function helperDivShow(event, id) {
<h3 class="noMsg2">Но если ты закрепишь сообщение, <br> тут что то изменится</h3> <h3 class="noMsg2">Но если ты закрепишь сообщение, <br> тут что то изменится</h3>
</div> </div>
{:else} {:else}
<div class="pinnedMain">
{#each pinnedMsg as msg} {#each pinnedMsg as msg}
<div class="mainPinned"> <div class="pinDiv">
<h3 class="msgPinned">{msg.message}</h3> <div class="pinImg"><img class="pinnedImg" src="{msg.avatar_image}" alt=""></div>
<img class="imgPinned" src="{msg.avatar_image}" alt=""> <div class="pinMsg"><h3 class="pinnedMsg">{msg.message}</h3></div>
</div> </div>
{/each} {/each}
</div>
{/if} {/if}
</div></div> </div></div>
@ -269,6 +356,76 @@ function helperDivShow(event, id) {
<style lang="scss"> <style lang="scss">
::-webkit-scrollbar-thumb:hover {
background: white; /* Цвет ползунка при наведении */
}
::-webkit-scrollbar-track {
background: #f1f1f1; /* Цвет трека */
border-radius: 6px; /* Радиус скругления */
}
::-webkit-scrollbar-thumb {
background-color: transparent; /* Цвет ползунка */
border-radius: 6px; /* Радиус скругления */
border: 3px solid #ffffff00; /* Цвет фона внутри ползунка */
}
.imageDiv{
position: relative;
padding-top: 5px;
}
.imageDelete{
position: absolute;
cursor: pointer;
}
.textMsg{
display: flex;
flex-direction: row;
}
.imageFile{
border-radius: 15px;
height: auto;
width: 250px;
}
.pinMsg{
width: 90%;
}
.pinImg{
width: 10%;
margin-right: 15px;
}
.pinnedImg{
width: 60px;
height: auto;
border-radius: 15px;
}
.pinnedMsg{
display: flex;
width: 90%;
}
.pinDiv{
display: flex;
width: 100%;
}
.pinnedMain{
display: flex;
flex-direction: column;
margin: 10px;
width: 100%;
height: 100%;
margin-bottom: 10px;
}
.nameAnswerDiv > p,img{ .nameAnswerDiv > p,img{
margin-left: 5px; margin-left: 5px;
} }
@ -296,6 +453,7 @@ function helperDivShow(event, id) {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
cursor: pointer;
} }
.helpImg{ .helpImg{
@ -303,6 +461,7 @@ function helperDivShow(event, id) {
width: 25px; width: 25px;
height: 25px; height: 25px;
margin-right: 5px; margin-right: 5px;
margin-left: -5px;
} }
.helperDiv{ .helperDiv{
@ -315,7 +474,6 @@ function helperDivShow(event, id) {
opacity: 0; opacity: 0;
border-bottom-left-radius: 15px; border-bottom-left-radius: 15px;
border-bottom-right-radius: 15px; border-bottom-right-radius: 15px;
border-bottom: 0;
border-top-right-radius: 15px; border-top-right-radius: 15px;
border-top-left-radius: 0; border-top-left-radius: 0;
@ -326,12 +484,6 @@ function helperDivShow(event, id) {
} }
.imgPinned{
height: 60px;
width: 60px;
border-radius: 15px;
}
.nameAnswerDiv{ .nameAnswerDiv{
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -346,21 +498,32 @@ function helperDivShow(event, id) {
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
} }
.aChatDivLeft{
display: flex;
flex-direction: row;
align-items: center;
/* Задаем блочный тип элемента */
width: 100%;
text-decoration: none;
}
.chatDivLeft{ .chatDivLeft{
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
width: 95%; width: 95%;
height: 75px; height: 75px;
margin-top: 10px; margin-top: 10px;
border-radius: 15px; border-radius: 15px;
border: 1px solid transparent; border: 1px solid transparent;
background: background:
linear-gradient(#101010, #101010) padding-box, linear-gradient(#101010, #101010) padding-box,
var(--gradient) border-box; var(--gradient) border-box;
} }
.chatImage{ .chatImage{
height: 60px; height: 60px;
width: 60px; width: 60px;
@ -378,6 +541,11 @@ function helperDivShow(event, id) {
//background-color: red; //background-color: red;
} }
.messageImage{
margin-top: 5pxS;
border-radius: 15px;
}
.messageMessage{ .messageMessage{
background-color: #7734AA; background-color: #7734AA;
border-radius: 15px; border-radius: 15px;
@ -406,11 +574,12 @@ padding-bottom: 10px;
margin-bottom: 10px; margin-bottom: 10px;
overflow: auto; overflow: auto;
//background-color: red; //background-color: red;
scroll-behavior: smooth;
} }
.inputDiv{ .inputDiv{
display: flex; display: flex;
flex-direction: row; flex-direction: column;
border-radius: 15px; border-radius: 15px;
border: 1px solid transparent; border: 1px solid transparent;
background: background:

View file

@ -50,7 +50,6 @@
let greeting = ' '; let greeting = ' ';
let greetings = [ let greetings = [
'Опять ты? Ну давай, заходи',
'Рад видеть тебя снова', 'Рад видеть тебя снова',
'Как прошел день?', 'Как прошел день?',
'Как настроение?', 'Как настроение?',
@ -73,7 +72,7 @@
<div class="loginField"> <div class="loginField">
<div class="loginMainDiv"> <div class="loginMainDiv">
<div class="loginDiv" transition:fly={{ y: -1920, duration: 1000, backInOut }}> <div class="loginDiv" transition:fly={{ y: -1920, duration: 1000, backInOut }}>
<div class="noise"> <div class="noiseGif">
<div class="marginDiv"> <div class="marginDiv">
<div class="greetingsDiv"> <div class="greetingsDiv">
@ -92,7 +91,7 @@
<div class="group"> <div class="group">
<h3>пароль</h3> <h3>пароль</h3>
<input placeholder=" " type="password" id="password" on:input={listener} bind:value={password} />z <input placeholder=" " type="password" id="password" on:input={listener} bind:value={password} />
</div> </div>
</div> </div>
@ -108,7 +107,7 @@
</div> </div>
</div> </div>
<div class="regDiv" transition:fly={{ y: -1920, duration: 800, backInOut }}> <div class="regDiv" transition:fly={{ y: -1920, duration: 800, backInOut }}>
<div class="noise"> <div class="noiseGif">
<div class="regregDiv"> <div class="regregDiv">
<h2 class="reg1">Нет аккаунта? Лох!</h2> <h2 class="reg1">Нет аккаунта? Лох!</h2>
<button class="reg2" on:click={() => (window.location.href = '/register')} <button class="reg2" on:click={() => (window.location.href = '/register')}