This commit is contained in:
UNIKNOW 2024-03-02 15:11:13 +04:00
parent 7ddd8485b2
commit 61ef56f308
23 changed files with 5316 additions and 56 deletions

View file

@ -3,6 +3,13 @@
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
"overrides": [
{
"files": "*.svelte",
"options": {
"parser": "svelte"
}
}
]
}

2499
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -3,6 +3,7 @@
"version": "0.0.1",
"private": true,
"scripts": {
"build:css": "postcss styles.css -o public/build/tailwind.css",
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
@ -13,12 +14,31 @@
"@sveltejs/adapter-auto": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
"@types/node": "^20.11.16",
"@types/websocket": "^1.0.10",
"autoprefixer": "^10.4.17",
"flowbite": "^2.2.1",
"flowbite-svelte": "^0.44.22",
"postcss": "^8.4.35",
"postcss-load-config": "^5.0.2",
"prettier": "^3.1.1",
"prettier-plugin-svelte": "^3.1.2",
"prettier-plugin-tailwindcss": "^0.5.9",
"stylus": "^0.55.0",
"svelte": "^4.2.7",
"svelte": "^4.2.10",
"svelte-preprocess": "^5.1.3",
"svelte-time": "^0.8.2",
"tailwindcss": "^3.4.1",
"vite": "^5.0.3"
},
"type": "module"
"type": "module",
"dependencies": {
"@sveltestrap/sveltestrap": "^6.2.4",
"@themesberg/flowbite": "^1.3.0",
"sass": "^1.70.0",
"socket.io": "^4.7.4",
"socket.io-client": "^4.7.4",
"websocket": "^1.0.34",
"ws": "^8.16.0"
}
}

2244
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load diff

13
postcss.config.cjs Normal file
View file

@ -0,0 +1,13 @@
const tailwindcss = require('tailwindcss');
const autoprefixer = require('autoprefixer');
const config = {
plugins: [
//Some plugins, like tailwindcss/nesting, need to run before Tailwind,
tailwindcss(),
//But others, like autoprefixer, need to run after,
autoprefixer
]
};
module.exports = config;

6
postcss.config.js Normal file
View file

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

3
src/app.css Normal file
View file

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View file

@ -8,5 +8,12 @@
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
<style>
html,body{
margin: 0;
padding: 0;
}
</style>
</body>
</html>

4
src/app.pcss Normal file
View file

@ -0,0 +1,4 @@
/* Write your global styles here, in PostCSS syntax */
@tailwind base;
@tailwind components;
@tailwind utilities;

73
src/lib/userFunction.js Normal file
View file

@ -0,0 +1,73 @@
export async function UserCheck(){
const response = await fetch('http://localhost:8000/api/users/me', {
method: 'GET',
credentials:'include'
})
if(!response.ok)
{
console.log(response.status)
}
if(response.ok)
{
const data = await response.json();
return data
}
}
export async function getLastMessages(){
const response = await fetch('http://localhost:8000/api/chat/get_some_messages/2?messages_loaded=0&messages_to_get=14',
{
method:'GET',
credentials:'include'
})
if(!response.ok)
{
console.log(response.status)
}
if(response.ok)
{
let msgMassive = await response.json();
let localTime
for(let i = 0; i< msgMassive.length;i++){
localTime = new Date(msgMassive[i].created_at)
msgMassive[i].created_at = localTime
}
console.log(msgMassive)
return msgMassive
}
}
export async function MessagePicToUrl(messagePic){
const DataForm = new FormData();
DataForm.append('file', messagePic)
const respone = await fetch('http://localhost:8000/api/images/upload_image',
{
method:"POST",
headers:{
'Accept': 'application/json'
},
body:DataForm
})
if(!respone.ok)
console.log("ошибка", respone.status)
if(respone.ok){
const data = await respone.json();
console.log("картинка принята")
return data.image_url;
}
}

11
src/lib/websocket.js Normal file
View file

@ -0,0 +1,11 @@
export default function createWebSocket(url, onMessageCallback) {
const socket = new WebSocket(url);
socket.addEventListener('message', (event) => {
const jsonData = JSON.parse(event.data);
onMessageCallback(jsonData);
//console.log(jsonData)
});
return socket;
}

View file

@ -1,3 +0,0 @@
@import '+page.svelte'
body
background-color: blue;

View file

