Готовая лабораторная 3

This commit is contained in:
Никита Потапов 2023-11-10 11:19:51 +04:00
parent 166c1acb42
commit 0f688cd350
8 changed files with 320 additions and 62 deletions

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,7 @@
<link rel="stylesheet" href="./static/css/post.css">
<link rel="stylesheet" href="./static/css/avatar.css">
<link rel="shortcut icon" href="./static/img/favicon.svg" type="image/svg+xml">
<script src="./static/js/feed.js" type="module"></script>
<title>Новости</title>
</head>
@ -72,8 +73,20 @@
</div>
<div class="wrapper d-flex justify-content-center">
<div class="center d-flex flex-column">
<div id="editorjs" class="border rounded-2 border-dark mb-2 p-2"></div>
<button class="btn btn-primary mb-2">Опубликовать</button>
<textarea placeholder="Что нового?" id="post-editor" class="border rounded-2 border-dark mb-2 p-2"></textarea>
<div id="title-image-block" class="modal-critery">
<input id="check-title-image" type="checkbox" style="display: none;">
<input id="input-title-image" name="titleImage" accept="image/*" type="file" onchange="showTitleImagePreview(event);" />
<label id="title-image-preview" for="input-title-image" title="Добавить изображение" class="rounded-2 mb-2">
<i id="empty-title-image" class="bi bi-camera"></i>
<img id="selected-title-image" class="rounded-2">
</label>
</div>
<button class="btn btn-primary mb-2" id="post-publication-button">Опубликовать</button>
<div class="mb-2 w-100" id="edit-block">
<button class="btn btn-danger me-2 w-100" id="edit-post-button-cancel">Отмена</button>
<button class="btn btn-success w-100" id="edit-post-button-accept">Применить</button>
</div>
<div class="posts-wrapper">
<div class="post mb-2 mb-sm-4 w-100 d-flex flex-column border rounded-2 border-dark">
<div
@ -486,6 +499,29 @@
</footer>
</body>
<script>document.getElementById('current-year').innerHTML = new Date().getFullYear();</script>
<script src="./static/js/feed.js" type="module"></script>
<script>
function showTitleImagePreview(event) {
var previewEmpty = document.getElementById("empty-title-image");
var previewSelected = document.getElementById("selected-title-image");
var check = document.getElementById("check-title-image");
var preview = document.getElementById('title-image-preview');
if (check != null) check.checked = true;
if (event.target.files.length > 0) {
var src = URL.createObjectURL(event.target.files[0]);
previewEmpty.style.display = "none";
previewSelected.style.display = "block";
previewSelected.style.opacity = "1.0";
previewSelected.src = src;
preview.title = "Редактировать изображение";
} else {
previewEmpty.style.display = "block";
previewSelected.style.display = "none";
previewSelected.style.opacity = "1.0";
previewSelected.src = "";
preview.title = "Добавить изображение";
}
}
</script>
</html>

View File

@ -77,3 +77,10 @@ header {
--bs-btn-hover-border-color: #235D70;
--bs-btn-active-bg: #235D70;
}
.dropdown:hover .dropdown-menu {
display: block;
margin-top: 0;
right: 0px;
/* remove the gap so it doesn't close */
}

View File

@ -31,7 +31,40 @@
}
}
#editorjs {
z-index: 9999;
min-height: 100px;
#post-editor {
min-height: calc(1em + 26px);
}
#title-image-preview {
width: 100%;
min-height: 100px;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-size: 50px;
cursor: pointer;
border: solid;
border-width: 1px;
}
#title-image-preview i {
opacity: 0.3;
transition: opacity .3s;
}
#title-image-preview:hover i {
opacity: 1.0;
}
#input-title-image {
display: none;
}
#selected-title-image {
width: 100%;
display: none;
}
#title-image-block img {
}

View File

@ -28,3 +28,7 @@
.post-body a:hover {
background-size: 100% 2px;
}
.post-body-text {
white-space: pre-wrap;
}

View File

