diff --git a/src/BukiVedi.App/BukiVedi.App.csproj b/src/BukiVedi.App/BukiVedi.App.csproj index e347a66..8f94c30 100644 --- a/src/BukiVedi.App/BukiVedi.App.csproj +++ b/src/BukiVedi.App/BukiVedi.App.csproj @@ -6,29 +6,6 @@ enable - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - @@ -40,6 +17,9 @@ Always + + Always + Always @@ -52,10 +32,31 @@ Always - + Always - + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + + Always + + Always @@ -74,60 +75,54 @@ Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest + Always - PreserveNewest - - Always - - PreserveNewest - - + Always Always - + Always @@ -136,9 +131,6 @@ Always - - PreserveNewest - Always @@ -160,6 +152,9 @@ Always + + Always + Always diff --git a/src/BukiVedi.App/Controllers/BooksController.cs b/src/BukiVedi.App/Controllers/BooksController.cs index 394e7bc..170249b 100644 --- a/src/BukiVedi.App/Controllers/BooksController.cs +++ b/src/BukiVedi.App/Controllers/BooksController.cs @@ -36,6 +36,17 @@ namespace BukiVedi.App.Controllers return Ok(await _handler.Search(request.Query, tag, OperationContext)); } + /// + /// Поиск книг по запросу + /// + /// Поисковый запрос + /// Список найденных книг + [HttpPost("qsearch")] + public async Task>> SearchAI() + { + return Ok(await _handler.SearchAI(OperationContext)); + } + #endregion /// diff --git a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfo.cs b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfo.cs index 4c7f986..d4b7992 100644 --- a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfo.cs +++ b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("BukiVedi.App")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+78ef654d3cca93dcdff7557d9a44da30b9d10a6a")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+708729b3f0a4a46b4ed8277753e30266f9b02ae4")] [assembly: System.Reflection.AssemblyProductAttribute("BukiVedi.App")] [assembly: System.Reflection.AssemblyTitleAttribute("BukiVedi.App")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfoInputs.cache b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfoInputs.cache index 40cf07e..d26e675 100644 --- a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfoInputs.cache +++ b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.AssemblyInfoInputs.cache @@ -1 +1 @@ -9dd3a16a8dacdbe5f2cc466ad40a08504afd3b209bf5aa3244617367acdb74bb +5d627d5604365b2a4254a699545c17a9cb8aa8618ee125b742c4fb1f29d8de02 diff --git a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.AssemblyReference.cache b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.AssemblyReference.cache index d44087f..c1dcec0 100644 Binary files a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.AssemblyReference.cache and b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.AssemblyReference.cache differ diff --git a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.FileListAbsolute.txt b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.FileListAbsolute.txt index 3a4911b..0e16737 100644 --- a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.FileListAbsolute.txt +++ b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.csproj.FileListAbsolute.txt @@ -88,10 +88,19 @@ G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\js\requests\i G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\js\scroll\index.js G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\script.js G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\assets\pencil.svg -G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\js\common\jquery.min.js G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\js\components\tags\index.js G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\js\utils\index.js G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\loginScript.js -G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\server.js G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\LemmaSharpPrebuilt.pdb G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\LemmaSharpPrebuilt.dll.config +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\assets\fortune.svg +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Raleway-Italic-VariableFont_wght.ttf +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Raleway-VariableFont_wght.ttf +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Roboto-300.ttf +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Roboto-300.woff +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Roboto-300.woff2 +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Roboto-400.ttf +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Roboto-400.woff +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\css\fonts\Roboto-400.woff2 +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\js\components\menu\index.js +G:\Documents\GitHub\BukiVedi\src\BukiVedi.App\bin\Debug\net8.0\web\README.md diff --git a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.pdb b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.pdb index fde160a..a8741c3 100644 Binary files a/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.pdb and b/src/BukiVedi.App/obj/Debug/net8.0/BukiVedi.App.pdb differ diff --git a/src/BukiVedi.App/web/README.md b/src/BukiVedi.App/web/README.md new file mode 100644 index 0000000..381f168 --- /dev/null +++ b/src/BukiVedi.App/web/README.md @@ -0,0 +1,12 @@ +# Vanilla JS Project +Vesion 1.0 + +## To run chrome for localhost: +` +"C:\Program Files\Google\Chrome\Application\chrome.exe" --disable-web-security --disable-gpu --user-data-dir=C:\Users\User\chromeTemp +` + +## To run project for [localhost](http://localhost:3001/login.html): +` +npm start +` \ No newline at end of file diff --git a/src/BukiVedi.App/web/assets/fortune.svg b/src/BukiVedi.App/web/assets/fortune.svg new file mode 100644 index 0000000..5a2500e --- /dev/null +++ b/src/BukiVedi.App/web/assets/fortune.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/BukiVedi.App/web/css/common.css b/src/BukiVedi.App/web/css/common.css index 96e5e2b..fc24cb1 100644 --- a/src/BukiVedi.App/web/css/common.css +++ b/src/BukiVedi.App/web/css/common.css @@ -76,6 +76,11 @@ input { margin-bottom: 12px; } +.mv-m { + margin-top: 20px; + margin-bottom: 20px; +} + .mt-m { margin-top: 20px; } @@ -130,6 +135,10 @@ input { padding-right: 12px; } +.pt-l { + padding-top: 40px; +} + /** * display forms **/ @@ -138,6 +147,11 @@ input { display: flex; } +.justify-c { + display: flex; + justify-content: center; +} + .d-bl { display: block; } diff --git a/src/BukiVedi.App/web/css/logo.css b/src/BukiVedi.App/web/css/logo.css index 83bf784..392e184 100644 --- a/src/BukiVedi.App/web/css/logo.css +++ b/src/BukiVedi.App/web/css/logo.css @@ -1,7 +1,7 @@ .background-logo { position: absolute; - top: -15px; + top: 25px; left: 20px; z-index: -1; overflow: hidden; diff --git a/src/BukiVedi.App/web/css/main.css b/src/BukiVedi.App/web/css/main.css index a894743..cda42a1 100644 --- a/src/BukiVedi.App/web/css/main.css +++ b/src/BukiVedi.App/web/css/main.css @@ -34,10 +34,16 @@ header { width: 20px; height: 100%; left: 10px; - top: 15px; + top: 55px; opacity: 0.25; } +.main-search-btn-wrapper { + position: absolute; + right: 5px; + top: 43px; +} + .main-search { display: block; width: 100%; @@ -47,6 +53,7 @@ header { border-radius: 14px; border-style: solid; border-width: 1px; + padding-right: 200px; } .main-search:hover { @@ -67,20 +74,23 @@ header { .main-search-btn { display: block; - margin-left: auto; - margin-right: auto; - max-width: 200px; - width: 200px; - height: 50px; + visibility: hidden; + max-width: 120px; + width: 120px; + height: 44px; min-width: 80px; background: var(--main-font-color); color: var(--main-bg-color); - font-size: 21px; + font-size: 17px; line-height: 25px; border-radius: 15px; cursor: pointer; } +.main-search-btn.visible { + visibility: visible; +} + .main-search-btn:hover { opacity: 0.9; } @@ -96,6 +106,61 @@ header { margin-right: auto; } +#fortuneButton { + background-color: transparent; + background-image: url(../assets/fortune.svg); + background-repeat: no-repeat; + width: 40px; + height: 40px; + border: 0; + margin-left: 12px; + margin-top: 3px; + position: relative; + transform-origin: center center; + +} + +#fortuneButton:hover { + cursor: pointer; + animation: antiClockwiseSpin 1s linear; + transform-origin: center center; +} + +#fortuneButton:active { + transform: translateY(2px); +} + +@keyframes antiClockwiseSpin { + 0% { + transform: rotate(180deg); + } + 100% { + transform: rotate(0deg); + } +} + +@keyframes antiClockwiseSpin { + 0% { + transform: rotate(180deg); + } + 100% { + transform: rotate(0deg); + } +} +/* +#fortuneButton:hover:after { + content: "Мне повезет!"; + color: var(--main-font-color); + width: 95px; + font-size: 13px; + padding: 5px; + border-radius: 5px; + background: #fff; + position: absolute; + opacity: 1; + z-index: 2; +} */ + /*======================================================== Book Section ========================================================*/ @@ -139,6 +204,7 @@ header { .main-book__card.card_closed { max-height: 450px; + min-height: 285px; overflow-y: hidden; } @@ -316,7 +382,7 @@ header { padding-bottom: 4px; background: #fff; line-height: 28px; - width: 200px; + width: 230px; vertical-align: middle; } @@ -342,7 +408,7 @@ header { .main-book__tags_title { position: relative; - width: 55px; + width: 113px; font-family: Vedi, Verdana, Tahoma; font-size: 14px; line-height: 24px; @@ -355,17 +421,23 @@ header { color: var(--active-color); } .pencil { - position: absolute; - top: 4px; - left: 40px; background-image: url(../assets/pencil.svg); background-repeat: no-repeat; width: 20px; - height: 100%; + height: 20px; + margin-left: 12px; + margin-top: 4px; } -.main-book__tags_title:hover, -.main-book__tags_title:active .pencil { +.main-book__pencil { + position: absolute; + top: 0; + left: 90px; +} + +.pencil:hover, +.pencil:active { + cursor: pointer; filter: brightness(0) saturate(100%) invert(43%) sepia(34%) saturate(7336%) hue-rotate(202deg) brightness(82%) contrast(97%); } @@ -486,7 +558,7 @@ header { box-sizing: border-box; border: 1px solid #ccc; border-radius: 4px; - background-color: #FFF; + background-color: #fff; color: var(--main-font-color); font-size: 14px; font-family: Vedi, Verdana, Tahoma; diff --git a/src/BukiVedi.App/web/index.html b/src/BukiVedi.App/web/index.html index b0bd963..630f46e 100644 --- a/src/BukiVedi.App/web/index.html +++ b/src/BukiVedi.App/web/index.html @@ -22,17 +22,21 @@