@ -1,10 +1,80 @@
<script>
import { onMount } from 'svelte';
import Header from './Header.svelte';
let username = "";
let password = "";
async function handleLogin() {
const response = await fetch('http://localhost:8000/users/login', {
method: 'POST',
credentials:'include',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify
({
email_or_username: username,
password: password,
})
})
.catch(function (err)
{
console.log('Ошибка:', err);
})
// В этом месте вы можете обработать успешный вход в систему, например, перенаправить пользователя на другую страницу
console.log('лох залогинен!');
}
async function handleLogout() {
try{
const response = await fetch('http://localhost:8000/users/logout',
{
method:'POST',
credentials:'include'
})
if (response.ok)
{
console.log("ты вышел, лох");
} else
{
console.error('Не вышел, лошара');
}
} catch (error)
{
console.error('Ошибка при выполнении выхода:', error.message);
}
}
</script>
<Header />
<body>
<form on:submit|preventDefault={handleLogin}>
<label for="username">Username:</label>
<input type="text" id="username" bind:value={username} />
<label for="password">Password:</label>
<input type="password" id="password" bind:value={password} />
<button type="submit">Login</button>
</form>
<button on:click={handleLogout}>Выход</button>
<style>
*{
font-weight: 600;
font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
}
</body>
</style>

View file