@ -1,49 +1,199 @@
import { ApiEndpoint } from "./apiendpoint";
import { ApiEndpoint, createPostObject } from "./apiendpoint";
import { generatePostHtml } from "./posts";
import EditorJS from "@editorjs/editorjs";
import Header from "@editorjs/header";
import List from "@editorjs/list";
import Link from "@editorjs/link";
import Quote from "@editorjs/quote";
import SimpleImage from "@editorjs/simple-image"
import Embed from "@editorjs/embed";
const editor = new EditorJS({
/**
* Id of Element that should contain the Editor
*/
holder: "editorjs",
/**
* Available Tools list.
* Pass Tool's class or Settings object for each Tool you want to use
*/
tools: {
header: Header,
list: List,
quote: Quote,
link: Link,
image: SimpleImage,
embed: Embed
},
});
const postApiEndpoint = new ApiEndpoint("posts");
const userApiEndpoint = new ApiEndpoint("users");
document.addEventListener("DOMContentLoaded", loadPosts);
// функция для получения содержимого файла в виде base64 строки
// https://ru.wikipedia.org/wiki/Base64
async function readImageFileToBase64(file) {
const reader = new FileReader();
// создание Promise-объекта для использования функции
// с помощью await (асинхронно) без коллбэков (callback)
// https://learn.javascript.ru/promise
return new Promise((resolve, reject) => {
// 2. "Возвращаем" содержимое когда файл прочитан
// через вызов resolve
// Если не использовать Promise, то всю работу по взаимодействию
// с REST API пришлось бы делать в обработчике (callback) функции
// onloadend
reader.onloadend = () => {
const fileContent = reader.result;
// Здесь могла бы быть работа с REST API
// Чтение заканчивает выполняться здесь
resolve(fileContent);
};
// 3. Возвращаем ошибку
reader.onerror = () => {
// Или здесь в случае ошибки
reject(new Error("oops, something went wrong with the file reader."));
};
// Шаг 1. Сначала читаем файл
// Чтение начинает выполняться здесь
reader.readAsDataURL(file);
});
}
let currentEditPostId = null;
async function buttonPostPublicationClicked() {
let text = document.getElementById("post-editor").value.trim();
const img = document.getElementById("input-title-image").files[0];
if (text == "" && img == null) {
return;
}
let imgBase64 = null;
if (img != null) {
imgBase64 = await readImageFileToBase64(img);
}
if (text == "")
{
text = null;
}
var post = createPostObject(1, new Date(), text, imgBase64);
await postApiEndpoint.createObject(post);
location.reload();
}
async function postObjectEdit(e) {
let text = document.getElementById("post-editor").value.trim();
const img = document.getElementById("input-title-image").files[0];
var check = document.getElementById("check-title-image").checked;
if (text == "" && img == null && check) {
return;
}
let imgBase64 = null;
if (img != null) {
imgBase64 = await readImageFileToBase64(img);
}
if (text == "") {
text = null;
}
var post = await postApiEndpoint.getObject(currentEditPostId);
post.text = text;
if (check)
{
post.img = imgBase64;
}
await postApiEndpoint.updateObject(post);
location.reload();
}
async function editPost(e) {
const btnId = e.target.id;
const postId = parseInt(btnId.split("-")[2]);
let postObj = await postApiEndpoint.getObject(postId);
document.getElementById("post-editor").value = postObj.text == null ? "" : postObj.text;
var textElement = document.getElementById("post-editor");
textElement.style.height = "auto";
textElement.style.height = textElement.scrollHeight + 2 + "px";
showImage(postObj.img);
scrollTo(0, 0);
document.getElementById("edit-block").style.display = 'flex';
document.getElementById("post-publication-button").style.display = 'none';
currentEditPostId = postId;
}
async function loadPosts() {
document.getElementById("edit-block").style.display = "none";
document.getElementById("edit-post-button-cancel").addEventListener('click', () => {location.reload();});
document.getElementById("edit-post-button-accept").addEventListener('click', postObjectEdit);
const posts = await postApiEndpoint.getObjects();
const center = document.getElementsByClassName("posts-wrapper")[0];
center.innerHTML = "";
for (let i = 0; i < posts.length; i++) {
const post = posts[i];
const postOwner = await userApiEndpoint.getObject(post.userId);
for (let j = 0; j < 3; j++)
center.innerHTML += generatePostHtml(post, postOwner);
center.innerHTML += generatePostHtml(post, postOwner);
}
const postDeleteButtons =
document.getElementsByClassName("post-delete-button");
for (let i = 0; i < postDeleteButtons.length; i++) {
const btn = postDeleteButtons[i];
btn.addEventListener('click', (e) => {
const btnId = e.target.id;
const postId = parseInt(btnId.split("-")[2]);
postApiEndpoint.deleteObject(postId);
var postDiv = document.getElementById("post-" + postId);
postDiv.parentNode.removeChild(postDiv);
});
}
const postEditButtons =
document.getElementsByClassName("post-edit-button");
for (let i = 0; i < postEditButtons.length; i++) {
const btn = postEditButtons[i];
btn.addEventListener("click", editPost);
}
var text = document.getElementById("post-editor");
function resize() {
if (text.value == "") {
text.style.height = "calc(1em + 26px)";
return;
}
text.style.height = "auto";
text.style.height = text.scrollHeight + 2 + "px";
}
/* 0-timeout to get the already changed text */
function delayedResize() {
window.setTimeout(resize, 0);
}
observe(text, "change", resize);
observe(text, "cut", delayedResize);
observe(text, "paste", delayedResize);
observe(text, "drop", delayedResize);
observe(text, "keydown", delayedResize);
text.focus();
text.select();
resize();
document.getElementById("post-publication-button").addEventListener('click', buttonPostPublicationClicked);
}
var observe;
if (window.attachEvent) {
observe = function (element, event, handler) {
element.attachEvent("on" + event, handler);
};
} else {
observe = function (element, event, handler) {
element.addEventListener(event, handler, false);
};
}
function showImage(img) {
var previewEmpty = document.getElementById("empty-title-image");
var previewSelected = document.getElementById("selected-title-image");
var check = document.getElementById("check-title-image");
var preview = document.getElementById("title-image-preview");
// if (check != null) check.checked = true;
if (img != null) {
var src = img;
previewEmpty.style.display = "none";
previewSelected.style.display = "block";
previewSelected.style.opacity = "1.0";
previewSelected.src = src;
preview.title = "Редактировать изображение";
} else {
previewEmpty.style.display = "block";
previewSelected.style.display = "none";
previewSelected.style.opacity = "1.0";
previewSelected.src = "";
preview.title = "Добавить изображение";
}
}