Vedi

-
+
+
+ + + +
-
diff --git a/src/BukiVedi.App/web/js/components/menu/index.js b/src/BukiVedi.App/web/js/components/menu/index.js new file mode 100644 index 0000000..c837962 --- /dev/null +++ b/src/BukiVedi.App/web/js/components/menu/index.js @@ -0,0 +1,112 @@ +import { waitForElement } from "../../requests/index.js"; +import { getMenu } from "../../constants/index.js"; +export default class MenuComponent { + element; + constructor({ + isBlocked = false, + hasReadLater = false, + authors = [], + bookid = null, + article, + }) { + this.isBlocked = isBlocked; + this.hasReadLater = hasReadLater; + this.authors = authors; + this.bookid = bookid; + this.article = article; + this.isOpened = false; + this.render(); + } + + get template() { + return ` + `; + } + + initialize() { + this.initEventListeners(); + } + + initEventListeners() { + const menuListener = this.article.querySelector(".main-book__menu"); + + menuListener.addEventListener("click", (event) => { + const isAction = event.target.closest("[data-action]"); + if (!!isAction) { + const id = isAction.dataset.action; + this.makeAction({ id }); + } else { + this.openMenu(); + } + }); + document.addEventListener("menu-outside-click", () => this.closeMenu()); + } + + get actions() { + return getMenu({ + isBlocked: this.isBlocked, + hasReadLater: this.hasReadLater, + }); + } + + getMenuBody() { + return this.actions + .map(({ label, id }) => { + return ` +
  • + ${label} +
  • `; + }) + .join(""); + } + + openMenu() { + this.element.classList.remove("main-book__menu_closed"); + + const ulElement = document.createElement("ul"); + ulElement.innerHTML = this.getMenuBody(); + + this.element.append(ulElement); + this.element.classList.add("main-book__menu_opened"); + this.isOpened = true; + } + + closeMenu() { + if (this.isOpened) { + this.element.classList.remove("main-book__menu_opened"); + this.element.firstElementChild.remove(); + this.element.classList.add("main-book__menu_closed"); + this.isOpened = false; + } + } + + async makeAction({ id }) { + this.article.classList.add("blur", "spinner"); + + const { action, value } = + this.actions.find((act) => act.id === Number(id)) || {}; + + await action({ id, bookid: this.bookid }); + + if (value) { + const { id, label } = value; + this[`${id}`] = label; + } + + this.article.classList.remove("spinner"); + this.closeMenu(); + } + + async waitRendered() { + await waitForElement(".main-book__menu"); + this.initialize(); + } + + async render() { + const divElement = document.createElement("div"); + divElement.innerHTML = this.template; + + this.element = divElement.firstElementChild; + this.waitRendered(); + } +} diff --git a/src/BukiVedi.App/web/js/components/tags/index.js b/src/BukiVedi.App/web/js/components/tags/index.js index 83d7ad5..d71211e 100644 --- a/src/BukiVedi.App/web/js/components/tags/index.js +++ b/src/BukiVedi.App/web/js/components/tags/index.js @@ -11,27 +11,37 @@ export default class TagsComponent { } get template() { - return ` -
    - Теги -
    -
    -
    ${this.getTags( - this.tags - )}
    - `; + return `
    + ${ + !(this.tags && this.tags.length) + ? this.getTitle() + : this.getTags(this.tags) + } +
    `; + } + + getTitle() { + return `
    + Добавить теги +
    +
    `; } + getTags(tags) { - return (tags || []) + return `
    + ${(tags || []) .map(({ id, name }) => { return `#${name}`; }) - .join(""); + .join("")} +
    `; } getTextarea(tags) { return ` - `; @@ -42,15 +52,13 @@ export default class TagsComponent { } initEventListeners() { - const tagsEditorListener = this.article.querySelector( - ".main-book__tags_title" - ); + const tagsEditorListener = this.article.querySelector(".main-book_edit"); tagsEditorListener.addEventListener("click", () => { - const tagsWrapper = this.article.querySelector(".main-book__tags"); - if (!this.isTextareaMode) { + const tagsWrapper = this.article.querySelector(".main-book_edit"); tagsWrapper.innerHTML = this.getTextarea(this.tags); + this.article.querySelector(".main-book__textarea").focus(); this.isTextareaMode = true; } @@ -64,7 +72,7 @@ export default class TagsComponent { } }); - document.addEventListener("outside-click", async () => { + document.addEventListener("tag-outside-click", async () => { if (this.isTextareaMode) { this.closeTextarea(); } @@ -72,22 +80,31 @@ export default class TagsComponent { } async closeTextarea() { - const tagsWrapper = this.article.querySelector(".main-book__tags"); + const tagsWrapper = this.article.querySelector(".main-book_edit"); const area = this.article.querySelector(".main-book__textarea"); const tags = area.value .split(" ") .map((item) => item.trim()) .filter((i) => i); - if (!!(tags || []).length > 0) { + + if ( + this.tags?.length !== tags?.length || + (this.tags || []).some(({ name }, index) => name !== tags[index]) + ) { await this.setTags({ bookid: this.bookid, tags }); } - tagsWrapper.innerHTML = this.getTags(this.tags); + + if (!!(tags || []).length > 0) { + tagsWrapper.innerHTML = this.getTags(this.tags); + } else { + tagsWrapper.innerHTML = this.getTitle(); + } this.isTextareaMode = false; } async waitRendered() { - await waitForElement(".main-book__tags"); + await waitForElement(".main-book_edit"); this.initialize(); } diff --git a/src/BukiVedi.App/web/js/constants/index.js b/src/BukiVedi.App/web/js/constants/index.js index 998ca84..30eb98c 100644 --- a/src/BukiVedi.App/web/js/constants/index.js +++ b/src/BukiVedi.App/web/js/constants/index.js @@ -1,40 +1,73 @@ -import * as requests from '../requests/index.js'; +import * as requests from "../requests/index.js"; export const SUCCESS = "success"; -export const menuEnum = [ +export const menuDict = [ { id: 1, - label: "Авторов в избранное", - action: requests.addAuthorToFavourites, + label: "В очередь на чтение", + action: requests.readLater, + value: { + id: 'hasReadLater', + label: true, + } }, { id: 2, - label: "В очередь на чтение", - action: requests.readLater, + label: "Убрать из очереди", + action: requests.removeFromReadLater, + value: { + id: 'hasReadLater', + label: false, + } }, { id: 3, label: "Игнорировать книгу", action: requests.ignoreBook, + value: { + id: 'isBlocked', + label: true, + } }, { id: 4, - label: "Игнорировать автора", - action: requests.ignoreAuthors, + label: "Перестать игнорировать книгу", + action: requests.stopIgnoreBook, + value: { + id: 'isBlocked', + label: false, + } + }, + { + id: 5, + label: "Авторов в избранное", + action: requests.addAuthorToFavourites, }, { id: 6, - label: "Заметка", + label: "Игнорировать авторов", + action: requests.ignoreAuthors, }, { id: 7, + label: "Заметка", + action: () => {}, + }, + { + id: 8, label: "Поделиться", + action: () => {}, }, ]; +export const getMenu = ({ isBlocked, hasReadLater }) => + menuDict + .filter((item) => (isBlocked ? item.id !== 3 : item.id !== 4)) + .filter((item) => (hasReadLater ? item.id !== 1 : item.id !== 2)); + export const DEFAULT_AUTHOR = { id: 0, - name: 'Неизвестно' + name: "Неизвестно", }; export const likeEnum = [ @@ -47,5 +80,5 @@ export const likeEnum = [ id: 1, label: "Не нравится", action: requests.removeBookToFavourites, - } -] \ No newline at end of file + }, +]; diff --git a/src/BukiVedi.App/web/js/main/index.js b/src/BukiVedi.App/web/js/main/index.js index e6fa073..26569a1 100644 --- a/src/BukiVedi.App/web/js/main/index.js +++ b/src/BukiVedi.App/web/js/main/index.js @@ -1,6 +1,7 @@ -import { menuEnum, likeEnum, DEFAULT_AUTHOR } from "../constants/index.js"; +import { likeEnum, DEFAULT_AUTHOR } from "../constants/index.js"; import { fetchData, + fetchRandomData, downloadBook, searchByAuthor, searchByTag, @@ -9,6 +10,7 @@ import { import { stopPropagation } from "../utils/index.js"; import TagsComponent from "../components/tags/index.js"; +import MenuComponent from "../components/menu/index.js"; export default class BookSection { subElements = []; @@ -37,31 +39,22 @@ export default class BookSection { const booksListener = document.querySelector(".main-book__wrapper"); booksListener.addEventListener("click", (event) => { let id, title; + stopPropagation(event); + + const menuEvent = new CustomEvent("menu-outside-click"); + const tagEvent = new CustomEvent("tag-outside-click"); + const isLike = event.target.closest("[data-like]"); - const isMenu = event.target.closest("[data-menu]"); - const isAction = event.target.closest("[data-action]"); const isLink = event.target.closest("[data-link]"); const isAuthor = event.target.closest("[data-author]"); const isMoreDetails = event.target.closest("[data-details]"); - const isTagWrapper = event.target.closest("[data-tags-wrapper]"); + const isTagWrapper = event.target.closest("[data-tags-editor]"); const isTag = event.target.closest("[data-tag]"); - - stopPropagation(event); + const isMenuWrapper = event.target.closest("[data-menu]"); switch (true) { - case !!isAction: - id = isAction.dataset.action; - const { authorid, bookid } = - isAction.parentNode.parentNode.dataset || {}; - this.makeAction({ id, authorid, bookid }); - - break; case !!isLike: this.makeLike(isLike); - break; - - case !!isMenu: - this.openMenu(isMenu); break; case !!isLink: @@ -80,20 +73,21 @@ export default class BookSection { this.toggleDetails(isMoreDetails); break; - case !isTagWrapper: - const myEvent = new CustomEvent("outside-click"); - document.dispatchEvent(myEvent); - this.closeMenu(); - + case !!isMenuWrapper: break; case !!isTag: id = isTag.dataset.tag; this.update({ isByTag: true, id }); - default: - this.closeMenu(); + break; + case !isTagWrapper && !isTag && !isMenuWrapper: + document.dispatchEvent(tagEvent); + document.dispatchEvent(menuEvent); break; + + default: + break; } }); } @@ -128,10 +122,6 @@ export default class BookSection {
    - -
    @@ -170,7 +160,7 @@ export default class BookSection {

    -
    +
    ${description || "Нет описания"} @@ -197,17 +187,6 @@ export default class BookSection { `; } - getMenuBody() { - return menuEnum - .map(({ label, id }) => { - return ` -
  • - ${label} -
  • `; - }) - .join(""); - } - getAuthors(authors) { return (authors || [DEFAULT_AUTHOR]) .map(({ id, name }) => { @@ -217,29 +196,6 @@ export default class BookSection { .join(""); } - openMenu(element) { - this.closeMenu(); - element.classList.remove("main-book__menu_closed"); - - const divElement = document.createElement("ul"); - divElement.innerHTML = this.getMenuBody(); - - element.append(divElement); - element.classList.add("main-book__menu_opened"); - } - - closeMenu() { - const elements = document.querySelectorAll(".main-book__menu_opened"); - - if (elements && elements.length) { - for (const subElement of elements) { - subElement.firstElementChild.remove(); - subElement.classList.remove("main-book__menu_opened"); - subElement.classList.add("main-book__menu_closed"); - } - } - } - toggleDetails(element) { const isOpened = element.classList.contains("opened"); const article = element.parentNode.parentNode; @@ -264,13 +220,23 @@ export default class BookSection { const articles = document.querySelectorAll("[data-card]"); articles.forEach((article, i) => { const tagWrapper = article.querySelector(".main-book__tags_wrapper"); + const menuWrapper = article.querySelector(".main-book__menu_wrapper"); const bookid = article.dataset.bookid; - const { tags } = this.data[i] || []; + const { tags, isBlocked, hasReadLater, authors } = this.data[i] || []; const tagsComponent = new TagsComponent({ tags, article, bookid }); + const menuComponent = new MenuComponent({ + isBlocked, + hasReadLater, + authors, + bookid, + article, + }); tagWrapper.append(tagsComponent.element); + menuWrapper.append(menuComponent.element); + // console.log('article.scrollHeight', article.scrollHeight); - if (article.scrollHeight > 502) { + if (article.scrollHeight > 424) { const arrow = article.querySelector(".main-book__arrow_wrapper"); arrow.classList.remove("hidden"); } @@ -295,27 +261,26 @@ export default class BookSection { const { bookid: likedBookId, like } = element.dataset || {}; const isLiked = like === "true"; - if (!isLiked) { - await likeEnum[0].action({ bookid: likedBookId }); - element.classList.remove("not-liked"); - element.classList.add("liked"); - } else { + if (isLiked) { await likeEnum[1].action({ bookid: likedBookId }); element.classList.remove("liked"); element.classList.add("not-liked"); + } else { + await likeEnum[0].action({ bookid: likedBookId }); + element.classList.remove("not-liked"); + element.classList.add("liked"); } element.dataset.like = !isLiked; this.data = this.data.map(({ isFavorite, id, ...rest }) => likedBookId === id - ? { id, isFavorite: !isFavorite, ...rest } + ? { id, ...rest, isFavorite: !isFavorite } : { id, isFavorite, ...rest, } ); - console.log("new data", this.data); this.element.classList.remove("spinner"); } @@ -326,6 +291,8 @@ export default class BookSection { this.data = await searchByAuthor({ id: params.id }); } else if (params?.isByTag) { this.data = await searchByTag({ id: params.id }); + } else if (params?.isByRandom) { + this.data = await fetchRandomData(); } else { const query = params; this.data = await fetchData({ query, url: this.url }); @@ -335,10 +302,11 @@ export default class BookSection { if (this.data && Object.values(this.data).length) { this.subElements.body.innerHTML = this.getBookBody(this.data); this.initialize(); + await this.countHeight(); } else { this.subElements.body.innerHTML = this.getEmptyBody(); } - await this.countHeight(); + this.element.classList.remove("spinner"); } @@ -375,48 +343,50 @@ export default class BookSection { } } -// this.data = [ -// { -// id: "2", -// authors: [{ id: "1", name: "123" }], -// description: -// "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", -// format: "123", -// genres: [], -// imageUrl: "", -// title: "What is Lorem Ipsum?", -// series: [], -// subseries: [], -// year: 1992, -// tags: [ -// { id: 1, name: "Интересно" }, -// { id: 22, name: "Почитать" }, -// { id: 155, name: "Рекомендовали" }, -// { id: 166, name: "завтра" }, -// { id: 11, name: "наверное" }, -// { id: 221, name: "Почитданетать" }, -// { id: 1515, name: "раздватри" }, -// { id: 1661, name: "возможно" }, -// ], -// isFavorite: true, -// }, -// { -// id: "22", -// authors: [{ id: "1", name: "123" }], -// description: -// "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", -// format: "123", -// genres: [], -// imageUrl: "", -// title: "dsfwqedwqefrwef", -// series: [], -// subseries: [], -// year: 1992, -// tags: [ -// { id: 1, name: "Интересно" }, -// { id: 22, name: "Почитать" }, -// { id: 155, name: "Рекомендовали" }, -// ], -// isFavorite: true, -// }, -// ]; +const testData = [ + { + id: "2", + authors: [{ id: "1", name: "123" }], + description: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", + format: "123", + genres: [], + imageUrl: "", + title: "What is Lorem Ipsum?", + series: [], + subseries: [], + year: 1992, + tags: [ + { id: 1, name: "Интересно" }, + { id: 22, name: "Почитать" }, + { id: 155, name: "Рекомендовали" }, + { id: 166, name: "завтра" }, + { id: 11, name: "наверное" }, + { id: 221, name: "Почитданетать" }, + { id: 1515, name: "раздватри" }, + { id: 1661, name: "возможно" }, + ], + isFavorite: true, + hasReadLater: true, + isBlocked: true, + }, + { + id: "22", + authors: [{ id: "1", name: "123" }], + description: + "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.", + format: "123", + genres: [], + imageUrl: "", + title: "dsfwqedwqefrwef", + series: [], + subseries: [], + year: 1992, + tags: [ + { id: 1, name: "Интересно" }, + { id: 22, name: "Почитать" }, + { id: 155, name: "Рекомендовали" }, + ], + isFavorite: true, + }, +]; diff --git a/src/BukiVedi.App/web/js/requests/index.js b/src/BukiVedi.App/web/js/requests/index.js index 36fb7f3..fb03a35 100644 --- a/src/BukiVedi.App/web/js/requests/index.js +++ b/src/BukiVedi.App/web/js/requests/index.js @@ -20,6 +20,24 @@ export const fetchData = ({ query, url }) => { }); }; +export const fetchRandomData = () => { + return $.ajax({ + contentType: "application/json; charset=utf-8", + dataType: "json", + type: "POST", + url: "../api/books/qsearch", + success: function (data, textStatus, jqXHR) { + if (textStatus === SUCCESS) { + return data; + } + return []; + }, + error: function (jqXHR, textStatus, errorThrown) { + console.log(jqXHR.statusText); + }, + }); +}; + export const addBookToFavourites = ({ bookid }) => { return $.ajax({ type: "POST", @@ -221,10 +239,6 @@ export const changeTags = ({ bookid, tags }) => { console.log(jqXHR.statusText); }, }); - - // return [ - // {id: "6611598e1468849d1b00570d", name: "шиза"} - // ]; }; export const searchByTag = ({ id }) => { diff --git a/src/BukiVedi.App/web/js/utils/index.js b/src/BukiVedi.App/web/js/utils/index.js index 9b8387a..b5e63b3 100644 --- a/src/BukiVedi.App/web/js/utils/index.js +++ b/src/BukiVedi.App/web/js/utils/index.js @@ -1,4 +1,4 @@ -export const stopPropagation = event => { - event.stopImmediatePropagation(); - event.stopPropagation(); -} \ No newline at end of file +export const stopPropagation = (event) => { + event.stopImmediatePropagation(); + event.stopPropagation(); +}; diff --git a/src/BukiVedi.App/web/script.js b/src/BukiVedi.App/web/script.js index 4576623..87fbbac 100644 --- a/src/BukiVedi.App/web/script.js +++ b/src/BukiVedi.App/web/script.js @@ -1,14 +1,17 @@ - import BookSection from "./js/main/index.js"; -import ButtonScroll from './js/scroll/index.js'; +import ButtonScroll from "./js/scroll/index.js"; const input = document.getElementById("search"); const books = document.getElementById("books"); const content = document.getElementById("content"); +const searchButton = document.getElementById("searchButton"); +const fortuneButton = document.getElementById("fortuneButton"); + const booksSection = new BookSection({ url: "../api/books/search", label: "books", }); + const click = () => { const query = document.getElementById("search").value; if (query) { @@ -16,8 +19,19 @@ const click = () => { } }; -searchButton.addEventListener("click", click); +const fortuneClick = () => { + booksSection.update({ isByRandom: true }); +}; +searchButton.addEventListener("click", click); +fortuneButton.addEventListener("click", fortuneClick); +input.addEventListener("input", function (e) { + if (e.target.value && e.target.value.length !== 0) { + searchButton.classList.add("visible"); + } else { + searchButton.classList.remove("visible"); + } +}); input.addEventListener("keypress", function (event) { if (event.key === "Enter") { click(); @@ -25,5 +39,5 @@ input.addEventListener("keypress", function (event) { }); books.append(booksSection.element); -const scroll = new ButtonScroll() -content.append(scroll.element) \ No newline at end of file +const scroll = new ButtonScroll(); +content.append(scroll.element); diff --git a/src/BukiVedi.App/web/server.js b/src/BukiVedi.App/web/server.js deleted file mode 100644 index fe8a538..0000000 --- a/src/BukiVedi.App/web/server.js +++ /dev/null @@ -1,52 +0,0 @@ -const path = require('path'); -const { createProxyMiddleware } = require('http-proxy-middleware'); -const cors = require('cors'); - -const express = require('express'); -const app = express(); - -const server = require('http').Server(app); -const router = require('express').Router() - -const options = { - target: 'https://book.ogoun.name/', // target host - changeOrigin: true, // needed for virtual hosted sites - ws: true, // proxy websockets - logLevel: 'debug', - secure: false, - cookieDomainRewrite: { - '*': 'localhost', - }, - onProxyRes(proxyRes, req, _res) { - if (proxyRes.headers['set-cookie'] !== undefined) { - req.cookie = proxyRes.headers['set-cookie']; - } - }, - onProxyReq(proxyReq, req, _res) { - if (req && req.cookie !== undefined) { - proxyReq.setHeader('Cookie', req.cookie[0]); - } - }, -}; - -const myLogger = function (req, res, next) { - next() -} - -app.use(myLogger) - -app.use(express.static(__dirname)); -app.use(express.static(__dirname + '/js')); -app.use(express.static(__dirname + '/css')); -app.use(express.static(__dirname + '/constants')); - -// app.get('/login', function (req, res) { -// res.sendFile(path.join(__dirname, '/login.html')); -// }); - -app.use('/', cors({ - credentials: true, - origin: 'http://localhost:3000', -}), createProxyMiddleware(options)); - -server.listen(3001); \ No newline at end of file diff --git a/src/BukiVedi.Shared/Apps/BooksHandler.cs b/src/BukiVedi.Shared/Apps/BooksHandler.cs index bb52744..0486e26 100644 --- a/src/BukiVedi.Shared/Apps/BooksHandler.cs +++ b/src/BukiVedi.Shared/Apps/BooksHandler.cs @@ -2,6 +2,7 @@ using BukiVedi.Shared.Models; using BukiVedi.Shared.Services; using BukiVedi.Shared.Services.Mappers; +using ZeroLevel.Patterns.Queries; using ZeroLevel.Services.FileSystem; namespace BukiVedi.Shared.Apps @@ -176,6 +177,12 @@ namespace BukiVedi.Shared.Apps return Enumerable.Empty(); } + public async Task> SearchAI(OperationContext context) + { + var books = (await _library.SearchBooksAI(context.OperationInitiator.Id)).ToArray(); + return await BookEntityMapper.Map(books, context); + } + public async Task UnblockBook(string id, OperationContext context) { var account_id = context.OperationInitiator.Id; diff --git a/src/BukiVedi.Shared/Apps/IBooksHandler.cs b/src/BukiVedi.Shared/Apps/IBooksHandler.cs index f166ae5..97c256f 100644 --- a/src/BukiVedi.Shared/Apps/IBooksHandler.cs +++ b/src/BukiVedi.Shared/Apps/IBooksHandler.cs @@ -5,6 +5,7 @@ namespace BukiVedi.Shared.Apps public interface IBooksHandler { Task> Search(string query, string? tag, OperationContext context); + Task> SearchAI(OperationContext context); Task AddToFavorite(string id, OperationContext context); Task AddAuthorsToFavorite(string id, OperationContext context); Task BlockBook(string id, OperationContext context); diff --git a/src/BukiVedi.Shared/IRepository.cs b/src/BukiVedi.Shared/IRepository.cs index a219803..e81b70d 100644 --- a/src/BukiVedi.Shared/IRepository.cs +++ b/src/BukiVedi.Shared/IRepository.cs @@ -16,7 +16,7 @@ namespace BukiVedi.Shared Task Count(FilterDefinition predicate); Task Exists(FilterDefinition filter); Task ExistById(string id); - + T[] GetRandomDocuments(int count); Task Write(T obj); diff --git a/src/BukiVedi.Shared/Services/Library.cs b/src/BukiVedi.Shared/Services/Library.cs index 41a118e..a707924 100644 --- a/src/BukiVedi.Shared/Services/Library.cs +++ b/src/BukiVedi.Shared/Services/Library.cs @@ -13,6 +13,7 @@ namespace BukiVedi.Shared.Services { Task> SearchBooksByAuthor(string author_id); Task> SearchBooks(string title); + Task SearchBooksAI(string accountId); Task> SearchByTagBooks(string accountId, string tagId); Task> SearchTaggedBooks(string accountId, string tag = null!); Task> SearchFavoritesBooks(string accountId); @@ -96,7 +97,7 @@ namespace BukiVedi.Shared.Services public async Task> SearchFavoritesBooks(string accountId) { - + IEnumerable bookIds = (await Tables.FavoriteBooks.Get(Filters.FavoriteBooks.ByUser(accountId)))?.Select(t => t.BookId)?.ToHashSet()!; if (bookIds != null && bookIds.Any()) { @@ -149,7 +150,7 @@ namespace BukiVedi.Shared.Services { IEnumerable bookIds; if (string.IsNullOrWhiteSpace(tag)) - { + { bookIds = (await Tables.UserTag.Get(Filters.Tags.ByUser(accountId)))?.Select(t => t.BookId)?.ToHashSet()!; } else @@ -439,5 +440,27 @@ namespace BukiVedi.Shared.Services await Tables.Books.Write(book); } } + + public Task SearchBooksAI(string accountId) + { + var books = Tables.Books.GetRandomDocuments(50); + var result = new BookEntity[books.Length]; + int index = 0; + foreach (var b in books) + { + result[index] = new BookEntity + { + Authors = GetAuthors(b), + Title = b.Title, + Year = b.Year, + Description = b.Description, + Format = b.Format, + Genre = new Genre(), + Id = b.Id, + }; + index++; + } + return Task.FromResult(result); + } } } diff --git a/src/BukiVedi.Shared/Services/MongoDB/MongoRepository.cs b/src/BukiVedi.Shared/Services/MongoDB/MongoRepository.cs index 3c54efc..30156b3 100644 --- a/src/BukiVedi.Shared/Services/MongoDB/MongoRepository.cs +++ b/src/BukiVedi.Shared/Services/MongoDB/MongoRepository.cs @@ -1,6 +1,7 @@ using BukiVedi.Shared.Entities; using MongoDB.Bson; using MongoDB.Driver; +using MongoDB.Driver.Linq; using System.Linq.Expressions; using ZeroLevel.Services.FileSystem; @@ -104,6 +105,12 @@ namespace BukiVedi.Shared.Services.MongoDB return result.ToEnumerable().ToArray(); } + public T[] GetRandomDocuments(int count) + { + var result = _collection.AsQueryable().Sample(count); + return result.ToArray(); + } + public async Task Count(FilterDefinition filter) { return await _collection.CountDocumentsAsync(filter); diff --git a/src/BukiVedi.Shared/bin/Debug/net8.0/BukiVedi.Shared.pdb b/src/BukiVedi.Shared/bin/Debug/net8.0/BukiVedi.Shared.pdb index 538f7b2..da2fa32 100644 Binary files a/src/BukiVedi.Shared/bin/Debug/net8.0/BukiVedi.Shared.pdb and b/src/BukiVedi.Shared/bin/Debug/net8.0/BukiVedi.Shared.pdb differ diff --git a/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfo.cs b/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfo.cs index 133b6ef..034f311 100644 --- a/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfo.cs +++ b/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfo.cs @@ -14,7 +14,7 @@ using System.Reflection; [assembly: System.Reflection.AssemblyCompanyAttribute("BukiVedi.Shared")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+78ef654d3cca93dcdff7557d9a44da30b9d10a6a")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+708729b3f0a4a46b4ed8277753e30266f9b02ae4")] [assembly: System.Reflection.AssemblyProductAttribute("BukiVedi.Shared")] [assembly: System.Reflection.AssemblyTitleAttribute("BukiVedi.Shared")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfoInputs.cache b/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfoInputs.cache index 8c5b692..6274847 100644 --- a/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfoInputs.cache +++ b/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.AssemblyInfoInputs.cache @@ -1 +1 @@ -43fea615f27b115ca71adae9d28aa42c583a4dce3ecf8ac2e68c45249fa1eb8e +47f2ecfa029d676b8f2f23a9542e5f91afdb2a5aba3b39b9691101443bb7f9e8 diff --git a/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.pdb b/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.pdb index 538f7b2..da2fa32 100644 Binary files a/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.pdb and b/src/BukiVedi.Shared/obj/Debug/net8.0/BukiVedi.Shared.pdb differ