@ -1,12 +1,142 @@
<header>
<p>
ну и что, что я вор?
</p>
<script>
import { onMount } from 'svelte';
import {UserCheck} from '$lib/userFunction';
let Nickname
let userImage
onMount(async () => {
const UserData = await UserCheck();
Nickname = UserData.username;
userImage = UserData.avatar_image;
console.log(userImage)
})
let isOpen = false;
let items1 = ['Профиль', 'Настройки'];
let items2 = ['Выйти'];
</script>
<header class="headerClass">
<div class="divsiteAvatar">
<img class="siteAvatar" src="./BP-NEON.png" alt="лого">
<p class="siteAvatarBP">Black Phoenix</p>
</div>
<div class="divUser">
<button class="buttonUser" on:click={() => isOpen = !isOpen}>
<h3 class="name">{Nickname}</h3>
<img class="userAvatar" src="{userImage}" alt="аватарка">
</button>
</div>
{#if isOpen}
<div class="dropdown">
{#each items1 as item (item)}
<div class="listItem">{item}</div>
{/each}
<div class="ListLine"></div>
{#each items2 as item (item)}
<div class="listItem">{item}</div>
{/each}
</div>
{/if}
</header>
<style>
p{
color: blueviolet;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
.buttonUser{
width: 100%;
height: 100%;
border-radius: 0px;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
display: flex;
justify-content:space-around;
align-items: center;
}
.ListLine{
border: 1px solid lightgray;
margin-top: 3px;
}
.dropdown {
cursor: pointer;
position: absolute;
right: 5vw;
top: 10vh;
width: 200px;
}
.listItem {
cursor: pointer;
}
div,h3{
font-weight: 600;
font-family:'Segoe UI', Tahoma, Geneva, Verdana, sans-serif !important;
}
.headerClass > * {
margin-bottom: 0;
}
.headerClass{
list-style: none;
display: grid;
grid-template-columns: 5vw 200px repeat(5, auto) 200px 5vw;
grid-template-rows: 9vh;
gap: 0;
}
.siteAvatar{
margin: 0;
padding: 0;
height: 100%;
object-fit: cover;
}
.siteAvatarBP{
display: flex;
align-items: center;
font-size: 20px;
}
.name{
}
.divsiteAvatar{
grid-column: 2;
display: flex;
justify-content:space-evenly;
align-items: center;
border: 1px solid black;
}
.divUser{
grid-column: 8;
display: flex;
align-items: center;
justify-content:space-around;
width: 100%;
height: 100%;
}
.userAvatar{
justify-content: right;
width: 70px;
border-radius: 50%;
max-width: 100%;
max-height: 100%;
display: flex;
align-items: center;
}
</style>

View file

@ -0,0 +1,5 @@
<script>
import Header from '../Header.svelte';
</script>
<Header />

View file

@ -0,0 +1,220 @@
<script>
import Header from '../Header.svelte';
import { onMount } from 'svelte';
import Time from "svelte-time";
import { dayjs } from "svelte-time";
import 'dayjs/locale/ru';
import {UserCheck,getLastMessages,MessagePicToUrl} from '$lib/userFunction.js';
import createWebSocket from '$lib/websocket';
dayjs.locale("ru");
console.clear();
let userId
let socket
let messages = [];
let messageText = '';
let messagePic;
onMount(async() => {
const userData = await UserCheck();
let userId = userData.id;
let chatId = 2
const websocketUrl = `ws://localhost:8000/api/chat/ws/${chatId}?user_id=${userId}`;
messages = await getLastMessages();
socket = createWebSocket(websocketUrl, (message) => {
messages = [message, ...messages]
});
});
function sendMessage() {
let messageUrl = MessagePicToUrl(messagePic);
console.log(messageUrl)
if (messageText.trim() !== '') {
socket.send(JSON.stringify({'message':messageText,"image_url": messageUrl}));
messageText = '';
}}
let textarea;
const updateTextareaSize = () => {
// Устанавливаем высоту textarea на основе количества строк
textarea.style.height = 'auto'; // Сбрасываем высоту на auto перед измерением
textarea.style.height = `${textarea.scrollHeight}px`;
console.log(textarea.style.height)
};
function handleKeyPress(event) {
if (event.key === 'Enter' && !event.shiftKey) {
// Если нажат Enter и не нажат Shift, отправляем сообщение
sendMessage(event);
}
}
</script>
<body class="backgroundPic">
<div class="DivHeader"><Header/></div>
<div>жопа2</div>
<div class="divChat">
<div class="chatBox">
{#each messages as message}
<div class="MsgAll">
<img class="MsgAva" src="{message.avatar_image}" alt="ава">
<div class="divMessage">
<div class="userFiled">
<h3 class="MsgName">{message.username}</h3>
<Time relative timestamp="{message.created_at}" ></Time>
</div>
<p class="MsgMsg">{message.message}</p>
{#if message.image_url != null}
<img class="MsgPic" src="{message.image_url}" alt="">
{/if}
</div>
</div>
{/each}
</div>
<form class="chatSend" on:submit={sendMessage}>
<textarea
placeholder="Введите сообщение" maxlength="2000" class="chatInput"
rows="1" bind:this={textarea} on:input={updateTextareaSize}
type="text" bind:value={messageText} on:keypress={handleKeyPress}
></textarea>
<input type="file" accept="image/*" bind:value={messagePic}>
</form>
</div>
<div>жопа4</div>
<div>жопа5</div>
<div>жопа6</div>
<div>жопа7</div>
</body>
<style>
.userFiled{
display: flex;
justify-content:flex-start;
}
.MsgName{
display: flex;
}
.MsgPic{
margin-left: 1vw;
margin-bottom: 1vh;
width: 80%;
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 15px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.9);
}
.MsgAva{
height: 6vh;
min-height: 50px;
width: auto;
border: 2px solid rgba(255, 255, 255, 0.6);
border-radius: 10px;
margin: 0.25vw;
}
.MsgAll{
display: flex;
flex: 90;
}
.divMessage{
background-color:rgba(255, 255, 255, 0.3);
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 15px;
box-shadow: 0 0px 30px rgba(0, 0, 0,0.2);
margin: 3px;
transform:translate(90deg);
}
.chatBox{
display: flex;
flex: 30;
flex-direction:column-reverse;
align-items:flex-start;
overflow-y: auto;
width: 100%;
/* From https://css.glass */
background-color: rgba(255, 255, 255, 0.20);
border-radius: 15px;
box-shadow: 0 4px 30px rgb(0,0,0,0.5);
backdrop-filter: blur(7px);
border: 1px solid rgba(255,255,255,0.43)
}
.DivHeader{
grid-column: 3 span;
border: 0!important;
margin: 0;
padding: 0;
}
.chatSend{
display: flex;
flex: 2;
width: 100%;
justify-content: space-between;
}
.chatInput{
width: 100%;
resize: none;
box-sizing: border-box;
}
.divChat{
flex-direction: column;
display: flex;
}
body{
display: grid;
grid-template-columns: 25vw 50vw 25vw; /* 2 колонки с равным распределением ширины */
grid-template-rows: 10vh 80vh 10vh;
}
body > *{
border: 1px solid black;
}
*{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
.backgroundPic{
grid-column: span 3;
grid-row: span 3;
background-color: #563E89;
background-image: url(./BPytka.png);
background-repeat: no-repeat;
background-size:50vh;
background-position-x:50%;
background-position-y:50%;
}
</style>

BIN
static/BP-NEON.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

BIN
static/BPytka.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 940 KiB

BIN
static/image/1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
static/logo.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

View file

@ -1,3 +1,4 @@
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
import adapter from '@sveltejs/adapter-auto';
/** @type {import('@sveltejs/kit').Config} */
@ -7,8 +8,9 @@ const config = {
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter()
}
},
preprocess: [vitePreprocess({})]
};
export default config;

12
tailwind.config.cjs Normal file
View file

@ -0,0 +1,12 @@
/** @type {import('tailwindcss').Config}*/
const config = {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {}
},
plugins: []
};
module.exports = config;

9
tailwind.config.js Normal file
View file

@ -0,0 +1,9 @@
/** @type {import('tailwindcss').Config} */
export default {
content: ['./src/**/*.{html,js,svelte,ts}'],
theme: {
extend: {},
},
plugins: [],
}