View File

@ -10,10 +10,14 @@ export function generatePostHtml(postObject, userObject) {
};
const postCreatedDateTime = new Date(postObject.createdDateTime);
const html = `<div class="post mb-2 mb-sm-4 w-100 d-flex flex-column border rounded-2 border-dark">
const html = `<div class="post mb-2 mb-sm-4 w-100 d-flex flex-column border rounded-2 border-dark" id="post-${
postObject.id
}">
<div class="post-header py-1 px-2 d-flex border-bottom border-dark justify-content-between align-items-center">
<a class="d-flex justify-content-start align-items-center">
<img src="./static/img/${userObject.avatarImg}" class="post-author-avatar avatar-small rounded-circle">
<img src="./static/img/${
userObject.avatarImg
}" class="post-author-avatar avatar-small rounded-circle">
<div class="post-meta ms-2 d-flex flex-column justify-content-center">
<div class="post-author-name">
${userObject.firstName} ${userObject.lastName}
@ -24,7 +28,17 @@ export function generatePostHtml(postObject, userObject) {
</div>
</a>
<a>
<i class="bi bi-three-dots fs-4"></i>
<div class="dropdown">
<i class="bi bi-three-dots fs-4" data-bs-toggle="dropdown" aria-expanded="false"></i>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item post-delete-button" id="delete-post-${
postObject.id
}"><i class="bi bi-trash3"></i> Удалить</a></li>
<li><a class="dropdown-item post-edit-button" id="edit-post-${
postObject.id
}"><i class="bi bi-pencil"></i> Редактировать</a></li>
</ul>
</div>
</a>
</div>
<div class="post-body">