From 28ebe798417404a0543e056f32543c938a34aee0 Mon Sep 17 00:00:00 2001 From: toximu Date: Thu, 10 Apr 2025 23:24:06 +0300 Subject: [PATCH 01/47] new branch --- server/CMakeLists.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 server/CMakeLists.txt diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt new file mode 100644 index 0000000..e69de29 From 2b2e60a4c98fa4e6d0a627a1f06e7ee6a16c32e8 Mon Sep 17 00:00:00 2001 From: tima bytsan <86739929+toximu@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:41:56 +0300 Subject: [PATCH 02/47] Delete server directory --- server/CMakeLists.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 server/CMakeLists.txt diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt deleted file mode 100644 index e69de29..0000000 From ebbe19747e3620e8022e6e8fb6b3d959b46da9e2 Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 00:41:39 +0300 Subject: [PATCH 03/47] change structure, declare base classes --- CMakeLists.txt | 32 +- client/CMakeLists.txt | 0 scripts/CMakeLists.txt | 17 - scripts/include/note.hpp | 46 --- scripts/include/project.hpp | 32 -- scripts/include/storage.hpp | 27 -- scripts/include/user.hpp | 29 -- scripts/src/note.cpp | 68 ---- scripts/src/project.cpp | 51 --- scripts/src/storage.cpp | 46 --- scripts/src/user.cpp | 39 -- server/CMakeLists.txt | 8 + server/Service/CMakeLists.txt | 31 ++ server/Service/include/common_call.h | 31 ++ server/Service/include/update_service.h | 30 ++ server/Service/src/update_service.cpp | 6 + {database => server/database}/CMakeLists.txt | 0 .../database}/include/database_manager.hpp | 0 .../database}/include/lr_dao.hpp | 0 .../database}/include/note_dao.hpp | 0 .../database}/include/project_dao.hpp | 0 .../database}/include/serialization.hpp | 0 .../database}/src/database_manager.cpp | 0 {database => server/database}/src/lr_dao.cpp | 0 .../database}/src/note_dao.cpp | 0 .../database}/src/project_dao.cpp | 0 .../database}/src/serialization.cpp | 0 ui/authorization-windows/CMakeLists.txt | 43 -- .../include/login_window.h | 32 -- .../include/login_window_style_sheet.h | 60 --- .../include/registration_window.h | 31 -- .../include/registration_window_style_sheet.h | 58 --- .../login_window_ru_RU.ts | 3 - ui/authorization-windows/src/login_window.cpp | 118 ------ .../src/registration_window.cpp | 154 -------- ui/authorization-windows/ui/login_window.ui | 131 ------- .../ui/registration_window.ui | 99 ----- ui/main-window/CMakeLists.txt | 39 -- ui/main-window/MainWindow_en_US.ts | 3 - ui/main-window/include/applicationwindow.h | 16 - ui/main-window/include/bottombar.h | 24 -- ui/main-window/include/main_window_style.hpp | 139 ------- ui/main-window/include/mainwindow.h | 43 -- ui/main-window/include/notelist.h | 29 -- ui/main-window/include/notewidget.h | 31 -- ui/main-window/include/projectitem.h | 19 - ui/main-window/include/projectlist.h | 22 -- ui/main-window/src/applicationwindow.cpp | 14 - ui/main-window/src/bottombar.cpp | 31 -- ui/main-window/src/mainwindow.cpp | 113 ------ ui/main-window/src/notelist.cpp | 66 ---- ui/main-window/src/notewidget.cpp | 53 --- ui/main-window/src/projectitem.cpp | 12 - ui/main-window/src/projectlist.cpp | 24 -- ui/note-widget/CMakeLists.txt | 40 -- ui/note-widget/NoteWidgetEfficio_ru_RU.ts | 3 - ui/note-widget/include/note_edit_dialog.h | 58 --- .../include/note_edit_dialog_styles.h | 174 --------- ui/note-widget/include/tags_dialog.h | 41 -- ui/note-widget/include/tags_dialog_styles.h | 123 ------ ui/note-widget/src/note_edit_dialog.cpp | 195 --------- ui/note-widget/src/tags_dialog.cpp | 87 ----- ui/note-widget/ui/note_edit_dialog.ui | 369 ------------------ 63 files changed, 109 insertions(+), 2881 deletions(-) create mode 100644 client/CMakeLists.txt delete mode 100644 scripts/CMakeLists.txt delete mode 100644 scripts/include/note.hpp delete mode 100644 scripts/include/project.hpp delete mode 100644 scripts/include/storage.hpp delete mode 100644 scripts/include/user.hpp delete mode 100644 scripts/src/note.cpp delete mode 100644 scripts/src/project.cpp delete mode 100644 scripts/src/storage.cpp delete mode 100644 scripts/src/user.cpp create mode 100644 server/Service/CMakeLists.txt create mode 100644 server/Service/include/common_call.h create mode 100644 server/Service/include/update_service.h create mode 100644 server/Service/src/update_service.cpp rename {database => server/database}/CMakeLists.txt (100%) rename {database => server/database}/include/database_manager.hpp (100%) rename {database => server/database}/include/lr_dao.hpp (100%) rename {database => server/database}/include/note_dao.hpp (100%) rename {database => server/database}/include/project_dao.hpp (100%) rename {database => server/database}/include/serialization.hpp (100%) rename {database => server/database}/src/database_manager.cpp (100%) rename {database => server/database}/src/lr_dao.cpp (100%) rename {database => server/database}/src/note_dao.cpp (100%) rename {database => server/database}/src/project_dao.cpp (100%) rename {database => server/database}/src/serialization.cpp (100%) delete mode 100644 ui/authorization-windows/CMakeLists.txt delete mode 100644 ui/authorization-windows/include/login_window.h delete mode 100644 ui/authorization-windows/include/login_window_style_sheet.h delete mode 100644 ui/authorization-windows/include/registration_window.h delete mode 100644 ui/authorization-windows/include/registration_window_style_sheet.h delete mode 100644 ui/authorization-windows/login_window_ru_RU.ts delete mode 100644 ui/authorization-windows/src/login_window.cpp delete mode 100644 ui/authorization-windows/src/registration_window.cpp delete mode 100644 ui/authorization-windows/ui/login_window.ui delete mode 100644 ui/authorization-windows/ui/registration_window.ui delete mode 100644 ui/main-window/CMakeLists.txt delete mode 100644 ui/main-window/MainWindow_en_US.ts delete mode 100644 ui/main-window/include/applicationwindow.h delete mode 100644 ui/main-window/include/bottombar.h delete mode 100644 ui/main-window/include/main_window_style.hpp delete mode 100644 ui/main-window/include/mainwindow.h delete mode 100644 ui/main-window/include/notelist.h delete mode 100644 ui/main-window/include/notewidget.h delete mode 100644 ui/main-window/include/projectitem.h delete mode 100644 ui/main-window/include/projectlist.h delete mode 100644 ui/main-window/src/applicationwindow.cpp delete mode 100644 ui/main-window/src/bottombar.cpp delete mode 100644 ui/main-window/src/mainwindow.cpp delete mode 100644 ui/main-window/src/notelist.cpp delete mode 100644 ui/main-window/src/notewidget.cpp delete mode 100644 ui/main-window/src/projectitem.cpp delete mode 100644 ui/main-window/src/projectlist.cpp delete mode 100644 ui/note-widget/CMakeLists.txt delete mode 100644 ui/note-widget/NoteWidgetEfficio_ru_RU.ts delete mode 100644 ui/note-widget/include/note_edit_dialog.h delete mode 100644 ui/note-widget/include/note_edit_dialog_styles.h delete mode 100644 ui/note-widget/include/tags_dialog.h delete mode 100644 ui/note-widget/include/tags_dialog_styles.h delete mode 100644 ui/note-widget/src/note_edit_dialog.cpp delete mode 100644 ui/note-widget/src/tags_dialog.cpp delete mode 100644 ui/note-widget/ui/note_edit_dialog.ui diff --git a/CMakeLists.txt b/CMakeLists.txt index 821a0f3..aa07c78 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,34 +1,8 @@ cmake_minimum_required(VERSION 3.16) -project(EfficioTaskTracker LANGUAGES CXX) +project(test-server) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) - -add_subdirectory(scripts) -add_subdirectory(database) -add_subdirectory(ui/main-window) -add_subdirectory(ui/authorization-windows) -add_subdirectory(ui/note-widget) +add_subdirectory(server) add_subdirectory(proto) -add_executable(EfficioTaskTracker main.cpp) - -target_link_libraries(EfficioTaskTracker PRIVATE - Qt6::Widgets - Qt6::Core - Qt6::Gui - Qt6::Sql - model-proto - efficio-rpc - Database - Scripts - AuthorizationWindows - MainWindow - NoteWidget -) \ No newline at end of file +add_subdirectory(client) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 0000000..e69de29 diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt deleted file mode 100644 index 73ccd8c..0000000 --- a/scripts/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(Scripts LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -file(GLOB SOURCES "src/*.cpp") -file(GLOB HEADERS "include/*.hpp") - -add_library(Scripts STATIC ${SOURCES} ${HEADERS}) - -target_include_directories(Scripts - PUBLIC - $ - $ -) \ No newline at end of file diff --git a/scripts/include/note.hpp b/scripts/include/note.hpp deleted file mode 100644 index e54e01a..0000000 --- a/scripts/include/note.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef NOTE_HPP -#define NOTE_HPP - -#include -#include -#include - -namespace project_storage_model { - -class Note { -public: - struct Tag { - std::string name; - std::string color; - }; - - Note(int id, std::string title, std::string text); - - [[nodiscard]] int get_id() const; - [[nodiscard]] const std::string &get_title() const; - [[nodiscard]] const std::string &get_text() const; - [[nodiscard]] const std::string &get_date() const; - [[nodiscard]] const std::vector &get_tags() const; - [[nodiscard]] const std::unordered_set &get_members() const; - - void set_title(const std::string &title); - void set_text(const std::string &text); - void add_tag(const std::string &tag, const std::string &color = "#e7624b"); - void set_date(const std::string &date); - void add_member(const std::string &member); - void remove_tag(const std::string &tag); - void clear_tags(); - void clear_members(); - -private: - int id_; - std::string title_; - std::string text_; - std::string date_; - std::vector tags_; - std::unordered_set members_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/scripts/include/project.hpp b/scripts/include/project.hpp deleted file mode 100644 index ad83932..0000000 --- a/scripts/include/project.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef PROJECT_HPP -#define PROJECT_HPP - -#include -#include -#include "note.hpp" - -namespace project_storage_model { - -class Project { -public: - Project(int id, const std::string &name, const std::string &description); - - [[nodiscard]] int get_id() const; - [[nodiscard]] const std::string &get_name() const; - [[nodiscard]] const std::string &get_description() const; - const std::list &get_notes() const; - - Note &add_note(const Note ¬e); - void remove_note(int note_id); - void edit_description(const std::string &description); - -private: - int id_; - std::string name_; - std::string description_; - std::list notes_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/scripts/include/storage.hpp b/scripts/include/storage.hpp deleted file mode 100644 index da6f948..0000000 --- a/scripts/include/storage.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef STORAGE_HPP -#define STORAGE_HPP - -#include -#include "project.hpp" -#include "user.hpp" - -namespace project_storage_model { - -class Storage { -public: - Project &add_project(const Project &project); - void remove_project(int project_id); - std::list &get_projects(); - - User &add_user(const User &user); - void remove_user(int user_id); - const std::list &get_users() const; - -private: - std::list projects_; - std::list users_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/scripts/include/user.hpp b/scripts/include/user.hpp deleted file mode 100644 index 957fe63..0000000 --- a/scripts/include/user.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef USER_HPP -#define USER_HPP - -#include -#include -#include "project.hpp" - -namespace project_storage_model { - -class User { -public: - User(int id, const std::string &username); - - [[nodiscard]] int get_id() const; - [[nodiscard]] const std::string &get_username() const; - const std::list &get_projects() const; - - Project &add_project(const Project &project); - void remove_project(int project_id); - -private: - int id_; - std::string username_; - std::list projects_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/scripts/src/note.cpp b/scripts/src/note.cpp deleted file mode 100644 index dcb8391..0000000 --- a/scripts/src/note.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "note.hpp" -#include -#include - -namespace project_storage_model { - -Note::Note(const int id, std::string title, std::string text) - : id_(id), title_(std::move(title)), text_(std::move(text)) { -} - -[[nodiscard]] int Note::get_id() const { - return id_; -} - -[[nodiscard]] const std::string &Note::get_title() const { - return title_; -} - -[[nodiscard]] const std::string &Note::get_text() const { - return text_; -} - -const std::string &Note::get_date() const { - return date_; -} - -const std::vector &Note::get_tags() const { - return tags_; -} - -const std::unordered_set &Note::get_members() const { - return members_; -} - -void Note::set_title(const std::string &title) { - title_ = title; -} - -void Note::set_text(const std::string &text) { - text_ = text; -} - -void Note::add_tag(const std::string &tag, const std::string &color) { - tags_.push_back({tag, color}); -} - -void Note::set_date(const std::string &date) { - date_ = date; -} - -void Note::add_member(const std::string &member) { - members_.insert(member); -} - -void Note::remove_tag(const std::string &tag) { - tags_.erase(std::remove_if(tags_.begin(), tags_.end(), - [&tag](const Tag& t) { return t.name == tag; }), tags_.end()); -} - -void Note::clear_tags() { - tags_.clear(); -} - -void Note::clear_members() { - members_.clear(); -} - -} // namespace project_storage_model \ No newline at end of file diff --git a/scripts/src/project.cpp b/scripts/src/project.cpp deleted file mode 100644 index a55e555..0000000 --- a/scripts/src/project.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "../include/project.hpp" -#include - -namespace project_storage_model { - -Project::Project( - int id, - const std::string &name, - const std::string &description -) - : id_(std::move(id)), - name_(std::move(name)), - description_(std::move(description)) { -} - -[[nodiscard]] int Project::get_id() const { - return id_; -} - -[[nodiscard]] const std::string &Project::get_name() const { - return name_; -} - -[[nodiscard]] const std::string &Project::get_description() const { - return description_; -} - -const std::list &Project::get_notes() const { - return notes_; -} - -Note &Project::add_note(const Note ¬e) { - notes_.push_back(note); - return notes_.back(); -} - -void Project::remove_note(int note_id) { - notes_.erase( - std::remove_if( - notes_.begin(), notes_.end(), - [note_id](const Note ¬e) { return note.get_id() == note_id; } - ), - notes_.end() - ); -} - -void Project::edit_description(const std::string &description) { - this->description_ = description; -} - -} // namespace project_storage_model diff --git a/scripts/src/storage.cpp b/scripts/src/storage.cpp deleted file mode 100644 index e452e6b..0000000 --- a/scripts/src/storage.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "../include/storage.hpp" -#include - -namespace project_storage_model { - -Project &Storage::add_project(const Project &project) { - projects_.push_back(project); - return projects_.back(); -} - -void Storage::remove_project(int project_id) { - projects_.erase( - std::remove_if( - projects_.begin(), projects_.end(), - [project_id](const Project &project) { - return project.get_id() == project_id; - } - ), - projects_.end() - ); -} - -std::list &Storage::get_projects() { - return projects_; -} - -User &Storage::add_user(const User &user) { - users_.push_back(user); - return users_.back(); -} - -void Storage::remove_user(int user_id) { - users_.erase( - std::remove_if( - users_.begin(), users_.end(), - [user_id](const User &user) { return user.get_id() == user_id; } - ), - users_.end() - ); -} - -const std::list &Storage::get_users() const { - return users_; -} - -} // namespace project_storage_model diff --git a/scripts/src/user.cpp b/scripts/src/user.cpp deleted file mode 100644 index e1cbb5f..0000000 --- a/scripts/src/user.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "../include/user.hpp" -#include - -namespace project_storage_model { - -User::User(int id, const std::string &username) - : id_(std::move(id)), username_(std::move(username)) { -} - -[[nodiscard]] int User::get_id() const { - return id_; -} - -[[nodiscard]] const std::string &User::get_username() const { - return username_; -} - -const std::list &User::get_projects() const { - return projects_; -} - -Project &User::add_project(const Project &project) { - projects_.push_back(project); - return projects_.back(); -} - -void User::remove_project(int project_id) { - projects_.erase( - std::remove_if( - projects_.begin(), projects_.end(), - [project_id](const Project &project) { - return project.get_id() == project_id; - } - ), - projects_.end() - ); -} - -} // namespace project_storage_model diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index e69de29..9733a05 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.16) +project(server) +add_subdirectory(Service) + + +add_executable(main main.cpp) + +target_link_libraries(main Service) \ No newline at end of file diff --git a/server/Service/CMakeLists.txt b/server/Service/CMakeLists.txt new file mode 100644 index 0000000..70b47a8 --- /dev/null +++ b/server/Service/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.16) + +project(Service LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Protobuf CONFIG REQUIRED) +find_package(gRPC CONFIG REQUIRED) +find_package(Threads) + + +file(GLOB SOURCES "src/*.cpp") +file(GLOB HEADERS "include/*.h") + +add_library(Service STATIC ${SOURCES} ${HEADERS}) + +target_link_libraries(Service + model-proto + efficio-rpc + protobuf::libprotobuf + gRPC::grpc + gRPC::grpc++ +) + +target_include_directories(${PROJECT_NAME} + + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR} +) \ No newline at end of file diff --git a/server/Service/include/common_call.h b/server/Service/include/common_call.h new file mode 100644 index 0000000..6a1380f --- /dev/null +++ b/server/Service/include/common_call.h @@ -0,0 +1,31 @@ +#ifndef CALL_DATA_H +#define CALL_DATA_H +#include + +using grpc::Server; +using grpc::ServerAsyncResponseWriter; +using grpc::ServerBuilder; +using grpc::ServerCompletionQueue; +using grpc::ServerContext; +using grpc::Status; + +class CommonCall +{ +public: + ServerCompletionQueue* cq_; + ServerContext ctx_; + enum CallStatus { CREATE, PROCESS, FINISH }; + CallStatus status_; +public: + explicit CommonCall(ServerCompletionQueue* cq): + cq_(cq), + status_(CREATE) + {} + + virtual ~CommonCall() = default; + + virtual void Proceed(bool = true) = 0; +}; + + +#endif \ No newline at end of file diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h new file mode 100644 index 0000000..6393610 --- /dev/null +++ b/server/Service/include/update_service.h @@ -0,0 +1,30 @@ +#ifndef UPDATE_SERVICE_H +#define UPDATE_SERVICE_H + +#include +#include +#include +#include + + +using grpc::Server; +using grpc::ServerAsyncResponseWriter; +using grpc::ServerBuilder; +using grpc::ServerCompletionQueue; +using grpc::ServerContext; +using grpc::Status; + + +using Efficio_proto::Update; + + +class UpdateService final { + Update::AsyncService service_; + +public: + class GetNoteCall; + // ... + void Run(uint16_t port); +}; + +#endif //UPDATE_SERVICE_H diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp new file mode 100644 index 0000000..ad9b63c --- /dev/null +++ b/server/Service/src/update_service.cpp @@ -0,0 +1,6 @@ +#include "update_service.h" +#include "common_call.h" + + +// need define all calls (rpc methods) +// class UpdateService::GetNoteCall : public CommonCall {}; diff --git a/database/CMakeLists.txt b/server/database/CMakeLists.txt similarity index 100% rename from database/CMakeLists.txt rename to server/database/CMakeLists.txt diff --git a/database/include/database_manager.hpp b/server/database/include/database_manager.hpp similarity index 100% rename from database/include/database_manager.hpp rename to server/database/include/database_manager.hpp diff --git a/database/include/lr_dao.hpp b/server/database/include/lr_dao.hpp similarity index 100% rename from database/include/lr_dao.hpp rename to server/database/include/lr_dao.hpp diff --git a/database/include/note_dao.hpp b/server/database/include/note_dao.hpp similarity index 100% rename from database/include/note_dao.hpp rename to server/database/include/note_dao.hpp diff --git a/database/include/project_dao.hpp b/server/database/include/project_dao.hpp similarity index 100% rename from database/include/project_dao.hpp rename to server/database/include/project_dao.hpp diff --git a/database/include/serialization.hpp b/server/database/include/serialization.hpp similarity index 100% rename from database/include/serialization.hpp rename to server/database/include/serialization.hpp diff --git a/database/src/database_manager.cpp b/server/database/src/database_manager.cpp similarity index 100% rename from database/src/database_manager.cpp rename to server/database/src/database_manager.cpp diff --git a/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp similarity index 100% rename from database/src/lr_dao.cpp rename to server/database/src/lr_dao.cpp diff --git a/database/src/note_dao.cpp b/server/database/src/note_dao.cpp similarity index 100% rename from database/src/note_dao.cpp rename to server/database/src/note_dao.cpp diff --git a/database/src/project_dao.cpp b/server/database/src/project_dao.cpp similarity index 100% rename from database/src/project_dao.cpp rename to server/database/src/project_dao.cpp diff --git a/database/src/serialization.cpp b/server/database/src/serialization.cpp similarity index 100% rename from database/src/serialization.cpp rename to server/database/src/serialization.cpp diff --git a/ui/authorization-windows/CMakeLists.txt b/ui/authorization-windows/CMakeLists.txt deleted file mode 100644 index e61f562..0000000 --- a/ui/authorization-windows/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(AuthorizationWindows LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) - -set(UI_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/ui/login_window.ui - ${CMAKE_CURRENT_SOURCE_DIR}/ui/registration_window.ui -) - -set(SOURCES - src/login_window.cpp - src/registration_window.cpp -) - -set(HEADERS - include/login_window.h - include/login_window_style_sheet.h - include/registration_window_style_sheet.h - include/registration_window.h -) - -file(GLOB TS_FILES "*.ts") - -add_library(AuthorizationWindows STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) - -target_include_directories(AuthorizationWindows - PUBLIC - $ - $ -) - -target_link_libraries(AuthorizationWindows PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database Scripts MainWindow) \ No newline at end of file diff --git a/ui/authorization-windows/include/login_window.h b/ui/authorization-windows/include/login_window.h deleted file mode 100644 index 5aed300..0000000 --- a/ui/authorization-windows/include/login_window.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "database_manager.hpp" -#include "lr_dao.hpp" - -QT_BEGIN_NAMESPACE - -namespace Ui { -class LoginWindow; -} - -QT_END_NAMESPACE - -class LoginWindow : public QWidget { - Q_OBJECT - -public: - explicit LoginWindow(QWidget *parent = nullptr); - ~LoginWindow(); - -private slots: - void on_switch_mode_clicked(); - void on_push_enter_clicked(); - -private: - Ui::LoginWindow *ui; -}; \ No newline at end of file diff --git a/ui/authorization-windows/include/login_window_style_sheet.h b/ui/authorization-windows/include/login_window_style_sheet.h deleted file mode 100644 index 3a6d776..0000000 --- a/ui/authorization-windows/include/login_window_style_sheet.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "ui_login_window.h" - -namespace Ui { -QString login_window_light_theme = R"( - QWidget { - background-color: #f5f5f5; - } - - QLabel { - font-family: 'Arial'; - font-weight: bold; - font-size: 13px; - color: #089083; - padding: 1px; - background-color: transparent; - } - - QPushButton#pushEnter { - font-family: 'Arial'; - font-weight: bold; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - font-weight: bold; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushEnter:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: #dadada; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid white; - background: white; - color: black; - padding: 5px; - } - - QLineEdit::placeholder { - color: #727272; - } - -)"; - -} // namespace Ui \ No newline at end of file diff --git a/ui/authorization-windows/include/registration_window.h b/ui/authorization-windows/include/registration_window.h deleted file mode 100644 index 35f4bc4..0000000 --- a/ui/authorization-windows/include/registration_window.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include -#include "database_manager.hpp" -#include "lr_dao.hpp" - -QT_BEGIN_NAMESPACE - -namespace Ui { -class RegistrationWindow; -} - -QT_END_NAMESPACE - -class RegistrationWindow : public QWidget { - Q_OBJECT - -public: - explicit RegistrationWindow(QWidget *parent = nullptr); - ~RegistrationWindow(); - bool is_strong_and_valid_password(const QString &password); - -private slots: - void on_switch_mode_clicked(); - void on_push_registration_clicked(); - -private: - Ui::RegistrationWindow *ui; -}; \ No newline at end of file diff --git a/ui/authorization-windows/include/registration_window_style_sheet.h b/ui/authorization-windows/include/registration_window_style_sheet.h deleted file mode 100644 index dc7a1ca..0000000 --- a/ui/authorization-windows/include/registration_window_style_sheet.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "ui_registration_window.h" - -namespace Ui { -QString registration_window_light_theme = R"( - QWidget { - background-color: #f5f5f5; - } - - QLabel { - font-family: 'Arial'; - background-color: transparent; - font-size: 13px; - color: #089083; - padding: 1px; - } - - QPushButton#pushRegistration { - font-weight: bold; - font-family: 'Arial'; - border-radius: 8px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushRegistration:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: #dadada; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid white; - background: white; - color: black; - padding: 5px; - } - - QLineEdit::placeholder { - color: #727272; - } - -)"; - -} // namespace Ui \ No newline at end of file diff --git a/ui/authorization-windows/login_window_ru_RU.ts b/ui/authorization-windows/login_window_ru_RU.ts deleted file mode 100644 index 32bf76c..0000000 --- a/ui/authorization-windows/login_window_ru_RU.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/ui/authorization-windows/src/login_window.cpp b/ui/authorization-windows/src/login_window.cpp deleted file mode 100644 index 0583ef5..0000000 --- a/ui/authorization-windows/src/login_window.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "login_window.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "applicationwindow.h" -#include "bottombar.h" -#include "database_manager.hpp" -#include "login_window_style_sheet.h" -#include "lr_dao.hpp" -#include "mainwindow.h" -#include "notelist.h" -#include "registration_window.h" -#include "serialization.hpp" - -LoginWindow::LoginWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::LoginWindow) { - ui->setupUi(this); - - setFixedSize(380, 480); - ui->inputLogin->setPlaceholderText("Введите логин:"); - ui->inputPassword->setPlaceholderText("Введите пароль:"); - setStyleSheet(Ui::login_window_light_theme); - ui->inputPassword->setEchoMode(QLineEdit::Password); - - connect( - ui->switchMode, &QPushButton::clicked, this, - &LoginWindow::on_switch_mode_clicked - ); - connect( - ui->pushEnter, &QPushButton::clicked, this, - &LoginWindow::on_push_enter_clicked - ); -} - -LoginWindow::~LoginWindow() { - delete ui; -} - -void LoginWindow::on_switch_mode_clicked() { - QWidget *parent = this->parentWidget(); - - QMainWindow *app_window = qobject_cast(parent); - - if (QWidget *old = app_window->centralWidget()) { - old->deleteLater(); - } - project_storage_model::Storage storage; - RegistrationWindow *registration_window = - new RegistrationWindow(app_window); - - app_window->setCentralWidget(registration_window); - QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); - int x = (screenGeometry.width() - registration_window->width()) / 2; - int y = (screenGeometry.height() - registration_window->height()) / 2; - app_window->move(x, y); - - this->close(); -} - -void LoginWindow::on_push_enter_clicked() { - QString login = ui->inputLogin->text(); - QString password = ui->inputPassword->text(); - - if (!login.isEmpty() && !password.isEmpty()) { - if (login.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина логина не должна превышать пятидесяти символов" - ); - } else if (password.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина пароля не должна превышать пятидесяти символов" - ); - } else if (LRDao::validate_user(login, password)) { - QMessageBox::information( - this, "Вход", "Вы успешно вошли! Добро пожаловать :)" - ); - QWidget *parent = this->parentWidget(); - - QMainWindow *app_window = qobject_cast(parent); - - if (QWidget *old = app_window->centralWidget()) { - old->deleteLater(); - } - // todo load all projects of user to storage - project_storage_model::Storage *storage = - new project_storage_model::Storage(); - Serialization::get_storage(*storage, login.toStdString()); - - Ui::MainWindow *main_window = - new Ui::MainWindow(app_window, login.toStdString(), storage); - - app_window->setCentralWidget(main_window); - app_window->resize(800, 600); - QRect screenGeometry = - QApplication::primaryScreen()->availableGeometry(); - int x = (screenGeometry.width() - main_window->width()) / 2; - int y = (screenGeometry.height() - main_window->height()) / 2; - app_window->move(x, y); - - this->close(); - } else { - QMessageBox::warning( - this, "Ошибка ввода данных", "Неверный логин или пароль!" - ); - } - } else { - QMessageBox::warning( - this, "Ошибка ввода данных", "Пожалуйста, заполните все поля!" - ); - } -} \ No newline at end of file diff --git a/ui/authorization-windows/src/registration_window.cpp b/ui/authorization-windows/src/registration_window.cpp deleted file mode 100644 index f30f7d8..0000000 --- a/ui/authorization-windows/src/registration_window.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "registration_window.h" -#include -#include -#include -#include -#include -#include -#include -#include "applicationwindow.h" -#include "bottombar.h" -#include "database_manager.hpp" -#include "login_window.h" -#include "lr_dao.hpp" -#include "mainwindow.h" -#include "notelist.h" -#include "registration_window.h" -#include "registration_window_style_sheet.h" - -RegistrationWindow::RegistrationWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::RegistrationWindow) { - ui->setupUi(this); - - setFixedSize(380, 480); - - ui->createLogin->setPlaceholderText("Введите логин:"); - ui->createPassword->setPlaceholderText("Введите пароль:"); - ui->repeatPassword->setPlaceholderText("Повторите пароль:"); - setStyleSheet(Ui::registration_window_light_theme); - ui->createPassword->setEchoMode(QLineEdit::Password); - ui->repeatPassword->setEchoMode(QLineEdit::Password); - - connect( - ui->pushRegistration, &QPushButton::clicked, this, - &RegistrationWindow::on_push_registration_clicked - ); - connect( - ui->switchMode, &QPushButton::clicked, this, - &RegistrationWindow::on_switch_mode_clicked - ); -} - -RegistrationWindow::~RegistrationWindow() { - delete ui; -} - -void RegistrationWindow::on_switch_mode_clicked() { - QWidget *parent = this->parentWidget(); - - QMainWindow *app_window = qobject_cast(parent); - - if (QWidget *old = app_window->centralWidget()) { - old->deleteLater(); - } - project_storage_model::Storage storage; - LoginWindow *login_window = new LoginWindow(app_window); - - app_window->setCentralWidget(login_window); - QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); - int x = (screenGeometry.width() - login_window->width()) / 2; - int y = (screenGeometry.height() - login_window->height()) / 2; - app_window->move(x, y); - - this->close(); -} - -bool RegistrationWindow::is_strong_and_valid_password(const QString &password) { - if (password.length() < 8) { - QMessageBox::warning( - nullptr, "Ошибка: недостаточно надежный пароль", - "Пароль должен содержать не менее восьми символов" - ); - return false; - } - - bool has_digit = false; - bool has_latin_letter = false; - - for (const QChar ch : password) { - if (!ch.isLetter() && !ch.isDigit()) { - QMessageBox::warning( - nullptr, "Ошибка", - "Пароль должен содержать только символы латиницы и цифры" - ); - return false; - } else if (ch.isLetter()) { - has_latin_letter = true; - } else if (ch.isDigit()) { - has_digit = true; - } - } - - if (!has_latin_letter) { - QMessageBox::warning( - nullptr, "Ошибка: недостаточно надежный пароль", - "Пароль должен содержать хотя бы одну букву" - ); - return false; - } - if (!has_digit) { - QMessageBox::warning( - nullptr, "Ошибка: недостаточно надежный пароль", - "Пароль должен содержать хотя бы одну цифру" - ); - return false; - } - - return true; -} - -void RegistrationWindow::on_push_registration_clicked() { - QString created_login = ui->createLogin->text(); - QString created_password = ui->createPassword->text(); - QString repeated_password = ui->repeatPassword->text(); - - if (!created_login.isEmpty() && !created_password.isEmpty() && - !repeated_password.isEmpty()) { - if (created_password != repeated_password) { - QMessageBox::warning(this, "Ошибка", "Пароли не совпадают!"); - } else if (created_login.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина логина не должна превышать пятидесяти символов" - ); - } else if (created_password.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина пароля не должна превышать пятидесяти символов" - ); - } else if (is_strong_and_valid_password(created_password)) { - int try_register_user = - LRDao::try_register_user(created_login, created_password); - if (try_register_user == 0) { - QMessageBox::warning( - this, "Ошибка", - "Извините, разрабы дауны и не подключили толком бд." - ); - } else if (try_register_user == -1) { - QMessageBox::warning( - this, "Ошибка", - "Пользователь с таким именем уже существует. Пожалуйста, " - "придумайте другое!" - ); - } else { - QMessageBox::information( - this, "Регистрация", - "Вы успешно зарегистрировались! Пожалуйста, выполните вход." - ); - on_switch_mode_clicked(); // TODO: fix - } - } - } else { - QMessageBox::warning(this, "Ошибка", "Пожалуйста, заполните все поля."); - } -} \ No newline at end of file diff --git a/ui/authorization-windows/ui/login_window.ui b/ui/authorization-windows/ui/login_window.ui deleted file mode 100644 index 02ffce3..0000000 --- a/ui/authorization-windows/ui/login_window.ui +++ /dev/null @@ -1,131 +0,0 @@ - - - LoginWindow - - - - 0 - 0 - 362 - 600 - - - - Dialog - - - - - 0 - 0 - 375 - 501 - - - - - - 120 - 280 - 131 - 51 - - - - font: 700 13pt "Arial"; -border-radius: 10px; - - - Войти - - - - - - 140 - 130 - 91 - 41 - - - - font: 700 21pt "Arial"; - - - Вход - - - - - - 62 - 175 - 251 - 41 - - - - border-radius: 10px; - - - Логин - - - - - - 62 - 225 - 251 - 41 - - - - border-radius: 10px; - - - Пароль - - - - - - 80 - 340 - 211 - 31 - - - - font: 7pt "Arial"; -border-radius: 10px; - - - Еще нет аккаунта? Зарегистрируйтесь! - - - - - - - 0 - 0 - 362 - 26 - - - - - - - 0 - 0 - 16 - 25 - - - - - - - \ No newline at end of file diff --git a/ui/authorization-windows/ui/registration_window.ui b/ui/authorization-windows/ui/registration_window.ui deleted file mode 100644 index d0b20d4..0000000 --- a/ui/authorization-windows/ui/registration_window.ui +++ /dev/null @@ -1,99 +0,0 @@ - - - RegistrationWindow - - - - 0 - 0 - 390 - 562 - - - - Dialog - - - - - 90 - 290 - 201 - 51 - - - - font: 700 12pt "Arial"; -border-radius: 10px; - - - Зарегистрироваться - - - - - - 110 - 350 - 161 - 31 - - - - font: 7pt "Arial"; -border-radius: 10px; - - - Уже есть аккаунт? Войдите! - - - - - - 60 - 140 - 261 - 41 - - - - - - - 60 - 190 - 261 - 41 - - - - - - - 60 - 240 - 261 - 41 - - - - - - - 100 - 90 - 201 - 51 - - - - font: 700 19pt "Arial"; - - - Регистрация - - - - - - \ No newline at end of file diff --git a/ui/main-window/CMakeLists.txt b/ui/main-window/CMakeLists.txt deleted file mode 100644 index c8e1f85..0000000 --- a/ui/main-window/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(MainWindow LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/*.cpp") -file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS "include/*.h") -file(GLOB UI_FILES CONFIGURE_DEPENDS "ui/*.ui") - -add_library(MainWindow STATIC - ${SOURCES} - ${HEADERS} - ${UI_FILES} -) - -target_include_directories(MainWindow PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR} -) - -target_link_libraries(MainWindow PRIVATE - Qt6::Widgets - Qt6::Core - Qt6::Gui - Qt6::Sql - Scripts - Database - NoteWidget -) \ No newline at end of file diff --git a/ui/main-window/MainWindow_en_US.ts b/ui/main-window/MainWindow_en_US.ts deleted file mode 100644 index a3740fb..0000000 --- a/ui/main-window/MainWindow_en_US.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/ui/main-window/include/applicationwindow.h b/ui/main-window/include/applicationwindow.h deleted file mode 100644 index ccb14f8..0000000 --- a/ui/main-window/include/applicationwindow.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef APPLICATIONWINDOW_H -#define APPLICATIONWINDOW_H - -#include - -namespace Ui { -class ApplicationWindow : public QMainWindow { - Q_OBJECT - -public: - explicit ApplicationWindow(std::string window_name); - -signals: -}; -} // namespace Ui -#endif // APPLICATIONWINDOW_H \ No newline at end of file diff --git a/ui/main-window/include/bottombar.h b/ui/main-window/include/bottombar.h deleted file mode 100644 index cc7945c..0000000 --- a/ui/main-window/include/bottombar.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef BOTTOMBAR_H -#define BOTTOMBAR_H - -#include -#include -#include -#include - -namespace Ui { -class BottomBar : public QWidget { - QHBoxLayout *main_layout_; - QLabel *project_name_; - QLabel *username_; - -public: - BottomBar( - QWidget *parent_, - std::string username_, - std::string project_name_ - ); -}; -} // namespace Ui - -#endif // BOTTOMBAR_H \ No newline at end of file diff --git a/ui/main-window/include/main_window_style.hpp b/ui/main-window/include/main_window_style.hpp deleted file mode 100644 index 3e594c1..0000000 --- a/ui/main-window/include/main_window_style.hpp +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef MAIN_WINDOW_STYLE_HPP -#define MAIN_WINDOW_STYLE_HPP -#include - -namespace Ui { - -QString main_window_style = R"( -#main-window { - background-color : #f5f5f5; -} - -#main-window QLabel { - font-weight: bold; -} - -QScrollBar:vertical { - border: none; - background: transparent; - width: 10px; - margin: 0; -} -QScrollBar::handle:vertical { - background: #c0c0c0; - border-radius: 5px; - min-height: 20px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, - QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; - height: 0px; - } -QScrollArea { - border: none; - background: transparent; -} - -#ProjectList { - background-color: white; - border-radius: 8px; - padding: 8px; - outline: 0; - font-family: 'Arial'; - font-weight: bold; - font-size: 14px; - color:rgb(44, 44, 44); -} - -#ProjectList::item { - background-color:rgb(207, 236, 233); - border: none; - padding: 10px; - margin: 2px; - border-radius: 6px; -} - -#ProjectList::item:hover { - background-color: rgb(178, 226, 221); -} - -#ProjectList::item:selected { - background-color: #089083; - color: #ffffff; -} - -#ProjectList QScrollBar:vertical { - border: none; - background: #FED6BC; - width: 10px; - margin: 0; -} - -#ProjectList QScrollBar::handle:vertical { - background: #c0c0c0; - border-radius: 5px; - min-height: 20px; -} - -#ProjectList QScrollBar::add-line:vertical, -#ProjectList QScrollBar::sub-line:vertical { - background: none; -} - -#ProjectList QScrollBar::add-page:vertical, -#ProjectList QScrollBar::sub-page:vertical { - background: none; -} - -#BottomBar { - background-color: rgb(33, 44, 50); - border-radius: 8px; - padding: 8px; - outline: 0; -} - -#BottomBar QLabel { - color : white; - font-family: 'Arial'; - font-size: 14px; -} - -#NoteList { - border-radius : 8px; - background-color: white; -} - -#NoteWidget { - background-color: #ffdda2; - border-radius: 8px; -} - -#NoteWidget QPushButton { - font-family: 'Arial'; - font-size: 13px; - font-weight: bold; - border-radius: 10px; - background-color:rgb(241, 201, 132); - color: rgb(33, 44, 50); - padding: 5px 10px; -} - -QPushButton { - font-family: 'Arial'; - font-size: 13px; - font-weight: bold; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - min-width: 60px; - min-height: 25px; -} - -)"; - - -} // namespace Ui - -#endif // MAIN_WINDOW_STYLE_HPP \ No newline at end of file diff --git a/ui/main-window/include/mainwindow.h b/ui/main-window/include/mainwindow.h deleted file mode 100644 index e5242f7..0000000 --- a/ui/main-window/include/mainwindow.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include "bottombar.h" -#include "notelist.h" -#include "projectlist.h" -#include "storage.hpp" - -namespace Ui { -class MainWindow : public QWidget { - Q_OBJECT - std::string username; - QVBoxLayout *main_layout_; - BottomBar *top_bar_; - QHBoxLayout *content_layout_; - ProjectList *project_list_; - NoteList *note_list_; - QWidget *content_widget_; - QPushButton *new_project_button_; - QPushButton *new_note_button_; - project_storage_model::Storage *storage_; - - friend ProjectList; -private slots: - void add_project(); - void add_note(); - -public: - explicit MainWindow( - QWidget *parent = nullptr, - std::string username = "none", - project_storage_model::Storage *storage = nullptr - ); -}; -} // namespace Ui -#endif // MAINWINDOW_H \ No newline at end of file diff --git a/ui/main-window/include/notelist.h b/ui/main-window/include/notelist.h deleted file mode 100644 index f406536..0000000 --- a/ui/main-window/include/notelist.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef NOTELIST_H -#define NOTELIST_H -#include -#include -#include -#include -#include "note.hpp" - -namespace Ui { -class NoteList : public QWidget { - friend class MainWindow; - Q_OBJECT - - QHBoxLayout *main_layout_; - - std::vector vertical_layouts_; - - int note_counter_ = 0; - -public: - void add_note_widget(const project_storage_model::Note *note); - void clear_note_list(); - NoteList(QWidget *parent); - -public slots: - void load_project_notes(QListWidgetItem *project); -}; -} // namespace Ui -#endif // NOTELIST_H \ No newline at end of file diff --git a/ui/main-window/include/notewidget.h b/ui/main-window/include/notewidget.h deleted file mode 100644 index f1f8f6a..0000000 --- a/ui/main-window/include/notewidget.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef NOTEWIDGET_H -#define NOTEWIDGET_H - -#include -#include -#include -#include -#include -#include "note.hpp" - -namespace Ui { -class NoteWidget : public QWidget { - Q_OBJECT - const project_storage_model::Note *model_note_; - QVBoxLayout *main_layout_; - QPushButton *open_button_; - QLabel *title_label_; - QLabel *text_label_; - -public: - explicit NoteWidget( - QWidget *parent = nullptr, - const project_storage_model::Note *model_note = nullptr - ); - -private slots: - - void open_note_window() const; -}; -} // namespace Ui -#endif // NOTEWIDGET_H \ No newline at end of file diff --git a/ui/main-window/include/projectitem.h b/ui/main-window/include/projectitem.h deleted file mode 100644 index 8b4a4a2..0000000 --- a/ui/main-window/include/projectitem.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PROJECTITEM_H -#define PROJECTITEM_H - -#include -#include -#include "notelist.h" -#include "project.hpp" - -namespace Ui { -class ProjectItem : public QListWidgetItem { - project_storage_model::Project *project_; - friend NoteList; - friend class MainWindow; - -public: - ProjectItem(QListWidget *listview, project_storage_model::Project *project); -}; -} // namespace Ui -#endif // PROJECTITEM_H \ No newline at end of file diff --git a/ui/main-window/include/projectlist.h b/ui/main-window/include/projectlist.h deleted file mode 100644 index d221695..0000000 --- a/ui/main-window/include/projectlist.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef PROJECTLIST_H -#define PROJECTLIST_H - -#include -#include -#include "project.hpp" -#include "storage.hpp" - -namespace Ui { -class ProjectList : public QListWidget { - Q_OBJECT - friend class MainWindow; - void add_project(project_storage_model::Project *project); - void load_projects(project_storage_model::Storage *storage); - -public: - explicit ProjectList(QWidget *parent = nullptr); - -signals: -}; -} // namespace Ui -#endif // PROJECTLIST_H \ No newline at end of file diff --git a/ui/main-window/src/applicationwindow.cpp b/ui/main-window/src/applicationwindow.cpp deleted file mode 100644 index 62c7bfe..0000000 --- a/ui/main-window/src/applicationwindow.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "applicationwindow.h" -#include -#include "mainwindow.h" - -namespace Ui { -ApplicationWindow::ApplicationWindow(std::string window_name_) - : QMainWindow{nullptr} { - this->setObjectName("ApplicationWindow"); - - this->setAttribute(Qt::WA_StyledBackground); - - this->setWindowTitle(window_name_.c_str()); -} -} // namespace Ui \ No newline at end of file diff --git a/ui/main-window/src/bottombar.cpp b/ui/main-window/src/bottombar.cpp deleted file mode 100644 index ee63c71..0000000 --- a/ui/main-window/src/bottombar.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "bottombar.h" -#include -#include -#include -#include - -namespace Ui { -BottomBar::BottomBar( - QWidget *parent, - std::string username, - std::string project_name -) - : QWidget(parent), - main_layout_(new QHBoxLayout()), - username_(new QLabel(username.c_str())), - project_name_(new QLabel(project_name.c_str())) { - this->setObjectName("BottomBar"); - this->setLayout(main_layout_); - this->setFixedHeight(40); - - project_name_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - username_->setAlignment(Qt::AlignVCenter | Qt::AlignRight); - - this->setSizePolicy( - QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum) - ); - - main_layout_->addWidget(project_name_); - main_layout_->addWidget(username_); -} -} // namespace Ui \ No newline at end of file diff --git a/ui/main-window/src/mainwindow.cpp b/ui/main-window/src/mainwindow.cpp deleted file mode 100644 index 907f902..0000000 --- a/ui/main-window/src/mainwindow.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "mainwindow.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "bottombar.h" -#include "lr_dao.hpp" -#include "main_window_style.hpp" -#include "note_dao.hpp" -#include "notelist.h" -#include "project.hpp" -#include "project_dao.hpp" -#include "projectitem.h" -#include "projectlist.h" - -namespace Ui { -MainWindow::MainWindow( - QWidget *parent, - std::string username, - project_storage_model::Storage *storage -) - : QWidget(parent), - username(username), - main_layout_(new QVBoxLayout(this)), - top_bar_(new BottomBar(this, username, "EFFICIO :: Таск-Трекер")), - content_layout_(new QHBoxLayout(this)), - project_list_(new ProjectList(this)), - note_list_(new NoteList(this)), - content_widget_(new QWidget(this)), - new_project_button_(new QPushButton("Новый проект", this)), - new_note_button_(new QPushButton("Новая заметка", this)), - storage_(storage) { - this->setObjectName("main-window"); - this->setAttribute(Qt::WA_StyledBackground); - this->setMinimumSize(QSize(800, 600)); - this->setStyleSheet(main_window_style); - - main_layout_->addWidget(top_bar_, Qt::AlignTop); - main_layout_->setAlignment(Qt::AlignCenter); - main_layout_->addWidget(content_widget_); - content_widget_->setLayout(content_layout_); - auto right_layout = new QVBoxLayout(content_widget_); - right_layout->addWidget(project_list_); - right_layout->addWidget(new_project_button_); - right_layout->addWidget(new_note_button_); - QScrollArea* scrollArea = new QScrollArea(content_widget_); - scrollArea->setWidgetResizable(true); - scrollArea->setWidget(note_list_); - content_layout_->addWidget(scrollArea, Qt::AlignRight); - content_layout_->addLayout(right_layout); - main_layout_->addWidget(content_widget_); - this->setLayout(main_layout_); - - this->project_list_->load_projects(storage); - - connect( - project_list_, &QListWidget::itemClicked, note_list_, - &NoteList::load_project_notes - ); - connect( - new_note_button_, &QPushButton::clicked, this, &Ui::MainWindow::add_note - ); - connect( - new_project_button_, &QPushButton::clicked, this, - &Ui::MainWindow::add_project - ); -} - -void MainWindow::add_project() { - bool ok; - QString name_of_project = QInputDialog::getText( - nullptr, "Название проекта:", "Введите название", QLineEdit::Normal, "", - &ok - ); - if (ok) { - int id = 0; - - if (DB::ProjectDAO::create_project(name_of_project.toStdString(), id)) { - LRDao::add_project_to_user(username, id); - auto &project = storage_->add_project( - Project(id, name_of_project.toStdString(), "") - ); - project_list_->add_project(&project); - } - } -} - -void MainWindow::add_note() { - auto project_item = - dynamic_cast(project_list_->currentItem()); - if (project_item) { - if (int id = 0; NoteDao::initialize_note(id)) { - DB::ProjectDAO::add_note_to_project( - project_item->project_->get_id(), id - ); - auto ¬e = - project_item->project_->add_note({id, "Пустая заметка", ""}); - note_list_->add_note_widget(¬e); - } - } else { - QMessageBox msg; - msg.setText("Проект не выбран!"); - msg.exec(); - } -} - -} // namespace Ui \ No newline at end of file diff --git a/ui/main-window/src/notelist.cpp b/ui/main-window/src/notelist.cpp deleted file mode 100644 index 4af7558..0000000 --- a/ui/main-window/src/notelist.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "notelist.h" -#include -#include -#include -#include -#include -#include "note.hpp" -#include "notewidget.h" -#include "projectitem.h" - -namespace Ui { -NoteList::NoteList(QWidget *parent) - : QWidget(parent), - main_layout_(new QHBoxLayout(this)), - vertical_layouts_(std::vector()){ - - this->setAttribute(Qt::WA_StyledBackground); - this->setObjectName("NoteList"); - this->setLayout(main_layout_); - vertical_layouts_.resize(4, nullptr); - for (auto &layout : vertical_layouts_) { - layout = new QVBoxLayout(this); - - main_layout_->addLayout(layout); - } -} - -void NoteList::add_note_widget(const project_storage_model::Note *note) { - auto current_layout = vertical_layouts_[note_counter_ % 4]; - if (current_layout->count() > 1) { - current_layout->removeItem( - current_layout->itemAt(current_layout->count() - 1) - ); - } - vertical_layouts_[note_counter_ % 4]->addWidget( - new NoteWidget(this, note), 0, Qt::AlignTop - ); - current_layout->addStretch(); - note_counter_++; -} - -void NoteList::load_project_notes(QListWidgetItem *project) { - ProjectItem *p = dynamic_cast(project); - assert(p != nullptr); - qDebug() << "Адрес проекта" - << QString::fromStdString(p->project_->get_name()) << ":" - << p->project_; - this->clear_note_list(); - note_counter_ = 0; - for (const auto ¬e : p->project_->get_notes()) { - this->add_note_widget(¬e); - } -} - -void NoteList::clear_note_list() { - for (auto &layout : vertical_layouts_) { - while (layout->count()) { - auto item = layout->takeAt(0); - auto widget = item->widget(); - if (widget) { - widget->deleteLater(); - } - } - } -} -} // namespace Ui \ No newline at end of file diff --git a/ui/main-window/src/notewidget.cpp b/ui/main-window/src/notewidget.cpp deleted file mode 100644 index 3ced750..0000000 --- a/ui/main-window/src/notewidget.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "notewidget.h" -#include -#include -#include -#include "note.hpp" -#include "note_edit_dialog.h" - -namespace Ui { -NoteWidget::NoteWidget( - QWidget *parent, - const project_storage_model::Note *model_note -) - : QWidget(parent), - model_note_(model_note), - main_layout_(new QVBoxLayout(this)), - open_button_(new QPushButton("Открыть")) { - this->setObjectName("NoteWidget"); - this->setMinimumWidth(100); - this->setFixedHeight(100); - title_label_ = new QLabel(model_note_->get_title().c_str(), this); - text_label_ = new QLabel(model_note_->get_text().c_str(), this); - - title_label_->setStyleSheet("color: rgb(33, 44, 50);"); - text_label_->setStyleSheet("color: rgb(33, 44, 50);"); - - main_layout_->addWidget(title_label_); - main_layout_->addWidget(text_label_); - - text_label_->setWordWrap(false); - title_label_->setWordWrap(false); - text_label_->setTextInteractionFlags(Qt::TextSelectableByMouse); - - main_layout_->addWidget(open_button_); - - connect( - open_button_, &QPushButton::clicked, this, &NoteWidget::open_note_window - ); - this->setLayout(main_layout_); - this->setAttribute(Qt::WA_StyledBackground); -} - -void NoteWidget::open_note_window() const { - auto dialog = new ::NoteEditDialog( - const_cast(qobject_cast(this)), - const_cast(model_note_) - ); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->exec(); - text_label_->setText(model_note_->get_text().c_str()); - title_label_->setText(model_note_->get_title().c_str()); - main_layout_->update(); -} -} // namespace Ui \ No newline at end of file diff --git a/ui/main-window/src/projectitem.cpp b/ui/main-window/src/projectitem.cpp deleted file mode 100644 index 4f38e85..0000000 --- a/ui/main-window/src/projectitem.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "projectitem.h" -#include - -namespace Ui { -ProjectItem::ProjectItem( - QListWidget *list_view, - project_storage_model::Project *project -) - : project_(project), - QListWidgetItem(project->get_name().c_str(), list_view) { -} -} // namespace Ui \ No newline at end of file diff --git a/ui/main-window/src/projectlist.cpp b/ui/main-window/src/projectlist.cpp deleted file mode 100644 index 3bd7828..0000000 --- a/ui/main-window/src/projectlist.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "projectlist.h" -#include -#include "mainwindow.h" -#include "project.hpp" -#include "projectitem.h" - -namespace Ui { - -ProjectList::ProjectList(QWidget *parent) : QListWidget{parent} { - this->setObjectName("ProjectList"); - this->setFixedWidth(200); -} - -void ProjectList::add_project(project_storage_model::Project *project) { - this->addItem(new ProjectItem(static_cast(this), project)); -} - -void ProjectList::load_projects(project_storage_model::Storage *storage) { - for (project_storage_model::Project &pr : storage->get_projects()) { - add_project(&pr); - } -} - -} // namespace Ui \ No newline at end of file diff --git a/ui/note-widget/CMakeLists.txt b/ui/note-widget/CMakeLists.txt deleted file mode 100644 index d062724..0000000 --- a/ui/note-widget/CMakeLists.txt +++ /dev/null @@ -1,40 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(NoteWidget LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) - -set(UI_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/ui/note_edit_dialog.ui -) - -set(SOURCES - src/note_edit_dialog.cpp - src/tags_dialog.cpp -) - -set(HEADERS - include/note_edit_dialog.h - include/tags_dialog.h -) - -file(GLOB TS_FILES "*.ts") - -add_library(NoteWidget STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) - -target_include_directories(NoteWidget - PUBLIC - $ - $ -) - -target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database Scripts) \ No newline at end of file diff --git a/ui/note-widget/NoteWidgetEfficio_ru_RU.ts b/ui/note-widget/NoteWidgetEfficio_ru_RU.ts deleted file mode 100644 index 32bf76c..0000000 --- a/ui/note-widget/NoteWidgetEfficio_ru_RU.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/ui/note-widget/include/note_edit_dialog.h b/ui/note-widget/include/note_edit_dialog.h deleted file mode 100644 index 46048a3..0000000 --- a/ui/note-widget/include/note_edit_dialog.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef NOTE_EDIT_DIALOG_H -#define NOTE_EDIT_DIALOG_H - -#include -#include -#include -#include "note.hpp" -#include "note_dao.hpp" -#include "tags_dialog.h" - -using namespace project_storage_model; - -QT_BEGIN_NAMESPACE - -namespace Ui { -class NoteEditDialog; -} - -QT_END_NAMESPACE - -class NoteEditDialog final : public QDialog { - Q_OBJECT - -public: - explicit NoteEditDialog( - QWidget* parent = nullptr, - Note* note = new Note(0, "NULL", "NULL") - ); - ~NoteEditDialog() override; - -private slots: - void on_save_button_click(); - void on_join_button_click(); - void on_add_members_button_click(); - void on_add_tags_button_click(); - -private: - void init_basic_fields(); - void init_additional_fields(); - void setup_connections(); - void setup_ui(); - - void add_member_avatar(const std::string& member); - - void clear_member_avatars(); - void update_tags_display(); - static QString create_tag_style_sheet(const QString& color); - - [[nodiscard]] bool try_save_note() const; - - Ui::NoteEditDialog* ui_{}; - std::vector> member_avatars_; - std::vector> tag_labels_; - QList selected_tags_; - Note* note_; -}; - -#endif // NOTE_EDIT_DIALOG_H \ No newline at end of file diff --git a/ui/note-widget/include/note_edit_dialog_styles.h b/ui/note-widget/include/note_edit_dialog_styles.h deleted file mode 100644 index 5ad1b63..0000000 --- a/ui/note-widget/include/note_edit_dialog_styles.h +++ /dev/null @@ -1,174 +0,0 @@ -#ifndef NOTE_EDIT_DIALOG_STYLES_H -#define NOTE_EDIT_DIALOG_STYLES_H - -#include - -namespace Ui { -QString light_theme = R"( - QDialog { - background-color: #f5f5f5; - } - - /* Заголовок и название проекта */ - - QLineEdit#titleLineEdit { - font-family: 'Arial'; - font-size: 25px; - font-weight: bold; - color: #089083; - border: none; - padding: 0; - background: transparent; - } - - QLabel#projectNameLabel { - font-family: 'Arial'; - font-size: 13px; - color: #727272; - padding: 1px; - } - - /* Описание заметки */ - - QLabel#descriptionLabel { - font-family: 'Arial'; - font-size: 18px; - font-weight: bold; - color: black; - } - - QTextEdit#descriptionTextEdit { - border-radius: 10px; - border: 1px solid #ffffff; - background: #ffffff; - color: black; - padding: 5px; - } - - QTextEdit#descriptionTextEdit::placeholder { - color: #727272; - } - - /* Основные кнопки */ - - QHBoxLayout#buttonsLayout { - align: left; - } - - QPushButton#saveButton { - font-family: 'Arial'; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - } - - QPushButton#saveButton:hover { - background-color: #d58745; - } - - QPushButton#cancelButton { - font-family: 'Arial'; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#cancelButton:hover { - background-color: #dadada; - } - - /* Боковое меню */ - - QPushButton#joinButton, - QPushButton#addMembersButton, - QPushButton#addDateButton, - QPushButton#addTagsButton { - font-family: 'Arial'; - background-color: #089083; - color: #ffffff; - padding: 5px 10px; - border-radius: 10px; - font-weight: bold; - text-align: left; - } - - QPushButton#joinButton:hover, - QPushButton#addMembersButton:hover, - QPushButton#addDateButton:hover, - QPushButton#addTagsButton:hover { - background-color: #01635d; - } - - QLabel#sidePanelLabel, - QLabel#sidePanelLabel_2 { - font-family: 'Arial'; - font-size: 14px; - font-weight: bold; - color: #727272; - } - - QLabel#membersLabel, - QLabel#tagsLabel, - QLabel#dateLabel { - color: #727272; - } - - /* Дополнительные элементы заметки */ - - QDateEdit#dateEdit { - font-family: 'Arial'; - border-radius: 5px; - background-color: #ffdda2; - color: #050505; - padding: 5px 5px; - border: none; - width: 70px; - height: 22px; - text-align: center; - font-weight: bold; - } - QDateEdit#dateEdit::drop-down { - border: none; - width: 20px; - } - - /* Окно с сообщением */ - - QMessageBox { - background-color: #ffffff; - } - - QMessageBox QLabel { - font-family: 'Arial'; - font-size: 14px; - color: #000000; - } - - QMessageBox QPushButton { - font-family: 'Arial'; - font-size: 13px; - font-weight: bold; - color: white; - background-color: #fea36b; - border-radius: 10px; - padding: 5px 15px; - min-width: 60px; - min-height: 25px; - } - - QMessageBox QPushButton:hover { - background-color: #d58745; - color: white; - } - - QMessageBox QPushButton:pressed { - background-color: #d58745; - border-color: #d58745; - } -)"; - -} // namespace Ui - -#endif // NOTE_EDIT_DIALOG_STYLES_H \ No newline at end of file diff --git a/ui/note-widget/include/tags_dialog.h b/ui/note-widget/include/tags_dialog.h deleted file mode 100644 index f8496c0..0000000 --- a/ui/note-widget/include/tags_dialog.h +++ /dev/null @@ -1,41 +0,0 @@ -#ifndef TAGS_DIALOG_H -#define TAGS_DIALOG_H - -#include -#include -#include -#include -#include -#include - -class TagsDialog final : public QDialog { - Q_OBJECT - -public: - const int MAX_TAGS_COUNT = 5; - const std::pair DIALOG_SIZE = std::make_pair(300, 250); - - struct Tag { - bool is_checked; - QString color; - QString name; - }; - - explicit TagsDialog( - const QList &initial_tags = QList(), - QWidget *parent = nullptr - ); - - [[nodiscard]] QList get_selected_tags() const; - -private: - void setup_ui(); - - std::unique_ptr check_boxes_[5]; - std::unique_ptr color_combo_boxes_[5]; - std::unique_ptr name_line_edits_[5]; - std::unique_ptr ok_button_; - std::unique_ptr cancel_button_; -}; - -#endif // TAGS_DIALOG_H \ No newline at end of file diff --git a/ui/note-widget/include/tags_dialog_styles.h b/ui/note-widget/include/tags_dialog_styles.h deleted file mode 100644 index 15fd320..0000000 --- a/ui/note-widget/include/tags_dialog_styles.h +++ /dev/null @@ -1,123 +0,0 @@ -#ifndef TAGS_DIALOG_STYLES_H -#define TAGS_DIALOG_STYLES_H - -#include "tags_dialog.h" - -namespace Ui { -QString tags_dialog_light_theme = R"( - QDialog { - background-color: #f5f5f5; - } - - /* Чекбоксы */ - QCheckBox { - font-family: 'Arial'; - color: #727272; - spacing: 5px; - } - - QCheckBox::indicator { - width: 16px; - height: 16px; - border-radius: 3px; - border: 1px solid #727272; - background-color: #ffffff; - } - - QCheckBox::indicator:checked { - background-color: #fea36b; - border: 1px solid #fea36b; - image: url(:/images/check.png); - } - - QCheckBox::indicator:hover { - border: 1px solid #fea36b; - } - - /* Выпадающие списки цветов */ - QComboBox { - font-family: 'Arial'; - border-radius: 10px; - background-color: #ffffff; - color: #727272; - padding: 5px 10px; - border: 1px solid #dadada; - } - - QComboBox:hover { - border: 1px solid #089083; - } - - QComboBox::drop-down { - width: 20px; - border: none; - } - - QComboBox::down-arrow { - image: url(:/images/down_arrow.png); - } - - QComboBox QAbstractItemView { - background-color: #ffffff; - color: #727272; - selection-background-color: #fea36b; - selection-color: white; - border: 1px solid #dadada; - border-radius: 5px; - } - - /* Поля ввода имени тега */ - QLineEdit { - font-family: 'Arial'; - border-radius: 10px; - background-color: #ffffff; - color: #727272; - padding: 5px 10px; - border: 1px solid #dadada; - } - - QLineEdit:hover, - QLineEdit:focus { - border: 1px solid #089083; - } - - QLineEdit::placeholder { - color: #a0a0a0; - } - - /* Кнопка OK */ - QPushButton#ok_button { - font-family: 'Arial'; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - font-weight: bold; - width: 30px; - height: 25px; - } - - QPushButton#ok_button:hover { - background-color: #d58745; - } - - /* Кнопка Отмена */ - QPushButton#cancel_button { - font-family: 'Arial'; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - font-weight: bold; - border: 1px solid #fea36b; - width: 30px; - height: 25px; - } - - QPushButton#cancel_button:hover { - background-color: #dadada; - } -)"; -} - -#endif // TAGS_DIALOG_STYLES_H \ No newline at end of file diff --git a/ui/note-widget/src/note_edit_dialog.cpp b/ui/note-widget/src/note_edit_dialog.cpp deleted file mode 100644 index ed18200..0000000 --- a/ui/note-widget/src/note_edit_dialog.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include "note_edit_dialog.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "./ui_note_edit_dialog.h" -#include "note_edit_dialog_styles.h" -#include "tags_dialog.h" - -NoteEditDialog::NoteEditDialog(QWidget* parent, Note* note) - : QDialog(parent), - ui_(new Ui::NoteEditDialog), - note_(note) { - ui_->setupUi(this); - setWindowTitle("EFFICIO"); - - init_basic_fields(); - init_additional_fields(); - setup_connections(); - setup_ui(); -} - -NoteEditDialog::~NoteEditDialog() { - delete ui_; -} - -void NoteEditDialog::init_basic_fields() { - ui_->titleLineEdit->setText(QString::fromStdString(note_->get_title())); - ui_->descriptionTextEdit->setText(QString::fromStdString(note_->get_text())); -} - -void NoteEditDialog::init_additional_fields() { - if (!note_->get_date().empty()) { - QDate date = QDate::fromString(QString::fromStdString(note_->get_date()), "yyyy-MM-dd"); - if (date.isValid()) { - ui_->dateEdit->setDate(date); - ui_->dateLabel->setVisible(true); - ui_->dateEdit->setVisible(true); - } - } - - if (!note_->get_members().empty()) { - ui_->membersLabel->setVisible(true); - for (const auto& member : note_->get_members()) { - add_member_avatar(member); - } - ui_->joinButton->setText("Покинуть"); - } - - if (!note_->get_tags().empty()) { - ui_->tagsLabel->setVisible(true); - for (const auto& tag : note_->get_tags()) { - TagsDialog::Tag tag_info; - tag_info.is_checked = true; - tag_info.color = QString::fromStdString(tag.color); - tag_info.name = QString::fromStdString(tag.name); - selected_tags_.append(tag_info); - - auto tag_label = std::make_unique(tag_info.name, this); - tag_label->setStyleSheet(create_tag_style_sheet(tag_info.color)); - ui_->tagsLayout->addWidget(tag_label.get()); - tag_labels_.push_back(std::move(tag_label)); - } - } -} - -void NoteEditDialog::setup_connections() { - connect(ui_->saveButton, &QPushButton::clicked, this, &NoteEditDialog::on_save_button_click); - connect(ui_->cancelButton, &QPushButton::clicked, this, &NoteEditDialog::reject); - connect(ui_->joinButton, &QPushButton::clicked, this, &NoteEditDialog::on_join_button_click); - connect(ui_->addMembersButton, &QPushButton::clicked, this, &NoteEditDialog::on_add_members_button_click); - connect(ui_->addDateButton, &QPushButton::clicked, this, [this]() { - const bool is_visible = ui_->dateLabel->isVisible(); - ui_->dateLabel->setVisible(!is_visible); - ui_->dateEdit->setVisible(!is_visible); - }); - connect(ui_->addTagsButton, &QPushButton::clicked, this, &NoteEditDialog::on_add_tags_button_click); -} - -void NoteEditDialog::setup_ui() { - setFixedSize(700, 480); - setStyleSheet(Ui::light_theme); - ui_->buttonsLayout->setAlignment(Qt::AlignLeft); -} - -void NoteEditDialog::on_save_button_click() { - if (try_save_note()) { - QMessageBox::information(this, "Заметка сохранена", - QString("Заголовок: %1\nСодержимое: %2") - .arg(ui_->titleLineEdit->text(), ui_->descriptionTextEdit->toPlainText())); - } else { - QMessageBox::information(this, "Ошибка", "Не удалось сохранить заметку"); - } - close(); -} - -void NoteEditDialog::on_join_button_click() { - const bool is_joined = ui_->membersLabel->isVisible(); - ui_->membersLabel->setVisible(!is_joined); - - if (!is_joined) { - const std::string current_user = "TODO"; - add_member_avatar(current_user); - note_->add_member(current_user); - ui_->joinButton->setText("Покинуть"); - } else { - std::string current_user = "TODO"; - clear_member_avatars(); - ui_->joinButton->setText("Присоединиться"); - } -} - -void NoteEditDialog::add_member_avatar(const std::string& member) { - QPixmap pixmap(32, 32); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - painter.setBrush(Qt::black); - painter.setPen(Qt::NoPen); - painter.drawEllipse(0, 0, 32, 32); - - auto member_label = std::make_unique(this); - member_label->setPixmap(pixmap); - member_label->setFixedSize(32, 32); - ui_->avatarsLayout->addWidget(member_label.get()); - member_avatars_.push_back(std::move(member_label)); -} - -void NoteEditDialog::clear_member_avatars() { - for (auto& avatar : member_avatars_) { - ui_->avatarsLayout->removeWidget(avatar.get()); - } - member_avatars_.clear(); -} - -void NoteEditDialog::on_add_members_button_click() { - QMessageBox::information(this, "Ошибка", "Другие участники не найдены :("); -} - -void NoteEditDialog::on_add_tags_button_click() { - TagsDialog tags_dialog(selected_tags_, this); - if (tags_dialog.exec() == Accepted) { - selected_tags_ = tags_dialog.get_selected_tags(); - update_tags_display(); - } -} - -void NoteEditDialog::update_tags_display() { - for (auto& tag_label : tag_labels_) { - ui_->tagsLayout->removeWidget(tag_label.get()); - } - tag_labels_.clear(); - - ui_->tagsLabel->setVisible(!selected_tags_.empty()); - for (const auto &[is_checked, color, name] : selected_tags_) { - if (is_checked) { - auto tag_label = std::make_unique(name, this); - tag_label->setStyleSheet(create_tag_style_sheet(color)); - ui_->tagsLayout->addWidget(tag_label.get()); - tag_labels_.push_back(std::move(tag_label)); - } - } -} - -QString NoteEditDialog::create_tag_style_sheet(const QString& color) { - return QString( - "background-color: %1; " - "color: white; " - "padding: 9px 10px; " - "border-radius: 5px; " - "font-family: 'Arial'; " - "font-size: 12px; " - "font-weight: bold;" - "width: 40px;" - "height: 25px;" - ).arg(color); -} - -bool NoteEditDialog::try_save_note() const { - note_->set_title(ui_->titleLineEdit->text().toStdString()); - note_->set_text(ui_->descriptionTextEdit->toPlainText().toStdString()); - note_->set_date(ui_->dateEdit->date().toString("yyyy-MM-dd").toStdString()); - - note_->clear_tags(); - for (const auto &[is_checked, color, name] : selected_tags_) { - if (is_checked) { - note_->add_tag(name.toStdString(), color.toStdString()); - } - } - - return NoteDao::update_note(*note_); -} \ No newline at end of file diff --git a/ui/note-widget/src/tags_dialog.cpp b/ui/note-widget/src/tags_dialog.cpp deleted file mode 100644 index 5a3d61e..0000000 --- a/ui/note-widget/src/tags_dialog.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "tags_dialog.h" -#include -#include -#include -#include "tags_dialog_styles.h" - -TagsDialog::TagsDialog(const QList &initial_tags, QWidget *parent) - : QDialog(parent) { - setup_ui(); - - for (int i = 0; i < qMin(MAX_TAGS_COUNT, initial_tags.size()); ++i) { - const auto &[is_checked, color, name] = initial_tags[i]; - check_boxes_[i]->setChecked(is_checked); - - const int color_index = color_combo_boxes_[i]->findData(color); - if (color_index != -1) { - color_combo_boxes_[i]->setCurrentIndex(color_index); - } - - name_line_edits_[i]->setText(name); - } - - setWindowTitle("Добавить теги"); - setModal(true); - setFixedSize(DIALOG_SIZE.first, DIALOG_SIZE.second); - setStyleSheet(Ui::tags_dialog_light_theme); -} - -void TagsDialog::setup_ui() { - auto *main_layout = new QVBoxLayout(this); - - for (int i = 0; i < MAX_TAGS_COUNT; ++i) { - auto *tag_layout = new QHBoxLayout(); - - check_boxes_[i] = std::make_unique(this); - check_boxes_[i]->setChecked(false); - tag_layout->addWidget(check_boxes_[i].get()); - - color_combo_boxes_[i] = std::make_unique(this); - color_combo_boxes_[i]->addItem("Красный", "#e7624b"); - color_combo_boxes_[i]->addItem("Синий", "#165d7b"); - color_combo_boxes_[i]->addItem("Розовый", "#bd6dab"); - color_combo_boxes_[i]->addItem("Зеленый", "#00b16b"); - color_combo_boxes_[i]->addItem("Желтый", "#e69f00"); - tag_layout->addWidget(color_combo_boxes_[i].get()); - - name_line_edits_[i] = std::make_unique(this); - name_line_edits_[i]->setPlaceholderText( - "Имя тега " + QString::number(i + 1) - ); - tag_layout->addWidget(name_line_edits_[i].get()); - - main_layout->addLayout(tag_layout); - } - - auto *button_layout = new QHBoxLayout; - - ok_button_ = std::make_unique("OK", this); - ok_button_->setObjectName("ok_button"); - button_layout->addWidget(ok_button_.get()); - - cancel_button_ = std::make_unique("Отмена", this); - cancel_button_->setObjectName("cancel_button"); - button_layout->addWidget(cancel_button_.get()); - - main_layout->addLayout(button_layout); - - connect(ok_button_.get(), &QPushButton::clicked, this, &TagsDialog::accept); - connect( - cancel_button_.get(), &QPushButton::clicked, this, &TagsDialog::reject - ); -} - -QList TagsDialog::get_selected_tags() const { - QList tags; - for (int i = 0; i < MAX_TAGS_COUNT; ++i) { - if (check_boxes_[i]->isChecked() && - !name_line_edits_[i]->text().isEmpty()) { - Tag tag; - tag.is_checked = true; - tag.color = color_combo_boxes_[i]->currentData().toString(); - tag.name = name_line_edits_[i]->text(); - tags.append(tag); - } - } - return tags; -} \ No newline at end of file diff --git a/ui/note-widget/ui/note_edit_dialog.ui b/ui/note-widget/ui/note_edit_dialog.ui deleted file mode 100644 index 60eebd8..0000000 --- a/ui/note-widget/ui/note_edit_dialog.ui +++ /dev/null @@ -1,369 +0,0 @@ - - - NoteEditDialog - - - - 0 - 0 - 700 - 480 - - - - NoteEditDialog - - - - - 20 - 20 - 451 - 434 - - - - - 45 - - - - - 6 - - - - - Название заметки - - - - - - - - Arial - 10 - false - true - - - - в проекте ToDo - - - - - - - - - 20 - - - - - 20 - - - Qt::AlignLeft - - - - - - - - Arial - 10 - true - - - - false - - - Участники - - - - - - - 5 - - - - - - - - - - - - Arial - 10 - true - - - - false - - - Срок - - - - - - - false - - - dd/MM/yyyy - - - true - - - - - - - - - - - - Arial - 10 - true - - - - Теги - - - false - - - - - - - 5 - - - - - - - - - - - - Arial - 14 - true - - - - Описание - - - - - - - - Arial - 10 - - - - Добавьте подробное описание для Вашей заметки. - - - - - - - 20 - - - - - - 70 - 45 - - - - - Arial - 11 - true - - - - Сохранить - - - - - - - - 70 - 45 - - - - - Arial - 11 - true - - - - Отмена - - - - - - - - - - - - - 504 - 20 - 171 - 291 - - - - - 30 - - - - - 6 - - - - - - Arial - 16 - false - false - - - - Предложения - - - - - - - 10 - - - - - - 0 - 40 - - - - Присоединиться - - - - - - - - - - - 12 - - - - - - Arial - 16 - false - false - - - - Добавить к заметке - - - - - - - 10 - - - - - - 0 - 40 - - - - Участники - - - - - - - - 0 - 40 - - - - Дата - - - - - - - - 0 - 40 - - - - Теги - - - - - - - - - - - - - \ No newline at end of file From 49fa0d0ea08ab1252803978210ec6230c4b2028c Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 00:46:22 +0300 Subject: [PATCH 04/47] return ui and scripts --- client/scripts/note.cpp | 43 ++ client/scripts/note.hpp | 33 ++ client/scripts/project.cpp | 46 +++ client/scripts/project.hpp | 32 ++ client/scripts/storage.cpp | 46 +++ client/scripts/storage.hpp | 27 ++ client/scripts/user.cpp | 40 ++ client/scripts/user.hpp | 29 ++ client/ui/MainWindow/CMakeLists.txt | 37 ++ client/ui/MainWindow/MainWindow_en_US.ts | 3 + .../ui/MainWindow/include/applicationwindow.h | 15 + client/ui/MainWindow/include/bottombar.h | 20 + client/ui/MainWindow/include/mainwindow.h | 39 ++ client/ui/MainWindow/include/notelist.h | 27 ++ client/ui/MainWindow/include/notewidget.h | 27 ++ client/ui/MainWindow/include/projectitem.h | 19 + client/ui/MainWindow/include/projectlist.h | 20 + .../ui/MainWindow/src/applicationwindow.cpp | 15 + client/ui/MainWindow/src/bottombar.cpp | 26 ++ client/ui/MainWindow/src/mainwindow.cpp | 166 ++++++++ client/ui/MainWindow/src/notelist.cpp | 61 +++ client/ui/MainWindow/src/notewidget.cpp | 27 ++ client/ui/MainWindow/src/projectitem.cpp | 9 + client/ui/MainWindow/src/projectlist.cpp | 18 + .../CMakeLists.txt | 61 +++ .../include/login_window.h | 33 ++ .../include/login_window_style_sheet.h | 105 +++++ .../include/registration_window.h | 32 ++ .../include/registration_window_style_sheet.h | 105 +++++ .../login_window_ru_RU.ts | 3 + .../src/login_window.cpp | 80 ++++ .../src/registration_window.cpp | 126 ++++++ .../ui/login_window.ui | 131 +++++++ .../ui/registration_window.ui | 99 +++++ client/ui/note-widget/CMakeLists.txt | 51 +++ .../ui/note-widget/NoteWidgetEfficio_ru_RU.ts | 3 + .../ui/note-widget/include/note_edit_dialog.h | 46 +++ .../include/note_edit_dialog_styles.h | 272 +++++++++++++ client/ui/note-widget/include/tags_dialog.h | 41 ++ .../note-widget/include/tags_dialog_styles.h | 123 ++++++ .../ui/note-widget/src/note_edit_dialog.cpp | 139 +++++++ client/ui/note-widget/src/tags_dialog.cpp | 87 +++++ client/ui/note-widget/ui/note_edit_dialog.ui | 369 ++++++++++++++++++ 43 files changed, 2731 insertions(+) create mode 100644 client/scripts/note.cpp create mode 100644 client/scripts/note.hpp create mode 100644 client/scripts/project.cpp create mode 100644 client/scripts/project.hpp create mode 100644 client/scripts/storage.cpp create mode 100644 client/scripts/storage.hpp create mode 100644 client/scripts/user.cpp create mode 100644 client/scripts/user.hpp create mode 100644 client/ui/MainWindow/CMakeLists.txt create mode 100644 client/ui/MainWindow/MainWindow_en_US.ts create mode 100644 client/ui/MainWindow/include/applicationwindow.h create mode 100644 client/ui/MainWindow/include/bottombar.h create mode 100644 client/ui/MainWindow/include/mainwindow.h create mode 100644 client/ui/MainWindow/include/notelist.h create mode 100644 client/ui/MainWindow/include/notewidget.h create mode 100644 client/ui/MainWindow/include/projectitem.h create mode 100644 client/ui/MainWindow/include/projectlist.h create mode 100644 client/ui/MainWindow/src/applicationwindow.cpp create mode 100644 client/ui/MainWindow/src/bottombar.cpp create mode 100644 client/ui/MainWindow/src/mainwindow.cpp create mode 100644 client/ui/MainWindow/src/notelist.cpp create mode 100644 client/ui/MainWindow/src/notewidget.cpp create mode 100644 client/ui/MainWindow/src/projectitem.cpp create mode 100644 client/ui/MainWindow/src/projectlist.cpp create mode 100644 client/ui/login-and-registration-windows/CMakeLists.txt create mode 100644 client/ui/login-and-registration-windows/include/login_window.h create mode 100644 client/ui/login-and-registration-windows/include/login_window_style_sheet.h create mode 100644 client/ui/login-and-registration-windows/include/registration_window.h create mode 100644 client/ui/login-and-registration-windows/include/registration_window_style_sheet.h create mode 100644 client/ui/login-and-registration-windows/login_window_ru_RU.ts create mode 100644 client/ui/login-and-registration-windows/src/login_window.cpp create mode 100644 client/ui/login-and-registration-windows/src/registration_window.cpp create mode 100644 client/ui/login-and-registration-windows/ui/login_window.ui create mode 100644 client/ui/login-and-registration-windows/ui/registration_window.ui create mode 100644 client/ui/note-widget/CMakeLists.txt create mode 100644 client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts create mode 100644 client/ui/note-widget/include/note_edit_dialog.h create mode 100644 client/ui/note-widget/include/note_edit_dialog_styles.h create mode 100644 client/ui/note-widget/include/tags_dialog.h create mode 100644 client/ui/note-widget/include/tags_dialog_styles.h create mode 100644 client/ui/note-widget/src/note_edit_dialog.cpp create mode 100644 client/ui/note-widget/src/tags_dialog.cpp create mode 100644 client/ui/note-widget/ui/note_edit_dialog.ui diff --git a/client/scripts/note.cpp b/client/scripts/note.cpp new file mode 100644 index 0000000..10ab536 --- /dev/null +++ b/client/scripts/note.cpp @@ -0,0 +1,43 @@ +#include "note.hpp" +#include +#include + +namespace project_storage_model { + +Note::Note(const int id, std::string title, std::string text) + : id_(id), title_(std::move(title)), text_(std::move(text)) { +} + +int Note::get_id() const { + return id_; +} + +const std::string &Note::get_title() const { + return title_; +} + +const std::string &Note::get_text() const { + return text_; +} + +const std::list &Note::get_tags() const { + return tags_; +} + +void Note::set_title(const std::string &title) { + this->title_ = title; +} + +void Note::set_text(const std::string &text) { + this->text_ = text; +} + +void Note::add_tag(const std::string &tag) { + tags_.push_back(tag); +} + +void Note::remove_tag(const std::string &tag) { + tags_.erase(std::remove(tags_.begin(), tags_.end(), tag), tags_.end()); +} + +} // namespace project_storage_model diff --git a/client/scripts/note.hpp b/client/scripts/note.hpp new file mode 100644 index 0000000..f8835a1 --- /dev/null +++ b/client/scripts/note.hpp @@ -0,0 +1,33 @@ +#ifndef NOTE_HPP +#define NOTE_HPP + +#include +#include + +namespace project_storage_model { + +class Note { +public: + Note() = default; + Note(int id, std::string title, std::string text); + + [[nodiscard]] int get_id() const; + [[nodiscard]] const std::string &get_title() const; + [[nodiscard]] const std::string &get_text() const; + [[nodiscard]] const std::list &get_tags() const; + + void set_title(const std::string &title); + void set_text(const std::string &text); + void add_tag(const std::string &tag); + void remove_tag(const std::string &tag); + +private: + int id_; + std::string title_; + std::string text_; + std::list tags_; +}; + +} // namespace project_storage_model + +#endif diff --git a/client/scripts/project.cpp b/client/scripts/project.cpp new file mode 100644 index 0000000..970c1be --- /dev/null +++ b/client/scripts/project.cpp @@ -0,0 +1,46 @@ +#include "project.hpp" +#include +#include + +namespace project_storage_model { + +Project::Project(const int id, std::string name, std::string description) + : id_(id), name_(std::move(name)), description_(std::move(description)) { +} + +int Project::get_id() const { + return id_; +} + +const std::string &Project::get_name() const { + return name_; +} + +const std::string &Project::get_description() const { + return description_; +} + +const std::list &Project::get_notes() const { + return notes_; +} + +Note &Project::add_note(const Note ¬e) { + notes_.push_back(note); + return notes_.back(); +} + +void Project::remove_note(int note_id) { + notes_.erase( + std::remove_if( + notes_.begin(), notes_.end(), + [note_id](const Note ¬e) { return note.get_id() == note_id; } + ), + notes_.end() + ); +} + +void Project::edit_description(const std::string &description) { + this->description_ = description; +} + +} // namespace project_storage_model diff --git a/client/scripts/project.hpp b/client/scripts/project.hpp new file mode 100644 index 0000000..2ff775d --- /dev/null +++ b/client/scripts/project.hpp @@ -0,0 +1,32 @@ +#ifndef PROJECT_HPP +#define PROJECT_HPP + +#include +#include +#include "note.hpp" + +namespace project_storage_model { + +class Project { +public: + Project(int id, std::string name, std::string description); + + [[nodiscard]] int get_id() const; + [[nodiscard]] const std::string &get_name() const; + [[nodiscard]] const std::string &get_description() const; + [[nodiscard]] const std::list &get_notes() const; + + Note &add_note(const Note ¬e); + void remove_note(int note_id); + void edit_description(const std::string &description); + +private: + int id_; + std::string name_; + std::string description_; + std::list notes_; +}; + +} // namespace project_storage_model + +#endif diff --git a/client/scripts/storage.cpp b/client/scripts/storage.cpp new file mode 100644 index 0000000..e0e454c --- /dev/null +++ b/client/scripts/storage.cpp @@ -0,0 +1,46 @@ +#include "storage.hpp" +#include + +namespace project_storage_model { + +Project &Storage::add_project(const Project &project) { + projects_.push_back(project); + return projects_.back(); +} + +void Storage::remove_project(int project_id) { + projects_.erase( + std::remove_if( + projects_.begin(), projects_.end(), + [project_id](const Project &project) { + return project.get_id() == project_id; + } + ), + projects_.end() + ); +} + +const std::list &Storage::get_projects() const { + return projects_; +} + +User &Storage::add_user(const User &user) { + users_.push_back(user); + return users_.back(); +} + +void Storage::remove_user(int user_id) { + users_.erase( + std::remove_if( + users_.begin(), users_.end(), + [user_id](const User &user) { return user.get_id() == user_id; } + ), + users_.end() + ); +} + +const std::list &Storage::get_users() const { + return users_; +} + +} // namespace project_storage_model diff --git a/client/scripts/storage.hpp b/client/scripts/storage.hpp new file mode 100644 index 0000000..2917c62 --- /dev/null +++ b/client/scripts/storage.hpp @@ -0,0 +1,27 @@ +#ifndef STORAGE_HPP +#define STORAGE_HPP + +#include +#include "project.hpp" +#include "user.hpp" + +namespace project_storage_model { + +class Storage { +public: + Project &add_project(const Project &project); + void remove_project(int project_id); + [[nodiscard]] const std::list &get_projects() const; + + User &add_user(const User &user); + void remove_user(int user_id); + [[nodiscard]] const std::list &get_users() const; + +private: + std::list projects_; + std::list users_; +}; + +} // namespace project_storage_model + +#endif diff --git a/client/scripts/user.cpp b/client/scripts/user.cpp new file mode 100644 index 0000000..833e82f --- /dev/null +++ b/client/scripts/user.cpp @@ -0,0 +1,40 @@ +#include "user.hpp" +#include +#include + +namespace project_storage_model { + +User::User(const int id, std::string username) + : id_(id), username_(std::move(username)) { +} + +int User::get_id() const { + return id_; +} + +const std::string &User::get_username() const { + return username_; +} + +const std::list &User::get_projects() const { + return projects_; +} + +Project &User::add_project(const Project &project) { + projects_.push_back(project); + return projects_.back(); +} + +void User::remove_project(int project_id) { + projects_.erase( + std::remove_if( + projects_.begin(), projects_.end(), + [project_id](const Project &project) { + return project.get_id() == project_id; + } + ), + projects_.end() + ); +} + +} // namespace project_storage_model diff --git a/client/scripts/user.hpp b/client/scripts/user.hpp new file mode 100644 index 0000000..00e3c45 --- /dev/null +++ b/client/scripts/user.hpp @@ -0,0 +1,29 @@ +#ifndef USER_HPP +#define USER_HPP + +#include +#include +#include "project.hpp" + +namespace project_storage_model { + +class User { +public: + User(int id, std::string username); + + [[nodiscard]] int get_id() const; + [[nodiscard]] const std::string &get_username() const; + [[nodiscard]] const std::list &get_projects() const; + + Project &add_project(const Project &project); + void remove_project(int project_id); + +private: + int id_; + std::string username_; + std::list projects_; +}; + +} // namespace project_storage_model + +#endif diff --git a/client/ui/MainWindow/CMakeLists.txt b/client/ui/MainWindow/CMakeLists.txt new file mode 100644 index 0000000..e4f7c3c --- /dev/null +++ b/client/ui/MainWindow/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.16) +project(MainWindow) + +set(CMAKE_CXX_STANDARD 17) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + + +#find_package(Qt6 REQUIRED COMPONENTS Widgets Core LinguistTools) +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Sql) +find_package(PostgreSQL REQUIRED) +link_directories(/path/to/postgresql/lib) + +file(GLOB MAIN_WINDOW_SRC "src/*") +file(GLOB MAIN_WINDOW_INCLUDE "include/*") + + +add_library(${PROJECT_NAME} + ${MAIN_WINDOW_SRC} + ${MAIN_WINDOW_INCLUDE} + MainWindow_en_US.ts +) + +include(GNUInstallDirs) +install(TARGETS ${PROJECT_NAME} + BUNDLE DESTINATION . + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) +target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core) +target_link_libraries(${PROJECT_NAME} PUBLIC project-storage-model) + +target_include_directories(${PROJECT_NAME} + PUBLIC ${PROJECT_SOURCE_DIR}/include +) \ No newline at end of file diff --git a/client/ui/MainWindow/MainWindow_en_US.ts b/client/ui/MainWindow/MainWindow_en_US.ts new file mode 100644 index 0000000..0d8b287 --- /dev/null +++ b/client/ui/MainWindow/MainWindow_en_US.ts @@ -0,0 +1,3 @@ + + + diff --git a/client/ui/MainWindow/include/applicationwindow.h b/client/ui/MainWindow/include/applicationwindow.h new file mode 100644 index 0000000..08ac0d7 --- /dev/null +++ b/client/ui/MainWindow/include/applicationwindow.h @@ -0,0 +1,15 @@ +#ifndef APPLICATIONWINDOW_H +#define APPLICATIONWINDOW_H + +#include +namespace Ui { +class ApplicationWindow : public QMainWindow { + Q_OBJECT + +public: + explicit ApplicationWindow(std::string window_name); + +signals: +}; +} // namespace Ui +#endif // APPLICATIONWINDOW_H diff --git a/client/ui/MainWindow/include/bottombar.h b/client/ui/MainWindow/include/bottombar.h new file mode 100644 index 0000000..b6c5119 --- /dev/null +++ b/client/ui/MainWindow/include/bottombar.h @@ -0,0 +1,20 @@ +#ifndef BOTTOMBAR_H +#define BOTTOMBAR_H + +#include +#include +#include +#include + +namespace Ui { +class BottomBar : public QWidget { + QHBoxLayout *main_layout_; + QLabel *project_name_; + QLabel *username_; + +public: + BottomBar(QWidget *parent_, std::string username_, std::string project_name_); +}; +} // namespace Ui + +#endif // BOTTOMBAR_H diff --git a/client/ui/MainWindow/include/mainwindow.h b/client/ui/MainWindow/include/mainwindow.h new file mode 100644 index 0000000..957e3e6 --- /dev/null +++ b/client/ui/MainWindow/include/mainwindow.h @@ -0,0 +1,39 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "bottombar.h" +#include "notelist.h" +#include "projectlist.h" +#include +#include +#include +#include +#include +#include +#include +#include "storage.hpp" + +namespace Ui { +class MainWindow : public QWidget { + Q_OBJECT + QVBoxLayout *main_layout_; + BottomBar *top_bar_; + QHBoxLayout *content_layout_; + ProjectList *project_list_; + NoteList *note_list_; + QWidget *content_widget_; + QPushButton *new_project_button_; + QPushButton *new_note_button_; + project_storage_model::Storage *storage_; + + friend ProjectList; +private slots: + void add_project(); + void add_note(); + +public: + explicit MainWindow(QWidget *parent = nullptr, std::string username = "none", + project_storage_model::Storage *storage = nullptr); +}; +} // namespace Ui +#endif // MAINWINDOW_H diff --git a/client/ui/MainWindow/include/notelist.h b/client/ui/MainWindow/include/notelist.h new file mode 100644 index 0000000..082567b --- /dev/null +++ b/client/ui/MainWindow/include/notelist.h @@ -0,0 +1,27 @@ +#ifndef NOTELIST_H +#define NOTELIST_H +#include "note.hpp" +#include +#include +#include +#include +namespace Ui { +class NoteList : public QWidget { + friend class MainWindow; + Q_OBJECT + + QHBoxLayout *main_layout_; + + std::vector vertical_layouts_; + int note_counter_ = 0; + +public: + void add_note_widget(const project_storage_model::Note *note); + void clear_note_list(); + NoteList(QWidget *parent); + +public slots: + void load_project_notes(QListWidgetItem *project); +}; +} // namespace Ui +#endif // NOTELIST_H diff --git a/client/ui/MainWindow/include/notewidget.h b/client/ui/MainWindow/include/notewidget.h new file mode 100644 index 0000000..720e589 --- /dev/null +++ b/client/ui/MainWindow/include/notewidget.h @@ -0,0 +1,27 @@ +#ifndef NOTEWIDGET_H +#define NOTEWIDGET_H + +#include "note.hpp" +#include +#include +#include +#include + +namespace Ui { +class NoteWidget : public QWidget { + Q_OBJECT + const project_storage_model::Note *model_note_; + QVBoxLayout *main_layout_; + QPushButton *open_button_; + +public: + explicit NoteWidget(QWidget *parent = nullptr, + const project_storage_model::Note *model_note = nullptr); + +private slots: + void open_note_window() const { + std::cout << this->model_note_->get_text() << std::endl; + } +}; +} // namespace Ui +#endif // NOTEWIDGET_H diff --git a/client/ui/MainWindow/include/projectitem.h b/client/ui/MainWindow/include/projectitem.h new file mode 100644 index 0000000..58b148e --- /dev/null +++ b/client/ui/MainWindow/include/projectitem.h @@ -0,0 +1,19 @@ +#ifndef PROJECTITEM_H +#define PROJECTITEM_H + +#include "notelist.h" +#include "project.hpp" +#include +#include + +namespace Ui { +class ProjectItem : public QListWidgetItem { + project_storage_model::Project *project_; + friend NoteList; + friend class MainWindow; + +public: + ProjectItem(QListWidget *listview, project_storage_model::Project *project); +}; +} // namespace Ui +#endif // PROJECTITEM_H diff --git a/client/ui/MainWindow/include/projectlist.h b/client/ui/MainWindow/include/projectlist.h new file mode 100644 index 0000000..6616e0a --- /dev/null +++ b/client/ui/MainWindow/include/projectlist.h @@ -0,0 +1,20 @@ +#ifndef PROJECTLIST_H +#define PROJECTLIST_H + +#include "project.hpp" +#include +#include + +namespace Ui { +class ProjectList : public QListWidget { + Q_OBJECT + friend class MainWindow; + void add_project(project_storage_model::Project *project); + +public: + explicit ProjectList(QWidget *parent = nullptr); + +signals: +}; +} // namespace Ui +#endif // PROJECTLIST_H diff --git a/client/ui/MainWindow/src/applicationwindow.cpp b/client/ui/MainWindow/src/applicationwindow.cpp new file mode 100644 index 0000000..ff86770 --- /dev/null +++ b/client/ui/MainWindow/src/applicationwindow.cpp @@ -0,0 +1,15 @@ +#include "applicationwindow.h" +#include "mainwindow.h" +#include + +namespace Ui { +ApplicationWindow::ApplicationWindow(std::string window_name_) + : QMainWindow{nullptr} { + this->setObjectName("ApplicationWindow"); + + this->setAttribute(Qt::WA_StyledBackground); + + this->setWindowTitle(window_name_.c_str()); + this->resize(800, 600); +} +} // namespace Ui diff --git a/client/ui/MainWindow/src/bottombar.cpp b/client/ui/MainWindow/src/bottombar.cpp new file mode 100644 index 0000000..10a20b8 --- /dev/null +++ b/client/ui/MainWindow/src/bottombar.cpp @@ -0,0 +1,26 @@ +#include "bottombar.h" +#include +#include +#include +#include +namespace Ui { +BottomBar::BottomBar(QWidget *parent, std::string username, + std::string project_name) + : QWidget(parent), main_layout_(new QHBoxLayout()), + username_(new QLabel(username.c_str())), + project_name_(new QLabel(project_name.c_str())) { + + this->setObjectName("BottomBar"); + this->setLayout(main_layout_); + this->setFixedHeight(40); + + project_name_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + username_->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + + this->setSizePolicy( + QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); + + main_layout_->addWidget(project_name_); + main_layout_->addWidget(username_); +} +} // namespace Ui diff --git a/client/ui/MainWindow/src/mainwindow.cpp b/client/ui/MainWindow/src/mainwindow.cpp new file mode 100644 index 0000000..0193878 --- /dev/null +++ b/client/ui/MainWindow/src/mainwindow.cpp @@ -0,0 +1,166 @@ +#include "mainwindow.h" +#include "bottombar.h" +#include "notelist.h" +#include "project.hpp" +#include "projectlist.h" +#include "projectitem.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace Ui { +MainWindow::MainWindow(QWidget *parent, std::string username, + project_storage_model::Storage *storage) + : QWidget(parent),main_layout_(new QVBoxLayout(this)), + top_bar_(new BottomBar(this, username, "эффишио - таск трекер.")), + content_layout_(new QHBoxLayout(this)), + project_list_(new ProjectList(this)), + note_list_(new NoteList(this)), + content_widget_(new QWidget(this)), + new_project_button_(new QPushButton("Новый проект", this)), + new_note_button_(new QPushButton("Новая заметка", this)), + storage_(storage) +{ + this->setObjectName("MainWindow"); + this->setAttribute(Qt::WA_StyledBackground); + this->setStyleSheet( + R"( +#MainWindow { + background-color : white; +} +#ProjectList { + background-color: #FED6BC; + border: 1px solid #e0e0e0; + border-radius: 8px; + padding: 8px; + outline: 0; + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 14px; + color: #000000; +} + +#ProjectList::item { + background-color: transparent; + border: none; + padding: 10px; + margin: 2px; + border-radius: 6px; +} + +#ProjectList::item:hover { + background-color: #f5f5f5; +} + +#ProjectList::item:selected { + background-color: #007aff; + color: #ffffff; +} + +#ProjectList QScrollBar:vertical { + border: none; + background: #f0f0f0; + width: 10px; + margin: 0; +} + +#ProjectList QScrollBar::handle:vertical { + background: #c0c0c0; + border-radius: 5px; + min-height: 20px; +} + +#ProjectList QScrollBar::add-line:vertical, +#ProjectList QScrollBar::sub-line:vertical { + background: none; +} + +#ProjectList QScrollBar::add-page:vertical, +#ProjectList QScrollBar::sub-page:vertical { + background: none; +} + +#BottomBar { + background-color: black; + border-radius: 8px; + padding: 8px; + outline: 0; +} + +#BottomBar QLabel { + color : white; + font-family: -apple-system, BlinkMacSystemFont, sans-serif; + font-size: 14px; +} + +#NoteWidget { + background-color: #C6D8FF; +border-radius: 8px; + +} + +#NoteList { + border : 1px solid #A9A9A9; +border-radius : 8px; +} + +)"); + + main_layout_->addWidget(top_bar_, Qt::AlignTop); + + main_layout_->setAlignment(Qt::AlignCenter); + main_layout_->addWidget(content_widget_); + + content_widget_->setLayout(content_layout_); + + auto right_layout = new QVBoxLayout(content_widget_); + right_layout->addWidget(project_list_); + right_layout->addWidget(new_project_button_); + right_layout->addWidget(new_note_button_); + + content_layout_->addWidget(note_list_, Qt::AlignRight); + content_layout_->addLayout(right_layout); + main_layout_->addWidget(content_widget_); + + this->setLayout(main_layout_); + + // connections + connect(project_list_, &QListWidget::itemClicked, note_list_, + &NoteList::load_project_notes); + connect(new_note_button_, &QPushButton::clicked, this, &Ui::MainWindow::add_note); + connect(new_project_button_, &QPushButton::clicked, this, &Ui::MainWindow::add_project); +} + +void MainWindow::add_project() { + + bool ok; + QString name_of_project = + QInputDialog::getText(nullptr, "Название проекта:", "Введите название", + QLineEdit::Normal, "", &ok); + if (ok) { + auto &project = storage_->add_project( + project_storage_model::Project(1, name_of_project.toStdString(), "")); + project_list_->add_project(&project); + } +} + +void MainWindow::add_note() { + auto project_item = dynamic_cast(project_list_->currentItem()); + // qDebug() << "Note add to " << project_item->project_->get_name() << "its address: " << project_item->project_; + if (project_item) { + auto ¬e = project_item->project_->add_note({1, "empty", ""}); + note_list_->add_note_widget(¬e); + } else { + QMessageBox msg; + msg.setText("Проект не выбран!"); + msg.exec(); + } +} + +} // namespace Ui diff --git a/client/ui/MainWindow/src/notelist.cpp b/client/ui/MainWindow/src/notelist.cpp new file mode 100644 index 0000000..e873d6d --- /dev/null +++ b/client/ui/MainWindow/src/notelist.cpp @@ -0,0 +1,61 @@ +#include "notelist.h" +#include "note.hpp" +#include "notewidget.h" +#include "projectitem.h" +#include +#include +#include +#include +#include + +namespace Ui { +NoteList::NoteList(QWidget *parent) + : QWidget(parent), main_layout_(new QHBoxLayout(this)), + vertical_layouts_(std::vector()) { + this->setAttribute(Qt::WA_StyledBackground); + this->setObjectName("NoteList"); + this->setLayout(main_layout_); + vertical_layouts_.resize(4, nullptr); + for (auto &layout : vertical_layouts_) { + layout = new QVBoxLayout(this); + + main_layout_->addLayout(layout); + } +} + +void NoteList::add_note_widget(const project_storage_model::Note *note) { + auto current_layout = vertical_layouts_[note_counter_ % 4]; + if (current_layout->count() > 1) { + current_layout->removeItem( + current_layout->itemAt(current_layout->count() - 1)); + } + vertical_layouts_[note_counter_ % 4]->addWidget(new NoteWidget(this, note), 0, + Qt::AlignTop); + current_layout->addStretch(); + note_counter_++; +} + +void NoteList::load_project_notes(QListWidgetItem *project) { + + ProjectItem *p = dynamic_cast(project); + assert(p != nullptr); + qDebug() << "Адрес проекта" << QString::fromStdString(p->project_->get_name()) << ":" <project_; + this->clear_note_list(); + note_counter_ = 0; + for (const auto ¬e : p->project_->get_notes()) { + this->add_note_widget(¬e); + } +} + +void NoteList::clear_note_list() { + for (auto &layout : vertical_layouts_) { + while (layout->count()) { + auto item = layout->takeAt(0); + auto widget = item->widget(); + if (widget) { + widget->deleteLater(); + } + } + } +} +} // namespace Ui diff --git a/client/ui/MainWindow/src/notewidget.cpp b/client/ui/MainWindow/src/notewidget.cpp new file mode 100644 index 0000000..8b2492f --- /dev/null +++ b/client/ui/MainWindow/src/notewidget.cpp @@ -0,0 +1,27 @@ +#include "notewidget.h" +#include "note.hpp" +#include +#include +#include + +namespace Ui { +NoteWidget::NoteWidget(QWidget *parent, + const project_storage_model::Note *model_note) + : QWidget(parent), model_note_(model_note), + main_layout_(new QVBoxLayout(this)), + open_button_(new QPushButton("Открыть")) { + this->setObjectName("NoteWidget"); + this->setMinimumWidth(100); + this->setMinimumHeight(80); + QLabel *title = new QLabel(model_note_->get_title().c_str(), this); + QLabel *text = new QLabel(model_note_->get_text().c_str(), this); + main_layout_->addWidget(title); + main_layout_->addWidget(text); + main_layout_->addWidget(open_button_); + + connect(open_button_, &QPushButton::clicked, this, + &NoteWidget::open_note_window); + this->setLayout(main_layout_); + this->setAttribute(Qt::WA_StyledBackground); +} +} // namespace Ui diff --git a/client/ui/MainWindow/src/projectitem.cpp b/client/ui/MainWindow/src/projectitem.cpp new file mode 100644 index 0000000..289d91c --- /dev/null +++ b/client/ui/MainWindow/src/projectitem.cpp @@ -0,0 +1,9 @@ +#include "projectitem.h" +#include + +namespace Ui { +ProjectItem::ProjectItem(QListWidget *list_view, + project_storage_model::Project *project) + : project_(project), + QListWidgetItem(project->get_name().c_str(), list_view) {} +} // namespace Ui diff --git a/client/ui/MainWindow/src/projectlist.cpp b/client/ui/MainWindow/src/projectlist.cpp new file mode 100644 index 0000000..beef58f --- /dev/null +++ b/client/ui/MainWindow/src/projectlist.cpp @@ -0,0 +1,18 @@ +#include "projectlist.h" +#include "mainwindow.h" +#include "project.hpp" +#include "projectitem.h" +#include + +namespace Ui { + +ProjectList::ProjectList(QWidget *parent) : QListWidget{parent} { + this->setObjectName("ProjectList"); + this->setFixedWidth(200); +} + +void ProjectList::add_project(project_storage_model::Project *project) { + this->addItem(new ProjectItem(static_cast(this), project)); +} + +} // namespace Ui diff --git a/client/ui/login-and-registration-windows/CMakeLists.txt b/client/ui/login-and-registration-windows/CMakeLists.txt new file mode 100644 index 0000000..141076a --- /dev/null +++ b/client/ui/login-and-registration-windows/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.16) +project(LoginRegistrationWindows VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Sql) +find_package(PostgreSQL REQUIRED) +link_directories(/path/to/postgresql/lib) + +find_package(Qt6 COMPONENTS Core Widgets Gui REQUIRED) + +set(TS_FILES login_window_ru_RU.ts) +set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) + +file(GLOB LOGIN_REGISTRATION_WINDOWS_SRC "src/*") +file(GLOB LOGIN_REGISTRATION_WINDOWS_INCLUDE "include/*") +file(GLOB LOGIN_REGISTRATION_WINDOWS_UI "ui/*") + + +add_library(${PROJECT_NAME} + ${LOGIN_REGISTRATION_WINDOWS_SRC} + ${LOGIN_REGISTRATION_WINDOWS_INCLUDE} + login_window_ru_RU.ts +) + +target_include_directories(LoginRegistrationWindows PUBLIC + ${CMAKE_SOURCE_DIR}/../../scripts + ${CMAKE_SOURCE_DIR}/../../database + $ +) + +target_link_libraries(LoginRegistrationWindows PRIVATE + Qt6::Core + Qt6::Widgets + Qt6::Sql +) + +set_target_properties(LoginRegistrationWindows PROPERTIES + ARCHIVE_OUTPUT_NAME LoginRegistrationWindows + POSITION_INDEPENDENT_CODE ON +) + +include(GNUInstallDirs) +install(TARGETS LoginRegistrationWindows + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} + FILES_MATCHING PATTERN "*.h*" +) + +target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core) + +qt_finalize_executable(LoginRegistrationWindows) \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/include/login_window.h b/client/ui/login-and-registration-windows/include/login_window.h new file mode 100644 index 0000000..c9f1297 --- /dev/null +++ b/client/ui/login-and-registration-windows/include/login_window.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include "database_manager.hpp" +#include "lr_dao.hpp" + +QT_BEGIN_NAMESPACE + +namespace Ui { +class LoginWindow; +} + +QT_END_NAMESPACE + +class LoginWindow : public QDialog { + Q_OBJECT + +public: + LoginWindow(QWidget *parent = nullptr); + ~LoginWindow(); + +private slots: + void on_switch_mode_clicked(); + void on_push_enter_clicked(); + +private: + Ui::LoginWindow *ui; +}; diff --git a/client/ui/login-and-registration-windows/include/login_window_style_sheet.h b/client/ui/login-and-registration-windows/include/login_window_style_sheet.h new file mode 100644 index 0000000..ce95d93 --- /dev/null +++ b/client/ui/login-and-registration-windows/include/login_window_style_sheet.h @@ -0,0 +1,105 @@ +#pragma once + +#include "ui_login_window.h" + +namespace Ui { +QString login_window_dark_theme = R"( + QDialog { + background-color: #202020; + } + + QLabel { + font-family: 'Arial'; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#pushEnter { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: black; + padding: 5px 10px; + } + + QPushButton#switchMode { + font-family: 'Arial'; + border-radius: 10px; + background-color: #131313; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#pushEnter:hover { + background-color: #d58745; + } + + QPushButton#switchMode:hover { + background-color: black; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid #131313; + background: #131313; + color: white; + padding: 5px; + } + + QLineEdit::placeholder { + color: #898989; + } +)"; + +QString login_window_light_theme = R"( + QDialog { + background-color: #f5f5f5; + } + + QLabel { + font-family: 'Arial'; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#pushEnter { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + } + + QPushButton#switchMode { + font-family: 'Arial'; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#pushEnter:hover { + background-color: #d58745; + } + + QPushButton#switchMode:hover { + background-color: #dadada; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid white; + background: white; + color: black; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + +)"; + +} // namespace Ui diff --git a/client/ui/login-and-registration-windows/include/registration_window.h b/client/ui/login-and-registration-windows/include/registration_window.h new file mode 100644 index 0000000..db56975 --- /dev/null +++ b/client/ui/login-and-registration-windows/include/registration_window.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include "database_manager.hpp" +#include "lr_dao.hpp" + +QT_BEGIN_NAMESPACE + +namespace Ui { +class RegistrationWindow; +} + +QT_END_NAMESPACE + +class RegistrationWindow : public QDialog { + Q_OBJECT + +public: + explicit RegistrationWindow(QWidget *parent = nullptr); + ~RegistrationWindow(); + bool is_strong_and_valid_password(const QString &password); + +private slots: + void on_switch_mode_clicked(); + void on_push_registration_clicked(); + +private: + Ui::RegistrationWindow *ui; +}; diff --git a/client/ui/login-and-registration-windows/include/registration_window_style_sheet.h b/client/ui/login-and-registration-windows/include/registration_window_style_sheet.h new file mode 100644 index 0000000..8b2995d --- /dev/null +++ b/client/ui/login-and-registration-windows/include/registration_window_style_sheet.h @@ -0,0 +1,105 @@ +#pragma once + +#include "ui_registration_window.h" + +namespace Ui { +QString registration_window_dark_theme = R"( + QDialog { + background-color: #202020; + } + + QLabel { + font-family: 'Arial'; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#pushRegistration { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: black; + padding: 5px 10px; + } + + QPushButton#switchMode { + font-family: 'Arial'; + border-radius: 10px; + background-color: #131313; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#pushRegistration:hover { + background-color: #d58745; + } + + QPushButton#switchMode:hover { + background-color: black; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid #131313; + background: #131313; + color: white; + padding: 5px; + } + + QLineEdit::placeholder { + color: #898989; + } +)"; + +QString registration_window_light_theme = R"( + QDialog { + background-color: #f5f5f5; + } + + QLabel { + font-family: 'Arial'; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#pushRegistration { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + } + + QPushButton#switchMode { + font-family: 'Arial'; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#pushRegistration:hover { + background-color: #d58745; + } + + QPushButton#switchMode:hover { + background-color: #dadada; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid white; + background: white; + color: black; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + +)"; + +} // namespace Ui diff --git a/client/ui/login-and-registration-windows/login_window_ru_RU.ts b/client/ui/login-and-registration-windows/login_window_ru_RU.ts new file mode 100644 index 0000000..e1de93e --- /dev/null +++ b/client/ui/login-and-registration-windows/login_window_ru_RU.ts @@ -0,0 +1,3 @@ + + + diff --git a/client/ui/login-and-registration-windows/src/login_window.cpp b/client/ui/login-and-registration-windows/src/login_window.cpp new file mode 100644 index 0000000..2d788f0 --- /dev/null +++ b/client/ui/login-and-registration-windows/src/login_window.cpp @@ -0,0 +1,80 @@ +#include "login_window.h" +#include "database_manager.hpp" +#include "login_window_style_sheet.h" +#include "lr_dao.hpp" +#include "registration_window.h" +#include "mainwindow.h" +#include "bottombar.h" +#include "notelist.h" + +LoginWindow::LoginWindow(QWidget *parent) + : QDialog(parent), ui(new Ui::LoginWindow) { + ui->setupUi(this); + + setFixedSize(380, 480); + ui->inputLogin->setPlaceholderText("Введите логин:"); + ui->inputPassword->setPlaceholderText("Введите пароль:"); + setStyleSheet(Ui::login_window_light_theme); + ui->inputPassword->setEchoMode(QLineEdit::Password); + + connect( + ui->switchMode, &QPushButton::clicked, this, + &LoginWindow::on_switch_mode_clicked + ); + connect( + ui->pushEnter, &QPushButton::clicked, this, + &LoginWindow::on_push_enter_clicked + ); +} + +LoginWindow::~LoginWindow() { + delete ui; +} + +void LoginWindow::on_switch_mode_clicked() { + hide(); + RegistrationWindow registration_window; + registration_window.show(); + registration_window.exec(); + this->close(); +} + +void LoginWindow::on_push_enter_clicked() { + QString login = ui->inputLogin->text(); + QString password = ui->inputPassword->text(); + + if (!login.isEmpty() && !password.isEmpty()) { + if (login.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина логина не должна превышать пятидесяти символов" + ); + } else if (password.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина пароля не должна превышать пятидесяти символов" + ); + } else if (LRDao::validate_user(login, password)) { + QMessageBox::information( + this, "Вход", "Вы успешно вошли! Добро пожаловать :)" + ); + hide(); + project_storage_model::Storage storage; + ApplicationWindow *app_window = new ApplicationWindow("efficio"); + MainWindow *main_window = new MainWindow(app_window, "username", &storage); + + app_window->setCentralWidget(main_window); + app_window->show(); + + this->close(); + } else { + QMessageBox::warning( + this, "Ошибка ввода данных", "Неверный логин или пароль!" + ); + } + } else { + QMessageBox::warning( + this, "Ошибка ввода данных", "Пожалуйста, заполните все поля!" + ); + } +} diff --git a/client/ui/login-and-registration-windows/src/registration_window.cpp b/client/ui/login-and-registration-windows/src/registration_window.cpp new file mode 100644 index 0000000..f53bf89 --- /dev/null +++ b/client/ui/login-and-registration-windows/src/registration_window.cpp @@ -0,0 +1,126 @@ +#include "registration_window.h" +#include +#include "database_manager.hpp" +#include "login_window.h" +#include "lr_dao.hpp" +#include "registration_window_style_sheet.h" + +RegistrationWindow::RegistrationWindow(QWidget *parent) + : QDialog(parent), ui(new Ui::RegistrationWindow) { + ui->setupUi(this); + + setFixedSize(380, 480); + + ui->createLogin->setPlaceholderText("Введите логин:"); + ui->createPassword->setPlaceholderText("Введите пароль:"); + ui->repeatPassword->setPlaceholderText("Повторите пароль:"); + setStyleSheet(Ui::registration_window_light_theme); + ui->createPassword->setEchoMode(QLineEdit::Password); + ui->repeatPassword->setEchoMode(QLineEdit::Password); + + connect( + ui->pushRegistration, &QPushButton::clicked, this, + &RegistrationWindow::on_push_registration_clicked + ); + connect( + ui->switchMode, &QPushButton::clicked, this, + &RegistrationWindow::on_switch_mode_clicked + ); +} + +RegistrationWindow::~RegistrationWindow() { + delete ui; +} + +void RegistrationWindow::on_switch_mode_clicked() { + hide(); + LoginWindow login_window; + login_window.show(); + login_window.exec(); +} + +bool RegistrationWindow::is_strong_and_valid_password(const QString &password) { + if (password.length() < 8) { + QMessageBox::warning( + nullptr, "Ошибка: недостаточно надежный пароль", + "Пароль должен содержать не менее восьми символов" + ); + return false; + } + + bool has_digit = false; + bool has_latin_letter = false; + + for (const QChar ch : password) { + if (!ch.isLetter() && !ch.isDigit()) { + QMessageBox::warning( + nullptr, "Ошибка", + "Пароль должен содержать только символы латиницы и цифры" + ); + return false; + } else if (ch.isLetter()) { + has_latin_letter = true; + } else if (ch.isDigit()) { + has_digit = true; + } + } + + if (!has_latin_letter) { + QMessageBox::warning( + nullptr, "Ошибка: недостаточно надежный пароль", + "Пароль должен содержать хотя бы одну букву" + ); + return false; + } + if (!has_digit) { + QMessageBox::warning( + nullptr, "Ошибка: недостаточно надежный пароль", + "Пароль должен содержать хотя бы одну цифру" + ); + return false; + } + + return true; +} + +void RegistrationWindow::on_push_registration_clicked() { + QString created_login = ui->createLogin->text(); + QString created_password = ui->createPassword->text(); + QString repeated_password = ui->repeatPassword->text(); + + if (!created_login.isEmpty() && !created_password.isEmpty() && + !repeated_password.isEmpty()) { + if (created_password != repeated_password) { + QMessageBox::warning(this, "Ошибка", "Пароли не совпадают!"); + } else if (created_login.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина логина не должна превышать пятидесяти символов" + ); + } else if (created_password.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина пароля не должна превышать пятидесяти символов" + ); + } else if (is_strong_and_valid_password(created_password)) { + if (!LRDao::register_user(created_login, created_password)) { + QMessageBox::warning( + this, "Ошибка", + "Пользователь с таким именем уже существует. Пожалуйста, " + "придумайте другое!" + ); + } else { + QMessageBox::information( + this, "Регистрация", + "Вы успешно зарегистрировались! Пожалуйста, выполните вход." + ); + hide(); + LoginWindow login_window; + login_window.show(); + login_window.exec(); + } + } + } else { + QMessageBox::warning(this, "Ошибка", "Пожалуйста, заполните все поля."); + } +} diff --git a/client/ui/login-and-registration-windows/ui/login_window.ui b/client/ui/login-and-registration-windows/ui/login_window.ui new file mode 100644 index 0000000..3192033 --- /dev/null +++ b/client/ui/login-and-registration-windows/ui/login_window.ui @@ -0,0 +1,131 @@ + + + LoginWindow + + + + 0 + 0 + 362 + 600 + + + + Dialog + + + + + 0 + 0 + 375 + 501 + + + + + + 120 + 280 + 131 + 51 + + + + font: 700 13pt "Arial"; +border-radius: 10px; + + + Войти + + + + + + 140 + 130 + 91 + 41 + + + + font: 700 21pt "Arial"; + + + Вход + + + + + + 62 + 175 + 251 + 41 + + + + border-radius: 10px; + + + Логин + + + + + + 62 + 225 + 251 + 41 + + + + border-radius: 10px; + + + Пароль + + + + + + 80 + 340 + 211 + 31 + + + + font: 7pt "Arial"; +border-radius: 10px; + + + Еще нет аккаунта? Зарегестрируйтесь! + + + + + + + 0 + 0 + 362 + 26 + + + + + + + 0 + 0 + 16 + 25 + + + + + + + diff --git a/client/ui/login-and-registration-windows/ui/registration_window.ui b/client/ui/login-and-registration-windows/ui/registration_window.ui new file mode 100644 index 0000000..77f1179 --- /dev/null +++ b/client/ui/login-and-registration-windows/ui/registration_window.ui @@ -0,0 +1,99 @@ + + + RegistrationWindow + + + + 0 + 0 + 390 + 562 + + + + Dialog + + + + + 90 + 290 + 201 + 51 + + + + font: 700 12pt "Arial"; +border-radius: 10px; + + + Зарегестрироваться + + + + + + 110 + 350 + 161 + 31 + + + + font: 7pt "Arial"; +border-radius: 10px; + + + Уже есть аккаунт? Войдите! + + + + + + 60 + 140 + 261 + 41 + + + + + + + 60 + 190 + 261 + 41 + + + + + + + 60 + 240 + 261 + 41 + + + + + + + 100 + 90 + 201 + 51 + + + + font: 700 19pt "Arial"; + + + Регистрация + + + + + + diff --git a/client/ui/note-widget/CMakeLists.txt b/client/ui/note-widget/CMakeLists.txt new file mode 100644 index 0000000..e9ec3a1 --- /dev/null +++ b/client/ui/note-widget/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.16) +project(NoteWidgetEfficio VERSION 0.1 LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) + +find_package(Qt6 REQUIRED COMPONENTS Core Widgets Sql) + +file(GLOB NOTE_WIDGET_SRC "src/*.cpp") +file(GLOB NOTE_WIDGET_INCLUDE "include/*.h") +file(GLOB NOTE_WIDGET_UI "ui/*.ui") + +set(TS_FILES NoteWidgetEfficio_ru_RU.ts) +set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) + +add_library(${PROJECT_NAME} STATIC + ${NOTE_WIDGET_SRC} + ${NOTE_WIDGET_UI} + ${TS_FILES} +) + +target_include_directories(NoteWidgetEfficio PUBLIC + ${CMAKE_SOURCE_DIR}/../../scripts + ${CMAKE_SOURCE_DIR}/../../database + $ +) + +target_link_libraries(NoteWidgetEfficio PUBLIC + Qt6::Core + Qt6::Widgets + Qt6::Sql +) + +set_target_properties(NoteWidgetEfficio PROPERTIES + ARCHIVE_OUTPUT_NAME NoteWidgetEfficio + POSITION_INDEPENDENT_CODE ON +) + +include(GNUInstallDirs) +install(TARGETS NoteWidgetEfficio + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +) +install(DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} + FILES_MATCHING PATTERN "*.h*" +) \ No newline at end of file diff --git a/client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts b/client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts new file mode 100644 index 0000000..e1de93e --- /dev/null +++ b/client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts @@ -0,0 +1,3 @@ + + + diff --git a/client/ui/note-widget/include/note_edit_dialog.h b/client/ui/note-widget/include/note_edit_dialog.h new file mode 100644 index 0000000..19f9005 --- /dev/null +++ b/client/ui/note-widget/include/note_edit_dialog.h @@ -0,0 +1,46 @@ +#ifndef NOTE_EDIT_DIALOG_H +#define NOTE_EDIT_DIALOG_H + +#include +#include +#include "note.hpp" +#include "note_dao.hpp" +#include "tags_dialog.h" + +using namespace project_storage_model; + +QT_BEGIN_NAMESPACE + +namespace Ui { +class NoteEditDialog; +} + +QT_END_NAMESPACE + +class NoteEditDialog final : public QDialog { + Q_OBJECT + +public: + explicit NoteEditDialog( + QWidget *parent = nullptr, + Note *note = new Note(0, "NULL", "NULL") + ); + ~NoteEditDialog() override; + +private slots: + void on_save_button_click(); + void on_join_button_click(); + void on_add_members_button_click(); + void on_add_tags_button_click(); + +private: + Ui::NoteEditDialog *ui_; + std::unique_ptr avatar_label_; + std::vector> tag_labels_; + QList selected_tags_; + Note *note_; + + [[nodiscard]] bool try_save_note() const; +}; + +#endif // NOTE_EDIT_DIALOG_H diff --git a/client/ui/note-widget/include/note_edit_dialog_styles.h b/client/ui/note-widget/include/note_edit_dialog_styles.h new file mode 100644 index 0000000..58d7377 --- /dev/null +++ b/client/ui/note-widget/include/note_edit_dialog_styles.h @@ -0,0 +1,272 @@ +#ifndef NOTE_EDIT_DIALOG_STYLES_H +#define NOTE_EDIT_DIALOG_STYLES_H + +#include + +namespace Ui { +QString dark_theme = R"( + QDialog { + background-color: #202020; + } + + QLineEdit#titleLineEdit { + font-family: 'Arial'; + font-size: 25px; + font-weight: bold; + color: #089083; + border: none; + padding: 0; + background: transparent; + } + + QLabel#projectNameLabel { + font-family: 'Arial'; + font-size: 13px; + color: #898989; + padding: 1px; + } + + QLabel#descriptionLabel { + font-family: 'Arial'; + font-size: 18px; + font-weight: bold; + color: white; + } + + QTextEdit#descriptionTextEdit { + border-radius: 10px; + border: 1px solid #131313; + background: #131313; + color: white; + padding: 5px; + } + + QTextEdit#descriptionTextEdit::placeholder { + color: #898989; + } + + QPushButton#saveButton { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: black; + padding: 5px 10px; + } + + QPushButton#saveButton:hover { + background-color: #d58745; + } + + QPushButton#cancelButton { + font-family: 'Arial'; + border-radius: 10px; + background-color: #131313; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#cancelButton:hover { + background-color: black; + } + + QPushButton#joinButton, + QPushButton#membersButton, + QPushButton#checklistButton, + QPushButton#datesButton, + QPushButton#attachmentButton, + QPushButton#tagsButton { + font-family: 'Arial'; + background-color: #089083; + color: #000000; + padding: 5px 10px; + border-radius: 5px; + font-weight: bold; + text-align: left; + } + + QPushButton#joinButton:hover, + QPushButton#membersButton:hover, + QPushButton#checklistButton:hover, + QPushButton#datesButton:hover, + QPushButton#attachmentButton:hover, + QPushButton#tagsButton:hover { + background-color: #01635d; + } + + QLabel#sidePanelLabel, + QLabel#sidePanelLabel_2 { + font-family: 'Arial'; + font-size: 14px; + font-weight: bold; + color: #898989; + } +)"; + +QString light_theme = R"( + QDialog { + background-color: #f5f5f5; + } + + /* Заголовок и название проекта */ + + QLineEdit#titleLineEdit { + font-family: 'Arial'; + font-size: 25px; + font-weight: bold; + color: #089083; + border: none; + padding: 0; + background: transparent; + } + + QLabel#projectNameLabel { + font-family: 'Arial'; + font-size: 13px; + color: #727272; + padding: 1px; + } + + /* Описание заметки */ + + QLabel#descriptionLabel { + font-family: 'Arial'; + font-size: 18px; + font-weight: bold; + color: black; + } + + QTextEdit#descriptionTextEdit { + border-radius: 10px; + border: 1px solid #ffffff; + background: #ffffff; + color: black; + padding: 5px; + } + + QTextEdit#descriptionTextEdit::placeholder { + color: #727272; + } + + /* Основные кнопки */ + + QHBoxLayout#buttonsLayout { + align: left; + } + + QPushButton#saveButton { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + } + + QPushButton#saveButton:hover { + background-color: #d58745; + } + + QPushButton#cancelButton { + font-family: 'Arial'; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#cancelButton:hover { + background-color: #dadada; + } + + /* Боковое меню */ + + QPushButton#joinButton, + QPushButton#addMembersButton, + QPushButton#addDateButton, + QPushButton#addTagsButton { + font-family: 'Arial'; + background-color: #089083; + color: #ffffff; + padding: 5px 10px; + border-radius: 10px; + font-weight: bold; + text-align: left; + } + + QPushButton#joinButton:hover, + QPushButton#addMembersButton:hover, + QPushButton#addDateButton:hover, + QPushButton#addTagsButton:hover { + background-color: #01635d; + } + + QLabel#sidePanelLabel, + QLabel#sidePanelLabel_2 { + font-family: 'Arial'; + font-size: 14px; + font-weight: bold; + color: #727272; + } + + QLabel#membersLabel, + QLabel#tagsLabel, + QLabel#dateLabel { + color: #727272; + } + + /* Дополнительные элементы заметки */ + + QDateEdit#dateEdit { + font-family: 'Arial'; + border-radius: 5px; + background-color: #ffdda2; + color: #050505; + padding: 5px 5px; + border: none; + width: 70px; + height: 22px; + text-align: center; + font-weight: bold; + } + QDateEdit#dateEdit::drop-down { + border: none; + width: 20px; + } + + /* Окно с сообщением */ + + QMessageBox { + background-color: #ffffff; + } + + QMessageBox QLabel { + font-family: 'Arial'; + font-size: 14px; + color: #000000; + } + + QMessageBox QPushButton { + font-family: 'Arial'; + font-size: 13px; + font-weight: bold; + color: white; + background-color: #fea36b; + border-radius: 10px; + padding: 5px 15px; + min-width: 60px; + min-height: 25px; + } + + QMessageBox QPushButton:hover { + background-color: #d58745; + color: white; + } + + QMessageBox QPushButton:pressed { + background-color: #d58745; + border-color: #d58745; + } +)"; + +} // namespace Ui + +#endif // NOTE_EDIT_DIALOG_STYLES_H diff --git a/client/ui/note-widget/include/tags_dialog.h b/client/ui/note-widget/include/tags_dialog.h new file mode 100644 index 0000000..f8496c0 --- /dev/null +++ b/client/ui/note-widget/include/tags_dialog.h @@ -0,0 +1,41 @@ +#ifndef TAGS_DIALOG_H +#define TAGS_DIALOG_H + +#include +#include +#include +#include +#include +#include + +class TagsDialog final : public QDialog { + Q_OBJECT + +public: + const int MAX_TAGS_COUNT = 5; + const std::pair DIALOG_SIZE = std::make_pair(300, 250); + + struct Tag { + bool is_checked; + QString color; + QString name; + }; + + explicit TagsDialog( + const QList &initial_tags = QList(), + QWidget *parent = nullptr + ); + + [[nodiscard]] QList get_selected_tags() const; + +private: + void setup_ui(); + + std::unique_ptr check_boxes_[5]; + std::unique_ptr color_combo_boxes_[5]; + std::unique_ptr name_line_edits_[5]; + std::unique_ptr ok_button_; + std::unique_ptr cancel_button_; +}; + +#endif // TAGS_DIALOG_H \ No newline at end of file diff --git a/client/ui/note-widget/include/tags_dialog_styles.h b/client/ui/note-widget/include/tags_dialog_styles.h new file mode 100644 index 0000000..ee5f06b --- /dev/null +++ b/client/ui/note-widget/include/tags_dialog_styles.h @@ -0,0 +1,123 @@ +#ifndef TAGS_DIALOG_STYLES_H +#define TAGS_DIALOG_STYLES_H + +#include "tags_dialog.h" + +namespace Ui { +QString tags_dialog_light_theme = R"( + QDialog { + background-color: #f5f5f5; + } + + /* Чекбоксы */ + QCheckBox { + font-family: 'Arial'; + color: #727272; + spacing: 5px; + } + + QCheckBox::indicator { + width: 16px; + height: 16px; + border-radius: 3px; + border: 1px solid #727272; + background-color: #ffffff; + } + + QCheckBox::indicator:checked { + background-color: #fea36b; + border: 1px solid #fea36b; + image: url(:/images/check.png); + } + + QCheckBox::indicator:hover { + border: 1px solid #fea36b; + } + + /* Выпадающие списки цветов */ + QComboBox { + font-family: 'Arial'; + border-radius: 10px; + background-color: #ffffff; + color: #727272; + padding: 5px 10px; + border: 1px solid #dadada; + } + + QComboBox:hover { + border: 1px solid #089083; + } + + QComboBox::drop-down { + width: 20px; + border: none; + } + + QComboBox::down-arrow { + image: url(:/images/down_arrow.png); + } + + QComboBox QAbstractItemView { + background-color: #ffffff; + color: #727272; + selection-background-color: #fea36b; + selection-color: white; + border: 1px solid #dadada; + border-radius: 5px; + } + + /* Поля ввода имени тега */ + QLineEdit { + font-family: 'Arial'; + border-radius: 10px; + background-color: #ffffff; + color: #727272; + padding: 5px 10px; + border: 1px solid #dadada; + } + + QLineEdit:hover, + QLineEdit:focus { + border: 1px solid #089083; + } + + QLineEdit::placeholder { + color: #a0a0a0; + } + + /* Кнопка OK */ + QPushButton#ok_button { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + font-weight: bold; + width: 30px; + height: 25px; + } + + QPushButton#ok_button:hover { + background-color: #d58745; + } + + /* Кнопка Отмена */ + QPushButton#cancel_button { + font-family: 'Arial'; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + font-weight: bold; + border: 1px solid #fea36b; + width: 30px; + height: 25px; + } + + QPushButton#cancel_button:hover { + background-color: #dadada; + } +)"; +} + +#endif // TAGS_DIALOG_STYLES_H diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp new file mode 100644 index 0000000..27d80ba --- /dev/null +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -0,0 +1,139 @@ +#include "note_edit_dialog.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "./ui_note_edit_dialog.h" +#include "note_edit_dialog_styles.h" +#include "tags_dialog.h" + +NoteEditDialog::NoteEditDialog(QWidget *parent, Note *note) + : QDialog(parent), + ui_(new Ui::NoteEditDialog), + avatar_label_(nullptr), + note_(note) { + ui_->setupUi(this); + + setFixedSize(700, 480); + setStyleSheet(Ui::light_theme); + ui_->buttonsLayout->setAlignment(Qt::AlignLeft); + + connect( + ui_->saveButton, &QPushButton::clicked, this, + &NoteEditDialog::on_save_button_click + ); + connect( + ui_->cancelButton, &QPushButton::clicked, this, &NoteEditDialog::reject + ); + connect( + ui_->joinButton, &QPushButton::clicked, this, + &NoteEditDialog::on_join_button_click + ); + connect( + ui_->addMembersButton, &QPushButton::clicked, this, + &NoteEditDialog::on_add_members_button_click + ); + connect(ui_->addDateButton, &QPushButton::clicked, this, [=]() { + const bool is_visible = ui_->dateLabel->isVisible(); + ui_->dateLabel->setVisible(!is_visible); + ui_->dateEdit->setVisible(!is_visible); + }); + connect( + ui_->addTagsButton, &QPushButton::clicked, this, + &NoteEditDialog::on_add_tags_button_click + ); +} + +NoteEditDialog::~NoteEditDialog() { + delete ui_; +} + +void NoteEditDialog::on_save_button_click() { + QString title = ui_->titleLineEdit->text(); + QString content = ui_->descriptionTextEdit->toPlainText(); + + const QString message = + QString("Заголовок: %1\nСодержимое: %2").arg(title, content); + + if (try_save_note()) { + QMessageBox::information(this, "Заметка сохранена", message); + } +} + +void NoteEditDialog::on_join_button_click() { + const bool is_joined = ui_->membersLabel->isVisible(); + + if (!is_joined) { + ui_->membersLabel->setVisible(true); + + QPixmap pixmap(32, 32); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + painter.setBrush(Qt::black); + painter.setPen(Qt::NoPen); + painter.drawEllipse(0, 0, 32, 32); + + avatar_label_ = std::make_unique(this); + avatar_label_->setPixmap(pixmap); + avatar_label_->setFixedSize(32, 32); + ui_->avatarsLayout->addWidget(avatar_label_.get()); + + ui_->joinButton->setText("Покинуть"); + } else { + ui_->membersLabel->setVisible(false); + + if (avatar_label_) { + ui_->avatarsLayout->removeWidget(avatar_label_.get()); + avatar_label_.reset(); + } + + ui_->joinButton->setText("Присоединиться"); + } +} + +void NoteEditDialog::on_add_members_button_click() { + QMessageBox::information(this, "Ошибка", "Другие участники не найдены :("); +} + +void NoteEditDialog::on_add_tags_button_click() { + TagsDialog tags_dialog(selected_tags_, this); + if (tags_dialog.exec() == Accepted) { + selected_tags_ = tags_dialog.get_selected_tags(); + + if (!ui_->tagsLabel->isVisible()) { + ui_->tagsLabel->setVisible(true); + } + + tag_labels_.clear(); + + for (const auto &[is_checked, color, name] : selected_tags_) { + auto tag_label = std::make_unique(name, this); + tag_label->setStyleSheet(QString("background-color: %1; " + "color: white; " + "padding: 9px 10px; " + "border-radius: 5px; " + "font-family: 'Arial'; " + "font-size: 12px; " + "font-weight: bold;" + "width: 40px;" + "height: 25px;") + .arg(color)); + ui_->tagsLayout->addWidget(tag_label.get()); + tag_labels_.push_back(std::move(tag_label)); + } + } + + if (selected_tags_.empty()) { + ui_->tagsLabel->setVisible(false); + } +} + +bool NoteEditDialog::try_save_note() const { + note_->set_title(ui_->titleLineEdit->text().toStdString()); + note_->set_text(ui_->descriptionTextEdit->toPlainText().toStdString()); + return NoteDao::create_note(*note_); +} diff --git a/client/ui/note-widget/src/tags_dialog.cpp b/client/ui/note-widget/src/tags_dialog.cpp new file mode 100644 index 0000000..5a3d61e --- /dev/null +++ b/client/ui/note-widget/src/tags_dialog.cpp @@ -0,0 +1,87 @@ +#include "tags_dialog.h" +#include +#include +#include +#include "tags_dialog_styles.h" + +TagsDialog::TagsDialog(const QList &initial_tags, QWidget *parent) + : QDialog(parent) { + setup_ui(); + + for (int i = 0; i < qMin(MAX_TAGS_COUNT, initial_tags.size()); ++i) { + const auto &[is_checked, color, name] = initial_tags[i]; + check_boxes_[i]->setChecked(is_checked); + + const int color_index = color_combo_boxes_[i]->findData(color); + if (color_index != -1) { + color_combo_boxes_[i]->setCurrentIndex(color_index); + } + + name_line_edits_[i]->setText(name); + } + + setWindowTitle("Добавить теги"); + setModal(true); + setFixedSize(DIALOG_SIZE.first, DIALOG_SIZE.second); + setStyleSheet(Ui::tags_dialog_light_theme); +} + +void TagsDialog::setup_ui() { + auto *main_layout = new QVBoxLayout(this); + + for (int i = 0; i < MAX_TAGS_COUNT; ++i) { + auto *tag_layout = new QHBoxLayout(); + + check_boxes_[i] = std::make_unique(this); + check_boxes_[i]->setChecked(false); + tag_layout->addWidget(check_boxes_[i].get()); + + color_combo_boxes_[i] = std::make_unique(this); + color_combo_boxes_[i]->addItem("Красный", "#e7624b"); + color_combo_boxes_[i]->addItem("Синий", "#165d7b"); + color_combo_boxes_[i]->addItem("Розовый", "#bd6dab"); + color_combo_boxes_[i]->addItem("Зеленый", "#00b16b"); + color_combo_boxes_[i]->addItem("Желтый", "#e69f00"); + tag_layout->addWidget(color_combo_boxes_[i].get()); + + name_line_edits_[i] = std::make_unique(this); + name_line_edits_[i]->setPlaceholderText( + "Имя тега " + QString::number(i + 1) + ); + tag_layout->addWidget(name_line_edits_[i].get()); + + main_layout->addLayout(tag_layout); + } + + auto *button_layout = new QHBoxLayout; + + ok_button_ = std::make_unique("OK", this); + ok_button_->setObjectName("ok_button"); + button_layout->addWidget(ok_button_.get()); + + cancel_button_ = std::make_unique("Отмена", this); + cancel_button_->setObjectName("cancel_button"); + button_layout->addWidget(cancel_button_.get()); + + main_layout->addLayout(button_layout); + + connect(ok_button_.get(), &QPushButton::clicked, this, &TagsDialog::accept); + connect( + cancel_button_.get(), &QPushButton::clicked, this, &TagsDialog::reject + ); +} + +QList TagsDialog::get_selected_tags() const { + QList tags; + for (int i = 0; i < MAX_TAGS_COUNT; ++i) { + if (check_boxes_[i]->isChecked() && + !name_line_edits_[i]->text().isEmpty()) { + Tag tag; + tag.is_checked = true; + tag.color = color_combo_boxes_[i]->currentData().toString(); + tag.name = name_line_edits_[i]->text(); + tags.append(tag); + } + } + return tags; +} \ No newline at end of file diff --git a/client/ui/note-widget/ui/note_edit_dialog.ui b/client/ui/note-widget/ui/note_edit_dialog.ui new file mode 100644 index 0000000..90ed1b9 --- /dev/null +++ b/client/ui/note-widget/ui/note_edit_dialog.ui @@ -0,0 +1,369 @@ + + + NoteEditDialog + + + + 0 + 0 + 700 + 480 + + + + NoteEditDialog + + + + + 20 + 20 + 451 + 434 + + + + + 45 + + + + + 6 + + + + + Название заметки + + + + + + + + Arial + 10 + false + true + + + + в проекте ToDo + + + + + + + + + 20 + + + + + 20 + + + Qt::AlignLeft + + + + + + + + Arial + 10 + true + + + + false + + + Участники + + + + + + + 5 + + + + + + + + + + + + Arial + 10 + true + + + + false + + + Срок + + + + + + + false + + + dd/MM/yyyy + + + true + + + + + + + + + + + + Arial + 10 + true + + + + Теги + + + false + + + + + + + 5 + + + + + + + + + + + + Arial + 14 + true + + + + Описание + + + + + + + + Arial + 10 + + + + Добавьте подробное описание для Вашей заметки. + + + + + + + 20 + + + + + + 70 + 45 + + + + + Arial + 11 + true + + + + Сохранить + + + + + + + + 70 + 45 + + + + + Arial + 11 + true + + + + Отмена + + + + + + + + + + + + + 504 + 20 + 171 + 291 + + + + + 30 + + + + + 6 + + + + + + Arial + 16 + false + false + + + + Предложения + + + + + + + 10 + + + + + + 0 + 40 + + + + Присоединиться + + + + + + + + + + + 12 + + + + + + Arial + 16 + false + false + + + + Добавить к заметке + + + + + + + 10 + + + + + + 0 + 40 + + + + Участники + + + + + + + + 0 + 40 + + + + Дата + + + + + + + + 0 + 40 + + + + Теги + + + + + + + + + + + + + From 12ac15f1516244c25c57a012b16e68f74c8fde47 Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 00:56:31 +0300 Subject: [PATCH 05/47] change .gitignore --- .gitignore | 7 ++++--- client/main.cpp | 30 ++++++++++++++++++++++++++++++ server/main.cpp | 5 +++++ 3 files changed, 39 insertions(+), 3 deletions(-) create mode 100644 client/main.cpp create mode 100644 server/main.cpp diff --git a/.gitignore b/.gitignore index 0ff8f19..f1a0782 100644 --- a/.gitignore +++ b/.gitignore @@ -55,7 +55,8 @@ doc/api/ *.out local.properties +**/main.cpp +!client/main.cpp +!server/main.cpp -main.cpp - -*pb* \ No newline at end of file +*pb* diff --git a/client/main.cpp b/client/main.cpp new file mode 100644 index 0000000..ba3cc2f --- /dev/null +++ b/client/main.cpp @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include "applicationwindow.h" +#include "mainwindow.h" +#include "note.hpp" +#include "storage.hpp" +#include "registration_window.h" +#include "login_window.h" + +using namespace Ui; + +int main(int argc, char *argv[]) { + QApplication a(argc, argv); + + QTranslator translator; + const QStringList uiLanguages = QLocale::system().uiLanguages(); + for (const QString &locale : uiLanguages) { + const QString baseName = "MainWindow_" + QLocale(locale).name(); + if (translator.load(":/i18n/" + baseName)) { + a.installTranslator(&translator); + break; + } + } + + Ui::LoginWindow login_window; + login_window.show(); + return login_window.exec(); +} \ No newline at end of file diff --git a/server/main.cpp b/server/main.cpp new file mode 100644 index 0000000..6736c7c --- /dev/null +++ b/server/main.cpp @@ -0,0 +1,5 @@ + + +int main() { + +} \ No newline at end of file From 1ae49bdfda9668c17e1e64e62bee6b05f22f80f4 Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 01:08:56 +0300 Subject: [PATCH 06/47] return cmake for client --- client/CMakeLists.txt | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index e69de29..5a82d38 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.16) + +project(EfficioTaskTracker LANGUAGES CXX) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_AUTOUIC ON) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) + +add_subdirectory(scripts) +add_subdirectory(database) +add_subdirectory(ui/main-window) +add_subdirectory(ui/authorization-windows) +add_subdirectory(ui/note-widget) +add_subdirectory(proto) +add_executable(EfficioTaskTracker main.cpp) + +target_link_libraries(EfficioTaskTracker PRIVATE + Qt6::Widgets + Qt6::Core + Qt6::Gui + Qt6::Sql + model-proto + efficio-rpc + Database + Scripts + AuthorizationWindows + MainWindow + NoteWidget +) From 050b1cfd1c6771868f7786c22bd550ba2cd50c2b Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 01:11:06 +0300 Subject: [PATCH 07/47] copy database to client --- client/database/database_manager.cpp | 63 ++++++++++++++++++++++++++ client/database/database_manager.hpp | 23 ++++++++++ client/database/lr_dao.cpp | 47 ++++++++++++++++++++ client/database/lr_dao.hpp | 21 +++++++++ client/database/note_dao.cpp | 66 ++++++++++++++++++++++++++++ client/database/note_dao.hpp | 19 ++++++++ 6 files changed, 239 insertions(+) create mode 100644 client/database/database_manager.cpp create mode 100644 client/database/database_manager.hpp create mode 100644 client/database/lr_dao.cpp create mode 100644 client/database/lr_dao.hpp create mode 100644 client/database/note_dao.cpp create mode 100644 client/database/note_dao.hpp diff --git a/client/database/database_manager.cpp b/client/database/database_manager.cpp new file mode 100644 index 0000000..ac214ba --- /dev/null +++ b/client/database/database_manager.cpp @@ -0,0 +1,63 @@ +#include "database_manager.hpp" +#include +#include +#include + +DatabaseManager::DatabaseManager() { + QSqlQuery query; + + database_ = QSqlDatabase::addDatabase("QPSQL"); + database_.setHostName("localhost"); + database_.setPort(5432); + database_.setDatabaseName("efficio"); + database_.setUserName("efficio"); + database_.setPassword("admin"); + database_.open(); + + query.exec( + "CREATE TABLE IF NOT EXISTS notes (" + "id SERIAL PRIMARY KEY, " + "title TEXT NOT NULL, " + "content TEXT NOT NULL" + ")" + ); + + query.exec( + "CREATE TABLE IF NOT EXISTS logins (" + "login VARCHAR(50) PRIMARY KEY, " + "password VARCHAR(50) NOT NULL " + ")" + ); +} + +DatabaseManager::~DatabaseManager() { + if (database_.isOpen()) { + database_.close(); + } +} + +DatabaseManager &DatabaseManager::get_instance() { + static DatabaseManager instance; + return instance; +} + +bool DatabaseManager::check_connection() const { + return database_.isOpen(); +} + +bool DatabaseManager::execute_query( + QSqlQuery &query, + const QString &query_str, + const QVariantList ¶ms +) { + query.prepare(query_str); + for (int i = 0; i < params.size(); i++) { + query.bindValue(i, params[i]); + } + + if (!query.exec()) { + qDebug() << "Query execution error:" << query.lastError().text(); + return false; + } + return true; +} \ No newline at end of file diff --git a/client/database/database_manager.hpp b/client/database/database_manager.hpp new file mode 100644 index 0000000..e9a2d5f --- /dev/null +++ b/client/database/database_manager.hpp @@ -0,0 +1,23 @@ +#ifndef DATABASE_MANAGER_HPP +#define DATABASE_MANAGER_HPP + +#include + +class DatabaseManager final { +public: + static DatabaseManager &get_instance(); + [[nodiscard]] bool check_connection() const; + bool execute_query( + QSqlQuery &query, + const QString &query_str, + const QVariantList ¶ms = {} + ); + +private: + explicit DatabaseManager(); + ~DatabaseManager(); + + QSqlDatabase database_; +}; + +#endif // DATABASE_MANAGER_HPP diff --git a/client/database/lr_dao.cpp b/client/database/lr_dao.cpp new file mode 100644 index 0000000..128504c --- /dev/null +++ b/client/database/lr_dao.cpp @@ -0,0 +1,47 @@ +#include "lr_dao.hpp" +#include +#include +#include +#include + +QString LRDao::hash_password(const QString &password) { + QByteArray hash = + QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256); + return hash.toHex(); +} + +bool LRDao::register_user(const QString &login, const QString &password) { + // maybe in future make it thread-safe? + QSqlQuery query; + + QString check_query_str = "SELECT * FROM users WHERE login = ?"; + QVariantList check_params = {login}; + bool check_query = DatabaseManager::get_instance().execute_query( + query, check_query_str, check_params + ); + + if (check_query) { + return false; + } + QString hashed_password = LRDao::hash_password(password); + QString insert_query_str = + "INSERT INTO users (login, password) VALUES (?, ?)"; + QVariantList insert_params = {login, hashed_password}; + + return DatabaseManager::get_instance().execute_query( + query, insert_query_str, insert_params + ); +} + +bool LRDao::validate_user(const QString &login, const QString &password) { + QSqlQuery query; + + QString hashed_password = LRDao::hash_password(password); + QString query_str = "SELECT * FROM users WHERE login = ? AND password = ?"; + QVariantList params = {login, hashed_password}; + + return DatabaseManager::get_instance().execute_query( + query, query_str, params + ); + return query.next(); +} diff --git a/client/database/lr_dao.hpp b/client/database/lr_dao.hpp new file mode 100644 index 0000000..677405c --- /dev/null +++ b/client/database/lr_dao.hpp @@ -0,0 +1,21 @@ +#ifndef LRDAO_H +#define LRDAO_H + +#include +#include +#include +#include +#include +#include "database_manager.hpp" + +class LRDao { +public: + LRDao() = default; + static bool register_user(const QString &login, const QString &password); + static bool validate_user(const QString &login, const QString &password); + +private: + static QString hash_password(const QString &password); +}; + +#endif // LRDAO_H diff --git a/client/database/note_dao.cpp b/client/database/note_dao.cpp new file mode 100644 index 0000000..387e471 --- /dev/null +++ b/client/database/note_dao.cpp @@ -0,0 +1,66 @@ +#include "note_dao.hpp" +#include +#include "database_manager.hpp" + +bool NoteDao::create_note(const Note ¬e) { + QSqlQuery query; + const QVariantList params = { + QString::fromStdString(note.get_title()), + QString::fromStdString(note.get_text())}; + + return DatabaseManager::get_instance().execute_query( + query, + "INSERT INTO notes (title, content) " + "VALUES (:title, :content)", + params + ); +} + +bool NoteDao::update_note(const Note ¬e) const { + QSqlQuery query; + const QString sql_query = + "UPDATE notes SET " + "title = :title, " + "content = :content " + "WHERE id = :id"; + const QVariantList params = { + QString::fromStdString(note.get_title()), + QString::fromStdString(note.get_text()), note.get_id()}; + + return DatabaseManager::get_instance().execute_query( + query, sql_query, params + ); +} + +bool NoteDao::delete_note(int id) const { + QSqlQuery query; + const QString sql_query = "DELETE FROM notes WHERE id = :id"; + return DatabaseManager::get_instance().execute_query( + query, sql_query, {id} + ); +} + +std::vector NoteDao::get_all_notes() const { + QSqlQuery query; + std::vector notes; + DatabaseManager::get_instance().execute_query(query, "SELECT * FROM notes"); + + while (query.next()) { + auto id = query.value("id").toUInt(); + auto title = query.value("title").toString().toStdString(); + auto text = query.value("content").toString().toStdString(); + notes.emplace_back(id, title, text); + } + + return notes; +} + +Note NoteDao::get_note_by_id(int id) const { + QSqlQuery query; + DatabaseManager::get_instance().execute_query( + query, "SELECT * FROM notes WHERE id = :id", {id} + ); + auto title = query.value("title").toString().toStdString(); + auto text = query.value("content").toString().toStdString(); + return {id, title, text}; +} diff --git a/client/database/note_dao.hpp b/client/database/note_dao.hpp new file mode 100644 index 0000000..0736f0f --- /dev/null +++ b/client/database/note_dao.hpp @@ -0,0 +1,19 @@ +#ifndef NOTEDAO_HPP +#define NOTEDAO_HPP + +#include "note.hpp" +#include + +using namespace project_storage_model; + +class NoteDao { +public: + NoteDao() = default; + static bool create_note(const Note ¬e); + bool update_note(const Note& note) const; + bool delete_note(int id) const; + [[nodiscard]] std::vector get_all_notes() const; + [[nodiscard]] Note get_note_by_id(int id) const; +}; + +#endif // NOTEDAO_HPP \ No newline at end of file From 5d93e07cff7f421bfe0165c08328d5071cb0172f Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 01:18:15 +0300 Subject: [PATCH 08/47] update client --- client/database/CMakeLists.txt | 21 ++ .../{ => include}/database_manager.hpp | 7 +- client/database/include/lr_dao.hpp | 19 ++ client/database/include/note_dao.hpp | 24 ++ client/database/include/project_dao.hpp | 18 ++ client/database/include/serialization.hpp | 13 ++ client/database/lr_dao.cpp | 47 ---- client/database/lr_dao.hpp | 21 -- client/database/note_dao.cpp | 66 ------ client/database/note_dao.hpp | 19 -- .../database/{ => src}/database_manager.cpp | 49 +++-- client/database/src/lr_dao.cpp | 81 +++++++ client/database/src/note_dao.cpp | 120 ++++++++++ client/database/src/project_dao.cpp | 64 ++++++ client/database/src/serialization.cpp | 41 ++++ client/scripts/CMakeLists.txt | 17 ++ client/scripts/include/note.hpp | 46 ++++ client/scripts/{ => include}/project.hpp | 6 +- client/scripts/{ => include}/storage.hpp | 6 +- client/scripts/{ => include}/user.hpp | 6 +- client/scripts/note.cpp | 43 ---- client/scripts/note.hpp | 33 --- client/scripts/src/note.cpp | 68 ++++++ client/scripts/{ => src}/project.cpp | 19 +- client/scripts/{ => src}/storage.cpp | 4 +- client/scripts/{ => src}/user.cpp | 11 +- client/ui/MainWindow/CMakeLists.txt | 37 ---- client/ui/MainWindow/include/bottombar.h | 20 -- client/ui/MainWindow/include/mainwindow.h | 39 ---- client/ui/MainWindow/include/notelist.h | 27 --- client/ui/MainWindow/include/notewidget.h | 27 --- client/ui/MainWindow/include/projectitem.h | 19 -- client/ui/MainWindow/include/projectlist.h | 20 -- .../ui/MainWindow/src/applicationwindow.cpp | 15 -- client/ui/MainWindow/src/bottombar.cpp | 26 --- client/ui/MainWindow/src/mainwindow.cpp | 166 -------------- client/ui/MainWindow/src/notelist.cpp | 61 ----- client/ui/MainWindow/src/notewidget.cpp | 27 --- client/ui/MainWindow/src/projectitem.cpp | 9 - client/ui/MainWindow/src/projectlist.cpp | 18 -- .../ui/authorization-windows/CMakeLists.txt | 43 ++++ .../include/login_window.h | 7 +- .../include/login_window_style_sheet.h | 60 +++++ .../include/registration_window.h | 5 +- .../include/registration_window_style_sheet.h | 58 +++++ .../login_window_ru_RU.ts | 2 +- .../src/login_window.cpp | 64 ++++-- .../src/registration_window.cpp | 50 ++++- .../ui/login_window.ui | 6 +- .../ui/registration_window.ui | 6 +- .../CMakeLists.txt | 61 ----- .../include/login_window_style_sheet.h | 105 --------- .../include/registration_window_style_sheet.h | 105 --------- client/ui/main-window/CMakeLists.txt | 39 ++++ .../MainWindow_en_US.ts | 2 +- .../include/applicationwindow.h | 9 +- client/ui/main-window/include/bottombar.h | 24 ++ .../main-window/include/main_window_style.hpp | 139 ++++++++++++ client/ui/main-window/include/mainwindow.h | 43 ++++ client/ui/main-window/include/notelist.h | 29 +++ client/ui/main-window/include/notewidget.h | 31 +++ client/ui/main-window/include/projectitem.h | 19 ++ client/ui/main-window/include/projectlist.h | 22 ++ .../ui/main-window/src/applicationwindow.cpp | 14 ++ client/ui/main-window/src/bottombar.cpp | 31 +++ client/ui/main-window/src/mainwindow.cpp | 113 ++++++++++ client/ui/main-window/src/notelist.cpp | 66 ++++++ client/ui/main-window/src/notewidget.cpp | 53 +++++ client/ui/main-window/src/projectitem.cpp | 12 + client/ui/main-window/src/projectlist.cpp | 24 ++ client/ui/note-widget/CMakeLists.txt | 59 ++--- .../ui/note-widget/NoteWidgetEfficio_ru_RU.ts | 2 +- .../ui/note-widget/include/note_edit_dialog.h | 28 ++- .../include/note_edit_dialog_styles.h | 100 +-------- .../note-widget/include/tags_dialog_styles.h | 2 +- .../ui/note-widget/src/note_edit_dialog.cpp | 208 +++++++++++------- client/ui/note-widget/ui/note_edit_dialog.ui | 4 +- 77 files changed, 1705 insertions(+), 1320 deletions(-) create mode 100644 client/database/CMakeLists.txt rename client/database/{ => include}/database_manager.hpp (76%) create mode 100644 client/database/include/lr_dao.hpp create mode 100644 client/database/include/note_dao.hpp create mode 100644 client/database/include/project_dao.hpp create mode 100644 client/database/include/serialization.hpp delete mode 100644 client/database/lr_dao.cpp delete mode 100644 client/database/lr_dao.hpp delete mode 100644 client/database/note_dao.cpp delete mode 100644 client/database/note_dao.hpp rename client/database/{ => src}/database_manager.cpp (53%) create mode 100644 client/database/src/lr_dao.cpp create mode 100644 client/database/src/note_dao.cpp create mode 100644 client/database/src/project_dao.cpp create mode 100644 client/database/src/serialization.cpp create mode 100644 client/scripts/CMakeLists.txt create mode 100644 client/scripts/include/note.hpp rename client/scripts/{ => include}/project.hpp (78%) rename client/scripts/{ => include}/storage.hpp (73%) rename client/scripts/{ => include}/user.hpp (76%) delete mode 100644 client/scripts/note.cpp delete mode 100644 client/scripts/note.hpp create mode 100644 client/scripts/src/note.cpp rename client/scripts/{ => src}/project.cpp (60%) rename client/scripts/{ => src}/storage.cpp (88%) rename client/scripts/{ => src}/user.cpp (69%) delete mode 100644 client/ui/MainWindow/CMakeLists.txt delete mode 100644 client/ui/MainWindow/include/bottombar.h delete mode 100644 client/ui/MainWindow/include/mainwindow.h delete mode 100644 client/ui/MainWindow/include/notelist.h delete mode 100644 client/ui/MainWindow/include/notewidget.h delete mode 100644 client/ui/MainWindow/include/projectitem.h delete mode 100644 client/ui/MainWindow/include/projectlist.h delete mode 100644 client/ui/MainWindow/src/applicationwindow.cpp delete mode 100644 client/ui/MainWindow/src/bottombar.cpp delete mode 100644 client/ui/MainWindow/src/mainwindow.cpp delete mode 100644 client/ui/MainWindow/src/notelist.cpp delete mode 100644 client/ui/MainWindow/src/notewidget.cpp delete mode 100644 client/ui/MainWindow/src/projectitem.cpp delete mode 100644 client/ui/MainWindow/src/projectlist.cpp create mode 100644 client/ui/authorization-windows/CMakeLists.txt rename client/ui/{login-and-registration-windows => authorization-windows}/include/login_window.h (75%) create mode 100644 client/ui/authorization-windows/include/login_window_style_sheet.h rename client/ui/{login-and-registration-windows => authorization-windows}/include/registration_window.h (84%) create mode 100644 client/ui/authorization-windows/include/registration_window_style_sheet.h rename client/ui/{login-and-registration-windows => authorization-windows}/login_window_ru_RU.ts (54%) rename client/ui/{login-and-registration-windows => authorization-windows}/src/login_window.cpp (53%) rename client/ui/{login-and-registration-windows => authorization-windows}/src/registration_window.cpp (73%) rename client/ui/{login-and-registration-windows => authorization-windows}/ui/login_window.ui (92%) rename client/ui/{login-and-registration-windows => authorization-windows}/ui/registration_window.ui (91%) delete mode 100644 client/ui/login-and-registration-windows/CMakeLists.txt delete mode 100644 client/ui/login-and-registration-windows/include/login_window_style_sheet.h delete mode 100644 client/ui/login-and-registration-windows/include/registration_window_style_sheet.h create mode 100644 client/ui/main-window/CMakeLists.txt rename client/ui/{MainWindow => main-window}/MainWindow_en_US.ts (54%) rename client/ui/{MainWindow => main-window}/include/applicationwindow.h (54%) create mode 100644 client/ui/main-window/include/bottombar.h create mode 100644 client/ui/main-window/include/main_window_style.hpp create mode 100644 client/ui/main-window/include/mainwindow.h create mode 100644 client/ui/main-window/include/notelist.h create mode 100644 client/ui/main-window/include/notewidget.h create mode 100644 client/ui/main-window/include/projectitem.h create mode 100644 client/ui/main-window/include/projectlist.h create mode 100644 client/ui/main-window/src/applicationwindow.cpp create mode 100644 client/ui/main-window/src/bottombar.cpp create mode 100644 client/ui/main-window/src/mainwindow.cpp create mode 100644 client/ui/main-window/src/notelist.cpp create mode 100644 client/ui/main-window/src/notewidget.cpp create mode 100644 client/ui/main-window/src/projectitem.cpp create mode 100644 client/ui/main-window/src/projectlist.cpp diff --git a/client/database/CMakeLists.txt b/client/database/CMakeLists.txt new file mode 100644 index 0000000..27bb557 --- /dev/null +++ b/client/database/CMakeLists.txt @@ -0,0 +1,21 @@ +cmake_minimum_required(VERSION 3.16) + +project(Database LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Core Sql REQUIRED) + +file(GLOB SOURCES "src/*.cpp") +file(GLOB HEADERS "include/*.hpp") + +add_library(Database STATIC ${SOURCES} ${HEADERS}) + +target_include_directories(Database + PUBLIC + $ + $ +) + +target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql Qt6::Widgets Scripts) \ No newline at end of file diff --git a/client/database/database_manager.hpp b/client/database/include/database_manager.hpp similarity index 76% rename from client/database/database_manager.hpp rename to client/database/include/database_manager.hpp index e9a2d5f..1bba4b6 100644 --- a/client/database/database_manager.hpp +++ b/client/database/include/database_manager.hpp @@ -6,18 +6,17 @@ class DatabaseManager final { public: static DatabaseManager &get_instance(); - [[nodiscard]] bool check_connection() const; bool execute_query( QSqlQuery &query, const QString &query_str, const QVariantList ¶ms = {} - ); + ) const; + [[nodiscard]] QSqlDatabase get_database() const; private: explicit DatabaseManager(); ~DatabaseManager(); - QSqlDatabase database_; }; -#endif // DATABASE_MANAGER_HPP +#endif // DATABASE_MANAGER_HPP \ No newline at end of file diff --git a/client/database/include/lr_dao.hpp b/client/database/include/lr_dao.hpp new file mode 100644 index 0000000..de8d5fb --- /dev/null +++ b/client/database/include/lr_dao.hpp @@ -0,0 +1,19 @@ +#ifndef LRDAO_H +#define LRDAO_H + +#include "database_manager.hpp" + +class LRDao { +public: + LRDao() = default; + static int try_register_user(const QString &login, const QString &password); + static bool validate_user(const QString &login, const QString &password); + static bool add_project_to_user(std::string user_login, int project_id); + static bool + get_user_projects(const std::string &login, std::vector &projects); + +private: + static QString hash_password(const QString &password); +}; + +#endif // LRDAO_H \ No newline at end of file diff --git a/client/database/include/note_dao.hpp b/client/database/include/note_dao.hpp new file mode 100644 index 0000000..0fbe980 --- /dev/null +++ b/client/database/include/note_dao.hpp @@ -0,0 +1,24 @@ +#ifndef NOTEDAO_HPP +#define NOTEDAO_HPP + +#include +#include +#include "note.hpp" + +using namespace project_storage_model; + +class NoteDao { +public: + NoteDao() = default; + static bool initialize_note(int &id); + static bool update_note(const Note ¬e); + static bool delete_note(int id); + [[nodiscard]] static std::vector get_all_notes(); + [[nodiscard]] static Note get_note_by_id(int id); + +private: + static QString convert_string_set_to_postgres_array(const std::unordered_set& string_set); + static QString convert_tags_to_postgres_array(const std::vector& tags); +}; + +#endif // NOTEDAO_HPP \ No newline at end of file diff --git a/client/database/include/project_dao.hpp b/client/database/include/project_dao.hpp new file mode 100644 index 0000000..a038af6 --- /dev/null +++ b/client/database/include/project_dao.hpp @@ -0,0 +1,18 @@ +#ifndef PROJECT_DAO_HPP +#define PROJECT_DAO_HPP + +#include "database_manager.hpp" +#include "project.hpp" + +namespace DB { + +class ProjectDAO { +public: + ProjectDAO() = default; + static bool create_project(const std::string &name, int &id); + static bool get_project(int id, std::string &name, std::vector ¬es); + static bool add_note_to_project(int project_id, int note_id); +}; +} // namespace DB + +#endif \ No newline at end of file diff --git a/client/database/include/serialization.hpp b/client/database/include/serialization.hpp new file mode 100644 index 0000000..f9b0def --- /dev/null +++ b/client/database/include/serialization.hpp @@ -0,0 +1,13 @@ +#ifndef SERIALIZATION_HPP +#define SERIALIZATION_HPP +#include "storage.hpp" + +class Serialization { +public: + static bool get_storage( + project_storage_model::Storage &storage, + const std::string &login + ); +}; + +#endif \ No newline at end of file diff --git a/client/database/lr_dao.cpp b/client/database/lr_dao.cpp deleted file mode 100644 index 128504c..0000000 --- a/client/database/lr_dao.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "lr_dao.hpp" -#include -#include -#include -#include - -QString LRDao::hash_password(const QString &password) { - QByteArray hash = - QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256); - return hash.toHex(); -} - -bool LRDao::register_user(const QString &login, const QString &password) { - // maybe in future make it thread-safe? - QSqlQuery query; - - QString check_query_str = "SELECT * FROM users WHERE login = ?"; - QVariantList check_params = {login}; - bool check_query = DatabaseManager::get_instance().execute_query( - query, check_query_str, check_params - ); - - if (check_query) { - return false; - } - QString hashed_password = LRDao::hash_password(password); - QString insert_query_str = - "INSERT INTO users (login, password) VALUES (?, ?)"; - QVariantList insert_params = {login, hashed_password}; - - return DatabaseManager::get_instance().execute_query( - query, insert_query_str, insert_params - ); -} - -bool LRDao::validate_user(const QString &login, const QString &password) { - QSqlQuery query; - - QString hashed_password = LRDao::hash_password(password); - QString query_str = "SELECT * FROM users WHERE login = ? AND password = ?"; - QVariantList params = {login, hashed_password}; - - return DatabaseManager::get_instance().execute_query( - query, query_str, params - ); - return query.next(); -} diff --git a/client/database/lr_dao.hpp b/client/database/lr_dao.hpp deleted file mode 100644 index 677405c..0000000 --- a/client/database/lr_dao.hpp +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef LRDAO_H -#define LRDAO_H - -#include -#include -#include -#include -#include -#include "database_manager.hpp" - -class LRDao { -public: - LRDao() = default; - static bool register_user(const QString &login, const QString &password); - static bool validate_user(const QString &login, const QString &password); - -private: - static QString hash_password(const QString &password); -}; - -#endif // LRDAO_H diff --git a/client/database/note_dao.cpp b/client/database/note_dao.cpp deleted file mode 100644 index 387e471..0000000 --- a/client/database/note_dao.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "note_dao.hpp" -#include -#include "database_manager.hpp" - -bool NoteDao::create_note(const Note ¬e) { - QSqlQuery query; - const QVariantList params = { - QString::fromStdString(note.get_title()), - QString::fromStdString(note.get_text())}; - - return DatabaseManager::get_instance().execute_query( - query, - "INSERT INTO notes (title, content) " - "VALUES (:title, :content)", - params - ); -} - -bool NoteDao::update_note(const Note ¬e) const { - QSqlQuery query; - const QString sql_query = - "UPDATE notes SET " - "title = :title, " - "content = :content " - "WHERE id = :id"; - const QVariantList params = { - QString::fromStdString(note.get_title()), - QString::fromStdString(note.get_text()), note.get_id()}; - - return DatabaseManager::get_instance().execute_query( - query, sql_query, params - ); -} - -bool NoteDao::delete_note(int id) const { - QSqlQuery query; - const QString sql_query = "DELETE FROM notes WHERE id = :id"; - return DatabaseManager::get_instance().execute_query( - query, sql_query, {id} - ); -} - -std::vector NoteDao::get_all_notes() const { - QSqlQuery query; - std::vector notes; - DatabaseManager::get_instance().execute_query(query, "SELECT * FROM notes"); - - while (query.next()) { - auto id = query.value("id").toUInt(); - auto title = query.value("title").toString().toStdString(); - auto text = query.value("content").toString().toStdString(); - notes.emplace_back(id, title, text); - } - - return notes; -} - -Note NoteDao::get_note_by_id(int id) const { - QSqlQuery query; - DatabaseManager::get_instance().execute_query( - query, "SELECT * FROM notes WHERE id = :id", {id} - ); - auto title = query.value("title").toString().toStdString(); - auto text = query.value("content").toString().toStdString(); - return {id, title, text}; -} diff --git a/client/database/note_dao.hpp b/client/database/note_dao.hpp deleted file mode 100644 index 0736f0f..0000000 --- a/client/database/note_dao.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef NOTEDAO_HPP -#define NOTEDAO_HPP - -#include "note.hpp" -#include - -using namespace project_storage_model; - -class NoteDao { -public: - NoteDao() = default; - static bool create_note(const Note ¬e); - bool update_note(const Note& note) const; - bool delete_note(int id) const; - [[nodiscard]] std::vector get_all_notes() const; - [[nodiscard]] Note get_note_by_id(int id) const; -}; - -#endif // NOTEDAO_HPP \ No newline at end of file diff --git a/client/database/database_manager.cpp b/client/database/src/database_manager.cpp similarity index 53% rename from client/database/database_manager.cpp rename to client/database/src/database_manager.cpp index ac214ba..ad5b9ec 100644 --- a/client/database/database_manager.cpp +++ b/client/database/src/database_manager.cpp @@ -4,8 +4,6 @@ #include DatabaseManager::DatabaseManager() { - QSqlQuery query; - database_ = QSqlDatabase::addDatabase("QPSQL"); database_.setHostName("localhost"); database_.setPort(5432); @@ -14,18 +12,31 @@ DatabaseManager::DatabaseManager() { database_.setPassword("admin"); database_.open(); + QSqlQuery query(database_); query.exec( "CREATE TABLE IF NOT EXISTS notes (" "id SERIAL PRIMARY KEY, " "title TEXT NOT NULL, " - "content TEXT NOT NULL" + "content TEXT, " + "members VARCHAR(50)[], " + "date VARCHAR(50), " + "tags VARCHAR(50)[]" ")" ); query.exec( - "CREATE TABLE IF NOT EXISTS logins (" + "CREATE TABLE IF NOT EXISTS users (" "login VARCHAR(50) PRIMARY KEY, " - "password VARCHAR(50) NOT NULL " + "password VARCHAR(50) NOT NULL, " + "projects INT[]" + ")" + ); + + query.exec( + "CREATE TABLE IF NOT EXISTS projects(" + "id SERIAL PRIMARY KEY, " + "name VARCHAR(50) NOT NULL, " + "notes INT[]" ")" ); } @@ -38,26 +49,32 @@ DatabaseManager::~DatabaseManager() { DatabaseManager &DatabaseManager::get_instance() { static DatabaseManager instance; + if (!instance.database_.isOpen() && !instance.database_.open()) { + throw std::runtime_error("Lost connection with database"); + } return instance; } -bool DatabaseManager::check_connection() const { - return database_.isOpen(); -} - bool DatabaseManager::execute_query( QSqlQuery &query, const QString &query_str, const QVariantList ¶ms -) { - query.prepare(query_str); +) const { + QSqlQuery temp(database_); + query = std::move(temp); + + if (!query.prepare(query_str)) { + qDebug() << "Prepare error:" << query.lastError(); + return false; + } + for (int i = 0; i < params.size(); i++) { query.bindValue(i, params[i]); } - if (!query.exec()) { - qDebug() << "Query execution error:" << query.lastError().text(); - return false; - } - return true; + return query.exec(); +} + +QSqlDatabase DatabaseManager::get_database() const { + return database_; } \ No newline at end of file diff --git a/client/database/src/lr_dao.cpp b/client/database/src/lr_dao.cpp new file mode 100644 index 0000000..3107f5b --- /dev/null +++ b/client/database/src/lr_dao.cpp @@ -0,0 +1,81 @@ +#include "lr_dao.hpp" +#include +#include +#include +#include +#include + +QString LRDao::hash_password(const QString &password) { + const QByteArray hash = + QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256); + return hash.toHex(); +} + +int LRDao::try_register_user(const QString &login, const QString &password) { + QSqlQuery query; + + const bool is_login_free = DatabaseManager::get_instance().execute_query( + query, "SELECT * FROM users WHERE login = ?", {login} + ); + + if (!is_login_free) { + return -1; + } + + QSqlQuery insert_query; + return DatabaseManager::get_instance().execute_query( + insert_query, "INSERT INTO users (login, password) VALUES (?, ?)", + {login, password} + ); +} + +bool LRDao::validate_user(const QString &login, const QString &password) { + QSqlQuery query; + + const QString query_str = + "SELECT * FROM users WHERE login = ? AND password = ?"; + const QVariantList params = {login, password}; + + const auto success = + DatabaseManager::get_instance().execute_query(query, query_str, params); + + return query.next() && success; +} + +bool LRDao::add_project_to_user(std::string user_login, int project_id) { + QSqlQuery query; + const QString query_str = + "UPDATE users SET projects = array_append(projects, ?)" + "WHERE login = ?"; + + const QVariantList params = { + QString::fromStdString(std::to_string(project_id)), + QString::fromStdString(user_login)}; + + return DatabaseManager::get_instance().execute_query( + query, query_str, params + ); +} + +bool LRDao::get_user_projects( + const std::string &login, + std::vector &projects +) { + QSqlQuery query; + const QString query_str = + "SELECT array_to_string(projects, ',') FROM users WHERE login = ?"; + + const QVariantList params = {QString::fromStdString(login)}; + const bool is_success = + DatabaseManager::get_instance().execute_query(query, query_str, params); + + if (is_success && query.next() && query.value(0).isValid()) { + QStringList ps = query.value(0).toString().split(","); + for (auto i : ps) { + projects.push_back(i.toInt()); + } + + return true; + } + return false; +} \ No newline at end of file diff --git a/client/database/src/note_dao.cpp b/client/database/src/note_dao.cpp new file mode 100644 index 0000000..369f192 --- /dev/null +++ b/client/database/src/note_dao.cpp @@ -0,0 +1,120 @@ +#include "note_dao.hpp" +#include +#include "database_manager.hpp" + +bool NoteDao::initialize_note(int &id) { + QSqlQuery query; + const auto is_successful = DatabaseManager::get_instance().execute_query( + query, + "INSERT INTO notes (title, content) " + "VALUES ('Пустая заметка', '')" + "RETURNING id" + ); + if (is_successful && query.next()) { + id = query.value(0).toInt(); + return true; + } + return false; +} + +bool NoteDao::update_note(const Note ¬e) { + QSqlQuery query; + + const QString sql_query = + "UPDATE notes SET " + "title = :title, " + "content = :content, " + "members = :members, " + "date = :date, " + "tags = :tags " + "WHERE id = :id"; + const QVariantList params = { + QString::fromStdString(note.get_title()), + QString::fromStdString(note.get_text()), + convert_string_set_to_postgres_array(note.get_members()), + QString::fromStdString(note.get_date()), + convert_tags_to_postgres_array(note.get_tags()), + note.get_id() + }; + + return DatabaseManager::get_instance().execute_query( + query, sql_query, params + ); +} + +bool NoteDao::delete_note(int id) { + QSqlQuery query; + const QString sql_query = "DELETE FROM notes WHERE id = :id"; + return DatabaseManager::get_instance().execute_query( + query, sql_query, {id} + ); +} + +std::vector NoteDao::get_all_notes() { + QSqlQuery query; + std::vector notes; + DatabaseManager::get_instance().execute_query(query, "SELECT * FROM notes"); + + while (query.next()) { + auto id = query.value("id").toUInt(); + auto title = query.value("title").toString().toStdString(); + auto text = query.value("content").toString().toStdString(); + notes.emplace_back(id, title, text); + } + + return notes; +} + +Note NoteDao::get_note_by_id(int id) { + QSqlQuery query; + DatabaseManager::get_instance().execute_query( + query, "SELECT title, content, array_to_string(tags, ','), date, array_to_string(members, ',') FROM notes WHERE id = ?", {id} + ); + query.next(); + auto title = query.value(0).toString().toStdString(); + auto text = query.value(1).toString().toStdString(); + auto date = query.value(3).toString().toStdString(); + Note result{id, title, text}; + auto tags_list = query.value(2).toString().split(","); + + if (!tags_list.empty() && tags_list[0] != "") { + for (const auto& tag_str : tags_list) { + + result.add_tag(tag_str.split(':')[0].toStdString(), tag_str.split(':')[1].toStdString()); + } + } + auto member_list = query.value(4).toString().split(","); + if (!member_list.empty() && member_list[0] != "") { + for (const auto& member : member_list) { + result.add_member(member.toStdString()); + } + } + + return result; +} + +QString NoteDao::convert_string_set_to_postgres_array(const std::unordered_set& string_set) { + if (string_set.empty()) { + return "{}"; + } + + QStringList buffer; + for (const auto& item : string_set) { + buffer << QString::fromStdString(item).replace(",", "\\,"); + } + return "{" + buffer.join(",") + "}"; +} + +QString NoteDao::convert_tags_to_postgres_array(const std::vector& tags) { + if (tags.empty()) { + return "{}"; + } + + QStringList buffer; + for (const auto &[name, color] : tags) { + buffer << QString("\"%1:%2\"") + .arg(QString::fromStdString(name).replace("\"", "\\\""), + QString::fromStdString(color).replace("\"", "\\\"")); + } + return "{" + buffer.join(",") + "}"; +} diff --git a/client/database/src/project_dao.cpp b/client/database/src/project_dao.cpp new file mode 100644 index 0000000..4370f4d --- /dev/null +++ b/client/database/src/project_dao.cpp @@ -0,0 +1,64 @@ +#include "project_dao.hpp" +#include +#include +#include +#include "database_manager.hpp" + +namespace DB { + +bool ProjectDAO::create_project(const std::string &name, int &id) { + // todo thread-safety + QSqlQuery query; + const QString query_str = + "INSERT INTO projects (name)" + " VALUES (:name)" + "RETURNING id"; + + const QVariantList params = {QString::fromStdString(name)}; + + bool is_successful = + DatabaseManager::get_instance().execute_query(query, query_str, params); + if (is_successful && query.next()) { + id = query.value(0).toInt(); + return true; + } + return false; +} + +bool ProjectDAO::get_project( + const int id, + std::string &name, + std::vector ¬es +) { + QSqlQuery query; + const QString query_str = + "SELECT name, array_to_string(notes, ',') FROM projects " + "WHERE id = ?"; + const QVariantList params = {id}; + const bool is_successful = + DatabaseManager::get_instance().execute_query(query, query_str, params); + + if (is_successful && query.next()) { + name = query.value("name").toString().toStdString(); + + for (const auto& i : query.value(1).toString().split(",")) { + notes.push_back(i.toInt()); + } + return true; + } + return false; +} + +bool ProjectDAO::add_note_to_project(const int project_id, const int note_id) { + QSqlQuery query; + const QString query_str = + "UPDATE projects " + "SET notes = array_append(notes, ?)" + "WHERE id = ?"; + const QVariantList params = {note_id, project_id}; + return DatabaseManager::get_instance().execute_query( + query, query_str, params + ); +} + +} // namespace DB \ No newline at end of file diff --git a/client/database/src/serialization.cpp b/client/database/src/serialization.cpp new file mode 100644 index 0000000..17e687e --- /dev/null +++ b/client/database/src/serialization.cpp @@ -0,0 +1,41 @@ +#include "serialization.hpp" +#include +#include +#include +#include "lr_dao.hpp" +#include "note.hpp" +#include "note_dao.hpp" +#include "project.hpp" +#include "project_dao.hpp" +#include "storage.hpp" + +bool Serialization::get_storage( + project_storage_model::Storage &storage, + const std::string &login +) { + std::vector projects; + if (LRDao::get_user_projects(login, projects)) { + for (auto p : projects) { + std::string project_name; + std::vector notes; + if (DB::ProjectDAO::get_project(p, project_name, notes)) { + Project project{p, project_name, ""}; + for (auto n : notes) { + if (n != 0) { + auto note = NoteDao::get_note_by_id(n); + project.add_note(note); + } + } + storage.add_project(Project(project)); + } else { + + return false; + } + } + } else { + + + return false; + } + return true; +} \ No newline at end of file diff --git a/client/scripts/CMakeLists.txt b/client/scripts/CMakeLists.txt new file mode 100644 index 0000000..73ccd8c --- /dev/null +++ b/client/scripts/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.16) + +project(Scripts LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +file(GLOB SOURCES "src/*.cpp") +file(GLOB HEADERS "include/*.hpp") + +add_library(Scripts STATIC ${SOURCES} ${HEADERS}) + +target_include_directories(Scripts + PUBLIC + $ + $ +) \ No newline at end of file diff --git a/client/scripts/include/note.hpp b/client/scripts/include/note.hpp new file mode 100644 index 0000000..e54e01a --- /dev/null +++ b/client/scripts/include/note.hpp @@ -0,0 +1,46 @@ +#ifndef NOTE_HPP +#define NOTE_HPP + +#include +#include +#include + +namespace project_storage_model { + +class Note { +public: + struct Tag { + std::string name; + std::string color; + }; + + Note(int id, std::string title, std::string text); + + [[nodiscard]] int get_id() const; + [[nodiscard]] const std::string &get_title() const; + [[nodiscard]] const std::string &get_text() const; + [[nodiscard]] const std::string &get_date() const; + [[nodiscard]] const std::vector &get_tags() const; + [[nodiscard]] const std::unordered_set &get_members() const; + + void set_title(const std::string &title); + void set_text(const std::string &text); + void add_tag(const std::string &tag, const std::string &color = "#e7624b"); + void set_date(const std::string &date); + void add_member(const std::string &member); + void remove_tag(const std::string &tag); + void clear_tags(); + void clear_members(); + +private: + int id_; + std::string title_; + std::string text_; + std::string date_; + std::vector tags_; + std::unordered_set members_; +}; + +} // namespace project_storage_model + +#endif \ No newline at end of file diff --git a/client/scripts/project.hpp b/client/scripts/include/project.hpp similarity index 78% rename from client/scripts/project.hpp rename to client/scripts/include/project.hpp index 2ff775d..ad83932 100644 --- a/client/scripts/project.hpp +++ b/client/scripts/include/project.hpp @@ -9,12 +9,12 @@ namespace project_storage_model { class Project { public: - Project(int id, std::string name, std::string description); + Project(int id, const std::string &name, const std::string &description); [[nodiscard]] int get_id() const; [[nodiscard]] const std::string &get_name() const; [[nodiscard]] const std::string &get_description() const; - [[nodiscard]] const std::list &get_notes() const; + const std::list &get_notes() const; Note &add_note(const Note ¬e); void remove_note(int note_id); @@ -29,4 +29,4 @@ class Project { } // namespace project_storage_model -#endif +#endif \ No newline at end of file diff --git a/client/scripts/storage.hpp b/client/scripts/include/storage.hpp similarity index 73% rename from client/scripts/storage.hpp rename to client/scripts/include/storage.hpp index 2917c62..da6f948 100644 --- a/client/scripts/storage.hpp +++ b/client/scripts/include/storage.hpp @@ -11,11 +11,11 @@ class Storage { public: Project &add_project(const Project &project); void remove_project(int project_id); - [[nodiscard]] const std::list &get_projects() const; + std::list &get_projects(); User &add_user(const User &user); void remove_user(int user_id); - [[nodiscard]] const std::list &get_users() const; + const std::list &get_users() const; private: std::list projects_; @@ -24,4 +24,4 @@ class Storage { } // namespace project_storage_model -#endif +#endif \ No newline at end of file diff --git a/client/scripts/user.hpp b/client/scripts/include/user.hpp similarity index 76% rename from client/scripts/user.hpp rename to client/scripts/include/user.hpp index 00e3c45..957fe63 100644 --- a/client/scripts/user.hpp +++ b/client/scripts/include/user.hpp @@ -9,11 +9,11 @@ namespace project_storage_model { class User { public: - User(int id, std::string username); + User(int id, const std::string &username); [[nodiscard]] int get_id() const; [[nodiscard]] const std::string &get_username() const; - [[nodiscard]] const std::list &get_projects() const; + const std::list &get_projects() const; Project &add_project(const Project &project); void remove_project(int project_id); @@ -26,4 +26,4 @@ class User { } // namespace project_storage_model -#endif +#endif \ No newline at end of file diff --git a/client/scripts/note.cpp b/client/scripts/note.cpp deleted file mode 100644 index 10ab536..0000000 --- a/client/scripts/note.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "note.hpp" -#include -#include - -namespace project_storage_model { - -Note::Note(const int id, std::string title, std::string text) - : id_(id), title_(std::move(title)), text_(std::move(text)) { -} - -int Note::get_id() const { - return id_; -} - -const std::string &Note::get_title() const { - return title_; -} - -const std::string &Note::get_text() const { - return text_; -} - -const std::list &Note::get_tags() const { - return tags_; -} - -void Note::set_title(const std::string &title) { - this->title_ = title; -} - -void Note::set_text(const std::string &text) { - this->text_ = text; -} - -void Note::add_tag(const std::string &tag) { - tags_.push_back(tag); -} - -void Note::remove_tag(const std::string &tag) { - tags_.erase(std::remove(tags_.begin(), tags_.end(), tag), tags_.end()); -} - -} // namespace project_storage_model diff --git a/client/scripts/note.hpp b/client/scripts/note.hpp deleted file mode 100644 index f8835a1..0000000 --- a/client/scripts/note.hpp +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef NOTE_HPP -#define NOTE_HPP - -#include -#include - -namespace project_storage_model { - -class Note { -public: - Note() = default; - Note(int id, std::string title, std::string text); - - [[nodiscard]] int get_id() const; - [[nodiscard]] const std::string &get_title() const; - [[nodiscard]] const std::string &get_text() const; - [[nodiscard]] const std::list &get_tags() const; - - void set_title(const std::string &title); - void set_text(const std::string &text); - void add_tag(const std::string &tag); - void remove_tag(const std::string &tag); - -private: - int id_; - std::string title_; - std::string text_; - std::list tags_; -}; - -} // namespace project_storage_model - -#endif diff --git a/client/scripts/src/note.cpp b/client/scripts/src/note.cpp new file mode 100644 index 0000000..dcb8391 --- /dev/null +++ b/client/scripts/src/note.cpp @@ -0,0 +1,68 @@ +#include "note.hpp" +#include +#include + +namespace project_storage_model { + +Note::Note(const int id, std::string title, std::string text) + : id_(id), title_(std::move(title)), text_(std::move(text)) { +} + +[[nodiscard]] int Note::get_id() const { + return id_; +} + +[[nodiscard]] const std::string &Note::get_title() const { + return title_; +} + +[[nodiscard]] const std::string &Note::get_text() const { + return text_; +} + +const std::string &Note::get_date() const { + return date_; +} + +const std::vector &Note::get_tags() const { + return tags_; +} + +const std::unordered_set &Note::get_members() const { + return members_; +} + +void Note::set_title(const std::string &title) { + title_ = title; +} + +void Note::set_text(const std::string &text) { + text_ = text; +} + +void Note::add_tag(const std::string &tag, const std::string &color) { + tags_.push_back({tag, color}); +} + +void Note::set_date(const std::string &date) { + date_ = date; +} + +void Note::add_member(const std::string &member) { + members_.insert(member); +} + +void Note::remove_tag(const std::string &tag) { + tags_.erase(std::remove_if(tags_.begin(), tags_.end(), + [&tag](const Tag& t) { return t.name == tag; }), tags_.end()); +} + +void Note::clear_tags() { + tags_.clear(); +} + +void Note::clear_members() { + members_.clear(); +} + +} // namespace project_storage_model \ No newline at end of file diff --git a/client/scripts/project.cpp b/client/scripts/src/project.cpp similarity index 60% rename from client/scripts/project.cpp rename to client/scripts/src/project.cpp index 970c1be..a55e555 100644 --- a/client/scripts/project.cpp +++ b/client/scripts/src/project.cpp @@ -1,22 +1,27 @@ -#include "project.hpp" +#include "../include/project.hpp" #include -#include namespace project_storage_model { -Project::Project(const int id, std::string name, std::string description) - : id_(id), name_(std::move(name)), description_(std::move(description)) { +Project::Project( + int id, + const std::string &name, + const std::string &description +) + : id_(std::move(id)), + name_(std::move(name)), + description_(std::move(description)) { } -int Project::get_id() const { +[[nodiscard]] int Project::get_id() const { return id_; } -const std::string &Project::get_name() const { +[[nodiscard]] const std::string &Project::get_name() const { return name_; } -const std::string &Project::get_description() const { +[[nodiscard]] const std::string &Project::get_description() const { return description_; } diff --git a/client/scripts/storage.cpp b/client/scripts/src/storage.cpp similarity index 88% rename from client/scripts/storage.cpp rename to client/scripts/src/storage.cpp index e0e454c..e452e6b 100644 --- a/client/scripts/storage.cpp +++ b/client/scripts/src/storage.cpp @@ -1,4 +1,4 @@ -#include "storage.hpp" +#include "../include/storage.hpp" #include namespace project_storage_model { @@ -20,7 +20,7 @@ void Storage::remove_project(int project_id) { ); } -const std::list &Storage::get_projects() const { +std::list &Storage::get_projects() { return projects_; } diff --git a/client/scripts/user.cpp b/client/scripts/src/user.cpp similarity index 69% rename from client/scripts/user.cpp rename to client/scripts/src/user.cpp index 833e82f..e1cbb5f 100644 --- a/client/scripts/user.cpp +++ b/client/scripts/src/user.cpp @@ -1,18 +1,17 @@ -#include "user.hpp" +#include "../include/user.hpp" #include -#include namespace project_storage_model { -User::User(const int id, std::string username) - : id_(id), username_(std::move(username)) { +User::User(int id, const std::string &username) + : id_(std::move(id)), username_(std::move(username)) { } -int User::get_id() const { +[[nodiscard]] int User::get_id() const { return id_; } -const std::string &User::get_username() const { +[[nodiscard]] const std::string &User::get_username() const { return username_; } diff --git a/client/ui/MainWindow/CMakeLists.txt b/client/ui/MainWindow/CMakeLists.txt deleted file mode 100644 index e4f7c3c..0000000 --- a/client/ui/MainWindow/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(MainWindow) - -set(CMAKE_CXX_STANDARD 17) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_AUTOUIC ON) - - -#find_package(Qt6 REQUIRED COMPONENTS Widgets Core LinguistTools) -find_package(Qt6 REQUIRED COMPONENTS Core Widgets Sql) -find_package(PostgreSQL REQUIRED) -link_directories(/path/to/postgresql/lib) - -file(GLOB MAIN_WINDOW_SRC "src/*") -file(GLOB MAIN_WINDOW_INCLUDE "include/*") - - -add_library(${PROJECT_NAME} - ${MAIN_WINDOW_SRC} - ${MAIN_WINDOW_INCLUDE} - MainWindow_en_US.ts -) - -include(GNUInstallDirs) -install(TARGETS ${PROJECT_NAME} - BUNDLE DESTINATION . - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) -target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core) -target_link_libraries(${PROJECT_NAME} PUBLIC project-storage-model) - -target_include_directories(${PROJECT_NAME} - PUBLIC ${PROJECT_SOURCE_DIR}/include -) \ No newline at end of file diff --git a/client/ui/MainWindow/include/bottombar.h b/client/ui/MainWindow/include/bottombar.h deleted file mode 100644 index b6c5119..0000000 --- a/client/ui/MainWindow/include/bottombar.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef BOTTOMBAR_H -#define BOTTOMBAR_H - -#include -#include -#include -#include - -namespace Ui { -class BottomBar : public QWidget { - QHBoxLayout *main_layout_; - QLabel *project_name_; - QLabel *username_; - -public: - BottomBar(QWidget *parent_, std::string username_, std::string project_name_); -}; -} // namespace Ui - -#endif // BOTTOMBAR_H diff --git a/client/ui/MainWindow/include/mainwindow.h b/client/ui/MainWindow/include/mainwindow.h deleted file mode 100644 index 957e3e6..0000000 --- a/client/ui/MainWindow/include/mainwindow.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include "bottombar.h" -#include "notelist.h" -#include "projectlist.h" -#include -#include -#include -#include -#include -#include -#include -#include "storage.hpp" - -namespace Ui { -class MainWindow : public QWidget { - Q_OBJECT - QVBoxLayout *main_layout_; - BottomBar *top_bar_; - QHBoxLayout *content_layout_; - ProjectList *project_list_; - NoteList *note_list_; - QWidget *content_widget_; - QPushButton *new_project_button_; - QPushButton *new_note_button_; - project_storage_model::Storage *storage_; - - friend ProjectList; -private slots: - void add_project(); - void add_note(); - -public: - explicit MainWindow(QWidget *parent = nullptr, std::string username = "none", - project_storage_model::Storage *storage = nullptr); -}; -} // namespace Ui -#endif // MAINWINDOW_H diff --git a/client/ui/MainWindow/include/notelist.h b/client/ui/MainWindow/include/notelist.h deleted file mode 100644 index 082567b..0000000 --- a/client/ui/MainWindow/include/notelist.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef NOTELIST_H -#define NOTELIST_H -#include "note.hpp" -#include -#include -#include -#include -namespace Ui { -class NoteList : public QWidget { - friend class MainWindow; - Q_OBJECT - - QHBoxLayout *main_layout_; - - std::vector vertical_layouts_; - int note_counter_ = 0; - -public: - void add_note_widget(const project_storage_model::Note *note); - void clear_note_list(); - NoteList(QWidget *parent); - -public slots: - void load_project_notes(QListWidgetItem *project); -}; -} // namespace Ui -#endif // NOTELIST_H diff --git a/client/ui/MainWindow/include/notewidget.h b/client/ui/MainWindow/include/notewidget.h deleted file mode 100644 index 720e589..0000000 --- a/client/ui/MainWindow/include/notewidget.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef NOTEWIDGET_H -#define NOTEWIDGET_H - -#include "note.hpp" -#include -#include -#include -#include - -namespace Ui { -class NoteWidget : public QWidget { - Q_OBJECT - const project_storage_model::Note *model_note_; - QVBoxLayout *main_layout_; - QPushButton *open_button_; - -public: - explicit NoteWidget(QWidget *parent = nullptr, - const project_storage_model::Note *model_note = nullptr); - -private slots: - void open_note_window() const { - std::cout << this->model_note_->get_text() << std::endl; - } -}; -} // namespace Ui -#endif // NOTEWIDGET_H diff --git a/client/ui/MainWindow/include/projectitem.h b/client/ui/MainWindow/include/projectitem.h deleted file mode 100644 index 58b148e..0000000 --- a/client/ui/MainWindow/include/projectitem.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PROJECTITEM_H -#define PROJECTITEM_H - -#include "notelist.h" -#include "project.hpp" -#include -#include - -namespace Ui { -class ProjectItem : public QListWidgetItem { - project_storage_model::Project *project_; - friend NoteList; - friend class MainWindow; - -public: - ProjectItem(QListWidget *listview, project_storage_model::Project *project); -}; -} // namespace Ui -#endif // PROJECTITEM_H diff --git a/client/ui/MainWindow/include/projectlist.h b/client/ui/MainWindow/include/projectlist.h deleted file mode 100644 index 6616e0a..0000000 --- a/client/ui/MainWindow/include/projectlist.h +++ /dev/null @@ -1,20 +0,0 @@ -#ifndef PROJECTLIST_H -#define PROJECTLIST_H - -#include "project.hpp" -#include -#include - -namespace Ui { -class ProjectList : public QListWidget { - Q_OBJECT - friend class MainWindow; - void add_project(project_storage_model::Project *project); - -public: - explicit ProjectList(QWidget *parent = nullptr); - -signals: -}; -} // namespace Ui -#endif // PROJECTLIST_H diff --git a/client/ui/MainWindow/src/applicationwindow.cpp b/client/ui/MainWindow/src/applicationwindow.cpp deleted file mode 100644 index ff86770..0000000 --- a/client/ui/MainWindow/src/applicationwindow.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "applicationwindow.h" -#include "mainwindow.h" -#include - -namespace Ui { -ApplicationWindow::ApplicationWindow(std::string window_name_) - : QMainWindow{nullptr} { - this->setObjectName("ApplicationWindow"); - - this->setAttribute(Qt::WA_StyledBackground); - - this->setWindowTitle(window_name_.c_str()); - this->resize(800, 600); -} -} // namespace Ui diff --git a/client/ui/MainWindow/src/bottombar.cpp b/client/ui/MainWindow/src/bottombar.cpp deleted file mode 100644 index 10a20b8..0000000 --- a/client/ui/MainWindow/src/bottombar.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "bottombar.h" -#include -#include -#include -#include -namespace Ui { -BottomBar::BottomBar(QWidget *parent, std::string username, - std::string project_name) - : QWidget(parent), main_layout_(new QHBoxLayout()), - username_(new QLabel(username.c_str())), - project_name_(new QLabel(project_name.c_str())) { - - this->setObjectName("BottomBar"); - this->setLayout(main_layout_); - this->setFixedHeight(40); - - project_name_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - username_->setAlignment(Qt::AlignVCenter | Qt::AlignRight); - - this->setSizePolicy( - QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum)); - - main_layout_->addWidget(project_name_); - main_layout_->addWidget(username_); -} -} // namespace Ui diff --git a/client/ui/MainWindow/src/mainwindow.cpp b/client/ui/MainWindow/src/mainwindow.cpp deleted file mode 100644 index 0193878..0000000 --- a/client/ui/MainWindow/src/mainwindow.cpp +++ /dev/null @@ -1,166 +0,0 @@ -#include "mainwindow.h" -#include "bottombar.h" -#include "notelist.h" -#include "project.hpp" -#include "projectlist.h" -#include "projectitem.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace Ui { -MainWindow::MainWindow(QWidget *parent, std::string username, - project_storage_model::Storage *storage) - : QWidget(parent),main_layout_(new QVBoxLayout(this)), - top_bar_(new BottomBar(this, username, "эффишио - таск трекер.")), - content_layout_(new QHBoxLayout(this)), - project_list_(new ProjectList(this)), - note_list_(new NoteList(this)), - content_widget_(new QWidget(this)), - new_project_button_(new QPushButton("Новый проект", this)), - new_note_button_(new QPushButton("Новая заметка", this)), - storage_(storage) -{ - this->setObjectName("MainWindow"); - this->setAttribute(Qt::WA_StyledBackground); - this->setStyleSheet( - R"( -#MainWindow { - background-color : white; -} -#ProjectList { - background-color: #FED6BC; - border: 1px solid #e0e0e0; - border-radius: 8px; - padding: 8px; - outline: 0; - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 14px; - color: #000000; -} - -#ProjectList::item { - background-color: transparent; - border: none; - padding: 10px; - margin: 2px; - border-radius: 6px; -} - -#ProjectList::item:hover { - background-color: #f5f5f5; -} - -#ProjectList::item:selected { - background-color: #007aff; - color: #ffffff; -} - -#ProjectList QScrollBar:vertical { - border: none; - background: #f0f0f0; - width: 10px; - margin: 0; -} - -#ProjectList QScrollBar::handle:vertical { - background: #c0c0c0; - border-radius: 5px; - min-height: 20px; -} - -#ProjectList QScrollBar::add-line:vertical, -#ProjectList QScrollBar::sub-line:vertical { - background: none; -} - -#ProjectList QScrollBar::add-page:vertical, -#ProjectList QScrollBar::sub-page:vertical { - background: none; -} - -#BottomBar { - background-color: black; - border-radius: 8px; - padding: 8px; - outline: 0; -} - -#BottomBar QLabel { - color : white; - font-family: -apple-system, BlinkMacSystemFont, sans-serif; - font-size: 14px; -} - -#NoteWidget { - background-color: #C6D8FF; -border-radius: 8px; - -} - -#NoteList { - border : 1px solid #A9A9A9; -border-radius : 8px; -} - -)"); - - main_layout_->addWidget(top_bar_, Qt::AlignTop); - - main_layout_->setAlignment(Qt::AlignCenter); - main_layout_->addWidget(content_widget_); - - content_widget_->setLayout(content_layout_); - - auto right_layout = new QVBoxLayout(content_widget_); - right_layout->addWidget(project_list_); - right_layout->addWidget(new_project_button_); - right_layout->addWidget(new_note_button_); - - content_layout_->addWidget(note_list_, Qt::AlignRight); - content_layout_->addLayout(right_layout); - main_layout_->addWidget(content_widget_); - - this->setLayout(main_layout_); - - // connections - connect(project_list_, &QListWidget::itemClicked, note_list_, - &NoteList::load_project_notes); - connect(new_note_button_, &QPushButton::clicked, this, &Ui::MainWindow::add_note); - connect(new_project_button_, &QPushButton::clicked, this, &Ui::MainWindow::add_project); -} - -void MainWindow::add_project() { - - bool ok; - QString name_of_project = - QInputDialog::getText(nullptr, "Название проекта:", "Введите название", - QLineEdit::Normal, "", &ok); - if (ok) { - auto &project = storage_->add_project( - project_storage_model::Project(1, name_of_project.toStdString(), "")); - project_list_->add_project(&project); - } -} - -void MainWindow::add_note() { - auto project_item = dynamic_cast(project_list_->currentItem()); - // qDebug() << "Note add to " << project_item->project_->get_name() << "its address: " << project_item->project_; - if (project_item) { - auto ¬e = project_item->project_->add_note({1, "empty", ""}); - note_list_->add_note_widget(¬e); - } else { - QMessageBox msg; - msg.setText("Проект не выбран!"); - msg.exec(); - } -} - -} // namespace Ui diff --git a/client/ui/MainWindow/src/notelist.cpp b/client/ui/MainWindow/src/notelist.cpp deleted file mode 100644 index e873d6d..0000000 --- a/client/ui/MainWindow/src/notelist.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include "notelist.h" -#include "note.hpp" -#include "notewidget.h" -#include "projectitem.h" -#include -#include -#include -#include -#include - -namespace Ui { -NoteList::NoteList(QWidget *parent) - : QWidget(parent), main_layout_(new QHBoxLayout(this)), - vertical_layouts_(std::vector()) { - this->setAttribute(Qt::WA_StyledBackground); - this->setObjectName("NoteList"); - this->setLayout(main_layout_); - vertical_layouts_.resize(4, nullptr); - for (auto &layout : vertical_layouts_) { - layout = new QVBoxLayout(this); - - main_layout_->addLayout(layout); - } -} - -void NoteList::add_note_widget(const project_storage_model::Note *note) { - auto current_layout = vertical_layouts_[note_counter_ % 4]; - if (current_layout->count() > 1) { - current_layout->removeItem( - current_layout->itemAt(current_layout->count() - 1)); - } - vertical_layouts_[note_counter_ % 4]->addWidget(new NoteWidget(this, note), 0, - Qt::AlignTop); - current_layout->addStretch(); - note_counter_++; -} - -void NoteList::load_project_notes(QListWidgetItem *project) { - - ProjectItem *p = dynamic_cast(project); - assert(p != nullptr); - qDebug() << "Адрес проекта" << QString::fromStdString(p->project_->get_name()) << ":" <project_; - this->clear_note_list(); - note_counter_ = 0; - for (const auto ¬e : p->project_->get_notes()) { - this->add_note_widget(¬e); - } -} - -void NoteList::clear_note_list() { - for (auto &layout : vertical_layouts_) { - while (layout->count()) { - auto item = layout->takeAt(0); - auto widget = item->widget(); - if (widget) { - widget->deleteLater(); - } - } - } -} -} // namespace Ui diff --git a/client/ui/MainWindow/src/notewidget.cpp b/client/ui/MainWindow/src/notewidget.cpp deleted file mode 100644 index 8b2492f..0000000 --- a/client/ui/MainWindow/src/notewidget.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "notewidget.h" -#include "note.hpp" -#include -#include -#include - -namespace Ui { -NoteWidget::NoteWidget(QWidget *parent, - const project_storage_model::Note *model_note) - : QWidget(parent), model_note_(model_note), - main_layout_(new QVBoxLayout(this)), - open_button_(new QPushButton("Открыть")) { - this->setObjectName("NoteWidget"); - this->setMinimumWidth(100); - this->setMinimumHeight(80); - QLabel *title = new QLabel(model_note_->get_title().c_str(), this); - QLabel *text = new QLabel(model_note_->get_text().c_str(), this); - main_layout_->addWidget(title); - main_layout_->addWidget(text); - main_layout_->addWidget(open_button_); - - connect(open_button_, &QPushButton::clicked, this, - &NoteWidget::open_note_window); - this->setLayout(main_layout_); - this->setAttribute(Qt::WA_StyledBackground); -} -} // namespace Ui diff --git a/client/ui/MainWindow/src/projectitem.cpp b/client/ui/MainWindow/src/projectitem.cpp deleted file mode 100644 index 289d91c..0000000 --- a/client/ui/MainWindow/src/projectitem.cpp +++ /dev/null @@ -1,9 +0,0 @@ -#include "projectitem.h" -#include - -namespace Ui { -ProjectItem::ProjectItem(QListWidget *list_view, - project_storage_model::Project *project) - : project_(project), - QListWidgetItem(project->get_name().c_str(), list_view) {} -} // namespace Ui diff --git a/client/ui/MainWindow/src/projectlist.cpp b/client/ui/MainWindow/src/projectlist.cpp deleted file mode 100644 index beef58f..0000000 --- a/client/ui/MainWindow/src/projectlist.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "projectlist.h" -#include "mainwindow.h" -#include "project.hpp" -#include "projectitem.h" -#include - -namespace Ui { - -ProjectList::ProjectList(QWidget *parent) : QListWidget{parent} { - this->setObjectName("ProjectList"); - this->setFixedWidth(200); -} - -void ProjectList::add_project(project_storage_model::Project *project) { - this->addItem(new ProjectItem(static_cast(this), project)); -} - -} // namespace Ui diff --git a/client/ui/authorization-windows/CMakeLists.txt b/client/ui/authorization-windows/CMakeLists.txt new file mode 100644 index 0000000..e61f562 --- /dev/null +++ b/client/ui/authorization-windows/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.16) + +project(AuthorizationWindows LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) + +set(UI_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/ui/login_window.ui + ${CMAKE_CURRENT_SOURCE_DIR}/ui/registration_window.ui +) + +set(SOURCES + src/login_window.cpp + src/registration_window.cpp +) + +set(HEADERS + include/login_window.h + include/login_window_style_sheet.h + include/registration_window_style_sheet.h + include/registration_window.h +) + +file(GLOB TS_FILES "*.ts") + +add_library(AuthorizationWindows STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) + +target_include_directories(AuthorizationWindows + PUBLIC + $ + $ +) + +target_link_libraries(AuthorizationWindows PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database Scripts MainWindow) \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/include/login_window.h b/client/ui/authorization-windows/include/login_window.h similarity index 75% rename from client/ui/login-and-registration-windows/include/login_window.h rename to client/ui/authorization-windows/include/login_window.h index c9f1297..5aed300 100644 --- a/client/ui/login-and-registration-windows/include/login_window.h +++ b/client/ui/authorization-windows/include/login_window.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -17,11 +16,11 @@ class LoginWindow; QT_END_NAMESPACE -class LoginWindow : public QDialog { +class LoginWindow : public QWidget { Q_OBJECT public: - LoginWindow(QWidget *parent = nullptr); + explicit LoginWindow(QWidget *parent = nullptr); ~LoginWindow(); private slots: @@ -30,4 +29,4 @@ private slots: private: Ui::LoginWindow *ui; -}; +}; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window_style_sheet.h b/client/ui/authorization-windows/include/login_window_style_sheet.h new file mode 100644 index 0000000..3a6d776 --- /dev/null +++ b/client/ui/authorization-windows/include/login_window_style_sheet.h @@ -0,0 +1,60 @@ +#pragma once + +#include "ui_login_window.h" + +namespace Ui { +QString login_window_light_theme = R"( + QWidget { + background-color: #f5f5f5; + } + + QLabel { + font-family: 'Arial'; + font-weight: bold; + font-size: 13px; + color: #089083; + padding: 1px; + background-color: transparent; + } + + QPushButton#pushEnter { + font-family: 'Arial'; + font-weight: bold; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + } + + QPushButton#switchMode { + font-family: 'Arial'; + font-weight: bold; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#pushEnter:hover { + background-color: #d58745; + } + + QPushButton#switchMode:hover { + background-color: #dadada; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid white; + background: white; + color: black; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + +)"; + +} // namespace Ui \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/include/registration_window.h b/client/ui/authorization-windows/include/registration_window.h similarity index 84% rename from client/ui/login-and-registration-windows/include/registration_window.h rename to client/ui/authorization-windows/include/registration_window.h index db56975..35f4bc4 100644 --- a/client/ui/login-and-registration-windows/include/registration_window.h +++ b/client/ui/authorization-windows/include/registration_window.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -15,7 +14,7 @@ class RegistrationWindow; QT_END_NAMESPACE -class RegistrationWindow : public QDialog { +class RegistrationWindow : public QWidget { Q_OBJECT public: @@ -29,4 +28,4 @@ private slots: private: Ui::RegistrationWindow *ui; -}; +}; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window_style_sheet.h b/client/ui/authorization-windows/include/registration_window_style_sheet.h new file mode 100644 index 0000000..dc7a1ca --- /dev/null +++ b/client/ui/authorization-windows/include/registration_window_style_sheet.h @@ -0,0 +1,58 @@ +#pragma once + +#include "ui_registration_window.h" + +namespace Ui { +QString registration_window_light_theme = R"( + QWidget { + background-color: #f5f5f5; + } + + QLabel { + font-family: 'Arial'; + background-color: transparent; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#pushRegistration { + font-weight: bold; + font-family: 'Arial'; + border-radius: 8px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + } + + QPushButton#switchMode { + font-family: 'Arial'; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#pushRegistration:hover { + background-color: #d58745; + } + + QPushButton#switchMode:hover { + background-color: #dadada; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid white; + background: white; + color: black; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + +)"; + +} // namespace Ui \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/login_window_ru_RU.ts b/client/ui/authorization-windows/login_window_ru_RU.ts similarity index 54% rename from client/ui/login-and-registration-windows/login_window_ru_RU.ts rename to client/ui/authorization-windows/login_window_ru_RU.ts index e1de93e..32bf76c 100644 --- a/client/ui/login-and-registration-windows/login_window_ru_RU.ts +++ b/client/ui/authorization-windows/login_window_ru_RU.ts @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/src/login_window.cpp b/client/ui/authorization-windows/src/login_window.cpp similarity index 53% rename from client/ui/login-and-registration-windows/src/login_window.cpp rename to client/ui/authorization-windows/src/login_window.cpp index 2d788f0..0583ef5 100644 --- a/client/ui/login-and-registration-windows/src/login_window.cpp +++ b/client/ui/authorization-windows/src/login_window.cpp @@ -1,14 +1,24 @@ #include "login_window.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "applicationwindow.h" +#include "bottombar.h" #include "database_manager.hpp" #include "login_window_style_sheet.h" #include "lr_dao.hpp" -#include "registration_window.h" #include "mainwindow.h" -#include "bottombar.h" #include "notelist.h" +#include "registration_window.h" +#include "serialization.hpp" LoginWindow::LoginWindow(QWidget *parent) - : QDialog(parent), ui(new Ui::LoginWindow) { + : QWidget(parent), ui(new Ui::LoginWindow) { ui->setupUi(this); setFixedSize(380, 480); @@ -32,10 +42,23 @@ LoginWindow::~LoginWindow() { } void LoginWindow::on_switch_mode_clicked() { - hide(); - RegistrationWindow registration_window; - registration_window.show(); - registration_window.exec(); + QWidget *parent = this->parentWidget(); + + QMainWindow *app_window = qobject_cast(parent); + + if (QWidget *old = app_window->centralWidget()) { + old->deleteLater(); + } + project_storage_model::Storage storage; + RegistrationWindow *registration_window = + new RegistrationWindow(app_window); + + app_window->setCentralWidget(registration_window); + QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); + int x = (screenGeometry.width() - registration_window->width()) / 2; + int y = (screenGeometry.height() - registration_window->height()) / 2; + app_window->move(x, y); + this->close(); } @@ -58,13 +81,28 @@ void LoginWindow::on_push_enter_clicked() { QMessageBox::information( this, "Вход", "Вы успешно вошли! Добро пожаловать :)" ); - hide(); - project_storage_model::Storage storage; - ApplicationWindow *app_window = new ApplicationWindow("efficio"); - MainWindow *main_window = new MainWindow(app_window, "username", &storage); + QWidget *parent = this->parentWidget(); + + QMainWindow *app_window = qobject_cast(parent); + + if (QWidget *old = app_window->centralWidget()) { + old->deleteLater(); + } + // todo load all projects of user to storage + project_storage_model::Storage *storage = + new project_storage_model::Storage(); + Serialization::get_storage(*storage, login.toStdString()); + + Ui::MainWindow *main_window = + new Ui::MainWindow(app_window, login.toStdString(), storage); app_window->setCentralWidget(main_window); - app_window->show(); + app_window->resize(800, 600); + QRect screenGeometry = + QApplication::primaryScreen()->availableGeometry(); + int x = (screenGeometry.width() - main_window->width()) / 2; + int y = (screenGeometry.height() - main_window->height()) / 2; + app_window->move(x, y); this->close(); } else { @@ -77,4 +115,4 @@ void LoginWindow::on_push_enter_clicked() { this, "Ошибка ввода данных", "Пожалуйста, заполните все поля!" ); } -} +} \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/src/registration_window.cpp b/client/ui/authorization-windows/src/registration_window.cpp similarity index 73% rename from client/ui/login-and-registration-windows/src/registration_window.cpp rename to client/ui/authorization-windows/src/registration_window.cpp index f53bf89..f30f7d8 100644 --- a/client/ui/login-and-registration-windows/src/registration_window.cpp +++ b/client/ui/authorization-windows/src/registration_window.cpp @@ -1,12 +1,23 @@ #include "registration_window.h" +#include #include +#include +#include +#include +#include +#include +#include "applicationwindow.h" +#include "bottombar.h" #include "database_manager.hpp" #include "login_window.h" #include "lr_dao.hpp" +#include "mainwindow.h" +#include "notelist.h" +#include "registration_window.h" #include "registration_window_style_sheet.h" RegistrationWindow::RegistrationWindow(QWidget *parent) - : QDialog(parent), ui(new Ui::RegistrationWindow) { + : QWidget(parent), ui(new Ui::RegistrationWindow) { ui->setupUi(this); setFixedSize(380, 480); @@ -33,10 +44,23 @@ RegistrationWindow::~RegistrationWindow() { } void RegistrationWindow::on_switch_mode_clicked() { - hide(); - LoginWindow login_window; - login_window.show(); - login_window.exec(); + QWidget *parent = this->parentWidget(); + + QMainWindow *app_window = qobject_cast(parent); + + if (QWidget *old = app_window->centralWidget()) { + old->deleteLater(); + } + project_storage_model::Storage storage; + LoginWindow *login_window = new LoginWindow(app_window); + + app_window->setCentralWidget(login_window); + QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); + int x = (screenGeometry.width() - login_window->width()) / 2; + int y = (screenGeometry.height() - login_window->height()) / 2; + app_window->move(x, y); + + this->close(); } bool RegistrationWindow::is_strong_and_valid_password(const QString &password) { @@ -103,7 +127,14 @@ void RegistrationWindow::on_push_registration_clicked() { "Длина пароля не должна превышать пятидесяти символов" ); } else if (is_strong_and_valid_password(created_password)) { - if (!LRDao::register_user(created_login, created_password)) { + int try_register_user = + LRDao::try_register_user(created_login, created_password); + if (try_register_user == 0) { + QMessageBox::warning( + this, "Ошибка", + "Извините, разрабы дауны и не подключили толком бд." + ); + } else if (try_register_user == -1) { QMessageBox::warning( this, "Ошибка", "Пользователь с таким именем уже существует. Пожалуйста, " @@ -114,13 +145,10 @@ void RegistrationWindow::on_push_registration_clicked() { this, "Регистрация", "Вы успешно зарегистрировались! Пожалуйста, выполните вход." ); - hide(); - LoginWindow login_window; - login_window.show(); - login_window.exec(); + on_switch_mode_clicked(); // TODO: fix } } } else { QMessageBox::warning(this, "Ошибка", "Пожалуйста, заполните все поля."); } -} +} \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/ui/login_window.ui b/client/ui/authorization-windows/ui/login_window.ui similarity index 92% rename from client/ui/login-and-registration-windows/ui/login_window.ui rename to client/ui/authorization-windows/ui/login_window.ui index 3192033..02ffce3 100644 --- a/client/ui/login-and-registration-windows/ui/login_window.ui +++ b/client/ui/authorization-windows/ui/login_window.ui @@ -1,7 +1,7 @@ LoginWindow - + 0 @@ -101,7 +101,7 @@ border-radius: 10px; border-radius: 10px; - Еще нет аккаунта? Зарегестрируйтесь! + Еще нет аккаунта? Зарегистрируйтесь! @@ -128,4 +128,4 @@ border-radius: 10px; - + \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/ui/registration_window.ui b/client/ui/authorization-windows/ui/registration_window.ui similarity index 91% rename from client/ui/login-and-registration-windows/ui/registration_window.ui rename to client/ui/authorization-windows/ui/registration_window.ui index 77f1179..d0b20d4 100644 --- a/client/ui/login-and-registration-windows/ui/registration_window.ui +++ b/client/ui/authorization-windows/ui/registration_window.ui @@ -1,7 +1,7 @@ RegistrationWindow - + 0 @@ -27,7 +27,7 @@ border-radius: 10px; - Зарегестрироваться + Зарегистрироваться @@ -96,4 +96,4 @@ border-radius: 10px; - + \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/CMakeLists.txt b/client/ui/login-and-registration-windows/CMakeLists.txt deleted file mode 100644 index 141076a..0000000 --- a/client/ui/login-and-registration-windows/CMakeLists.txt +++ /dev/null @@ -1,61 +0,0 @@ -cmake_minimum_required(VERSION 3.16) -project(LoginRegistrationWindows VERSION 0.1 LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) - -find_package(Qt6 REQUIRED COMPONENTS Core Widgets Sql) -find_package(PostgreSQL REQUIRED) -link_directories(/path/to/postgresql/lib) - -find_package(Qt6 COMPONENTS Core Widgets Gui REQUIRED) - -set(TS_FILES login_window_ru_RU.ts) -set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) - -file(GLOB LOGIN_REGISTRATION_WINDOWS_SRC "src/*") -file(GLOB LOGIN_REGISTRATION_WINDOWS_INCLUDE "include/*") -file(GLOB LOGIN_REGISTRATION_WINDOWS_UI "ui/*") - - -add_library(${PROJECT_NAME} - ${LOGIN_REGISTRATION_WINDOWS_SRC} - ${LOGIN_REGISTRATION_WINDOWS_INCLUDE} - login_window_ru_RU.ts -) - -target_include_directories(LoginRegistrationWindows PUBLIC - ${CMAKE_SOURCE_DIR}/../../scripts - ${CMAKE_SOURCE_DIR}/../../database - $ -) - -target_link_libraries(LoginRegistrationWindows PRIVATE - Qt6::Core - Qt6::Widgets - Qt6::Sql -) - -set_target_properties(LoginRegistrationWindows PROPERTIES - ARCHIVE_OUTPUT_NAME LoginRegistrationWindows - POSITION_INDEPENDENT_CODE ON -) - -include(GNUInstallDirs) -install(TARGETS LoginRegistrationWindows - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} -) -install(DIRECTORY include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} - FILES_MATCHING PATTERN "*.h*" -) - -target_link_libraries(${PROJECT_NAME} PRIVATE Qt6::Widgets Qt6::Core) - -qt_finalize_executable(LoginRegistrationWindows) \ No newline at end of file diff --git a/client/ui/login-and-registration-windows/include/login_window_style_sheet.h b/client/ui/login-and-registration-windows/include/login_window_style_sheet.h deleted file mode 100644 index ce95d93..0000000 --- a/client/ui/login-and-registration-windows/include/login_window_style_sheet.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include "ui_login_window.h" - -namespace Ui { -QString login_window_dark_theme = R"( - QDialog { - background-color: #202020; - } - - QLabel { - font-family: 'Arial'; - font-size: 13px; - color: #089083; - padding: 1px; - } - - QPushButton#pushEnter { - font-family: 'Arial'; - border-radius: 10px; - background-color: #fea36b; - color: black; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - border-radius: 10px; - background-color: #131313; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushEnter:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: black; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid #131313; - background: #131313; - color: white; - padding: 5px; - } - - QLineEdit::placeholder { - color: #898989; - } -)"; - -QString login_window_light_theme = R"( - QDialog { - background-color: #f5f5f5; - } - - QLabel { - font-family: 'Arial'; - font-size: 13px; - color: #089083; - padding: 1px; - } - - QPushButton#pushEnter { - font-family: 'Arial'; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushEnter:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: #dadada; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid white; - background: white; - color: black; - padding: 5px; - } - - QLineEdit::placeholder { - color: #727272; - } - -)"; - -} // namespace Ui diff --git a/client/ui/login-and-registration-windows/include/registration_window_style_sheet.h b/client/ui/login-and-registration-windows/include/registration_window_style_sheet.h deleted file mode 100644 index 8b2995d..0000000 --- a/client/ui/login-and-registration-windows/include/registration_window_style_sheet.h +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once - -#include "ui_registration_window.h" - -namespace Ui { -QString registration_window_dark_theme = R"( - QDialog { - background-color: #202020; - } - - QLabel { - font-family: 'Arial'; - font-size: 13px; - color: #089083; - padding: 1px; - } - - QPushButton#pushRegistration { - font-family: 'Arial'; - border-radius: 10px; - background-color: #fea36b; - color: black; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - border-radius: 10px; - background-color: #131313; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushRegistration:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: black; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid #131313; - background: #131313; - color: white; - padding: 5px; - } - - QLineEdit::placeholder { - color: #898989; - } -)"; - -QString registration_window_light_theme = R"( - QDialog { - background-color: #f5f5f5; - } - - QLabel { - font-family: 'Arial'; - font-size: 13px; - color: #089083; - padding: 1px; - } - - QPushButton#pushRegistration { - font-family: 'Arial'; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushRegistration:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: #dadada; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid white; - background: white; - color: black; - padding: 5px; - } - - QLineEdit::placeholder { - color: #727272; - } - -)"; - -} // namespace Ui diff --git a/client/ui/main-window/CMakeLists.txt b/client/ui/main-window/CMakeLists.txt new file mode 100644 index 0000000..c8e1f85 --- /dev/null +++ b/client/ui/main-window/CMakeLists.txt @@ -0,0 +1,39 @@ +cmake_minimum_required(VERSION 3.16) + +project(MainWindow LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/*.cpp") +file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS "include/*.h") +file(GLOB UI_FILES CONFIGURE_DEPENDS "ui/*.ui") + +add_library(MainWindow STATIC + ${SOURCES} + ${HEADERS} + ${UI_FILES} +) + +target_include_directories(MainWindow PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR} +) + +target_link_libraries(MainWindow PRIVATE + Qt6::Widgets + Qt6::Core + Qt6::Gui + Qt6::Sql + Scripts + Database + NoteWidget +) \ No newline at end of file diff --git a/client/ui/MainWindow/MainWindow_en_US.ts b/client/ui/main-window/MainWindow_en_US.ts similarity index 54% rename from client/ui/MainWindow/MainWindow_en_US.ts rename to client/ui/main-window/MainWindow_en_US.ts index 0d8b287..a3740fb 100644 --- a/client/ui/MainWindow/MainWindow_en_US.ts +++ b/client/ui/main-window/MainWindow_en_US.ts @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/client/ui/MainWindow/include/applicationwindow.h b/client/ui/main-window/include/applicationwindow.h similarity index 54% rename from client/ui/MainWindow/include/applicationwindow.h rename to client/ui/main-window/include/applicationwindow.h index 08ac0d7..ccb14f8 100644 --- a/client/ui/MainWindow/include/applicationwindow.h +++ b/client/ui/main-window/include/applicationwindow.h @@ -2,14 +2,15 @@ #define APPLICATIONWINDOW_H #include + namespace Ui { class ApplicationWindow : public QMainWindow { - Q_OBJECT + Q_OBJECT public: - explicit ApplicationWindow(std::string window_name); + explicit ApplicationWindow(std::string window_name); signals: }; -} // namespace Ui -#endif // APPLICATIONWINDOW_H +} // namespace Ui +#endif // APPLICATIONWINDOW_H \ No newline at end of file diff --git a/client/ui/main-window/include/bottombar.h b/client/ui/main-window/include/bottombar.h new file mode 100644 index 0000000..cc7945c --- /dev/null +++ b/client/ui/main-window/include/bottombar.h @@ -0,0 +1,24 @@ +#ifndef BOTTOMBAR_H +#define BOTTOMBAR_H + +#include +#include +#include +#include + +namespace Ui { +class BottomBar : public QWidget { + QHBoxLayout *main_layout_; + QLabel *project_name_; + QLabel *username_; + +public: + BottomBar( + QWidget *parent_, + std::string username_, + std::string project_name_ + ); +}; +} // namespace Ui + +#endif // BOTTOMBAR_H \ No newline at end of file diff --git a/client/ui/main-window/include/main_window_style.hpp b/client/ui/main-window/include/main_window_style.hpp new file mode 100644 index 0000000..3e594c1 --- /dev/null +++ b/client/ui/main-window/include/main_window_style.hpp @@ -0,0 +1,139 @@ +#ifndef MAIN_WINDOW_STYLE_HPP +#define MAIN_WINDOW_STYLE_HPP +#include + +namespace Ui { + +QString main_window_style = R"( +#main-window { + background-color : #f5f5f5; +} + +#main-window QLabel { + font-weight: bold; +} + +QScrollBar:vertical { + border: none; + background: transparent; + width: 10px; + margin: 0; +} +QScrollBar::handle:vertical { + background: #c0c0c0; + border-radius: 5px; + min-height: 20px; +} + +QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, + QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { + background: none; + height: 0px; + } +QScrollArea { + border: none; + background: transparent; +} + +#ProjectList { + background-color: white; + border-radius: 8px; + padding: 8px; + outline: 0; + font-family: 'Arial'; + font-weight: bold; + font-size: 14px; + color:rgb(44, 44, 44); +} + +#ProjectList::item { + background-color:rgb(207, 236, 233); + border: none; + padding: 10px; + margin: 2px; + border-radius: 6px; +} + +#ProjectList::item:hover { + background-color: rgb(178, 226, 221); +} + +#ProjectList::item:selected { + background-color: #089083; + color: #ffffff; +} + +#ProjectList QScrollBar:vertical { + border: none; + background: #FED6BC; + width: 10px; + margin: 0; +} + +#ProjectList QScrollBar::handle:vertical { + background: #c0c0c0; + border-radius: 5px; + min-height: 20px; +} + +#ProjectList QScrollBar::add-line:vertical, +#ProjectList QScrollBar::sub-line:vertical { + background: none; +} + +#ProjectList QScrollBar::add-page:vertical, +#ProjectList QScrollBar::sub-page:vertical { + background: none; +} + +#BottomBar { + background-color: rgb(33, 44, 50); + border-radius: 8px; + padding: 8px; + outline: 0; +} + +#BottomBar QLabel { + color : white; + font-family: 'Arial'; + font-size: 14px; +} + +#NoteList { + border-radius : 8px; + background-color: white; +} + +#NoteWidget { + background-color: #ffdda2; + border-radius: 8px; +} + +#NoteWidget QPushButton { + font-family: 'Arial'; + font-size: 13px; + font-weight: bold; + border-radius: 10px; + background-color:rgb(241, 201, 132); + color: rgb(33, 44, 50); + padding: 5px 10px; +} + +QPushButton { + font-family: 'Arial'; + font-size: 13px; + font-weight: bold; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + min-width: 60px; + min-height: 25px; +} + +)"; + + +} // namespace Ui + +#endif // MAIN_WINDOW_STYLE_HPP \ No newline at end of file diff --git a/client/ui/main-window/include/mainwindow.h b/client/ui/main-window/include/mainwindow.h new file mode 100644 index 0000000..e5242f7 --- /dev/null +++ b/client/ui/main-window/include/mainwindow.h @@ -0,0 +1,43 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include +#include +#include +#include +#include +#include +#include "bottombar.h" +#include "notelist.h" +#include "projectlist.h" +#include "storage.hpp" + +namespace Ui { +class MainWindow : public QWidget { + Q_OBJECT + std::string username; + QVBoxLayout *main_layout_; + BottomBar *top_bar_; + QHBoxLayout *content_layout_; + ProjectList *project_list_; + NoteList *note_list_; + QWidget *content_widget_; + QPushButton *new_project_button_; + QPushButton *new_note_button_; + project_storage_model::Storage *storage_; + + friend ProjectList; +private slots: + void add_project(); + void add_note(); + +public: + explicit MainWindow( + QWidget *parent = nullptr, + std::string username = "none", + project_storage_model::Storage *storage = nullptr + ); +}; +} // namespace Ui +#endif // MAINWINDOW_H \ No newline at end of file diff --git a/client/ui/main-window/include/notelist.h b/client/ui/main-window/include/notelist.h new file mode 100644 index 0000000..f406536 --- /dev/null +++ b/client/ui/main-window/include/notelist.h @@ -0,0 +1,29 @@ +#ifndef NOTELIST_H +#define NOTELIST_H +#include +#include +#include +#include +#include "note.hpp" + +namespace Ui { +class NoteList : public QWidget { + friend class MainWindow; + Q_OBJECT + + QHBoxLayout *main_layout_; + + std::vector vertical_layouts_; + + int note_counter_ = 0; + +public: + void add_note_widget(const project_storage_model::Note *note); + void clear_note_list(); + NoteList(QWidget *parent); + +public slots: + void load_project_notes(QListWidgetItem *project); +}; +} // namespace Ui +#endif // NOTELIST_H \ No newline at end of file diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h new file mode 100644 index 0000000..f1f8f6a --- /dev/null +++ b/client/ui/main-window/include/notewidget.h @@ -0,0 +1,31 @@ +#ifndef NOTEWIDGET_H +#define NOTEWIDGET_H + +#include +#include +#include +#include +#include +#include "note.hpp" + +namespace Ui { +class NoteWidget : public QWidget { + Q_OBJECT + const project_storage_model::Note *model_note_; + QVBoxLayout *main_layout_; + QPushButton *open_button_; + QLabel *title_label_; + QLabel *text_label_; + +public: + explicit NoteWidget( + QWidget *parent = nullptr, + const project_storage_model::Note *model_note = nullptr + ); + +private slots: + + void open_note_window() const; +}; +} // namespace Ui +#endif // NOTEWIDGET_H \ No newline at end of file diff --git a/client/ui/main-window/include/projectitem.h b/client/ui/main-window/include/projectitem.h new file mode 100644 index 0000000..8b4a4a2 --- /dev/null +++ b/client/ui/main-window/include/projectitem.h @@ -0,0 +1,19 @@ +#ifndef PROJECTITEM_H +#define PROJECTITEM_H + +#include +#include +#include "notelist.h" +#include "project.hpp" + +namespace Ui { +class ProjectItem : public QListWidgetItem { + project_storage_model::Project *project_; + friend NoteList; + friend class MainWindow; + +public: + ProjectItem(QListWidget *listview, project_storage_model::Project *project); +}; +} // namespace Ui +#endif // PROJECTITEM_H \ No newline at end of file diff --git a/client/ui/main-window/include/projectlist.h b/client/ui/main-window/include/projectlist.h new file mode 100644 index 0000000..d221695 --- /dev/null +++ b/client/ui/main-window/include/projectlist.h @@ -0,0 +1,22 @@ +#ifndef PROJECTLIST_H +#define PROJECTLIST_H + +#include +#include +#include "project.hpp" +#include "storage.hpp" + +namespace Ui { +class ProjectList : public QListWidget { + Q_OBJECT + friend class MainWindow; + void add_project(project_storage_model::Project *project); + void load_projects(project_storage_model::Storage *storage); + +public: + explicit ProjectList(QWidget *parent = nullptr); + +signals: +}; +} // namespace Ui +#endif // PROJECTLIST_H \ No newline at end of file diff --git a/client/ui/main-window/src/applicationwindow.cpp b/client/ui/main-window/src/applicationwindow.cpp new file mode 100644 index 0000000..62c7bfe --- /dev/null +++ b/client/ui/main-window/src/applicationwindow.cpp @@ -0,0 +1,14 @@ +#include "applicationwindow.h" +#include +#include "mainwindow.h" + +namespace Ui { +ApplicationWindow::ApplicationWindow(std::string window_name_) + : QMainWindow{nullptr} { + this->setObjectName("ApplicationWindow"); + + this->setAttribute(Qt::WA_StyledBackground); + + this->setWindowTitle(window_name_.c_str()); +} +} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/bottombar.cpp b/client/ui/main-window/src/bottombar.cpp new file mode 100644 index 0000000..ee63c71 --- /dev/null +++ b/client/ui/main-window/src/bottombar.cpp @@ -0,0 +1,31 @@ +#include "bottombar.h" +#include +#include +#include +#include + +namespace Ui { +BottomBar::BottomBar( + QWidget *parent, + std::string username, + std::string project_name +) + : QWidget(parent), + main_layout_(new QHBoxLayout()), + username_(new QLabel(username.c_str())), + project_name_(new QLabel(project_name.c_str())) { + this->setObjectName("BottomBar"); + this->setLayout(main_layout_); + this->setFixedHeight(40); + + project_name_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + username_->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + + this->setSizePolicy( + QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum) + ); + + main_layout_->addWidget(project_name_); + main_layout_->addWidget(username_); +} +} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/mainwindow.cpp b/client/ui/main-window/src/mainwindow.cpp new file mode 100644 index 0000000..907f902 --- /dev/null +++ b/client/ui/main-window/src/mainwindow.cpp @@ -0,0 +1,113 @@ +#include "mainwindow.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "bottombar.h" +#include "lr_dao.hpp" +#include "main_window_style.hpp" +#include "note_dao.hpp" +#include "notelist.h" +#include "project.hpp" +#include "project_dao.hpp" +#include "projectitem.h" +#include "projectlist.h" + +namespace Ui { +MainWindow::MainWindow( + QWidget *parent, + std::string username, + project_storage_model::Storage *storage +) + : QWidget(parent), + username(username), + main_layout_(new QVBoxLayout(this)), + top_bar_(new BottomBar(this, username, "EFFICIO :: Таск-Трекер")), + content_layout_(new QHBoxLayout(this)), + project_list_(new ProjectList(this)), + note_list_(new NoteList(this)), + content_widget_(new QWidget(this)), + new_project_button_(new QPushButton("Новый проект", this)), + new_note_button_(new QPushButton("Новая заметка", this)), + storage_(storage) { + this->setObjectName("main-window"); + this->setAttribute(Qt::WA_StyledBackground); + this->setMinimumSize(QSize(800, 600)); + this->setStyleSheet(main_window_style); + + main_layout_->addWidget(top_bar_, Qt::AlignTop); + main_layout_->setAlignment(Qt::AlignCenter); + main_layout_->addWidget(content_widget_); + content_widget_->setLayout(content_layout_); + auto right_layout = new QVBoxLayout(content_widget_); + right_layout->addWidget(project_list_); + right_layout->addWidget(new_project_button_); + right_layout->addWidget(new_note_button_); + QScrollArea* scrollArea = new QScrollArea(content_widget_); + scrollArea->setWidgetResizable(true); + scrollArea->setWidget(note_list_); + content_layout_->addWidget(scrollArea, Qt::AlignRight); + content_layout_->addLayout(right_layout); + main_layout_->addWidget(content_widget_); + this->setLayout(main_layout_); + + this->project_list_->load_projects(storage); + + connect( + project_list_, &QListWidget::itemClicked, note_list_, + &NoteList::load_project_notes + ); + connect( + new_note_button_, &QPushButton::clicked, this, &Ui::MainWindow::add_note + ); + connect( + new_project_button_, &QPushButton::clicked, this, + &Ui::MainWindow::add_project + ); +} + +void MainWindow::add_project() { + bool ok; + QString name_of_project = QInputDialog::getText( + nullptr, "Название проекта:", "Введите название", QLineEdit::Normal, "", + &ok + ); + if (ok) { + int id = 0; + + if (DB::ProjectDAO::create_project(name_of_project.toStdString(), id)) { + LRDao::add_project_to_user(username, id); + auto &project = storage_->add_project( + Project(id, name_of_project.toStdString(), "") + ); + project_list_->add_project(&project); + } + } +} + +void MainWindow::add_note() { + auto project_item = + dynamic_cast(project_list_->currentItem()); + if (project_item) { + if (int id = 0; NoteDao::initialize_note(id)) { + DB::ProjectDAO::add_note_to_project( + project_item->project_->get_id(), id + ); + auto ¬e = + project_item->project_->add_note({id, "Пустая заметка", ""}); + note_list_->add_note_widget(¬e); + } + } else { + QMessageBox msg; + msg.setText("Проект не выбран!"); + msg.exec(); + } +} + +} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/notelist.cpp b/client/ui/main-window/src/notelist.cpp new file mode 100644 index 0000000..4af7558 --- /dev/null +++ b/client/ui/main-window/src/notelist.cpp @@ -0,0 +1,66 @@ +#include "notelist.h" +#include +#include +#include +#include +#include +#include "note.hpp" +#include "notewidget.h" +#include "projectitem.h" + +namespace Ui { +NoteList::NoteList(QWidget *parent) + : QWidget(parent), + main_layout_(new QHBoxLayout(this)), + vertical_layouts_(std::vector()){ + + this->setAttribute(Qt::WA_StyledBackground); + this->setObjectName("NoteList"); + this->setLayout(main_layout_); + vertical_layouts_.resize(4, nullptr); + for (auto &layout : vertical_layouts_) { + layout = new QVBoxLayout(this); + + main_layout_->addLayout(layout); + } +} + +void NoteList::add_note_widget(const project_storage_model::Note *note) { + auto current_layout = vertical_layouts_[note_counter_ % 4]; + if (current_layout->count() > 1) { + current_layout->removeItem( + current_layout->itemAt(current_layout->count() - 1) + ); + } + vertical_layouts_[note_counter_ % 4]->addWidget( + new NoteWidget(this, note), 0, Qt::AlignTop + ); + current_layout->addStretch(); + note_counter_++; +} + +void NoteList::load_project_notes(QListWidgetItem *project) { + ProjectItem *p = dynamic_cast(project); + assert(p != nullptr); + qDebug() << "Адрес проекта" + << QString::fromStdString(p->project_->get_name()) << ":" + << p->project_; + this->clear_note_list(); + note_counter_ = 0; + for (const auto ¬e : p->project_->get_notes()) { + this->add_note_widget(¬e); + } +} + +void NoteList::clear_note_list() { + for (auto &layout : vertical_layouts_) { + while (layout->count()) { + auto item = layout->takeAt(0); + auto widget = item->widget(); + if (widget) { + widget->deleteLater(); + } + } + } +} +} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp new file mode 100644 index 0000000..3ced750 --- /dev/null +++ b/client/ui/main-window/src/notewidget.cpp @@ -0,0 +1,53 @@ +#include "notewidget.h" +#include +#include +#include +#include "note.hpp" +#include "note_edit_dialog.h" + +namespace Ui { +NoteWidget::NoteWidget( + QWidget *parent, + const project_storage_model::Note *model_note +) + : QWidget(parent), + model_note_(model_note), + main_layout_(new QVBoxLayout(this)), + open_button_(new QPushButton("Открыть")) { + this->setObjectName("NoteWidget"); + this->setMinimumWidth(100); + this->setFixedHeight(100); + title_label_ = new QLabel(model_note_->get_title().c_str(), this); + text_label_ = new QLabel(model_note_->get_text().c_str(), this); + + title_label_->setStyleSheet("color: rgb(33, 44, 50);"); + text_label_->setStyleSheet("color: rgb(33, 44, 50);"); + + main_layout_->addWidget(title_label_); + main_layout_->addWidget(text_label_); + + text_label_->setWordWrap(false); + title_label_->setWordWrap(false); + text_label_->setTextInteractionFlags(Qt::TextSelectableByMouse); + + main_layout_->addWidget(open_button_); + + connect( + open_button_, &QPushButton::clicked, this, &NoteWidget::open_note_window + ); + this->setLayout(main_layout_); + this->setAttribute(Qt::WA_StyledBackground); +} + +void NoteWidget::open_note_window() const { + auto dialog = new ::NoteEditDialog( + const_cast(qobject_cast(this)), + const_cast(model_note_) + ); + dialog->setAttribute(Qt::WA_DeleteOnClose); + dialog->exec(); + text_label_->setText(model_note_->get_text().c_str()); + title_label_->setText(model_note_->get_title().c_str()); + main_layout_->update(); +} +} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/projectitem.cpp b/client/ui/main-window/src/projectitem.cpp new file mode 100644 index 0000000..4f38e85 --- /dev/null +++ b/client/ui/main-window/src/projectitem.cpp @@ -0,0 +1,12 @@ +#include "projectitem.h" +#include + +namespace Ui { +ProjectItem::ProjectItem( + QListWidget *list_view, + project_storage_model::Project *project +) + : project_(project), + QListWidgetItem(project->get_name().c_str(), list_view) { +} +} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/projectlist.cpp b/client/ui/main-window/src/projectlist.cpp new file mode 100644 index 0000000..3bd7828 --- /dev/null +++ b/client/ui/main-window/src/projectlist.cpp @@ -0,0 +1,24 @@ +#include "projectlist.h" +#include +#include "mainwindow.h" +#include "project.hpp" +#include "projectitem.h" + +namespace Ui { + +ProjectList::ProjectList(QWidget *parent) : QListWidget{parent} { + this->setObjectName("ProjectList"); + this->setFixedWidth(200); +} + +void ProjectList::add_project(project_storage_model::Project *project) { + this->addItem(new ProjectItem(static_cast(this), project)); +} + +void ProjectList::load_projects(project_storage_model::Storage *storage) { + for (project_storage_model::Project &pr : storage->get_projects()) { + add_project(&pr); + } +} + +} // namespace Ui \ No newline at end of file diff --git a/client/ui/note-widget/CMakeLists.txt b/client/ui/note-widget/CMakeLists.txt index e9ec3a1..d062724 100644 --- a/client/ui/note-widget/CMakeLists.txt +++ b/client/ui/note-widget/CMakeLists.txt @@ -1,51 +1,40 @@ cmake_minimum_required(VERSION 3.16) -project(NoteWidgetEfficio VERSION 0.1 LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 17) +project(NoteWidget LANGUAGES CXX) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt6 REQUIRED COMPONENTS Core Widgets Sql) +find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) -file(GLOB NOTE_WIDGET_SRC "src/*.cpp") -file(GLOB NOTE_WIDGET_INCLUDE "include/*.h") -file(GLOB NOTE_WIDGET_UI "ui/*.ui") +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) -set(TS_FILES NoteWidgetEfficio_ru_RU.ts) set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) -add_library(${PROJECT_NAME} STATIC - ${NOTE_WIDGET_SRC} - ${NOTE_WIDGET_UI} - ${TS_FILES} +set(UI_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/ui/note_edit_dialog.ui ) -target_include_directories(NoteWidgetEfficio PUBLIC - ${CMAKE_SOURCE_DIR}/../../scripts - ${CMAKE_SOURCE_DIR}/../../database - $ +set(SOURCES + src/note_edit_dialog.cpp + src/tags_dialog.cpp ) -target_link_libraries(NoteWidgetEfficio PUBLIC - Qt6::Core - Qt6::Widgets - Qt6::Sql +set(HEADERS + include/note_edit_dialog.h + include/tags_dialog.h ) -set_target_properties(NoteWidgetEfficio PROPERTIES - ARCHIVE_OUTPUT_NAME NoteWidgetEfficio - POSITION_INDEPENDENT_CODE ON -) +file(GLOB TS_FILES "*.ts") + +add_library(NoteWidget STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) -include(GNUInstallDirs) -install(TARGETS NoteWidgetEfficio - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} +target_include_directories(NoteWidget + PUBLIC + $ + $ ) -install(DIRECTORY include/ - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} - FILES_MATCHING PATTERN "*.h*" -) \ No newline at end of file + +target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database Scripts) \ No newline at end of file diff --git a/client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts b/client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts index e1de93e..32bf76c 100644 --- a/client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts +++ b/client/ui/note-widget/NoteWidgetEfficio_ru_RU.ts @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/client/ui/note-widget/include/note_edit_dialog.h b/client/ui/note-widget/include/note_edit_dialog.h index 19f9005..46048a3 100644 --- a/client/ui/note-widget/include/note_edit_dialog.h +++ b/client/ui/note-widget/include/note_edit_dialog.h @@ -3,6 +3,7 @@ #include #include +#include #include "note.hpp" #include "note_dao.hpp" #include "tags_dialog.h" @@ -22,8 +23,8 @@ class NoteEditDialog final : public QDialog { public: explicit NoteEditDialog( - QWidget *parent = nullptr, - Note *note = new Note(0, "NULL", "NULL") + QWidget* parent = nullptr, + Note* note = new Note(0, "NULL", "NULL") ); ~NoteEditDialog() override; @@ -34,13 +35,24 @@ private slots: void on_add_tags_button_click(); private: - Ui::NoteEditDialog *ui_; - std::unique_ptr avatar_label_; - std::vector> tag_labels_; - QList selected_tags_; - Note *note_; + void init_basic_fields(); + void init_additional_fields(); + void setup_connections(); + void setup_ui(); + + void add_member_avatar(const std::string& member); + + void clear_member_avatars(); + void update_tags_display(); + static QString create_tag_style_sheet(const QString& color); [[nodiscard]] bool try_save_note() const; + + Ui::NoteEditDialog* ui_{}; + std::vector> member_avatars_; + std::vector> tag_labels_; + QList selected_tags_; + Note* note_; }; -#endif // NOTE_EDIT_DIALOG_H +#endif // NOTE_EDIT_DIALOG_H \ No newline at end of file diff --git a/client/ui/note-widget/include/note_edit_dialog_styles.h b/client/ui/note-widget/include/note_edit_dialog_styles.h index 58d7377..5ad1b63 100644 --- a/client/ui/note-widget/include/note_edit_dialog_styles.h +++ b/client/ui/note-widget/include/note_edit_dialog_styles.h @@ -4,104 +4,6 @@ #include namespace Ui { -QString dark_theme = R"( - QDialog { - background-color: #202020; - } - - QLineEdit#titleLineEdit { - font-family: 'Arial'; - font-size: 25px; - font-weight: bold; - color: #089083; - border: none; - padding: 0; - background: transparent; - } - - QLabel#projectNameLabel { - font-family: 'Arial'; - font-size: 13px; - color: #898989; - padding: 1px; - } - - QLabel#descriptionLabel { - font-family: 'Arial'; - font-size: 18px; - font-weight: bold; - color: white; - } - - QTextEdit#descriptionTextEdit { - border-radius: 10px; - border: 1px solid #131313; - background: #131313; - color: white; - padding: 5px; - } - - QTextEdit#descriptionTextEdit::placeholder { - color: #898989; - } - - QPushButton#saveButton { - font-family: 'Arial'; - border-radius: 10px; - background-color: #fea36b; - color: black; - padding: 5px 10px; - } - - QPushButton#saveButton:hover { - background-color: #d58745; - } - - QPushButton#cancelButton { - font-family: 'Arial'; - border-radius: 10px; - background-color: #131313; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#cancelButton:hover { - background-color: black; - } - - QPushButton#joinButton, - QPushButton#membersButton, - QPushButton#checklistButton, - QPushButton#datesButton, - QPushButton#attachmentButton, - QPushButton#tagsButton { - font-family: 'Arial'; - background-color: #089083; - color: #000000; - padding: 5px 10px; - border-radius: 5px; - font-weight: bold; - text-align: left; - } - - QPushButton#joinButton:hover, - QPushButton#membersButton:hover, - QPushButton#checklistButton:hover, - QPushButton#datesButton:hover, - QPushButton#attachmentButton:hover, - QPushButton#tagsButton:hover { - background-color: #01635d; - } - - QLabel#sidePanelLabel, - QLabel#sidePanelLabel_2 { - font-family: 'Arial'; - font-size: 14px; - font-weight: bold; - color: #898989; - } -)"; - QString light_theme = R"( QDialog { background-color: #f5f5f5; @@ -269,4 +171,4 @@ QString light_theme = R"( } // namespace Ui -#endif // NOTE_EDIT_DIALOG_STYLES_H +#endif // NOTE_EDIT_DIALOG_STYLES_H \ No newline at end of file diff --git a/client/ui/note-widget/include/tags_dialog_styles.h b/client/ui/note-widget/include/tags_dialog_styles.h index ee5f06b..15fd320 100644 --- a/client/ui/note-widget/include/tags_dialog_styles.h +++ b/client/ui/note-widget/include/tags_dialog_styles.h @@ -120,4 +120,4 @@ QString tags_dialog_light_theme = R"( )"; } -#endif // TAGS_DIALOG_STYLES_H +#endif // TAGS_DIALOG_STYLES_H \ No newline at end of file diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index 27d80ba..ed18200 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -6,93 +6,134 @@ #include #include #include -#include +#include #include "./ui_note_edit_dialog.h" #include "note_edit_dialog_styles.h" #include "tags_dialog.h" -NoteEditDialog::NoteEditDialog(QWidget *parent, Note *note) +NoteEditDialog::NoteEditDialog(QWidget* parent, Note* note) : QDialog(parent), ui_(new Ui::NoteEditDialog), - avatar_label_(nullptr), note_(note) { ui_->setupUi(this); + setWindowTitle("EFFICIO"); - setFixedSize(700, 480); - setStyleSheet(Ui::light_theme); - ui_->buttonsLayout->setAlignment(Qt::AlignLeft); + init_basic_fields(); + init_additional_fields(); + setup_connections(); + setup_ui(); +} + +NoteEditDialog::~NoteEditDialog() { + delete ui_; +} + +void NoteEditDialog::init_basic_fields() { + ui_->titleLineEdit->setText(QString::fromStdString(note_->get_title())); + ui_->descriptionTextEdit->setText(QString::fromStdString(note_->get_text())); +} - connect( - ui_->saveButton, &QPushButton::clicked, this, - &NoteEditDialog::on_save_button_click - ); - connect( - ui_->cancelButton, &QPushButton::clicked, this, &NoteEditDialog::reject - ); - connect( - ui_->joinButton, &QPushButton::clicked, this, - &NoteEditDialog::on_join_button_click - ); - connect( - ui_->addMembersButton, &QPushButton::clicked, this, - &NoteEditDialog::on_add_members_button_click - ); - connect(ui_->addDateButton, &QPushButton::clicked, this, [=]() { +void NoteEditDialog::init_additional_fields() { + if (!note_->get_date().empty()) { + QDate date = QDate::fromString(QString::fromStdString(note_->get_date()), "yyyy-MM-dd"); + if (date.isValid()) { + ui_->dateEdit->setDate(date); + ui_->dateLabel->setVisible(true); + ui_->dateEdit->setVisible(true); + } + } + + if (!note_->get_members().empty()) { + ui_->membersLabel->setVisible(true); + for (const auto& member : note_->get_members()) { + add_member_avatar(member); + } + ui_->joinButton->setText("Покинуть"); + } + + if (!note_->get_tags().empty()) { + ui_->tagsLabel->setVisible(true); + for (const auto& tag : note_->get_tags()) { + TagsDialog::Tag tag_info; + tag_info.is_checked = true; + tag_info.color = QString::fromStdString(tag.color); + tag_info.name = QString::fromStdString(tag.name); + selected_tags_.append(tag_info); + + auto tag_label = std::make_unique(tag_info.name, this); + tag_label->setStyleSheet(create_tag_style_sheet(tag_info.color)); + ui_->tagsLayout->addWidget(tag_label.get()); + tag_labels_.push_back(std::move(tag_label)); + } + } +} + +void NoteEditDialog::setup_connections() { + connect(ui_->saveButton, &QPushButton::clicked, this, &NoteEditDialog::on_save_button_click); + connect(ui_->cancelButton, &QPushButton::clicked, this, &NoteEditDialog::reject); + connect(ui_->joinButton, &QPushButton::clicked, this, &NoteEditDialog::on_join_button_click); + connect(ui_->addMembersButton, &QPushButton::clicked, this, &NoteEditDialog::on_add_members_button_click); + connect(ui_->addDateButton, &QPushButton::clicked, this, [this]() { const bool is_visible = ui_->dateLabel->isVisible(); ui_->dateLabel->setVisible(!is_visible); ui_->dateEdit->setVisible(!is_visible); }); - connect( - ui_->addTagsButton, &QPushButton::clicked, this, - &NoteEditDialog::on_add_tags_button_click - ); + connect(ui_->addTagsButton, &QPushButton::clicked, this, &NoteEditDialog::on_add_tags_button_click); } -NoteEditDialog::~NoteEditDialog() { - delete ui_; +void NoteEditDialog::setup_ui() { + setFixedSize(700, 480); + setStyleSheet(Ui::light_theme); + ui_->buttonsLayout->setAlignment(Qt::AlignLeft); } void NoteEditDialog::on_save_button_click() { - QString title = ui_->titleLineEdit->text(); - QString content = ui_->descriptionTextEdit->toPlainText(); - - const QString message = - QString("Заголовок: %1\nСодержимое: %2").arg(title, content); - if (try_save_note()) { - QMessageBox::information(this, "Заметка сохранена", message); + QMessageBox::information(this, "Заметка сохранена", + QString("Заголовок: %1\nСодержимое: %2") + .arg(ui_->titleLineEdit->text(), ui_->descriptionTextEdit->toPlainText())); + } else { + QMessageBox::information(this, "Ошибка", "Не удалось сохранить заметку"); } + close(); } void NoteEditDialog::on_join_button_click() { const bool is_joined = ui_->membersLabel->isVisible(); + ui_->membersLabel->setVisible(!is_joined); if (!is_joined) { - ui_->membersLabel->setVisible(true); - - QPixmap pixmap(32, 32); - pixmap.fill(Qt::transparent); - QPainter painter(&pixmap); - painter.setBrush(Qt::black); - painter.setPen(Qt::NoPen); - painter.drawEllipse(0, 0, 32, 32); - - avatar_label_ = std::make_unique(this); - avatar_label_->setPixmap(pixmap); - avatar_label_->setFixedSize(32, 32); - ui_->avatarsLayout->addWidget(avatar_label_.get()); - + const std::string current_user = "TODO"; + add_member_avatar(current_user); + note_->add_member(current_user); ui_->joinButton->setText("Покинуть"); } else { - ui_->membersLabel->setVisible(false); + std::string current_user = "TODO"; + clear_member_avatars(); + ui_->joinButton->setText("Присоединиться"); + } +} - if (avatar_label_) { - ui_->avatarsLayout->removeWidget(avatar_label_.get()); - avatar_label_.reset(); - } +void NoteEditDialog::add_member_avatar(const std::string& member) { + QPixmap pixmap(32, 32); + pixmap.fill(Qt::transparent); + QPainter painter(&pixmap); + painter.setBrush(Qt::black); + painter.setPen(Qt::NoPen); + painter.drawEllipse(0, 0, 32, 32); + + auto member_label = std::make_unique(this); + member_label->setPixmap(pixmap); + member_label->setFixedSize(32, 32); + ui_->avatarsLayout->addWidget(member_label.get()); + member_avatars_.push_back(std::move(member_label)); +} - ui_->joinButton->setText("Присоединиться"); +void NoteEditDialog::clear_member_avatars() { + for (auto& avatar : member_avatars_) { + ui_->avatarsLayout->removeWidget(avatar.get()); } + member_avatars_.clear(); } void NoteEditDialog::on_add_members_button_click() { @@ -103,37 +144,52 @@ void NoteEditDialog::on_add_tags_button_click() { TagsDialog tags_dialog(selected_tags_, this); if (tags_dialog.exec() == Accepted) { selected_tags_ = tags_dialog.get_selected_tags(); + update_tags_display(); + } +} - if (!ui_->tagsLabel->isVisible()) { - ui_->tagsLabel->setVisible(true); - } - - tag_labels_.clear(); +void NoteEditDialog::update_tags_display() { + for (auto& tag_label : tag_labels_) { + ui_->tagsLayout->removeWidget(tag_label.get()); + } + tag_labels_.clear(); - for (const auto &[is_checked, color, name] : selected_tags_) { + ui_->tagsLabel->setVisible(!selected_tags_.empty()); + for (const auto &[is_checked, color, name] : selected_tags_) { + if (is_checked) { auto tag_label = std::make_unique(name, this); - tag_label->setStyleSheet(QString("background-color: %1; " - "color: white; " - "padding: 9px 10px; " - "border-radius: 5px; " - "font-family: 'Arial'; " - "font-size: 12px; " - "font-weight: bold;" - "width: 40px;" - "height: 25px;") - .arg(color)); + tag_label->setStyleSheet(create_tag_style_sheet(color)); ui_->tagsLayout->addWidget(tag_label.get()); tag_labels_.push_back(std::move(tag_label)); } } +} - if (selected_tags_.empty()) { - ui_->tagsLabel->setVisible(false); - } +QString NoteEditDialog::create_tag_style_sheet(const QString& color) { + return QString( + "background-color: %1; " + "color: white; " + "padding: 9px 10px; " + "border-radius: 5px; " + "font-family: 'Arial'; " + "font-size: 12px; " + "font-weight: bold;" + "width: 40px;" + "height: 25px;" + ).arg(color); } bool NoteEditDialog::try_save_note() const { note_->set_title(ui_->titleLineEdit->text().toStdString()); note_->set_text(ui_->descriptionTextEdit->toPlainText().toStdString()); - return NoteDao::create_note(*note_); -} + note_->set_date(ui_->dateEdit->date().toString("yyyy-MM-dd").toStdString()); + + note_->clear_tags(); + for (const auto &[is_checked, color, name] : selected_tags_) { + if (is_checked) { + note_->add_tag(name.toStdString(), color.toStdString()); + } + } + + return NoteDao::update_note(*note_); +} \ No newline at end of file diff --git a/client/ui/note-widget/ui/note_edit_dialog.ui b/client/ui/note-widget/ui/note_edit_dialog.ui index 90ed1b9..60eebd8 100644 --- a/client/ui/note-widget/ui/note_edit_dialog.ui +++ b/client/ui/note-widget/ui/note_edit_dialog.ui @@ -1,7 +1,7 @@ NoteEditDialog - + 0 @@ -366,4 +366,4 @@ - + \ No newline at end of file From 8e3c15b82ff52794d8674c2d51400094fb82d49e Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 01:22:47 +0300 Subject: [PATCH 09/47] client works --- client/CMakeLists.txt | 3 --- client/main.cpp | 36 ++++++++++++++++++++---------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 5a82d38..5bdc76b 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -16,7 +16,6 @@ add_subdirectory(database) add_subdirectory(ui/main-window) add_subdirectory(ui/authorization-windows) add_subdirectory(ui/note-widget) -add_subdirectory(proto) add_executable(EfficioTaskTracker main.cpp) target_link_libraries(EfficioTaskTracker PRIVATE @@ -24,8 +23,6 @@ target_link_libraries(EfficioTaskTracker PRIVATE Qt6::Core Qt6::Gui Qt6::Sql - model-proto - efficio-rpc Database Scripts AuthorizationWindows diff --git a/client/main.cpp b/client/main.cpp index ba3cc2f..8af3d0d 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,30 +1,34 @@ #include #include -#include +#include +#include #include #include "applicationwindow.h" -#include "mainwindow.h" -#include "note.hpp" -#include "storage.hpp" -#include "registration_window.h" #include "login_window.h" - -using namespace Ui; +#include "mainwindow.h" int main(int argc, char *argv[]) { - QApplication a(argc, argv); + QApplication application(argc, argv); QTranslator translator; - const QStringList uiLanguages = QLocale::system().uiLanguages(); - for (const QString &locale : uiLanguages) { - const QString baseName = "MainWindow_" + QLocale(locale).name(); - if (translator.load(":/i18n/" + baseName)) { - a.installTranslator(&translator); + const QStringList ui_languages = QLocale::system().uiLanguages(); + for (const QString &locale : ui_languages) { + const QString base_name = "MainWindow_" + QLocale(locale).name(); + if (translator.load(":/i18n/" + base_name)) { + QApplication::installTranslator(&translator); break; } } - Ui::LoginWindow login_window; - login_window.show(); - return login_window.exec(); + auto *app_window = new Ui::ApplicationWindow("EFFICIO"); + auto *login_window = new LoginWindow(app_window); + + app_window->setCentralWidget(login_window); + const QRect screen_geometry = QApplication::primaryScreen()->availableGeometry(); + const int x = (screen_geometry.width() - login_window->width()) / 2; + const int y = (screen_geometry.height() - login_window->height()) / 2; + app_window->move(x, y); + app_window->show(); + + return QApplication::exec(); } \ No newline at end of file From 25b4b2697dda1e784ad123141b27ee5b7356993d Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 22:06:58 +0300 Subject: [PATCH 10/47] declare client classes, some refactor server --- client/CMakeLists.txt | 1 + client/client/CMakeLists.txt | 31 +++++++++++++++ client/client/include/client_implementation.h | 20 ++++++++++ client/client/include/common_client_call.h | 25 ++++++++++++ client/client/include/update_requests.h | 39 +++++++++++++++++++ client/client/src/client_implementation.cpp | 18 +++++++++ client/client/src/update_requests.cpp | 18 +++++++++ .../{common_call.h => common_server_call.h} | 11 ++---- .../Service/include/server_implementation.h | 18 +++++++++ server/Service/include/update_service.h | 17 +++----- server/Service/src/server_implementation.cpp | 1 + server/Service/src/update_service.cpp | 9 +++-- 12 files changed, 187 insertions(+), 21 deletions(-) create mode 100644 client/client/CMakeLists.txt create mode 100644 client/client/include/client_implementation.h create mode 100644 client/client/include/common_client_call.h create mode 100644 client/client/include/update_requests.h create mode 100644 client/client/src/client_implementation.cpp create mode 100644 client/client/src/update_requests.cpp rename server/Service/include/{common_call.h => common_server_call.h} (64%) create mode 100644 server/Service/include/server_implementation.h create mode 100644 server/Service/src/server_implementation.cpp diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 5bdc76b..3b06231 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -16,6 +16,7 @@ add_subdirectory(database) add_subdirectory(ui/main-window) add_subdirectory(ui/authorization-windows) add_subdirectory(ui/note-widget) +add_subdirectory(client) add_executable(EfficioTaskTracker main.cpp) target_link_libraries(EfficioTaskTracker PRIVATE diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt new file mode 100644 index 0000000..79d0467 --- /dev/null +++ b/client/client/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.16) + +project(Service LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Protobuf CONFIG REQUIRED) +find_package(gRPC CONFIG REQUIRED) +find_package(Threads) + + +file(GLOB SOURCES "src/*.cpp") +file(GLOB HEADERS "include/*.h") + +add_library(Client STATIC ${SOURCES} ${HEADERS}) + +target_link_libraries(Client + model-proto + efficio-rpc + protobuf::libprotobuf + gRPC::grpc + gRPC::grpc++ +) + +target_include_directories(Client + + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR} +) \ No newline at end of file diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h new file mode 100644 index 0000000..85b380e --- /dev/null +++ b/client/client/include/client_implementation.h @@ -0,0 +1,20 @@ + +#ifndef CLIENTIMPLEMENTATION_H +#define CLIENTIMPLEMENTATION_H + +#include + +using grpc::CompletionQueue; +using grpc::Channel; + +class ClientImplementation { + CompletionQueue cq_; + std::shared_ptr channel_; +public: + CompletionQueue *get_cq() { return &cq_; } + std::shared_ptr get_channel() { return channel_; } + explicit ClientImplementation(std::shared_ptr channel); + void CompleteRpc(); +}; + +#endif //CLIENTIMPLEMENTATION_H diff --git a/client/client/include/common_client_call.h b/client/client/include/common_client_call.h new file mode 100644 index 0000000..7c52ef1 --- /dev/null +++ b/client/client/include/common_client_call.h @@ -0,0 +1,25 @@ +#ifndef COMMON_CLIENT_CALL_H +#define COMMON_CLIENT_CALL_H + +#include + +using grpc::ClientContext; +using grpc::Status; + + +class CommonClientCall +{ +public: + + + explicit CommonClientCall() = default; + virtual ~CommonClientCall() = default; + + ClientContext context; + Status status; + + virtual void Proceed(bool = true) = 0; +}; + + +#endif //COMMON_CLIENT_CALL_H diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h new file mode 100644 index 0000000..a476f40 --- /dev/null +++ b/client/client/include/update_requests.h @@ -0,0 +1,39 @@ +#ifndef UPDATE_REQUESTS_H +#define UPDATE_REQUESTS_H + +#include +#include +#include + +using grpc::Channel; + +using Efficio_proto::Update; +using Efficio_proto::Note; +using Efficio_proto::Project; +using Efficio_proto::Storage; +class UpdateRequests { + public: + class GetNoteClientCall; + class GetProjectClientCall; + class CreateNoteClientCall; + class CreateProjectClientCall; + class TryJoinProjectClientCall; + + bool get_note(Note *note); + bool get_project(Project *project); + bool create_note(Note *note); + bool create_project(Project *project); + bool try_join_project(Project *project); + + + + explicit UpdateRequests(std::shared_ptr channel): + stub_(Update::NewStub(channel)) {}; + private: + std::unique_ptr stub_; + + +}; + + +#endif //UPDATE_REQUESTS_H diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp new file mode 100644 index 0000000..db4f531 --- /dev/null +++ b/client/client/src/client_implementation.cpp @@ -0,0 +1,18 @@ +#include "client_implementation.h" +#include +#include +#include +#include + + +using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::ClientContext; +using grpc::CompletionQueue; +using grpc::Status; + +ClientImplementation::ClientImplementation(std::shared_ptr channel) : + channel_(channel) { }; + + + diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp new file mode 100644 index 0000000..95f0206 --- /dev/null +++ b/client/client/src/update_requests.cpp @@ -0,0 +1,18 @@ +#include +#include +#include +#include + + +using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::ClientContext; +using grpc::CompletionQueue; +using grpc::Status; + +using Efficio_proto::GetProjectRequest; +using Efficio_proto::GetProjectResponse; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; +using Efficio_proto::Update; + diff --git a/server/Service/include/common_call.h b/server/Service/include/common_server_call.h similarity index 64% rename from server/Service/include/common_call.h rename to server/Service/include/common_server_call.h index 6a1380f..bf9d190 100644 --- a/server/Service/include/common_call.h +++ b/server/Service/include/common_server_call.h @@ -2,14 +2,11 @@ #define CALL_DATA_H #include -using grpc::Server; -using grpc::ServerAsyncResponseWriter; -using grpc::ServerBuilder; + using grpc::ServerCompletionQueue; using grpc::ServerContext; -using grpc::Status; -class CommonCall +class CommonServerCall { public: ServerCompletionQueue* cq_; @@ -17,12 +14,12 @@ class CommonCall enum CallStatus { CREATE, PROCESS, FINISH }; CallStatus status_; public: - explicit CommonCall(ServerCompletionQueue* cq): + explicit CommonServerCall(ServerCompletionQueue* cq): cq_(cq), status_(CREATE) {} - virtual ~CommonCall() = default; + virtual ~CommonServerCall() = default; virtual void Proceed(bool = true) = 0; }; diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h new file mode 100644 index 0000000..14d3517 --- /dev/null +++ b/server/Service/include/server_implementation.h @@ -0,0 +1,18 @@ +#ifndef SERVERIMPLEMENTATION_H +#define SERVERIMPLEMENTATION_H + +#include + + +using grpc::Server; +using grpc::ServerCompletionQueue; + +class ServerImplementation final { + std::unique_ptr cq_; + std::unique_ptr server_; + public: + void Run(uint16_t port); + void HandleRpcs(); +}; + +#endif //SERVERIMPLEMENTATION_H diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 6393610..18756ce 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -6,25 +6,20 @@ #include #include - -using grpc::Server; -using grpc::ServerAsyncResponseWriter; -using grpc::ServerBuilder; -using grpc::ServerCompletionQueue; using grpc::ServerContext; -using grpc::Status; - using Efficio_proto::Update; class UpdateService final { Update::AsyncService service_; - + ServerContext ctx_; public: - class GetNoteCall; - // ... - void Run(uint16_t port); + class GetNoteServerCall; + class GetProjectServerCall; + class TryJoinProjectServerCall; + class CreateProjectServerCall; + class CreateNoteServerCall; }; #endif //UPDATE_SERVICE_H diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/server/Service/src/server_implementation.cpp @@ -0,0 +1 @@ + diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index ad9b63c..9ad1602 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -1,6 +1,9 @@ #include "update_service.h" -#include "common_call.h" - +#include "common_server_call.h" // need define all calls (rpc methods) -// class UpdateService::GetNoteCall : public CommonCall {}; +// class UpdateService::GetNoteCall : public CommonServerCall {}; + +class UpdateService::GetProjectServerCall : public CommonServerCall { + +}; \ No newline at end of file From 2d417c05c10b40dcb2d5950fabf694792ee7f6a3 Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 12 Apr 2025 22:11:52 +0300 Subject: [PATCH 11/47] try make request --- client/client/src/client_implementation.cpp | 22 ++++++++++++++++++++ client/client/src/update_requests.cpp | 23 +++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index db4f531..8ac50a2 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -14,5 +14,27 @@ using grpc::Status; ClientImplementation::ClientImplementation(std::shared_ptr channel) : channel_(channel) { }; +void ClientImplementation::CompleteRpc() { + void* got_tag; + bool ok = false; + + + while (cq_.Next(&got_tag, &ok)) { + + CommonClientCall* call = static_cast(got_tag); + + // Verify that the request was completed successfully. Note that "ok" + // corresponds solely to the request for updates introduced by Finish(). + assert(ok); + + if (call->status.ok()) + call->Proceed(); + else + std::cout << "RPC failed" << std::endl; + + // Once we're complete, deallocate the call object. + delete call; + } +} diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 95f0206..f0275cc 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -16,3 +16,26 @@ using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::Update; +class UpdateRequests::GetProjectClientCall : public CommonClientCall { + GetProjectResponse response; + std::unique_ptr> response_reader; + + void Proceed(bool ok) override { + + if (ok && status.ok()) { + if (response.has_error_string()) { + std::cout << response.error_string() << std::endl; + } else if (response.has_project()) { + std::cout << response.project().title() << std::endl; + } + } + } +public: + GetProjectClientCall(const GetProjectRequest& request, + CompletionQueue& cq_, + std::unique_ptr& stub_) : CommonClientCall() + { + response_reader = stub_->AsyncGetProject(&context, request, &cq_); + response_reader->Finish(&response, &status, (void*)this); + } +}; \ No newline at end of file From b9c007d3fd961e68c235474a2460965b8b110383 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Sun, 13 Apr 2025 21:57:14 +0300 Subject: [PATCH 12/47] feat: add async Note calls and update server startup logic --- .../Service/include/server_implementation.h | 4 +- server/Service/include/update_service.h | 44 ++++++++++++++--- server/Service/src/server_implementation.cpp | 27 ++++++++++ server/Service/src/update_service.cpp | 49 +++++++++++++++++-- server/main.cpp | 6 ++- 5 files changed, 116 insertions(+), 14 deletions(-) diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h index 14d3517..bb88749 100644 --- a/server/Service/include/server_implementation.h +++ b/server/Service/include/server_implementation.h @@ -2,7 +2,7 @@ #define SERVERIMPLEMENTATION_H #include - +#include "update_service.h" using grpc::Server; using grpc::ServerCompletionQueue; @@ -12,7 +12,7 @@ class ServerImplementation final { std::unique_ptr server_; public: void Run(uint16_t port); - void HandleRpcs(); + void HandleRPCs(UpdateService& update_service) const; }; #endif //SERVERIMPLEMENTATION_H diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 18756ce..17e7142 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -1,25 +1,57 @@ #ifndef UPDATE_SERVICE_H #define UPDATE_SERVICE_H -#include -#include #include #include +#include +#include +#include "common_server_call.h" using grpc::ServerContext; +using grpc::ServerAsyncResponseWriter; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; using Efficio_proto::Update; - +using Efficio_proto::CreateNoteRequest; class UpdateService final { Update::AsyncService service_; ServerContext ctx_; + std::unique_ptr cq_; + std::unique_ptr server_; public: - class GetNoteServerCall; - class GetProjectServerCall; + class GetNoteServerCall final : public CommonServerCall { + GetNoteRequest request_; + ServerAsyncResponseWriter responder_; + UpdateService &service_; + + public: + explicit GetNoteServerCall(UpdateService &service, ServerCompletionQueue *cq); + void Proceed(bool ok) override; + }; + + class GetProjectServerCall final : public CommonServerCall { + public: + explicit GetProjectServerCall(UpdateService& service); + void Proceed(bool ok) override; + + // TODO: finish this call + }; + class TryJoinProjectServerCall; class CreateProjectServerCall; - class CreateNoteServerCall; + + class CreateNoteServerCall final : public CommonServerCall { + CreateNoteRequest request_; + ServerAsyncResponseWriter responder_; + UpdateService &service_; + public: + explicit CreateNoteServerCall(UpdateService& service, ServerCompletionQueue *cq); + void Proceed(bool) override; + }; + + Update::AsyncService& get_service(); }; #endif //UPDATE_SERVICE_H diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index 8b13789..1fd581a 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -1 +1,28 @@ +#include "server_implementation.h" +#include "common_server_call.h" +using Efficio_proto::GetNoteRequest; + +void ServerImplementation::Run(const uint16_t port) { + grpc::ServerBuilder builder; + UpdateService update_service; + + builder.AddListeningPort( + "0.0.0.0:" + std::to_string(port), grpc::InsecureServerCredentials() + ); + builder.RegisterService(&update_service.get_service()); + + cq_ = builder.AddCompletionQueue(); + server_ = builder.BuildAndStart(); + HandleRPCs(update_service); +} + +void ServerImplementation::HandleRPCs(UpdateService& update_service) const { + new UpdateService::GetNoteServerCall(update_service, cq_.get()); + + void *tag; + bool ok; + while (cq_->Next(&tag, &ok)) { + static_cast(tag)->Proceed(ok); + } +} diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 9ad1602..9dbf6d9 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -1,9 +1,50 @@ #include "update_service.h" #include "common_server_call.h" -// need define all calls (rpc methods) -// class UpdateService::GetNoteCall : public CommonServerCall {}; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; -class UpdateService::GetProjectServerCall : public CommonServerCall { +using grpc::ServerAsyncResponseWriter; -}; \ No newline at end of file +UpdateService::GetNoteServerCall::GetNoteServerCall(UpdateService &service, ServerCompletionQueue *cq) + : CommonServerCall(cq), + responder_(&ctx_), + service_(service) { +} + +void UpdateService::GetNoteServerCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + + switch (status_) { + case CREATE: { + new GetNoteServerCall(service_, cq_); + service_.service_.RequestGetNote( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + status_ = PROCESS; + break; + } + case PROCESS: { + const GetNoteResponse response; + + // TODO: query processing logic + + responder_.Finish(response, grpc::Status::OK, this); + status_ = FINISH; + break; + } + case FINISH: { + delete this; + break; + } + } +} + +class UpdateService::CreateNoteServerCall final : public CommonServerCall {}; + +Update::AsyncService& UpdateService::get_service() { + return service_; +} \ No newline at end of file diff --git a/server/main.cpp b/server/main.cpp index 6736c7c..bbda3c9 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -1,5 +1,7 @@ - +#include "server_implementation.h" int main() { - + ServerImplementation server; + server.Run(50051); + return 0; } \ No newline at end of file From c13cd0fdc4ebe7fcde1dc3869845ce27a32f5f7c Mon Sep 17 00:00:00 2001 From: toximu Date: Sun, 13 Apr 2025 23:24:36 +0300 Subject: [PATCH 13/47] restruct server --- server/Service/include/server_implementation.h | 2 +- server/Service/include/update_service.h | 12 ++++++++---- server/Service/src/server_implementation.cpp | 13 ++++++------- server/Service/src/update_service.cpp | 10 ++++++---- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h index bb88749..af6e563 100644 --- a/server/Service/include/server_implementation.h +++ b/server/Service/include/server_implementation.h @@ -12,7 +12,7 @@ class ServerImplementation final { std::unique_ptr server_; public: void Run(uint16_t port); - void HandleRPCs(UpdateService& update_service) const; + void HandleRPCs() const; }; #endif //SERVERIMPLEMENTATION_H diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 17e7142..aec6a0a 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -18,16 +18,18 @@ using Efficio_proto::CreateNoteRequest; class UpdateService final { Update::AsyncService service_; ServerContext ctx_; - std::unique_ptr cq_; - std::unique_ptr server_; + ServerCompletionQueue *cq_; + public: + + explicit UpdateService(ServerCompletionQueue* cq); class GetNoteServerCall final : public CommonServerCall { GetNoteRequest request_; ServerAsyncResponseWriter responder_; - UpdateService &service_; + Update::AsyncService *service_; public: - explicit GetNoteServerCall(UpdateService &service, ServerCompletionQueue *cq); + explicit GetNoteServerCall(Update::AsyncService *service, ServerCompletionQueue *cq); void Proceed(bool ok) override; }; @@ -52,6 +54,8 @@ class UpdateService final { }; Update::AsyncService& get_service(); + + }; #endif //UPDATE_SERVICE_H diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index 1fd581a..fff6c2c 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -5,21 +5,20 @@ using Efficio_proto::GetNoteRequest; void ServerImplementation::Run(const uint16_t port) { grpc::ServerBuilder builder; - UpdateService update_service; - + cq_ = builder.AddCompletionQueue(); builder.AddListeningPort( "0.0.0.0:" + std::to_string(port), grpc::InsecureServerCredentials() ); + + + UpdateService update_service(cq_.get()); builder.RegisterService(&update_service.get_service()); - cq_ = builder.AddCompletionQueue(); server_ = builder.BuildAndStart(); - HandleRPCs(update_service); + HandleRPCs(); } -void ServerImplementation::HandleRPCs(UpdateService& update_service) const { - new UpdateService::GetNoteServerCall(update_service, cq_.get()); - +void ServerImplementation::HandleRPCs() const { void *tag; bool ok; while (cq_->Next(&tag, &ok)) { diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 9dbf6d9..06d2d29 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -6,7 +6,11 @@ using Efficio_proto::GetNoteResponse; using grpc::ServerAsyncResponseWriter; -UpdateService::GetNoteServerCall::GetNoteServerCall(UpdateService &service, ServerCompletionQueue *cq) +UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq) { + new GetNoteServerCall(&service_, cq_); +} + +UpdateService::GetNoteServerCall::GetNoteServerCall(Update::AsyncService *service, ServerCompletionQueue *cq) : CommonServerCall(cq), responder_(&ctx_), service_(service) { @@ -21,7 +25,7 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { switch (status_) { case CREATE: { new GetNoteServerCall(service_, cq_); - service_.service_.RequestGetNote( + service_->RequestGetNote( &ctx_, &request_, &responder_, cq_, cq_, this ); status_ = PROCESS; @@ -43,8 +47,6 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { } } -class UpdateService::CreateNoteServerCall final : public CommonServerCall {}; - Update::AsyncService& UpdateService::get_service() { return service_; } \ No newline at end of file From 3a68304062e4194398ac9a2b6934a916a3975c13 Mon Sep 17 00:00:00 2001 From: toximu Date: Mon, 14 Apr 2025 14:01:43 +0300 Subject: [PATCH 14/47] add GetProjectServerCall --- CMakeLists.txt | 2 +- client/client/CMakeLists.txt | 7 ++- client/client/include/update_requests.h | 31 +++++++++++-- client/client/src/client_implementation.cpp | 9 +++- client/client/src/update_requests.cpp | 21 ++++----- server/CMakeLists.txt | 6 +-- server/Service/include/update_service.h | 14 ++++-- server/Service/src/server_implementation.cpp | 4 +- server/Service/src/update_service.cpp | 49 ++++++++++++++++++-- 9 files changed, 112 insertions(+), 31 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index aa07c78..d22996c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,4 +5,4 @@ project(test-server) add_subdirectory(server) add_subdirectory(proto) -add_subdirectory(client) +add_subdirectory(client/client) diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt index 79d0467..0675005 100644 --- a/client/client/CMakeLists.txt +++ b/client/client/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(Service LANGUAGES CXX) +project(Client LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -28,4 +28,9 @@ target_include_directories(Client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR} +) + +add_executable(client main.cpp) +target_link_libraries(client + Client ) \ No newline at end of file diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index a476f40..9ee5771 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -1,20 +1,42 @@ #ifndef UPDATE_REQUESTS_H #define UPDATE_REQUESTS_H -#include #include #include +#include +#include "common_client_call.h" using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::CompletionQueue; using Efficio_proto::Update; using Efficio_proto::Note; using Efficio_proto::Project; using Efficio_proto::Storage; +using Efficio_proto::GetProjectRequest; +using Efficio_proto::GetProjectResponse; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; + class UpdateRequests { public: + + + + class GetProjectClientCall final : public CommonClientCall { + GetProjectResponse response; + std::unique_ptr> response_reader; + public: + void Proceed(bool ok) override; + + GetProjectClientCall(const GetProjectRequest& request, + CompletionQueue* cq_, + std::unique_ptr& stub_); + }; + + class GetNoteClientCall; - class GetProjectClientCall; class CreateNoteClientCall; class CreateProjectClientCall; class TryJoinProjectClientCall; @@ -27,9 +49,10 @@ class UpdateRequests { - explicit UpdateRequests(std::shared_ptr channel): - stub_(Update::NewStub(channel)) {}; + explicit UpdateRequests(std::shared_ptr channel, CompletionQueue *cq): + stub_(Update::NewStub(channel)), cq_(cq) {}; private: + CompletionQueue* cq_; std::unique_ptr stub_; diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index 8ac50a2..b652901 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -1,8 +1,10 @@ #include "client_implementation.h" +#include "update_requests.h" #include #include #include #include +#include using grpc::Channel; @@ -12,7 +14,12 @@ using grpc::CompletionQueue; using grpc::Status; ClientImplementation::ClientImplementation(std::shared_ptr channel) : - channel_(channel) { }; + channel_(channel) { + std::thread t(&ClientImplementation::CompleteRpc, this); + UpdateRequests update_requests(channel, &cq_); + update_requests.get_project(nullptr); + t.join(); +}; void ClientImplementation::CompleteRpc() { diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index f0275cc..5634b0d 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -16,12 +16,7 @@ using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::Update; -class UpdateRequests::GetProjectClientCall : public CommonClientCall { - GetProjectResponse response; - std::unique_ptr> response_reader; - - void Proceed(bool ok) override { - +void UpdateRequests::GetProjectClientCall::Proceed(bool ok) { if (ok && status.ok()) { if (response.has_error_string()) { std::cout << response.error_string() << std::endl; @@ -30,12 +25,16 @@ class UpdateRequests::GetProjectClientCall : public CommonClientCall { } } } -public: - GetProjectClientCall(const GetProjectRequest& request, - CompletionQueue& cq_, + +UpdateRequests::GetProjectClientCall::GetProjectClientCall(const GetProjectRequest& request, + CompletionQueue* cq_, std::unique_ptr& stub_) : CommonClientCall() { - response_reader = stub_->AsyncGetProject(&context, request, &cq_); + response_reader = stub_->AsyncGetProject(&context, request, cq_); response_reader->Finish(&response, &status, (void*)this); } -}; \ No newline at end of file + +bool UpdateRequests::get_project(Project *project) { + GetProjectRequest request; + new GetProjectClientCall(request, cq_, stub_); + } diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 9733a05..75dc5d8 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,8 +1,8 @@ cmake_minimum_required(VERSION 3.16) -project(server) +project(Server) add_subdirectory(Service) -add_executable(main main.cpp) +add_executable(server main.cpp) -target_link_libraries(main Service) \ No newline at end of file +target_link_libraries(server Service) \ No newline at end of file diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index aec6a0a..f3b9bb2 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -12,6 +12,8 @@ using grpc::ServerAsyncResponseWriter; using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; +using Efficio_proto::GetProjectRequest; +using Efficio_proto::GetProjectResponse; using Efficio_proto::Update; using Efficio_proto::CreateNoteRequest; @@ -34,11 +36,13 @@ class UpdateService final { }; class GetProjectServerCall final : public CommonServerCall { + GetProjectRequest request_; + GetProjectResponse response_; + ServerAsyncResponseWriter responder_; + Update::AsyncService *service_; public: - explicit GetProjectServerCall(UpdateService& service); - void Proceed(bool ok) override; - - // TODO: finish this call + explicit GetProjectServerCall(Update::AsyncService* service, ServerCompletionQueue *cq); + void Proceed(bool) override; }; class TryJoinProjectServerCall; @@ -54,7 +58,7 @@ class UpdateService final { }; Update::AsyncService& get_service(); - + void run(); }; diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index fff6c2c..cf317bd 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -7,14 +7,14 @@ void ServerImplementation::Run(const uint16_t port) { grpc::ServerBuilder builder; cq_ = builder.AddCompletionQueue(); builder.AddListeningPort( - "0.0.0.0:" + std::to_string(port), grpc::InsecureServerCredentials() + "localhost:" + std::to_string(port), grpc::InsecureServerCredentials() ); UpdateService update_service(cq_.get()); builder.RegisterService(&update_service.get_service()); - server_ = builder.BuildAndStart(); + update_service.run(); HandleRPCs(); } diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 06d2d29..a1628a3 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -1,13 +1,20 @@ #include "update_service.h" #include "common_server_call.h" +using grpc::ServerAsyncResponseWriter; + using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; +using Efficio_proto::Project; -using grpc::ServerAsyncResponseWriter; -UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq) { - new GetNoteServerCall(&service_, cq_); +UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq), service_() { + // new GetNoteServerCall(&service_, cq_); + +} + +void UpdateService::run() { + new GetProjectServerCall(&service_, cq_); } UpdateService::GetNoteServerCall::GetNoteServerCall(Update::AsyncService *service, ServerCompletionQueue *cq) @@ -47,6 +54,42 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { } } +UpdateService::GetProjectServerCall::GetProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq +) : CommonServerCall(cq), responder_(&ctx_), service_(service) +{ + this->Proceed(true); +} + +void UpdateService::GetProjectServerCall::Proceed(const bool ok = true) { + switch (status_) { + case CREATE: { + status_ = PROCESS; + service_->RequestGetProject(&ctx_,&request_,&responder_, cq_, cq_, this); + std::cout << "get project start listening" << std::endl; + break; + } + case PROCESS: { + status_ = FINISH; + std::cout << "get project get request" << std::endl; + new GetProjectServerCall(service_, cq_); + auto *project = new Project; + project->set_title("Yoo!"); + response_.set_allocated_project(project); + responder_.Finish(response_, grpc::Status::OK, this); + break; + } case FINISH: { + if (status_ == FINISH) { + delete this; + } else { + std::cout << "get project why not finish" << std::endl; + } + } + } +} + + Update::AsyncService& UpdateService::get_service() { return service_; } \ No newline at end of file From 626d5c917429a3f88d98c2a4d7d769c18b0a8070 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Thu, 17 Apr 2025 02:20:32 +0300 Subject: [PATCH 15/47] feat: implement server calls with DB integration --- CMakeLists.txt | 1 + client/CMakeLists.txt | 8 +- client/client/include/update_requests.h | 5 +- client/client/src/update_requests.cpp | 1 - client/database/CMakeLists.txt | 21 --- client/database/include/database_manager.hpp | 22 --- client/database/include/lr_dao.hpp | 19 --- client/database/include/note_dao.hpp | 24 --- client/database/include/project_dao.hpp | 18 -- client/database/include/serialization.hpp | 13 -- client/database/src/database_manager.cpp | 80 --------- client/database/src/lr_dao.cpp | 81 --------- client/database/src/note_dao.cpp | 120 ------------- client/database/src/project_dao.cpp | 64 ------- client/database/src/serialization.cpp | 41 ----- client/main.cpp | 22 +-- client/scripts/CMakeLists.txt | 17 -- client/scripts/include/note.hpp | 46 ----- client/scripts/include/project.hpp | 32 ---- client/scripts/include/storage.hpp | 27 --- client/scripts/include/user.hpp | 29 ---- client/scripts/src/note.cpp | 68 -------- client/scripts/src/project.cpp | 51 ------ client/scripts/src/storage.cpp | 46 ----- client/scripts/src/user.cpp | 39 ----- .../ui/authorization-windows/CMakeLists.txt | 43 ----- .../include/login_window.h | 32 ---- .../include/login_window_style_sheet.h | 60 ------- .../include/registration_window.h | 31 ---- .../include/registration_window_style_sheet.h | 58 ------- .../login_window_ru_RU.ts | 3 - .../src/login_window.cpp | 118 ------------- .../src/registration_window.cpp | 154 ----------------- .../authorization-windows/ui/login_window.ui | 131 --------------- .../ui/registration_window.ui | 99 ----------- client/ui/main-window/CMakeLists.txt | 39 ----- client/ui/main-window/MainWindow_en_US.ts | 3 - .../main-window/include/applicationwindow.h | 16 -- client/ui/main-window/include/bottombar.h | 24 --- .../main-window/include/main_window_style.hpp | 139 ---------------- client/ui/main-window/include/mainwindow.h | 43 ----- client/ui/main-window/include/notelist.h | 29 ---- client/ui/main-window/include/notewidget.h | 31 ---- client/ui/main-window/include/projectitem.h | 19 --- client/ui/main-window/include/projectlist.h | 22 --- .../ui/main-window/src/applicationwindow.cpp | 14 -- client/ui/main-window/src/bottombar.cpp | 31 ---- client/ui/main-window/src/mainwindow.cpp | 113 ------------- client/ui/main-window/src/notelist.cpp | 66 -------- client/ui/main-window/src/notewidget.cpp | 53 ------ client/ui/main-window/src/projectitem.cpp | 12 -- client/ui/main-window/src/projectlist.cpp | 24 --- client/ui/note-widget/CMakeLists.txt | 4 +- .../ui/note-widget/include/note_edit_dialog.h | 9 +- .../include/note_edit_dialog_styles.h | 2 +- client/ui/note-widget/include/tags_dialog.h | 3 +- .../ui/note-widget/src/note_edit_dialog.cpp | 75 ++++++--- client/ui/note-widget/src/tags_dialog.cpp | 23 ++- server/CMakeLists.txt | 4 +- server/Service/CMakeLists.txt | 4 +- server/Service/include/update_service.h | 5 +- server/Service/src/server_implementation.cpp | 6 +- server/Service/src/update_service.cpp | 64 ++++++- server/database/CMakeLists.txt | 2 +- server/database/include/database_manager.hpp | 15 +- server/database/include/lr_dao.hpp | 19 --- server/database/include/note_dao.hpp | 16 +- server/database/include/project_dao.hpp | 18 -- server/database/include/serialization.hpp | 13 -- server/database/src/database_manager.cpp | 61 +++---- server/database/src/lr_dao.cpp | 81 --------- server/database/src/note_dao.cpp | 157 ++++++++---------- server/database/src/project_dao.cpp | 64 ------- server/database/src/serialization.cpp | 41 ----- server/main.cpp | 2 + 75 files changed, 257 insertions(+), 2733 deletions(-) delete mode 100644 client/database/CMakeLists.txt delete mode 100644 client/database/include/database_manager.hpp delete mode 100644 client/database/include/lr_dao.hpp delete mode 100644 client/database/include/note_dao.hpp delete mode 100644 client/database/include/project_dao.hpp delete mode 100644 client/database/include/serialization.hpp delete mode 100644 client/database/src/database_manager.cpp delete mode 100644 client/database/src/lr_dao.cpp delete mode 100644 client/database/src/note_dao.cpp delete mode 100644 client/database/src/project_dao.cpp delete mode 100644 client/database/src/serialization.cpp delete mode 100644 client/scripts/CMakeLists.txt delete mode 100644 client/scripts/include/note.hpp delete mode 100644 client/scripts/include/project.hpp delete mode 100644 client/scripts/include/storage.hpp delete mode 100644 client/scripts/include/user.hpp delete mode 100644 client/scripts/src/note.cpp delete mode 100644 client/scripts/src/project.cpp delete mode 100644 client/scripts/src/storage.cpp delete mode 100644 client/scripts/src/user.cpp delete mode 100644 client/ui/authorization-windows/CMakeLists.txt delete mode 100644 client/ui/authorization-windows/include/login_window.h delete mode 100644 client/ui/authorization-windows/include/login_window_style_sheet.h delete mode 100644 client/ui/authorization-windows/include/registration_window.h delete mode 100644 client/ui/authorization-windows/include/registration_window_style_sheet.h delete mode 100644 client/ui/authorization-windows/login_window_ru_RU.ts delete mode 100644 client/ui/authorization-windows/src/login_window.cpp delete mode 100644 client/ui/authorization-windows/src/registration_window.cpp delete mode 100644 client/ui/authorization-windows/ui/login_window.ui delete mode 100644 client/ui/authorization-windows/ui/registration_window.ui delete mode 100644 client/ui/main-window/CMakeLists.txt delete mode 100644 client/ui/main-window/MainWindow_en_US.ts delete mode 100644 client/ui/main-window/include/applicationwindow.h delete mode 100644 client/ui/main-window/include/bottombar.h delete mode 100644 client/ui/main-window/include/main_window_style.hpp delete mode 100644 client/ui/main-window/include/mainwindow.h delete mode 100644 client/ui/main-window/include/notelist.h delete mode 100644 client/ui/main-window/include/notewidget.h delete mode 100644 client/ui/main-window/include/projectitem.h delete mode 100644 client/ui/main-window/include/projectlist.h delete mode 100644 client/ui/main-window/src/applicationwindow.cpp delete mode 100644 client/ui/main-window/src/bottombar.cpp delete mode 100644 client/ui/main-window/src/mainwindow.cpp delete mode 100644 client/ui/main-window/src/notelist.cpp delete mode 100644 client/ui/main-window/src/notewidget.cpp delete mode 100644 client/ui/main-window/src/projectitem.cpp delete mode 100644 client/ui/main-window/src/projectlist.cpp delete mode 100644 server/database/include/lr_dao.hpp delete mode 100644 server/database/include/project_dao.hpp delete mode 100644 server/database/include/serialization.hpp delete mode 100644 server/database/src/lr_dao.cpp delete mode 100644 server/database/src/project_dao.cpp delete mode 100644 server/database/src/serialization.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index aa07c78..1e46327 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,3 +6,4 @@ project(test-server) add_subdirectory(server) add_subdirectory(proto) add_subdirectory(client) +add_subdirectory(server/database) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 3b06231..98bb415 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -11,10 +11,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) -add_subdirectory(scripts) -add_subdirectory(database) -add_subdirectory(ui/main-window) -add_subdirectory(ui/authorization-windows) add_subdirectory(ui/note-widget) add_subdirectory(client) add_executable(EfficioTaskTracker main.cpp) @@ -25,8 +21,6 @@ target_link_libraries(EfficioTaskTracker PRIVATE Qt6::Gui Qt6::Sql Database - Scripts - AuthorizationWindows - MainWindow NoteWidget + model-proto ) diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index a476f40..5a5d9c7 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -11,6 +11,7 @@ using Efficio_proto::Update; using Efficio_proto::Note; using Efficio_proto::Project; using Efficio_proto::Storage; + class UpdateRequests { public: class GetNoteClientCall; @@ -25,14 +26,10 @@ class UpdateRequests { bool create_project(Project *project); bool try_join_project(Project *project); - - explicit UpdateRequests(std::shared_ptr channel): stub_(Update::NewStub(channel)) {}; private: std::unique_ptr stub_; - - }; diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 95f0206..b1df4f6 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -3,7 +3,6 @@ #include #include - using grpc::Channel; using grpc::ClientAsyncResponseReader; using grpc::ClientContext; diff --git a/client/database/CMakeLists.txt b/client/database/CMakeLists.txt deleted file mode 100644 index 27bb557..0000000 --- a/client/database/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(Database LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 COMPONENTS Core Sql REQUIRED) - -file(GLOB SOURCES "src/*.cpp") -file(GLOB HEADERS "include/*.hpp") - -add_library(Database STATIC ${SOURCES} ${HEADERS}) - -target_include_directories(Database - PUBLIC - $ - $ -) - -target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql Qt6::Widgets Scripts) \ No newline at end of file diff --git a/client/database/include/database_manager.hpp b/client/database/include/database_manager.hpp deleted file mode 100644 index 1bba4b6..0000000 --- a/client/database/include/database_manager.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DATABASE_MANAGER_HPP -#define DATABASE_MANAGER_HPP - -#include - -class DatabaseManager final { -public: - static DatabaseManager &get_instance(); - bool execute_query( - QSqlQuery &query, - const QString &query_str, - const QVariantList ¶ms = {} - ) const; - [[nodiscard]] QSqlDatabase get_database() const; - -private: - explicit DatabaseManager(); - ~DatabaseManager(); - QSqlDatabase database_; -}; - -#endif // DATABASE_MANAGER_HPP \ No newline at end of file diff --git a/client/database/include/lr_dao.hpp b/client/database/include/lr_dao.hpp deleted file mode 100644 index de8d5fb..0000000 --- a/client/database/include/lr_dao.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LRDAO_H -#define LRDAO_H - -#include "database_manager.hpp" - -class LRDao { -public: - LRDao() = default; - static int try_register_user(const QString &login, const QString &password); - static bool validate_user(const QString &login, const QString &password); - static bool add_project_to_user(std::string user_login, int project_id); - static bool - get_user_projects(const std::string &login, std::vector &projects); - -private: - static QString hash_password(const QString &password); -}; - -#endif // LRDAO_H \ No newline at end of file diff --git a/client/database/include/note_dao.hpp b/client/database/include/note_dao.hpp deleted file mode 100644 index 0fbe980..0000000 --- a/client/database/include/note_dao.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef NOTEDAO_HPP -#define NOTEDAO_HPP - -#include -#include -#include "note.hpp" - -using namespace project_storage_model; - -class NoteDao { -public: - NoteDao() = default; - static bool initialize_note(int &id); - static bool update_note(const Note ¬e); - static bool delete_note(int id); - [[nodiscard]] static std::vector get_all_notes(); - [[nodiscard]] static Note get_note_by_id(int id); - -private: - static QString convert_string_set_to_postgres_array(const std::unordered_set& string_set); - static QString convert_tags_to_postgres_array(const std::vector& tags); -}; - -#endif // NOTEDAO_HPP \ No newline at end of file diff --git a/client/database/include/project_dao.hpp b/client/database/include/project_dao.hpp deleted file mode 100644 index a038af6..0000000 --- a/client/database/include/project_dao.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PROJECT_DAO_HPP -#define PROJECT_DAO_HPP - -#include "database_manager.hpp" -#include "project.hpp" - -namespace DB { - -class ProjectDAO { -public: - ProjectDAO() = default; - static bool create_project(const std::string &name, int &id); - static bool get_project(int id, std::string &name, std::vector ¬es); - static bool add_note_to_project(int project_id, int note_id); -}; -} // namespace DB - -#endif \ No newline at end of file diff --git a/client/database/include/serialization.hpp b/client/database/include/serialization.hpp deleted file mode 100644 index f9b0def..0000000 --- a/client/database/include/serialization.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SERIALIZATION_HPP -#define SERIALIZATION_HPP -#include "storage.hpp" - -class Serialization { -public: - static bool get_storage( - project_storage_model::Storage &storage, - const std::string &login - ); -}; - -#endif \ No newline at end of file diff --git a/client/database/src/database_manager.cpp b/client/database/src/database_manager.cpp deleted file mode 100644 index ad5b9ec..0000000 --- a/client/database/src/database_manager.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "database_manager.hpp" -#include -#include -#include - -DatabaseManager::DatabaseManager() { - database_ = QSqlDatabase::addDatabase("QPSQL"); - database_.setHostName("localhost"); - database_.setPort(5432); - database_.setDatabaseName("efficio"); - database_.setUserName("efficio"); - database_.setPassword("admin"); - database_.open(); - - QSqlQuery query(database_); - query.exec( - "CREATE TABLE IF NOT EXISTS notes (" - "id SERIAL PRIMARY KEY, " - "title TEXT NOT NULL, " - "content TEXT, " - "members VARCHAR(50)[], " - "date VARCHAR(50), " - "tags VARCHAR(50)[]" - ")" - ); - - query.exec( - "CREATE TABLE IF NOT EXISTS users (" - "login VARCHAR(50) PRIMARY KEY, " - "password VARCHAR(50) NOT NULL, " - "projects INT[]" - ")" - ); - - query.exec( - "CREATE TABLE IF NOT EXISTS projects(" - "id SERIAL PRIMARY KEY, " - "name VARCHAR(50) NOT NULL, " - "notes INT[]" - ")" - ); -} - -DatabaseManager::~DatabaseManager() { - if (database_.isOpen()) { - database_.close(); - } -} - -DatabaseManager &DatabaseManager::get_instance() { - static DatabaseManager instance; - if (!instance.database_.isOpen() && !instance.database_.open()) { - throw std::runtime_error("Lost connection with database"); - } - return instance; -} - -bool DatabaseManager::execute_query( - QSqlQuery &query, - const QString &query_str, - const QVariantList ¶ms -) const { - QSqlQuery temp(database_); - query = std::move(temp); - - if (!query.prepare(query_str)) { - qDebug() << "Prepare error:" << query.lastError(); - return false; - } - - for (int i = 0; i < params.size(); i++) { - query.bindValue(i, params[i]); - } - - return query.exec(); -} - -QSqlDatabase DatabaseManager::get_database() const { - return database_; -} \ No newline at end of file diff --git a/client/database/src/lr_dao.cpp b/client/database/src/lr_dao.cpp deleted file mode 100644 index 3107f5b..0000000 --- a/client/database/src/lr_dao.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "lr_dao.hpp" -#include -#include -#include -#include -#include - -QString LRDao::hash_password(const QString &password) { - const QByteArray hash = - QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256); - return hash.toHex(); -} - -int LRDao::try_register_user(const QString &login, const QString &password) { - QSqlQuery query; - - const bool is_login_free = DatabaseManager::get_instance().execute_query( - query, "SELECT * FROM users WHERE login = ?", {login} - ); - - if (!is_login_free) { - return -1; - } - - QSqlQuery insert_query; - return DatabaseManager::get_instance().execute_query( - insert_query, "INSERT INTO users (login, password) VALUES (?, ?)", - {login, password} - ); -} - -bool LRDao::validate_user(const QString &login, const QString &password) { - QSqlQuery query; - - const QString query_str = - "SELECT * FROM users WHERE login = ? AND password = ?"; - const QVariantList params = {login, password}; - - const auto success = - DatabaseManager::get_instance().execute_query(query, query_str, params); - - return query.next() && success; -} - -bool LRDao::add_project_to_user(std::string user_login, int project_id) { - QSqlQuery query; - const QString query_str = - "UPDATE users SET projects = array_append(projects, ?)" - "WHERE login = ?"; - - const QVariantList params = { - QString::fromStdString(std::to_string(project_id)), - QString::fromStdString(user_login)}; - - return DatabaseManager::get_instance().execute_query( - query, query_str, params - ); -} - -bool LRDao::get_user_projects( - const std::string &login, - std::vector &projects -) { - QSqlQuery query; - const QString query_str = - "SELECT array_to_string(projects, ',') FROM users WHERE login = ?"; - - const QVariantList params = {QString::fromStdString(login)}; - const bool is_success = - DatabaseManager::get_instance().execute_query(query, query_str, params); - - if (is_success && query.next() && query.value(0).isValid()) { - QStringList ps = query.value(0).toString().split(","); - for (auto i : ps) { - projects.push_back(i.toInt()); - } - - return true; - } - return false; -} \ No newline at end of file diff --git a/client/database/src/note_dao.cpp b/client/database/src/note_dao.cpp deleted file mode 100644 index 369f192..0000000 --- a/client/database/src/note_dao.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "note_dao.hpp" -#include -#include "database_manager.hpp" - -bool NoteDao::initialize_note(int &id) { - QSqlQuery query; - const auto is_successful = DatabaseManager::get_instance().execute_query( - query, - "INSERT INTO notes (title, content) " - "VALUES ('Пустая заметка', '')" - "RETURNING id" - ); - if (is_successful && query.next()) { - id = query.value(0).toInt(); - return true; - } - return false; -} - -bool NoteDao::update_note(const Note ¬e) { - QSqlQuery query; - - const QString sql_query = - "UPDATE notes SET " - "title = :title, " - "content = :content, " - "members = :members, " - "date = :date, " - "tags = :tags " - "WHERE id = :id"; - const QVariantList params = { - QString::fromStdString(note.get_title()), - QString::fromStdString(note.get_text()), - convert_string_set_to_postgres_array(note.get_members()), - QString::fromStdString(note.get_date()), - convert_tags_to_postgres_array(note.get_tags()), - note.get_id() - }; - - return DatabaseManager::get_instance().execute_query( - query, sql_query, params - ); -} - -bool NoteDao::delete_note(int id) { - QSqlQuery query; - const QString sql_query = "DELETE FROM notes WHERE id = :id"; - return DatabaseManager::get_instance().execute_query( - query, sql_query, {id} - ); -} - -std::vector NoteDao::get_all_notes() { - QSqlQuery query; - std::vector notes; - DatabaseManager::get_instance().execute_query(query, "SELECT * FROM notes"); - - while (query.next()) { - auto id = query.value("id").toUInt(); - auto title = query.value("title").toString().toStdString(); - auto text = query.value("content").toString().toStdString(); - notes.emplace_back(id, title, text); - } - - return notes; -} - -Note NoteDao::get_note_by_id(int id) { - QSqlQuery query; - DatabaseManager::get_instance().execute_query( - query, "SELECT title, content, array_to_string(tags, ','), date, array_to_string(members, ',') FROM notes WHERE id = ?", {id} - ); - query.next(); - auto title = query.value(0).toString().toStdString(); - auto text = query.value(1).toString().toStdString(); - auto date = query.value(3).toString().toStdString(); - Note result{id, title, text}; - auto tags_list = query.value(2).toString().split(","); - - if (!tags_list.empty() && tags_list[0] != "") { - for (const auto& tag_str : tags_list) { - - result.add_tag(tag_str.split(':')[0].toStdString(), tag_str.split(':')[1].toStdString()); - } - } - auto member_list = query.value(4).toString().split(","); - if (!member_list.empty() && member_list[0] != "") { - for (const auto& member : member_list) { - result.add_member(member.toStdString()); - } - } - - return result; -} - -QString NoteDao::convert_string_set_to_postgres_array(const std::unordered_set& string_set) { - if (string_set.empty()) { - return "{}"; - } - - QStringList buffer; - for (const auto& item : string_set) { - buffer << QString::fromStdString(item).replace(",", "\\,"); - } - return "{" + buffer.join(",") + "}"; -} - -QString NoteDao::convert_tags_to_postgres_array(const std::vector& tags) { - if (tags.empty()) { - return "{}"; - } - - QStringList buffer; - for (const auto &[name, color] : tags) { - buffer << QString("\"%1:%2\"") - .arg(QString::fromStdString(name).replace("\"", "\\\""), - QString::fromStdString(color).replace("\"", "\\\"")); - } - return "{" + buffer.join(",") + "}"; -} diff --git a/client/database/src/project_dao.cpp b/client/database/src/project_dao.cpp deleted file mode 100644 index 4370f4d..0000000 --- a/client/database/src/project_dao.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "project_dao.hpp" -#include -#include -#include -#include "database_manager.hpp" - -namespace DB { - -bool ProjectDAO::create_project(const std::string &name, int &id) { - // todo thread-safety - QSqlQuery query; - const QString query_str = - "INSERT INTO projects (name)" - " VALUES (:name)" - "RETURNING id"; - - const QVariantList params = {QString::fromStdString(name)}; - - bool is_successful = - DatabaseManager::get_instance().execute_query(query, query_str, params); - if (is_successful && query.next()) { - id = query.value(0).toInt(); - return true; - } - return false; -} - -bool ProjectDAO::get_project( - const int id, - std::string &name, - std::vector ¬es -) { - QSqlQuery query; - const QString query_str = - "SELECT name, array_to_string(notes, ',') FROM projects " - "WHERE id = ?"; - const QVariantList params = {id}; - const bool is_successful = - DatabaseManager::get_instance().execute_query(query, query_str, params); - - if (is_successful && query.next()) { - name = query.value("name").toString().toStdString(); - - for (const auto& i : query.value(1).toString().split(",")) { - notes.push_back(i.toInt()); - } - return true; - } - return false; -} - -bool ProjectDAO::add_note_to_project(const int project_id, const int note_id) { - QSqlQuery query; - const QString query_str = - "UPDATE projects " - "SET notes = array_append(notes, ?)" - "WHERE id = ?"; - const QVariantList params = {note_id, project_id}; - return DatabaseManager::get_instance().execute_query( - query, query_str, params - ); -} - -} // namespace DB \ No newline at end of file diff --git a/client/database/src/serialization.cpp b/client/database/src/serialization.cpp deleted file mode 100644 index 17e687e..0000000 --- a/client/database/src/serialization.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "serialization.hpp" -#include -#include -#include -#include "lr_dao.hpp" -#include "note.hpp" -#include "note_dao.hpp" -#include "project.hpp" -#include "project_dao.hpp" -#include "storage.hpp" - -bool Serialization::get_storage( - project_storage_model::Storage &storage, - const std::string &login -) { - std::vector projects; - if (LRDao::get_user_projects(login, projects)) { - for (auto p : projects) { - std::string project_name; - std::vector notes; - if (DB::ProjectDAO::get_project(p, project_name, notes)) { - Project project{p, project_name, ""}; - for (auto n : notes) { - if (n != 0) { - auto note = NoteDao::get_note_by_id(n); - project.add_note(note); - } - } - storage.add_project(Project(project)); - } else { - - return false; - } - } - } else { - - - return false; - } - return true; -} \ No newline at end of file diff --git a/client/main.cpp b/client/main.cpp index 8af3d0d..51b956c 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,34 +1,24 @@ #include #include -#include -#include +#include #include -#include "applicationwindow.h" -#include "login_window.h" -#include "mainwindow.h" +#include "note_edit_dialog.h" int main(int argc, char *argv[]) { QApplication application(argc, argv); QTranslator translator; const QStringList ui_languages = QLocale::system().uiLanguages(); + for (const QString &locale : ui_languages) { - const QString base_name = "MainWindow_" + QLocale(locale).name(); + const QString base_name = "NoteWidgetEfficio_" + QLocale(locale).name(); if (translator.load(":/i18n/" + base_name)) { QApplication::installTranslator(&translator); break; } } - auto *app_window = new Ui::ApplicationWindow("EFFICIO"); - auto *login_window = new LoginWindow(app_window); - - app_window->setCentralWidget(login_window); - const QRect screen_geometry = QApplication::primaryScreen()->availableGeometry(); - const int x = (screen_geometry.width() - login_window->width()) / 2; - const int y = (screen_geometry.height() - login_window->height()) / 2; - app_window->move(x, y); - app_window->show(); - + NoteEditDialog dialog(nullptr, new Note); + dialog.show(); return QApplication::exec(); } \ No newline at end of file diff --git a/client/scripts/CMakeLists.txt b/client/scripts/CMakeLists.txt deleted file mode 100644 index 73ccd8c..0000000 --- a/client/scripts/CMakeLists.txt +++ /dev/null @@ -1,17 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(Scripts LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -file(GLOB SOURCES "src/*.cpp") -file(GLOB HEADERS "include/*.hpp") - -add_library(Scripts STATIC ${SOURCES} ${HEADERS}) - -target_include_directories(Scripts - PUBLIC - $ - $ -) \ No newline at end of file diff --git a/client/scripts/include/note.hpp b/client/scripts/include/note.hpp deleted file mode 100644 index e54e01a..0000000 --- a/client/scripts/include/note.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef NOTE_HPP -#define NOTE_HPP - -#include -#include -#include - -namespace project_storage_model { - -class Note { -public: - struct Tag { - std::string name; - std::string color; - }; - - Note(int id, std::string title, std::string text); - - [[nodiscard]] int get_id() const; - [[nodiscard]] const std::string &get_title() const; - [[nodiscard]] const std::string &get_text() const; - [[nodiscard]] const std::string &get_date() const; - [[nodiscard]] const std::vector &get_tags() const; - [[nodiscard]] const std::unordered_set &get_members() const; - - void set_title(const std::string &title); - void set_text(const std::string &text); - void add_tag(const std::string &tag, const std::string &color = "#e7624b"); - void set_date(const std::string &date); - void add_member(const std::string &member); - void remove_tag(const std::string &tag); - void clear_tags(); - void clear_members(); - -private: - int id_; - std::string title_; - std::string text_; - std::string date_; - std::vector tags_; - std::unordered_set members_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/client/scripts/include/project.hpp b/client/scripts/include/project.hpp deleted file mode 100644 index ad83932..0000000 --- a/client/scripts/include/project.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef PROJECT_HPP -#define PROJECT_HPP - -#include -#include -#include "note.hpp" - -namespace project_storage_model { - -class Project { -public: - Project(int id, const std::string &name, const std::string &description); - - [[nodiscard]] int get_id() const; - [[nodiscard]] const std::string &get_name() const; - [[nodiscard]] const std::string &get_description() const; - const std::list &get_notes() const; - - Note &add_note(const Note ¬e); - void remove_note(int note_id); - void edit_description(const std::string &description); - -private: - int id_; - std::string name_; - std::string description_; - std::list notes_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/client/scripts/include/storage.hpp b/client/scripts/include/storage.hpp deleted file mode 100644 index da6f948..0000000 --- a/client/scripts/include/storage.hpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef STORAGE_HPP -#define STORAGE_HPP - -#include -#include "project.hpp" -#include "user.hpp" - -namespace project_storage_model { - -class Storage { -public: - Project &add_project(const Project &project); - void remove_project(int project_id); - std::list &get_projects(); - - User &add_user(const User &user); - void remove_user(int user_id); - const std::list &get_users() const; - -private: - std::list projects_; - std::list users_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/client/scripts/include/user.hpp b/client/scripts/include/user.hpp deleted file mode 100644 index 957fe63..0000000 --- a/client/scripts/include/user.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef USER_HPP -#define USER_HPP - -#include -#include -#include "project.hpp" - -namespace project_storage_model { - -class User { -public: - User(int id, const std::string &username); - - [[nodiscard]] int get_id() const; - [[nodiscard]] const std::string &get_username() const; - const std::list &get_projects() const; - - Project &add_project(const Project &project); - void remove_project(int project_id); - -private: - int id_; - std::string username_; - std::list projects_; -}; - -} // namespace project_storage_model - -#endif \ No newline at end of file diff --git a/client/scripts/src/note.cpp b/client/scripts/src/note.cpp deleted file mode 100644 index dcb8391..0000000 --- a/client/scripts/src/note.cpp +++ /dev/null @@ -1,68 +0,0 @@ -#include "note.hpp" -#include -#include - -namespace project_storage_model { - -Note::Note(const int id, std::string title, std::string text) - : id_(id), title_(std::move(title)), text_(std::move(text)) { -} - -[[nodiscard]] int Note::get_id() const { - return id_; -} - -[[nodiscard]] const std::string &Note::get_title() const { - return title_; -} - -[[nodiscard]] const std::string &Note::get_text() const { - return text_; -} - -const std::string &Note::get_date() const { - return date_; -} - -const std::vector &Note::get_tags() const { - return tags_; -} - -const std::unordered_set &Note::get_members() const { - return members_; -} - -void Note::set_title(const std::string &title) { - title_ = title; -} - -void Note::set_text(const std::string &text) { - text_ = text; -} - -void Note::add_tag(const std::string &tag, const std::string &color) { - tags_.push_back({tag, color}); -} - -void Note::set_date(const std::string &date) { - date_ = date; -} - -void Note::add_member(const std::string &member) { - members_.insert(member); -} - -void Note::remove_tag(const std::string &tag) { - tags_.erase(std::remove_if(tags_.begin(), tags_.end(), - [&tag](const Tag& t) { return t.name == tag; }), tags_.end()); -} - -void Note::clear_tags() { - tags_.clear(); -} - -void Note::clear_members() { - members_.clear(); -} - -} // namespace project_storage_model \ No newline at end of file diff --git a/client/scripts/src/project.cpp b/client/scripts/src/project.cpp deleted file mode 100644 index a55e555..0000000 --- a/client/scripts/src/project.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "../include/project.hpp" -#include - -namespace project_storage_model { - -Project::Project( - int id, - const std::string &name, - const std::string &description -) - : id_(std::move(id)), - name_(std::move(name)), - description_(std::move(description)) { -} - -[[nodiscard]] int Project::get_id() const { - return id_; -} - -[[nodiscard]] const std::string &Project::get_name() const { - return name_; -} - -[[nodiscard]] const std::string &Project::get_description() const { - return description_; -} - -const std::list &Project::get_notes() const { - return notes_; -} - -Note &Project::add_note(const Note ¬e) { - notes_.push_back(note); - return notes_.back(); -} - -void Project::remove_note(int note_id) { - notes_.erase( - std::remove_if( - notes_.begin(), notes_.end(), - [note_id](const Note ¬e) { return note.get_id() == note_id; } - ), - notes_.end() - ); -} - -void Project::edit_description(const std::string &description) { - this->description_ = description; -} - -} // namespace project_storage_model diff --git a/client/scripts/src/storage.cpp b/client/scripts/src/storage.cpp deleted file mode 100644 index e452e6b..0000000 --- a/client/scripts/src/storage.cpp +++ /dev/null @@ -1,46 +0,0 @@ -#include "../include/storage.hpp" -#include - -namespace project_storage_model { - -Project &Storage::add_project(const Project &project) { - projects_.push_back(project); - return projects_.back(); -} - -void Storage::remove_project(int project_id) { - projects_.erase( - std::remove_if( - projects_.begin(), projects_.end(), - [project_id](const Project &project) { - return project.get_id() == project_id; - } - ), - projects_.end() - ); -} - -std::list &Storage::get_projects() { - return projects_; -} - -User &Storage::add_user(const User &user) { - users_.push_back(user); - return users_.back(); -} - -void Storage::remove_user(int user_id) { - users_.erase( - std::remove_if( - users_.begin(), users_.end(), - [user_id](const User &user) { return user.get_id() == user_id; } - ), - users_.end() - ); -} - -const std::list &Storage::get_users() const { - return users_; -} - -} // namespace project_storage_model diff --git a/client/scripts/src/user.cpp b/client/scripts/src/user.cpp deleted file mode 100644 index e1cbb5f..0000000 --- a/client/scripts/src/user.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "../include/user.hpp" -#include - -namespace project_storage_model { - -User::User(int id, const std::string &username) - : id_(std::move(id)), username_(std::move(username)) { -} - -[[nodiscard]] int User::get_id() const { - return id_; -} - -[[nodiscard]] const std::string &User::get_username() const { - return username_; -} - -const std::list &User::get_projects() const { - return projects_; -} - -Project &User::add_project(const Project &project) { - projects_.push_back(project); - return projects_.back(); -} - -void User::remove_project(int project_id) { - projects_.erase( - std::remove_if( - projects_.begin(), projects_.end(), - [project_id](const Project &project) { - return project.get_id() == project_id; - } - ), - projects_.end() - ); -} - -} // namespace project_storage_model diff --git a/client/ui/authorization-windows/CMakeLists.txt b/client/ui/authorization-windows/CMakeLists.txt deleted file mode 100644 index e61f562..0000000 --- a/client/ui/authorization-windows/CMakeLists.txt +++ /dev/null @@ -1,43 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(AuthorizationWindows LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) - -set(UI_FILES - ${CMAKE_CURRENT_SOURCE_DIR}/ui/login_window.ui - ${CMAKE_CURRENT_SOURCE_DIR}/ui/registration_window.ui -) - -set(SOURCES - src/login_window.cpp - src/registration_window.cpp -) - -set(HEADERS - include/login_window.h - include/login_window_style_sheet.h - include/registration_window_style_sheet.h - include/registration_window.h -) - -file(GLOB TS_FILES "*.ts") - -add_library(AuthorizationWindows STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) - -target_include_directories(AuthorizationWindows - PUBLIC - $ - $ -) - -target_link_libraries(AuthorizationWindows PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database Scripts MainWindow) \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window.h b/client/ui/authorization-windows/include/login_window.h deleted file mode 100644 index 5aed300..0000000 --- a/client/ui/authorization-windows/include/login_window.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include "database_manager.hpp" -#include "lr_dao.hpp" - -QT_BEGIN_NAMESPACE - -namespace Ui { -class LoginWindow; -} - -QT_END_NAMESPACE - -class LoginWindow : public QWidget { - Q_OBJECT - -public: - explicit LoginWindow(QWidget *parent = nullptr); - ~LoginWindow(); - -private slots: - void on_switch_mode_clicked(); - void on_push_enter_clicked(); - -private: - Ui::LoginWindow *ui; -}; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window_style_sheet.h b/client/ui/authorization-windows/include/login_window_style_sheet.h deleted file mode 100644 index 3a6d776..0000000 --- a/client/ui/authorization-windows/include/login_window_style_sheet.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "ui_login_window.h" - -namespace Ui { -QString login_window_light_theme = R"( - QWidget { - background-color: #f5f5f5; - } - - QLabel { - font-family: 'Arial'; - font-weight: bold; - font-size: 13px; - color: #089083; - padding: 1px; - background-color: transparent; - } - - QPushButton#pushEnter { - font-family: 'Arial'; - font-weight: bold; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - font-weight: bold; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushEnter:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: #dadada; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid white; - background: white; - color: black; - padding: 5px; - } - - QLineEdit::placeholder { - color: #727272; - } - -)"; - -} // namespace Ui \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window.h b/client/ui/authorization-windows/include/registration_window.h deleted file mode 100644 index 35f4bc4..0000000 --- a/client/ui/authorization-windows/include/registration_window.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include -#include -#include -#include "database_manager.hpp" -#include "lr_dao.hpp" - -QT_BEGIN_NAMESPACE - -namespace Ui { -class RegistrationWindow; -} - -QT_END_NAMESPACE - -class RegistrationWindow : public QWidget { - Q_OBJECT - -public: - explicit RegistrationWindow(QWidget *parent = nullptr); - ~RegistrationWindow(); - bool is_strong_and_valid_password(const QString &password); - -private slots: - void on_switch_mode_clicked(); - void on_push_registration_clicked(); - -private: - Ui::RegistrationWindow *ui; -}; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window_style_sheet.h b/client/ui/authorization-windows/include/registration_window_style_sheet.h deleted file mode 100644 index dc7a1ca..0000000 --- a/client/ui/authorization-windows/include/registration_window_style_sheet.h +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "ui_registration_window.h" - -namespace Ui { -QString registration_window_light_theme = R"( - QWidget { - background-color: #f5f5f5; - } - - QLabel { - font-family: 'Arial'; - background-color: transparent; - font-size: 13px; - color: #089083; - padding: 1px; - } - - QPushButton#pushRegistration { - font-weight: bold; - font-family: 'Arial'; - border-radius: 8px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - } - - QPushButton#switchMode { - font-family: 'Arial'; - border-radius: 10px; - background-color: white; - color: #fea36b; - padding: 5px 10px; - } - - QPushButton#pushRegistration:hover { - background-color: #d58745; - } - - QPushButton#switchMode:hover { - background-color: #dadada; - } - - QLineEdit { - border-radius: 10px; - border: 1px solid white; - background: white; - color: black; - padding: 5px; - } - - QLineEdit::placeholder { - color: #727272; - } - -)"; - -} // namespace Ui \ No newline at end of file diff --git a/client/ui/authorization-windows/login_window_ru_RU.ts b/client/ui/authorization-windows/login_window_ru_RU.ts deleted file mode 100644 index 32bf76c..0000000 --- a/client/ui/authorization-windows/login_window_ru_RU.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/client/ui/authorization-windows/src/login_window.cpp b/client/ui/authorization-windows/src/login_window.cpp deleted file mode 100644 index 0583ef5..0000000 --- a/client/ui/authorization-windows/src/login_window.cpp +++ /dev/null @@ -1,118 +0,0 @@ -#include "login_window.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include "applicationwindow.h" -#include "bottombar.h" -#include "database_manager.hpp" -#include "login_window_style_sheet.h" -#include "lr_dao.hpp" -#include "mainwindow.h" -#include "notelist.h" -#include "registration_window.h" -#include "serialization.hpp" - -LoginWindow::LoginWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::LoginWindow) { - ui->setupUi(this); - - setFixedSize(380, 480); - ui->inputLogin->setPlaceholderText("Введите логин:"); - ui->inputPassword->setPlaceholderText("Введите пароль:"); - setStyleSheet(Ui::login_window_light_theme); - ui->inputPassword->setEchoMode(QLineEdit::Password); - - connect( - ui->switchMode, &QPushButton::clicked, this, - &LoginWindow::on_switch_mode_clicked - ); - connect( - ui->pushEnter, &QPushButton::clicked, this, - &LoginWindow::on_push_enter_clicked - ); -} - -LoginWindow::~LoginWindow() { - delete ui; -} - -void LoginWindow::on_switch_mode_clicked() { - QWidget *parent = this->parentWidget(); - - QMainWindow *app_window = qobject_cast(parent); - - if (QWidget *old = app_window->centralWidget()) { - old->deleteLater(); - } - project_storage_model::Storage storage; - RegistrationWindow *registration_window = - new RegistrationWindow(app_window); - - app_window->setCentralWidget(registration_window); - QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); - int x = (screenGeometry.width() - registration_window->width()) / 2; - int y = (screenGeometry.height() - registration_window->height()) / 2; - app_window->move(x, y); - - this->close(); -} - -void LoginWindow::on_push_enter_clicked() { - QString login = ui->inputLogin->text(); - QString password = ui->inputPassword->text(); - - if (!login.isEmpty() && !password.isEmpty()) { - if (login.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина логина не должна превышать пятидесяти символов" - ); - } else if (password.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина пароля не должна превышать пятидесяти символов" - ); - } else if (LRDao::validate_user(login, password)) { - QMessageBox::information( - this, "Вход", "Вы успешно вошли! Добро пожаловать :)" - ); - QWidget *parent = this->parentWidget(); - - QMainWindow *app_window = qobject_cast(parent); - - if (QWidget *old = app_window->centralWidget()) { - old->deleteLater(); - } - // todo load all projects of user to storage - project_storage_model::Storage *storage = - new project_storage_model::Storage(); - Serialization::get_storage(*storage, login.toStdString()); - - Ui::MainWindow *main_window = - new Ui::MainWindow(app_window, login.toStdString(), storage); - - app_window->setCentralWidget(main_window); - app_window->resize(800, 600); - QRect screenGeometry = - QApplication::primaryScreen()->availableGeometry(); - int x = (screenGeometry.width() - main_window->width()) / 2; - int y = (screenGeometry.height() - main_window->height()) / 2; - app_window->move(x, y); - - this->close(); - } else { - QMessageBox::warning( - this, "Ошибка ввода данных", "Неверный логин или пароль!" - ); - } - } else { - QMessageBox::warning( - this, "Ошибка ввода данных", "Пожалуйста, заполните все поля!" - ); - } -} \ No newline at end of file diff --git a/client/ui/authorization-windows/src/registration_window.cpp b/client/ui/authorization-windows/src/registration_window.cpp deleted file mode 100644 index f30f7d8..0000000 --- a/client/ui/authorization-windows/src/registration_window.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include "registration_window.h" -#include -#include -#include -#include -#include -#include -#include -#include "applicationwindow.h" -#include "bottombar.h" -#include "database_manager.hpp" -#include "login_window.h" -#include "lr_dao.hpp" -#include "mainwindow.h" -#include "notelist.h" -#include "registration_window.h" -#include "registration_window_style_sheet.h" - -RegistrationWindow::RegistrationWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::RegistrationWindow) { - ui->setupUi(this); - - setFixedSize(380, 480); - - ui->createLogin->setPlaceholderText("Введите логин:"); - ui->createPassword->setPlaceholderText("Введите пароль:"); - ui->repeatPassword->setPlaceholderText("Повторите пароль:"); - setStyleSheet(Ui::registration_window_light_theme); - ui->createPassword->setEchoMode(QLineEdit::Password); - ui->repeatPassword->setEchoMode(QLineEdit::Password); - - connect( - ui->pushRegistration, &QPushButton::clicked, this, - &RegistrationWindow::on_push_registration_clicked - ); - connect( - ui->switchMode, &QPushButton::clicked, this, - &RegistrationWindow::on_switch_mode_clicked - ); -} - -RegistrationWindow::~RegistrationWindow() { - delete ui; -} - -void RegistrationWindow::on_switch_mode_clicked() { - QWidget *parent = this->parentWidget(); - - QMainWindow *app_window = qobject_cast(parent); - - if (QWidget *old = app_window->centralWidget()) { - old->deleteLater(); - } - project_storage_model::Storage storage; - LoginWindow *login_window = new LoginWindow(app_window); - - app_window->setCentralWidget(login_window); - QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); - int x = (screenGeometry.width() - login_window->width()) / 2; - int y = (screenGeometry.height() - login_window->height()) / 2; - app_window->move(x, y); - - this->close(); -} - -bool RegistrationWindow::is_strong_and_valid_password(const QString &password) { - if (password.length() < 8) { - QMessageBox::warning( - nullptr, "Ошибка: недостаточно надежный пароль", - "Пароль должен содержать не менее восьми символов" - ); - return false; - } - - bool has_digit = false; - bool has_latin_letter = false; - - for (const QChar ch : password) { - if (!ch.isLetter() && !ch.isDigit()) { - QMessageBox::warning( - nullptr, "Ошибка", - "Пароль должен содержать только символы латиницы и цифры" - ); - return false; - } else if (ch.isLetter()) { - has_latin_letter = true; - } else if (ch.isDigit()) { - has_digit = true; - } - } - - if (!has_latin_letter) { - QMessageBox::warning( - nullptr, "Ошибка: недостаточно надежный пароль", - "Пароль должен содержать хотя бы одну букву" - ); - return false; - } - if (!has_digit) { - QMessageBox::warning( - nullptr, "Ошибка: недостаточно надежный пароль", - "Пароль должен содержать хотя бы одну цифру" - ); - return false; - } - - return true; -} - -void RegistrationWindow::on_push_registration_clicked() { - QString created_login = ui->createLogin->text(); - QString created_password = ui->createPassword->text(); - QString repeated_password = ui->repeatPassword->text(); - - if (!created_login.isEmpty() && !created_password.isEmpty() && - !repeated_password.isEmpty()) { - if (created_password != repeated_password) { - QMessageBox::warning(this, "Ошибка", "Пароли не совпадают!"); - } else if (created_login.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина логина не должна превышать пятидесяти символов" - ); - } else if (created_password.size() > 50) { - QMessageBox::warning( - this, "Ошибка", - "Длина пароля не должна превышать пятидесяти символов" - ); - } else if (is_strong_and_valid_password(created_password)) { - int try_register_user = - LRDao::try_register_user(created_login, created_password); - if (try_register_user == 0) { - QMessageBox::warning( - this, "Ошибка", - "Извините, разрабы дауны и не подключили толком бд." - ); - } else if (try_register_user == -1) { - QMessageBox::warning( - this, "Ошибка", - "Пользователь с таким именем уже существует. Пожалуйста, " - "придумайте другое!" - ); - } else { - QMessageBox::information( - this, "Регистрация", - "Вы успешно зарегистрировались! Пожалуйста, выполните вход." - ); - on_switch_mode_clicked(); // TODO: fix - } - } - } else { - QMessageBox::warning(this, "Ошибка", "Пожалуйста, заполните все поля."); - } -} \ No newline at end of file diff --git a/client/ui/authorization-windows/ui/login_window.ui b/client/ui/authorization-windows/ui/login_window.ui deleted file mode 100644 index 02ffce3..0000000 --- a/client/ui/authorization-windows/ui/login_window.ui +++ /dev/null @@ -1,131 +0,0 @@ - - - LoginWindow - - - - 0 - 0 - 362 - 600 - - - - Dialog - - - - - 0 - 0 - 375 - 501 - - - - - - 120 - 280 - 131 - 51 - - - - font: 700 13pt "Arial"; -border-radius: 10px; - - - Войти - - - - - - 140 - 130 - 91 - 41 - - - - font: 700 21pt "Arial"; - - - Вход - - - - - - 62 - 175 - 251 - 41 - - - - border-radius: 10px; - - - Логин - - - - - - 62 - 225 - 251 - 41 - - - - border-radius: 10px; - - - Пароль - - - - - - 80 - 340 - 211 - 31 - - - - font: 7pt "Arial"; -border-radius: 10px; - - - Еще нет аккаунта? Зарегистрируйтесь! - - - - - - - 0 - 0 - 362 - 26 - - - - - - - 0 - 0 - 16 - 25 - - - - - - - \ No newline at end of file diff --git a/client/ui/authorization-windows/ui/registration_window.ui b/client/ui/authorization-windows/ui/registration_window.ui deleted file mode 100644 index d0b20d4..0000000 --- a/client/ui/authorization-windows/ui/registration_window.ui +++ /dev/null @@ -1,99 +0,0 @@ - - - RegistrationWindow - - - - 0 - 0 - 390 - 562 - - - - Dialog - - - - - 90 - 290 - 201 - 51 - - - - font: 700 12pt "Arial"; -border-radius: 10px; - - - Зарегистрироваться - - - - - - 110 - 350 - 161 - 31 - - - - font: 7pt "Arial"; -border-radius: 10px; - - - Уже есть аккаунт? Войдите! - - - - - - 60 - 140 - 261 - 41 - - - - - - - 60 - 190 - 261 - 41 - - - - - - - 60 - 240 - 261 - 41 - - - - - - - 100 - 90 - 201 - 51 - - - - font: 700 19pt "Arial"; - - - Регистрация - - - - - - \ No newline at end of file diff --git a/client/ui/main-window/CMakeLists.txt b/client/ui/main-window/CMakeLists.txt deleted file mode 100644 index c8e1f85..0000000 --- a/client/ui/main-window/CMakeLists.txt +++ /dev/null @@ -1,39 +0,0 @@ -cmake_minimum_required(VERSION 3.16) - -project(MainWindow LANGUAGES CXX) - -set(CMAKE_CXX_STANDARD 20) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - -find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) - -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTORCC ON) - -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/*.cpp") -file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS "include/*.h") -file(GLOB UI_FILES CONFIGURE_DEPENDS "ui/*.ui") - -add_library(MainWindow STATIC - ${SOURCES} - ${HEADERS} - ${UI_FILES} -) - -target_include_directories(MainWindow PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_BINARY_DIR} -) - -target_link_libraries(MainWindow PRIVATE - Qt6::Widgets - Qt6::Core - Qt6::Gui - Qt6::Sql - Scripts - Database - NoteWidget -) \ No newline at end of file diff --git a/client/ui/main-window/MainWindow_en_US.ts b/client/ui/main-window/MainWindow_en_US.ts deleted file mode 100644 index a3740fb..0000000 --- a/client/ui/main-window/MainWindow_en_US.ts +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/client/ui/main-window/include/applicationwindow.h b/client/ui/main-window/include/applicationwindow.h deleted file mode 100644 index ccb14f8..0000000 --- a/client/ui/main-window/include/applicationwindow.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef APPLICATIONWINDOW_H -#define APPLICATIONWINDOW_H - -#include - -namespace Ui { -class ApplicationWindow : public QMainWindow { - Q_OBJECT - -public: - explicit ApplicationWindow(std::string window_name); - -signals: -}; -} // namespace Ui -#endif // APPLICATIONWINDOW_H \ No newline at end of file diff --git a/client/ui/main-window/include/bottombar.h b/client/ui/main-window/include/bottombar.h deleted file mode 100644 index cc7945c..0000000 --- a/client/ui/main-window/include/bottombar.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef BOTTOMBAR_H -#define BOTTOMBAR_H - -#include -#include -#include -#include - -namespace Ui { -class BottomBar : public QWidget { - QHBoxLayout *main_layout_; - QLabel *project_name_; - QLabel *username_; - -public: - BottomBar( - QWidget *parent_, - std::string username_, - std::string project_name_ - ); -}; -} // namespace Ui - -#endif // BOTTOMBAR_H \ No newline at end of file diff --git a/client/ui/main-window/include/main_window_style.hpp b/client/ui/main-window/include/main_window_style.hpp deleted file mode 100644 index 3e594c1..0000000 --- a/client/ui/main-window/include/main_window_style.hpp +++ /dev/null @@ -1,139 +0,0 @@ -#ifndef MAIN_WINDOW_STYLE_HPP -#define MAIN_WINDOW_STYLE_HPP -#include - -namespace Ui { - -QString main_window_style = R"( -#main-window { - background-color : #f5f5f5; -} - -#main-window QLabel { - font-weight: bold; -} - -QScrollBar:vertical { - border: none; - background: transparent; - width: 10px; - margin: 0; -} -QScrollBar::handle:vertical { - background: #c0c0c0; - border-radius: 5px; - min-height: 20px; -} - -QScrollBar::add-line:vertical, QScrollBar::sub-line:vertical, - QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical { - background: none; - height: 0px; - } -QScrollArea { - border: none; - background: transparent; -} - -#ProjectList { - background-color: white; - border-radius: 8px; - padding: 8px; - outline: 0; - font-family: 'Arial'; - font-weight: bold; - font-size: 14px; - color:rgb(44, 44, 44); -} - -#ProjectList::item { - background-color:rgb(207, 236, 233); - border: none; - padding: 10px; - margin: 2px; - border-radius: 6px; -} - -#ProjectList::item:hover { - background-color: rgb(178, 226, 221); -} - -#ProjectList::item:selected { - background-color: #089083; - color: #ffffff; -} - -#ProjectList QScrollBar:vertical { - border: none; - background: #FED6BC; - width: 10px; - margin: 0; -} - -#ProjectList QScrollBar::handle:vertical { - background: #c0c0c0; - border-radius: 5px; - min-height: 20px; -} - -#ProjectList QScrollBar::add-line:vertical, -#ProjectList QScrollBar::sub-line:vertical { - background: none; -} - -#ProjectList QScrollBar::add-page:vertical, -#ProjectList QScrollBar::sub-page:vertical { - background: none; -} - -#BottomBar { - background-color: rgb(33, 44, 50); - border-radius: 8px; - padding: 8px; - outline: 0; -} - -#BottomBar QLabel { - color : white; - font-family: 'Arial'; - font-size: 14px; -} - -#NoteList { - border-radius : 8px; - background-color: white; -} - -#NoteWidget { - background-color: #ffdda2; - border-radius: 8px; -} - -#NoteWidget QPushButton { - font-family: 'Arial'; - font-size: 13px; - font-weight: bold; - border-radius: 10px; - background-color:rgb(241, 201, 132); - color: rgb(33, 44, 50); - padding: 5px 10px; -} - -QPushButton { - font-family: 'Arial'; - font-size: 13px; - font-weight: bold; - border-radius: 10px; - background-color: #fea36b; - color: white; - padding: 5px 10px; - min-width: 60px; - min-height: 25px; -} - -)"; - - -} // namespace Ui - -#endif // MAIN_WINDOW_STYLE_HPP \ No newline at end of file diff --git a/client/ui/main-window/include/mainwindow.h b/client/ui/main-window/include/mainwindow.h deleted file mode 100644 index e5242f7..0000000 --- a/client/ui/main-window/include/mainwindow.h +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include -#include -#include -#include -#include -#include -#include "bottombar.h" -#include "notelist.h" -#include "projectlist.h" -#include "storage.hpp" - -namespace Ui { -class MainWindow : public QWidget { - Q_OBJECT - std::string username; - QVBoxLayout *main_layout_; - BottomBar *top_bar_; - QHBoxLayout *content_layout_; - ProjectList *project_list_; - NoteList *note_list_; - QWidget *content_widget_; - QPushButton *new_project_button_; - QPushButton *new_note_button_; - project_storage_model::Storage *storage_; - - friend ProjectList; -private slots: - void add_project(); - void add_note(); - -public: - explicit MainWindow( - QWidget *parent = nullptr, - std::string username = "none", - project_storage_model::Storage *storage = nullptr - ); -}; -} // namespace Ui -#endif // MAINWINDOW_H \ No newline at end of file diff --git a/client/ui/main-window/include/notelist.h b/client/ui/main-window/include/notelist.h deleted file mode 100644 index f406536..0000000 --- a/client/ui/main-window/include/notelist.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef NOTELIST_H -#define NOTELIST_H -#include -#include -#include -#include -#include "note.hpp" - -namespace Ui { -class NoteList : public QWidget { - friend class MainWindow; - Q_OBJECT - - QHBoxLayout *main_layout_; - - std::vector vertical_layouts_; - - int note_counter_ = 0; - -public: - void add_note_widget(const project_storage_model::Note *note); - void clear_note_list(); - NoteList(QWidget *parent); - -public slots: - void load_project_notes(QListWidgetItem *project); -}; -} // namespace Ui -#endif // NOTELIST_H \ No newline at end of file diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h deleted file mode 100644 index f1f8f6a..0000000 --- a/client/ui/main-window/include/notewidget.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef NOTEWIDGET_H -#define NOTEWIDGET_H - -#include -#include -#include -#include -#include -#include "note.hpp" - -namespace Ui { -class NoteWidget : public QWidget { - Q_OBJECT - const project_storage_model::Note *model_note_; - QVBoxLayout *main_layout_; - QPushButton *open_button_; - QLabel *title_label_; - QLabel *text_label_; - -public: - explicit NoteWidget( - QWidget *parent = nullptr, - const project_storage_model::Note *model_note = nullptr - ); - -private slots: - - void open_note_window() const; -}; -} // namespace Ui -#endif // NOTEWIDGET_H \ No newline at end of file diff --git a/client/ui/main-window/include/projectitem.h b/client/ui/main-window/include/projectitem.h deleted file mode 100644 index 8b4a4a2..0000000 --- a/client/ui/main-window/include/projectitem.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef PROJECTITEM_H -#define PROJECTITEM_H - -#include -#include -#include "notelist.h" -#include "project.hpp" - -namespace Ui { -class ProjectItem : public QListWidgetItem { - project_storage_model::Project *project_; - friend NoteList; - friend class MainWindow; - -public: - ProjectItem(QListWidget *listview, project_storage_model::Project *project); -}; -} // namespace Ui -#endif // PROJECTITEM_H \ No newline at end of file diff --git a/client/ui/main-window/include/projectlist.h b/client/ui/main-window/include/projectlist.h deleted file mode 100644 index d221695..0000000 --- a/client/ui/main-window/include/projectlist.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef PROJECTLIST_H -#define PROJECTLIST_H - -#include -#include -#include "project.hpp" -#include "storage.hpp" - -namespace Ui { -class ProjectList : public QListWidget { - Q_OBJECT - friend class MainWindow; - void add_project(project_storage_model::Project *project); - void load_projects(project_storage_model::Storage *storage); - -public: - explicit ProjectList(QWidget *parent = nullptr); - -signals: -}; -} // namespace Ui -#endif // PROJECTLIST_H \ No newline at end of file diff --git a/client/ui/main-window/src/applicationwindow.cpp b/client/ui/main-window/src/applicationwindow.cpp deleted file mode 100644 index 62c7bfe..0000000 --- a/client/ui/main-window/src/applicationwindow.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "applicationwindow.h" -#include -#include "mainwindow.h" - -namespace Ui { -ApplicationWindow::ApplicationWindow(std::string window_name_) - : QMainWindow{nullptr} { - this->setObjectName("ApplicationWindow"); - - this->setAttribute(Qt::WA_StyledBackground); - - this->setWindowTitle(window_name_.c_str()); -} -} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/bottombar.cpp b/client/ui/main-window/src/bottombar.cpp deleted file mode 100644 index ee63c71..0000000 --- a/client/ui/main-window/src/bottombar.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "bottombar.h" -#include -#include -#include -#include - -namespace Ui { -BottomBar::BottomBar( - QWidget *parent, - std::string username, - std::string project_name -) - : QWidget(parent), - main_layout_(new QHBoxLayout()), - username_(new QLabel(username.c_str())), - project_name_(new QLabel(project_name.c_str())) { - this->setObjectName("BottomBar"); - this->setLayout(main_layout_); - this->setFixedHeight(40); - - project_name_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); - username_->setAlignment(Qt::AlignVCenter | Qt::AlignRight); - - this->setSizePolicy( - QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum) - ); - - main_layout_->addWidget(project_name_); - main_layout_->addWidget(username_); -} -} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/mainwindow.cpp b/client/ui/main-window/src/mainwindow.cpp deleted file mode 100644 index 907f902..0000000 --- a/client/ui/main-window/src/mainwindow.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "mainwindow.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "bottombar.h" -#include "lr_dao.hpp" -#include "main_window_style.hpp" -#include "note_dao.hpp" -#include "notelist.h" -#include "project.hpp" -#include "project_dao.hpp" -#include "projectitem.h" -#include "projectlist.h" - -namespace Ui { -MainWindow::MainWindow( - QWidget *parent, - std::string username, - project_storage_model::Storage *storage -) - : QWidget(parent), - username(username), - main_layout_(new QVBoxLayout(this)), - top_bar_(new BottomBar(this, username, "EFFICIO :: Таск-Трекер")), - content_layout_(new QHBoxLayout(this)), - project_list_(new ProjectList(this)), - note_list_(new NoteList(this)), - content_widget_(new QWidget(this)), - new_project_button_(new QPushButton("Новый проект", this)), - new_note_button_(new QPushButton("Новая заметка", this)), - storage_(storage) { - this->setObjectName("main-window"); - this->setAttribute(Qt::WA_StyledBackground); - this->setMinimumSize(QSize(800, 600)); - this->setStyleSheet(main_window_style); - - main_layout_->addWidget(top_bar_, Qt::AlignTop); - main_layout_->setAlignment(Qt::AlignCenter); - main_layout_->addWidget(content_widget_); - content_widget_->setLayout(content_layout_); - auto right_layout = new QVBoxLayout(content_widget_); - right_layout->addWidget(project_list_); - right_layout->addWidget(new_project_button_); - right_layout->addWidget(new_note_button_); - QScrollArea* scrollArea = new QScrollArea(content_widget_); - scrollArea->setWidgetResizable(true); - scrollArea->setWidget(note_list_); - content_layout_->addWidget(scrollArea, Qt::AlignRight); - content_layout_->addLayout(right_layout); - main_layout_->addWidget(content_widget_); - this->setLayout(main_layout_); - - this->project_list_->load_projects(storage); - - connect( - project_list_, &QListWidget::itemClicked, note_list_, - &NoteList::load_project_notes - ); - connect( - new_note_button_, &QPushButton::clicked, this, &Ui::MainWindow::add_note - ); - connect( - new_project_button_, &QPushButton::clicked, this, - &Ui::MainWindow::add_project - ); -} - -void MainWindow::add_project() { - bool ok; - QString name_of_project = QInputDialog::getText( - nullptr, "Название проекта:", "Введите название", QLineEdit::Normal, "", - &ok - ); - if (ok) { - int id = 0; - - if (DB::ProjectDAO::create_project(name_of_project.toStdString(), id)) { - LRDao::add_project_to_user(username, id); - auto &project = storage_->add_project( - Project(id, name_of_project.toStdString(), "") - ); - project_list_->add_project(&project); - } - } -} - -void MainWindow::add_note() { - auto project_item = - dynamic_cast(project_list_->currentItem()); - if (project_item) { - if (int id = 0; NoteDao::initialize_note(id)) { - DB::ProjectDAO::add_note_to_project( - project_item->project_->get_id(), id - ); - auto ¬e = - project_item->project_->add_note({id, "Пустая заметка", ""}); - note_list_->add_note_widget(¬e); - } - } else { - QMessageBox msg; - msg.setText("Проект не выбран!"); - msg.exec(); - } -} - -} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/notelist.cpp b/client/ui/main-window/src/notelist.cpp deleted file mode 100644 index 4af7558..0000000 --- a/client/ui/main-window/src/notelist.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "notelist.h" -#include -#include -#include -#include -#include -#include "note.hpp" -#include "notewidget.h" -#include "projectitem.h" - -namespace Ui { -NoteList::NoteList(QWidget *parent) - : QWidget(parent), - main_layout_(new QHBoxLayout(this)), - vertical_layouts_(std::vector()){ - - this->setAttribute(Qt::WA_StyledBackground); - this->setObjectName("NoteList"); - this->setLayout(main_layout_); - vertical_layouts_.resize(4, nullptr); - for (auto &layout : vertical_layouts_) { - layout = new QVBoxLayout(this); - - main_layout_->addLayout(layout); - } -} - -void NoteList::add_note_widget(const project_storage_model::Note *note) { - auto current_layout = vertical_layouts_[note_counter_ % 4]; - if (current_layout->count() > 1) { - current_layout->removeItem( - current_layout->itemAt(current_layout->count() - 1) - ); - } - vertical_layouts_[note_counter_ % 4]->addWidget( - new NoteWidget(this, note), 0, Qt::AlignTop - ); - current_layout->addStretch(); - note_counter_++; -} - -void NoteList::load_project_notes(QListWidgetItem *project) { - ProjectItem *p = dynamic_cast(project); - assert(p != nullptr); - qDebug() << "Адрес проекта" - << QString::fromStdString(p->project_->get_name()) << ":" - << p->project_; - this->clear_note_list(); - note_counter_ = 0; - for (const auto ¬e : p->project_->get_notes()) { - this->add_note_widget(¬e); - } -} - -void NoteList::clear_note_list() { - for (auto &layout : vertical_layouts_) { - while (layout->count()) { - auto item = layout->takeAt(0); - auto widget = item->widget(); - if (widget) { - widget->deleteLater(); - } - } - } -} -} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp deleted file mode 100644 index 3ced750..0000000 --- a/client/ui/main-window/src/notewidget.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "notewidget.h" -#include -#include -#include -#include "note.hpp" -#include "note_edit_dialog.h" - -namespace Ui { -NoteWidget::NoteWidget( - QWidget *parent, - const project_storage_model::Note *model_note -) - : QWidget(parent), - model_note_(model_note), - main_layout_(new QVBoxLayout(this)), - open_button_(new QPushButton("Открыть")) { - this->setObjectName("NoteWidget"); - this->setMinimumWidth(100); - this->setFixedHeight(100); - title_label_ = new QLabel(model_note_->get_title().c_str(), this); - text_label_ = new QLabel(model_note_->get_text().c_str(), this); - - title_label_->setStyleSheet("color: rgb(33, 44, 50);"); - text_label_->setStyleSheet("color: rgb(33, 44, 50);"); - - main_layout_->addWidget(title_label_); - main_layout_->addWidget(text_label_); - - text_label_->setWordWrap(false); - title_label_->setWordWrap(false); - text_label_->setTextInteractionFlags(Qt::TextSelectableByMouse); - - main_layout_->addWidget(open_button_); - - connect( - open_button_, &QPushButton::clicked, this, &NoteWidget::open_note_window - ); - this->setLayout(main_layout_); - this->setAttribute(Qt::WA_StyledBackground); -} - -void NoteWidget::open_note_window() const { - auto dialog = new ::NoteEditDialog( - const_cast(qobject_cast(this)), - const_cast(model_note_) - ); - dialog->setAttribute(Qt::WA_DeleteOnClose); - dialog->exec(); - text_label_->setText(model_note_->get_text().c_str()); - title_label_->setText(model_note_->get_title().c_str()); - main_layout_->update(); -} -} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/projectitem.cpp b/client/ui/main-window/src/projectitem.cpp deleted file mode 100644 index 4f38e85..0000000 --- a/client/ui/main-window/src/projectitem.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include "projectitem.h" -#include - -namespace Ui { -ProjectItem::ProjectItem( - QListWidget *list_view, - project_storage_model::Project *project -) - : project_(project), - QListWidgetItem(project->get_name().c_str(), list_view) { -} -} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/projectlist.cpp b/client/ui/main-window/src/projectlist.cpp deleted file mode 100644 index 3bd7828..0000000 --- a/client/ui/main-window/src/projectlist.cpp +++ /dev/null @@ -1,24 +0,0 @@ -#include "projectlist.h" -#include -#include "mainwindow.h" -#include "project.hpp" -#include "projectitem.h" - -namespace Ui { - -ProjectList::ProjectList(QWidget *parent) : QListWidget{parent} { - this->setObjectName("ProjectList"); - this->setFixedWidth(200); -} - -void ProjectList::add_project(project_storage_model::Project *project) { - this->addItem(new ProjectItem(static_cast(this), project)); -} - -void ProjectList::load_projects(project_storage_model::Storage *storage) { - for (project_storage_model::Project &pr : storage->get_projects()) { - add_project(&pr); - } -} - -} // namespace Ui \ No newline at end of file diff --git a/client/ui/note-widget/CMakeLists.txt b/client/ui/note-widget/CMakeLists.txt index d062724..6d842e7 100644 --- a/client/ui/note-widget/CMakeLists.txt +++ b/client/ui/note-widget/CMakeLists.txt @@ -34,7 +34,9 @@ add_library(NoteWidget STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) target_include_directories(NoteWidget PUBLIC $ + $ $ ) -target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database Scripts) \ No newline at end of file +# TODO: remove "Service" and "efficio-rpc" from this list +target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto Service efficio-rpc) \ No newline at end of file diff --git a/client/ui/note-widget/include/note_edit_dialog.h b/client/ui/note-widget/include/note_edit_dialog.h index 46048a3..f141a15 100644 --- a/client/ui/note-widget/include/note_edit_dialog.h +++ b/client/ui/note-widget/include/note_edit_dialog.h @@ -4,11 +4,10 @@ #include #include #include -#include "note.hpp" -#include "note_dao.hpp" +#include "model-proto/model.pb.h" #include "tags_dialog.h" -using namespace project_storage_model; +using Efficio_proto::Note; QT_BEGIN_NAMESPACE @@ -24,7 +23,7 @@ class NoteEditDialog final : public QDialog { public: explicit NoteEditDialog( QWidget* parent = nullptr, - Note* note = new Note(0, "NULL", "NULL") + Note* note = nullptr ); ~NoteEditDialog() override; @@ -44,7 +43,7 @@ private slots: void clear_member_avatars(); void update_tags_display(); - static QString create_tag_style_sheet(const QString& color); + static QString create_tag_style_sheet(int color_code); [[nodiscard]] bool try_save_note() const; diff --git a/client/ui/note-widget/include/note_edit_dialog_styles.h b/client/ui/note-widget/include/note_edit_dialog_styles.h index 5ad1b63..147a083 100644 --- a/client/ui/note-widget/include/note_edit_dialog_styles.h +++ b/client/ui/note-widget/include/note_edit_dialog_styles.h @@ -4,7 +4,7 @@ #include namespace Ui { -QString light_theme = R"( +inline QString light_theme = R"( QDialog { background-color: #f5f5f5; } diff --git a/client/ui/note-widget/include/tags_dialog.h b/client/ui/note-widget/include/tags_dialog.h index f8496c0..0033c4c 100644 --- a/client/ui/note-widget/include/tags_dialog.h +++ b/client/ui/note-widget/include/tags_dialog.h @@ -17,7 +17,7 @@ class TagsDialog final : public QDialog { struct Tag { bool is_checked; - QString color; + int color; QString name; }; @@ -27,6 +27,7 @@ class TagsDialog final : public QDialog { ); [[nodiscard]] QList get_selected_tags() const; + static QString get_color_by_code(int code); private: void setup_ui(); diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index ed18200..fe4b508 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -10,11 +10,23 @@ #include "./ui_note_edit_dialog.h" #include "note_edit_dialog_styles.h" #include "tags_dialog.h" +#include +#include "efficio-rpc-proto/efficio.grpc.pb.h" + +using grpc::Channel; +using grpc::ClientContext; +using grpc::Status; +using Efficio_proto::Update; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; NoteEditDialog::NoteEditDialog(QWidget* parent, Note* note) : QDialog(parent), ui_(new Ui::NoteEditDialog), note_(note) { + if (note_ == nullptr) { + std::cerr << "Not a valid note!\n"; + } ui_->setupUi(this); setWindowTitle("EFFICIO"); @@ -29,13 +41,13 @@ NoteEditDialog::~NoteEditDialog() { } void NoteEditDialog::init_basic_fields() { - ui_->titleLineEdit->setText(QString::fromStdString(note_->get_title())); - ui_->descriptionTextEdit->setText(QString::fromStdString(note_->get_text())); + ui_->titleLineEdit->setText(QString::fromStdString(note_->title())); + ui_->descriptionTextEdit->setText(QString::fromStdString(note_->text())); } void NoteEditDialog::init_additional_fields() { - if (!note_->get_date().empty()) { - QDate date = QDate::fromString(QString::fromStdString(note_->get_date()), "yyyy-MM-dd"); + if (!note_->date().empty()) { + QDate date = QDate::fromString(QString::fromStdString(note_->date()), "yyyy-MM-dd"); if (date.isValid()) { ui_->dateEdit->setDate(date); ui_->dateLabel->setVisible(true); @@ -43,21 +55,21 @@ void NoteEditDialog::init_additional_fields() { } } - if (!note_->get_members().empty()) { + if (!note_->members().empty()) { ui_->membersLabel->setVisible(true); - for (const auto& member : note_->get_members()) { + for (const auto& member : note_->members()) { add_member_avatar(member); } ui_->joinButton->setText("Покинуть"); } - if (!note_->get_tags().empty()) { + if (!note_->tags().empty()) { ui_->tagsLabel->setVisible(true); - for (const auto& tag : note_->get_tags()) { + for (const auto& tag : note_->tags()) { TagsDialog::Tag tag_info; tag_info.is_checked = true; - tag_info.color = QString::fromStdString(tag.color); - tag_info.name = QString::fromStdString(tag.name); + tag_info.color = tag.color(); + tag_info.name = QString::fromStdString(tag.text()); selected_tags_.append(tag_info); auto tag_label = std::make_unique(tag_info.name, this); @@ -95,7 +107,6 @@ void NoteEditDialog::on_save_button_click() { } else { QMessageBox::information(this, "Ошибка", "Не удалось сохранить заметку"); } - close(); } void NoteEditDialog::on_join_button_click() { @@ -105,7 +116,7 @@ void NoteEditDialog::on_join_button_click() { if (!is_joined) { const std::string current_user = "TODO"; add_member_avatar(current_user); - note_->add_member(current_user); + note_->add_members(current_user); ui_->joinButton->setText("Покинуть"); } else { std::string current_user = "TODO"; @@ -155,17 +166,17 @@ void NoteEditDialog::update_tags_display() { tag_labels_.clear(); ui_->tagsLabel->setVisible(!selected_tags_.empty()); - for (const auto &[is_checked, color, name] : selected_tags_) { + for (const auto &[is_checked, color_code, name] : selected_tags_) { if (is_checked) { auto tag_label = std::make_unique(name, this); - tag_label->setStyleSheet(create_tag_style_sheet(color)); + tag_label->setStyleSheet(create_tag_style_sheet(color_code)); ui_->tagsLayout->addWidget(tag_label.get()); tag_labels_.push_back(std::move(tag_label)); } } } -QString NoteEditDialog::create_tag_style_sheet(const QString& color) { +QString NoteEditDialog::create_tag_style_sheet(const int color_code) { return QString( "background-color: %1; " "color: white; " @@ -176,20 +187,40 @@ QString NoteEditDialog::create_tag_style_sheet(const QString& color) { "font-weight: bold;" "width: 40px;" "height: 25px;" - ).arg(color); + ).arg(TagsDialog::get_color_by_code(color_code)); } bool NoteEditDialog::try_save_note() const { + // TODO: add saving members and tags note_->set_title(ui_->titleLineEdit->text().toStdString()); note_->set_text(ui_->descriptionTextEdit->toPlainText().toStdString()); note_->set_date(ui_->dateEdit->date().toString("yyyy-MM-dd").toStdString()); - note_->clear_tags(); - for (const auto &[is_checked, color, name] : selected_tags_) { - if (is_checked) { - note_->add_tag(name.toStdString(), color.toStdString()); + // TODO: move to client part + const auto channel = + CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); + + ClientContext context; + context.set_deadline( + std::chrono::system_clock::now() + std::chrono::seconds(5) + ); + + Efficio_proto::CreateNoteRequest request; + request.mutable_user()->set_login("test"); + + try { + Update::Stub stub(channel); + Efficio_proto::CreateNoteResponse response; + + Status status = stub.CreateNote(&context, request, &response); + + if (!status.ok()) { + throw std::runtime_error(status.error_message()); } + return true; + } + catch (const std::exception& e) { + std::cerr << "Error creating note: " << e.what() << std::endl; + return false; } - - return NoteDao::update_note(*note_); } \ No newline at end of file diff --git a/client/ui/note-widget/src/tags_dialog.cpp b/client/ui/note-widget/src/tags_dialog.cpp index 5a3d61e..74b933b 100644 --- a/client/ui/note-widget/src/tags_dialog.cpp +++ b/client/ui/note-widget/src/tags_dialog.cpp @@ -37,11 +37,11 @@ void TagsDialog::setup_ui() { tag_layout->addWidget(check_boxes_[i].get()); color_combo_boxes_[i] = std::make_unique(this); - color_combo_boxes_[i]->addItem("Красный", "#e7624b"); - color_combo_boxes_[i]->addItem("Синий", "#165d7b"); - color_combo_boxes_[i]->addItem("Розовый", "#bd6dab"); - color_combo_boxes_[i]->addItem("Зеленый", "#00b16b"); - color_combo_boxes_[i]->addItem("Желтый", "#e69f00"); + color_combo_boxes_[i]->addItem("Красный", get_color_by_code(0)); + color_combo_boxes_[i]->addItem("Синий", get_color_by_code(1)); + color_combo_boxes_[i]->addItem("Розовый", get_color_by_code(2)); + color_combo_boxes_[i]->addItem("Зеленый", get_color_by_code(3)); + color_combo_boxes_[i]->addItem("Желтый", get_color_by_code(4)); tag_layout->addWidget(color_combo_boxes_[i].get()); name_line_edits_[i] = std::make_unique(this); @@ -78,10 +78,21 @@ QList TagsDialog::get_selected_tags() const { !name_line_edits_[i]->text().isEmpty()) { Tag tag; tag.is_checked = true; - tag.color = color_combo_boxes_[i]->currentData().toString(); + tag.color = color_combo_boxes_[i]->currentData().toInt(); tag.name = name_line_edits_[i]->text(); tags.append(tag); } } return tags; +} + +QString TagsDialog::get_color_by_code(const int code) { + switch (code) { + case 0: return "#e7624b"; + case 1: return "#165d7b"; + case 2: return "#bd6dab"; + case 3: return "#00b16b"; + case 4: return "#e69f00"; + default: return "ffffff"; + } } \ No newline at end of file diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 9733a05..38096d0 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -3,6 +3,6 @@ project(server) add_subdirectory(Service) -add_executable(main main.cpp) +add_executable(start_server main.cpp) -target_link_libraries(main Service) \ No newline at end of file +target_link_libraries(start_server Service Database) \ No newline at end of file diff --git a/server/Service/CMakeLists.txt b/server/Service/CMakeLists.txt index 70b47a8..a8f0c79 100644 --- a/server/Service/CMakeLists.txt +++ b/server/Service/CMakeLists.txt @@ -5,6 +5,7 @@ project(Service LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +find_package(libpqxx REQUIRED) find_package(Protobuf CONFIG REQUIRED) find_package(gRPC CONFIG REQUIRED) find_package(Threads) @@ -16,15 +17,16 @@ file(GLOB HEADERS "include/*.h") add_library(Service STATIC ${SOURCES} ${HEADERS}) target_link_libraries(Service + Database model-proto efficio-rpc protobuf::libprotobuf gRPC::grpc gRPC::grpc++ + pqxx ) target_include_directories(${PROJECT_NAME} - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR} diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 17e7142..6e971e8 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -14,6 +14,7 @@ using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::Update; using Efficio_proto::CreateNoteRequest; +using Efficio_proto::CreateNoteResponse; class UpdateService final { Update::AsyncService service_; @@ -44,11 +45,11 @@ class UpdateService final { class CreateNoteServerCall final : public CommonServerCall { CreateNoteRequest request_; - ServerAsyncResponseWriter responder_; + ServerAsyncResponseWriter responder_; UpdateService &service_; public: explicit CreateNoteServerCall(UpdateService& service, ServerCompletionQueue *cq); - void Proceed(bool) override; + void Proceed(bool ok) override; }; Update::AsyncService& get_service(); diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index 1fd581a..2a63f4b 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -8,10 +8,9 @@ void ServerImplementation::Run(const uint16_t port) { UpdateService update_service; builder.AddListeningPort( - "0.0.0.0:" + std::to_string(port), grpc::InsecureServerCredentials() + "127.0.0.1:" + std::to_string(port), grpc::InsecureServerCredentials() ); builder.RegisterService(&update_service.get_service()); - cq_ = builder.AddCompletionQueue(); server_ = builder.BuildAndStart(); HandleRPCs(update_service); @@ -19,8 +18,9 @@ void ServerImplementation::Run(const uint16_t port) { void ServerImplementation::HandleRPCs(UpdateService& update_service) const { new UpdateService::GetNoteServerCall(update_service, cq_.get()); + new UpdateService::CreateNoteServerCall(update_service, cq_.get()); - void *tag; + void* tag; bool ok; while (cq_->Next(&tag, &ok)) { static_cast(tag)->Proceed(ok); diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 9dbf6d9..6559117 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -1,8 +1,10 @@ #include "update_service.h" #include "common_server_call.h" +#include "note_dao.hpp" using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; +using Efficio_proto::Note; using grpc::ServerAsyncResponseWriter; @@ -10,6 +12,16 @@ UpdateService::GetNoteServerCall::GetNoteServerCall(UpdateService &service, Serv : CommonServerCall(cq), responder_(&ctx_), service_(service) { + + service_.service_.RequestGetNote( + &ctx_, + &request_, + &responder_, + cq_, + cq_, + this + ); + status_ = PROCESS; } void UpdateService::GetNoteServerCall::Proceed(const bool ok) { @@ -19,18 +31,54 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { } switch (status_) { - case CREATE: { + case PROCESS: { new GetNoteServerCall(service_, cq_); - service_.service_.RequestGetNote( - &ctx_, &request_, &responder_, cq_, cq_, this - ); - status_ = PROCESS; + + GetNoteResponse response; + + const auto note = NoteDao::get_note(request_.id(), request_.user().login()); + response.mutable_note()->CopyFrom(note); + + responder_.Finish(response, grpc::Status::OK, this); + status_ = FINISH; + break; + } + case FINISH: { + delete this; break; } + } +} + +UpdateService::CreateNoteServerCall::CreateNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + service_.service_.RequestCreateNote( + &ctx_, + &request_, + &responder_, + cq_, + cq_, + this + ); + status_ = PROCESS; +} + +void UpdateService::CreateNoteServerCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + + switch (status_) { case PROCESS: { - const GetNoteResponse response; + new CreateNoteServerCall(service_, cq_); + CreateNoteResponse response; - // TODO: query processing logic + const auto new_note = NoteDao::initialize_note_for_user(request_.user().login()); + response.mutable_note()->CopyFrom(new_note); responder_.Finish(response, grpc::Status::OK, this); status_ = FINISH; @@ -43,8 +91,6 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { } } -class UpdateService::CreateNoteServerCall final : public CommonServerCall {}; - Update::AsyncService& UpdateService::get_service() { return service_; } \ No newline at end of file diff --git a/server/database/CMakeLists.txt b/server/database/CMakeLists.txt index 27bb557..fd87718 100644 --- a/server/database/CMakeLists.txt +++ b/server/database/CMakeLists.txt @@ -18,4 +18,4 @@ target_include_directories(Database $ ) -target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql Qt6::Widgets Scripts) \ No newline at end of file +target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql model-proto pqxx) \ No newline at end of file diff --git a/server/database/include/database_manager.hpp b/server/database/include/database_manager.hpp index 1bba4b6..8f3fd17 100644 --- a/server/database/include/database_manager.hpp +++ b/server/database/include/database_manager.hpp @@ -1,22 +1,19 @@ #ifndef DATABASE_MANAGER_HPP #define DATABASE_MANAGER_HPP -#include +#include +#include "note_dao.hpp" class DatabaseManager final { public: static DatabaseManager &get_instance(); - bool execute_query( - QSqlQuery &query, - const QString &query_str, - const QVariantList ¶ms = {} - ) const; - [[nodiscard]] QSqlDatabase get_database() const; + pqxx::connection& get_connection(); + [[nodiscard]] static std::string get_connection_string(); private: explicit DatabaseManager(); - ~DatabaseManager(); - QSqlDatabase database_; + ~DatabaseManager() = default; + std::unique_ptr connection_; }; #endif // DATABASE_MANAGER_HPP \ No newline at end of file diff --git a/server/database/include/lr_dao.hpp b/server/database/include/lr_dao.hpp deleted file mode 100644 index de8d5fb..0000000 --- a/server/database/include/lr_dao.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef LRDAO_H -#define LRDAO_H - -#include "database_manager.hpp" - -class LRDao { -public: - LRDao() = default; - static int try_register_user(const QString &login, const QString &password); - static bool validate_user(const QString &login, const QString &password); - static bool add_project_to_user(std::string user_login, int project_id); - static bool - get_user_projects(const std::string &login, std::vector &projects); - -private: - static QString hash_password(const QString &password); -}; - -#endif // LRDAO_H \ No newline at end of file diff --git a/server/database/include/note_dao.hpp b/server/database/include/note_dao.hpp index 0fbe980..d973ae9 100644 --- a/server/database/include/note_dao.hpp +++ b/server/database/include/note_dao.hpp @@ -1,24 +1,16 @@ #ifndef NOTEDAO_HPP #define NOTEDAO_HPP -#include -#include -#include "note.hpp" +#include "model-proto/model.pb.h" -using namespace project_storage_model; +using Efficio_proto::Note; class NoteDao { public: NoteDao() = default; - static bool initialize_note(int &id); + static Note initialize_note_for_user(const std::string& login); static bool update_note(const Note ¬e); - static bool delete_note(int id); - [[nodiscard]] static std::vector get_all_notes(); - [[nodiscard]] static Note get_note_by_id(int id); - -private: - static QString convert_string_set_to_postgres_array(const std::unordered_set& string_set); - static QString convert_tags_to_postgres_array(const std::vector& tags); + [[nodiscard]] static Note get_note(int note_id, const std::string &login); }; #endif // NOTEDAO_HPP \ No newline at end of file diff --git a/server/database/include/project_dao.hpp b/server/database/include/project_dao.hpp deleted file mode 100644 index a038af6..0000000 --- a/server/database/include/project_dao.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef PROJECT_DAO_HPP -#define PROJECT_DAO_HPP - -#include "database_manager.hpp" -#include "project.hpp" - -namespace DB { - -class ProjectDAO { -public: - ProjectDAO() = default; - static bool create_project(const std::string &name, int &id); - static bool get_project(int id, std::string &name, std::vector ¬es); - static bool add_note_to_project(int project_id, int note_id); -}; -} // namespace DB - -#endif \ No newline at end of file diff --git a/server/database/include/serialization.hpp b/server/database/include/serialization.hpp deleted file mode 100644 index f9b0def..0000000 --- a/server/database/include/serialization.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef SERIALIZATION_HPP -#define SERIALIZATION_HPP -#include "storage.hpp" - -class Serialization { -public: - static bool get_storage( - project_storage_model::Storage &storage, - const std::string &login - ); -}; - -#endif \ No newline at end of file diff --git a/server/database/src/database_manager.cpp b/server/database/src/database_manager.cpp index ad5b9ec..e2cb9e0 100644 --- a/server/database/src/database_manager.cpp +++ b/server/database/src/database_manager.cpp @@ -1,80 +1,59 @@ #include "database_manager.hpp" -#include -#include -#include +#include DatabaseManager::DatabaseManager() { - database_ = QSqlDatabase::addDatabase("QPSQL"); - database_.setHostName("localhost"); - database_.setPort(5432); - database_.setDatabaseName("efficio"); - database_.setUserName("efficio"); - database_.setPassword("admin"); - database_.open(); + connection_ = std::make_unique(get_connection_string()); + pqxx::work transaction(*connection_); - QSqlQuery query(database_); - query.exec( + transaction.exec( "CREATE TABLE IF NOT EXISTS notes (" "id SERIAL PRIMARY KEY, " "title TEXT NOT NULL, " "content TEXT, " + "user_id VARCHAR(50) REFERENCES users(login), " "members VARCHAR(50)[], " "date VARCHAR(50), " "tags VARCHAR(50)[]" ")" ); - query.exec( + transaction.exec( "CREATE TABLE IF NOT EXISTS users (" "login VARCHAR(50) PRIMARY KEY, " "password VARCHAR(50) NOT NULL, " + "token VARCHAR(100), " "projects INT[]" ")" ); - query.exec( - "CREATE TABLE IF NOT EXISTS projects(" + transaction.exec( + "CREATE TABLE IF NOT EXISTS projects (" "id SERIAL PRIMARY KEY, " "name VARCHAR(50) NOT NULL, " - "notes INT[]" + "owner VARCHAR(50) REFERENCES users(login), " + "notes INT[], " + "members VARCHAR(50)[]" ")" ); -} -DatabaseManager::~DatabaseManager() { - if (database_.isOpen()) { - database_.close(); - } + transaction.commit(); } DatabaseManager &DatabaseManager::get_instance() { static DatabaseManager instance; - if (!instance.database_.isOpen() && !instance.database_.open()) { + if (!instance.connection_->is_open()) { throw std::runtime_error("Lost connection with database"); } return instance; } -bool DatabaseManager::execute_query( - QSqlQuery &query, - const QString &query_str, - const QVariantList ¶ms -) const { - QSqlQuery temp(database_); - query = std::move(temp); - - if (!query.prepare(query_str)) { - qDebug() << "Prepare error:" << query.lastError(); - return false; +pqxx::connection &DatabaseManager::get_connection() { + if (!connection_->is_open()) { + connection_ = std::make_unique(get_connection_string()); } - - for (int i = 0; i < params.size(); i++) { - query.bindValue(i, params[i]); - } - - return query.exec(); + return *connection_; } -QSqlDatabase DatabaseManager::get_database() const { - return database_; +std::string DatabaseManager::get_connection_string() { + return "postgresql://efficio:admin@localhost/efficio"; } \ No newline at end of file diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp deleted file mode 100644 index 3107f5b..0000000 --- a/server/database/src/lr_dao.cpp +++ /dev/null @@ -1,81 +0,0 @@ -#include "lr_dao.hpp" -#include -#include -#include -#include -#include - -QString LRDao::hash_password(const QString &password) { - const QByteArray hash = - QCryptographicHash::hash(password.toUtf8(), QCryptographicHash::Sha256); - return hash.toHex(); -} - -int LRDao::try_register_user(const QString &login, const QString &password) { - QSqlQuery query; - - const bool is_login_free = DatabaseManager::get_instance().execute_query( - query, "SELECT * FROM users WHERE login = ?", {login} - ); - - if (!is_login_free) { - return -1; - } - - QSqlQuery insert_query; - return DatabaseManager::get_instance().execute_query( - insert_query, "INSERT INTO users (login, password) VALUES (?, ?)", - {login, password} - ); -} - -bool LRDao::validate_user(const QString &login, const QString &password) { - QSqlQuery query; - - const QString query_str = - "SELECT * FROM users WHERE login = ? AND password = ?"; - const QVariantList params = {login, password}; - - const auto success = - DatabaseManager::get_instance().execute_query(query, query_str, params); - - return query.next() && success; -} - -bool LRDao::add_project_to_user(std::string user_login, int project_id) { - QSqlQuery query; - const QString query_str = - "UPDATE users SET projects = array_append(projects, ?)" - "WHERE login = ?"; - - const QVariantList params = { - QString::fromStdString(std::to_string(project_id)), - QString::fromStdString(user_login)}; - - return DatabaseManager::get_instance().execute_query( - query, query_str, params - ); -} - -bool LRDao::get_user_projects( - const std::string &login, - std::vector &projects -) { - QSqlQuery query; - const QString query_str = - "SELECT array_to_string(projects, ',') FROM users WHERE login = ?"; - - const QVariantList params = {QString::fromStdString(login)}; - const bool is_success = - DatabaseManager::get_instance().execute_query(query, query_str, params); - - if (is_success && query.next() && query.value(0).isValid()) { - QStringList ps = query.value(0).toString().split(","); - for (auto i : ps) { - projects.push_back(i.toInt()); - } - - return true; - } - return false; -} \ No newline at end of file diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index 369f192..aaebf92 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -1,26 +1,37 @@ #include "note_dao.hpp" -#include #include "database_manager.hpp" +#include -bool NoteDao::initialize_note(int &id) { - QSqlQuery query; - const auto is_successful = DatabaseManager::get_instance().execute_query( - query, +Note NoteDao::initialize_note_for_user(const std::string &login) { + auto& connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = "INSERT INTO notes (title, content) " - "VALUES ('Пустая заметка', '')" - "RETURNING id" - ); - if (is_successful && query.next()) { - id = query.value(0).toInt(); - return true; + "VALUES ($1, $2) " + "RETURNING id, title, content"; + + const pqxx::result result = + transaction.exec_params(query, "Пустая заметка", ""); + + if (result.empty()) { + return {}; } - return false; + + const pqxx::row row = result[0]; + const auto note = new Note(); + note->set_id(row["id"].as()); + note->set_title(row["title"].as()); + + transaction.commit(); + return *note; } bool NoteDao::update_note(const Note ¬e) { - QSqlQuery query; + auto& connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); - const QString sql_query = + const std::string query = "UPDATE notes SET " "title = :title, " "content = :content, " @@ -28,93 +39,63 @@ bool NoteDao::update_note(const Note ¬e) { "date = :date, " "tags = :tags " "WHERE id = :id"; - const QVariantList params = { - QString::fromStdString(note.get_title()), - QString::fromStdString(note.get_text()), - convert_string_set_to_postgres_array(note.get_members()), - QString::fromStdString(note.get_date()), - convert_tags_to_postgres_array(note.get_tags()), - note.get_id() - }; - - return DatabaseManager::get_instance().execute_query( - query, sql_query, params - ); -} -bool NoteDao::delete_note(int id) { - QSqlQuery query; - const QString sql_query = "DELETE FROM notes WHERE id = :id"; - return DatabaseManager::get_instance().execute_query( - query, sql_query, {id} - ); + // TODO: I'm not sure, it will work correctly because note.members() and note.tags() return strange type + // const pqxx::result result = transaction.exec_params( + // query, note.title(), note.text(), note.members(), note.date(), + // note.tags(), note.id() + // ); + // + // if (result.empty()) { + // return false; + // } + + transaction.commit(); + return true; } -std::vector NoteDao::get_all_notes() { - QSqlQuery query; - std::vector notes; - DatabaseManager::get_instance().execute_query(query, "SELECT * FROM notes"); +Note NoteDao::get_note(const int note_id, const std::string &login) { + pqxx::connection connection(DatabaseManager::get_connection_string()); + pqxx::work transaction(connection); - while (query.next()) { - auto id = query.value("id").toUInt(); - auto title = query.value("title").toString().toStdString(); - auto text = query.value("content").toString().toStdString(); - notes.emplace_back(id, title, text); - } + const std::string query = + "SELECT * FROM notes WHERE id = " + transaction.quote(note_id) + + " AND user_id = " + transaction.quote(login); - return notes; -} + const pqxx::result result = transaction.exec(query); -Note NoteDao::get_note_by_id(int id) { - QSqlQuery query; - DatabaseManager::get_instance().execute_query( - query, "SELECT title, content, array_to_string(tags, ','), date, array_to_string(members, ',') FROM notes WHERE id = ?", {id} - ); - query.next(); - auto title = query.value(0).toString().toStdString(); - auto text = query.value(1).toString().toStdString(); - auto date = query.value(3).toString().toStdString(); - Note result{id, title, text}; - auto tags_list = query.value(2).toString().split(","); - - if (!tags_list.empty() && tags_list[0] != "") { - for (const auto& tag_str : tags_list) { - - result.add_tag(tag_str.split(':')[0].toStdString(), tag_str.split(':')[1].toStdString()); - } - } - auto member_list = query.value(4).toString().split(","); - if (!member_list.empty() && member_list[0] != "") { - for (const auto& member : member_list) { - result.add_member(member.toStdString()); - } + if (result.empty()) { + return {}; } - return result; -} + const pqxx::row row = result[0]; -QString NoteDao::convert_string_set_to_postgres_array(const std::unordered_set& string_set) { - if (string_set.empty()) { - return "{}"; - } + const auto note = new Note(); - QStringList buffer; - for (const auto& item : string_set) { - buffer << QString::fromStdString(item).replace(",", "\\,"); + note->set_id(row["id"].as()); + note->set_title(row["title"].as()); + note->set_text(row["text"].as()); + + if (!row["date"].is_null()) { + note->set_date(row["date"].as()); } - return "{" + buffer.join(",") + "}"; -} -QString NoteDao::convert_tags_to_postgres_array(const std::vector& tags) { - if (tags.empty()) { - return "{}"; + if (!row["members"].is_null()) { + note->add_members(row["members"].as()); } - QStringList buffer; - for (const auto &[name, color] : tags) { - buffer << QString("\"%1:%2\"") - .arg(QString::fromStdString(name).replace("\"", "\\\""), - QString::fromStdString(color).replace("\"", "\\\"")); + const pqxx::result tags = transaction.exec( + "SELECT * FROM notes WHERE note_id = " + transaction.quote(note->id()) + ); + + for (const auto &tag_row : tags) { + auto *tag = note->add_tags(); + tag->set_text(tag_row["text"].as()); + tag->set_color( + static_cast(tag_row["color"].as()) + ); } - return "{" + buffer.join(",") + "}"; + + transaction.commit(); + return *note; } diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp deleted file mode 100644 index 4370f4d..0000000 --- a/server/database/src/project_dao.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "project_dao.hpp" -#include -#include -#include -#include "database_manager.hpp" - -namespace DB { - -bool ProjectDAO::create_project(const std::string &name, int &id) { - // todo thread-safety - QSqlQuery query; - const QString query_str = - "INSERT INTO projects (name)" - " VALUES (:name)" - "RETURNING id"; - - const QVariantList params = {QString::fromStdString(name)}; - - bool is_successful = - DatabaseManager::get_instance().execute_query(query, query_str, params); - if (is_successful && query.next()) { - id = query.value(0).toInt(); - return true; - } - return false; -} - -bool ProjectDAO::get_project( - const int id, - std::string &name, - std::vector ¬es -) { - QSqlQuery query; - const QString query_str = - "SELECT name, array_to_string(notes, ',') FROM projects " - "WHERE id = ?"; - const QVariantList params = {id}; - const bool is_successful = - DatabaseManager::get_instance().execute_query(query, query_str, params); - - if (is_successful && query.next()) { - name = query.value("name").toString().toStdString(); - - for (const auto& i : query.value(1).toString().split(",")) { - notes.push_back(i.toInt()); - } - return true; - } - return false; -} - -bool ProjectDAO::add_note_to_project(const int project_id, const int note_id) { - QSqlQuery query; - const QString query_str = - "UPDATE projects " - "SET notes = array_append(notes, ?)" - "WHERE id = ?"; - const QVariantList params = {note_id, project_id}; - return DatabaseManager::get_instance().execute_query( - query, query_str, params - ); -} - -} // namespace DB \ No newline at end of file diff --git a/server/database/src/serialization.cpp b/server/database/src/serialization.cpp deleted file mode 100644 index 17e687e..0000000 --- a/server/database/src/serialization.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "serialization.hpp" -#include -#include -#include -#include "lr_dao.hpp" -#include "note.hpp" -#include "note_dao.hpp" -#include "project.hpp" -#include "project_dao.hpp" -#include "storage.hpp" - -bool Serialization::get_storage( - project_storage_model::Storage &storage, - const std::string &login -) { - std::vector projects; - if (LRDao::get_user_projects(login, projects)) { - for (auto p : projects) { - std::string project_name; - std::vector notes; - if (DB::ProjectDAO::get_project(p, project_name, notes)) { - Project project{p, project_name, ""}; - for (auto n : notes) { - if (n != 0) { - auto note = NoteDao::get_note_by_id(n); - project.add_note(note); - } - } - storage.add_project(Project(project)); - } else { - - return false; - } - } - } else { - - - return false; - } - return true; -} \ No newline at end of file diff --git a/server/main.cpp b/server/main.cpp index bbda3c9..8ff247b 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -2,6 +2,8 @@ int main() { ServerImplementation server; + std::cout << "Preparing...\n"; server.Run(50051); + std::cout << "Server started!\n"; return 0; } \ No newline at end of file From 068fcdbadddf1e6c074a48c429eb2621df5f2590 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Sat, 19 Apr 2025 21:55:18 +0300 Subject: [PATCH 16/47] feat: implement client calls --- client/CMakeLists.txt | 6 +- client/client/include/client_implementation.h | 5 +- client/client/include/update_requests.h | 56 +++++++++-- client/client/src/client_implementation.cpp | 30 +++++- client/client/src/update_requests.cpp | 95 ++++++++++++++++++- client/main.cpp | 14 +++ client/ui/note-widget/CMakeLists.txt | 3 +- .../ui/note-widget/src/note_edit_dialog.cpp | 29 +----- server/Service/src/update_service.cpp | 11 ++- server/database/include/note_dao.hpp | 2 +- server/database/src/note_dao.cpp | 23 +++-- server/main.cpp | 3 +- 12 files changed, 212 insertions(+), 65 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 98bb415..c81637b 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(EfficioTaskTracker LANGUAGES CXX) +project(start_client LANGUAGES CXX) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTORCC ON) @@ -13,9 +13,9 @@ find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) add_subdirectory(ui/note-widget) add_subdirectory(client) -add_executable(EfficioTaskTracker main.cpp) +add_executable(start_client main.cpp) -target_link_libraries(EfficioTaskTracker PRIVATE +target_link_libraries(start_client PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 85b380e..90f232f 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -1,4 +1,3 @@ - #ifndef CLIENTIMPLEMENTATION_H #define CLIENTIMPLEMENTATION_H @@ -13,8 +12,8 @@ class ClientImplementation { public: CompletionQueue *get_cq() { return &cq_; } std::shared_ptr get_channel() { return channel_; } - explicit ClientImplementation(std::shared_ptr channel); + explicit ClientImplementation(const std::shared_ptr &channel); void CompleteRpc(); }; -#endif //CLIENTIMPLEMENTATION_H +#endif //CLIENTIMPLEMENTATION_H \ No newline at end of file diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 5a5d9c7..2af4e0b 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -1,9 +1,10 @@ #ifndef UPDATE_REQUESTS_H #define UPDATE_REQUESTS_H -#include #include #include +#include +#include "common_client_call.h" using grpc::Channel; @@ -12,23 +13,62 @@ using Efficio_proto::Note; using Efficio_proto::Project; using Efficio_proto::Storage; +using Efficio_proto::GetNoteResponse; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::CreateNoteResponse; +using Efficio_proto::CreateNoteRequest; + class UpdateRequests { +public: + class GetNoteClientCall final : public CommonClientCall { + GetNoteResponse reply_; + std::unique_ptr> + responder_; + public: - class GetNoteClientCall; + GetNoteClientCall( + const GetNoteRequest &request, + grpc::CompletionQueue *cq, + const std::unique_ptr &stub + ); + void Proceed(bool ok = true) override; + GetNoteResponse get_reply(); + }; + class GetProjectClientCall; - class CreateNoteClientCall; + + class CreateNoteClientCall final : public CommonClientCall { + CreateNoteResponse reply_; + std::unique_ptr> + responder_; + + public: + CreateNoteClientCall( + const CreateNoteRequest &request, + grpc::CompletionQueue *cq, + const std::unique_ptr &stub + ); + void Proceed(bool ok = true) override; + CreateNoteResponse get_reply(); + }; + class CreateProjectClientCall; class TryJoinProjectClientCall; - bool get_note(Note *note); + bool fetch_note(Note *note) const; bool get_project(Project *project); - bool create_note(Note *note); + bool create_note(Note *note) const; bool create_project(Project *project); bool try_join_project(Project *project); - explicit UpdateRequests(std::shared_ptr channel): - stub_(Update::NewStub(channel)) {}; - private: + explicit UpdateRequests( + const std::shared_ptr &channel, + grpc::CompletionQueue *cq + ) + : cq_(cq), stub_(Update::NewStub(channel)) {}; + +private: + grpc::CompletionQueue *cq_; std::unique_ptr stub_; }; diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index db4f531..6192e8e 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -1,9 +1,8 @@ #include "client_implementation.h" +#include "update_requests.h" #include #include #include -#include - using grpc::Channel; using grpc::ClientAsyncResponseReader; @@ -11,8 +10,31 @@ using grpc::ClientContext; using grpc::CompletionQueue; using grpc::Status; -ClientImplementation::ClientImplementation(std::shared_ptr channel) : - channel_(channel) { }; +ClientImplementation::ClientImplementation( + const std::shared_ptr &channel +) + : channel_(channel) { + const UpdateRequests update_requests(channel, &cq_); + + // TODO: in future we should remove id setter [NOW IT'S ONLY FOR TEST] + const auto temp_note = new Note(); + update_requests.create_note(temp_note); + update_requests.fetch_note(temp_note); +} + +void ClientImplementation::CompleteRpc() { + void *tag; + bool ok = false; + while (cq_.Next(&tag, &ok)) { + const auto call = static_cast(tag); + if (call->status.ok()) { + call->Proceed(); + } else { + std::cout << "[CLIENT]: RPC failed\n"; + } + delete call; + } +} diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index b1df4f6..6a40a6d 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -1,6 +1,6 @@ -#include #include #include +#include #include using grpc::Channel; @@ -15,3 +15,96 @@ using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::Update; +UpdateRequests::GetNoteClientCall::GetNoteClientCall( + const GetNoteRequest &request, + CompletionQueue* cq, + const std::unique_ptr &stub +) : responder_(stub->AsyncGetNote(&context, request, cq)) { + context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(5)); + responder_->Finish(&reply_, &status, this); + std::cout << "[CLIENT]: FETCH NOTE REQUEST SENT\n"; +} + +void UpdateRequests::GetNoteClientCall::Proceed(const bool ok) { + if (!ok) { + std::cout << "[CLIENT WARNING]: RPC failed\n"; + } + delete this; +} + +GetNoteResponse UpdateRequests::GetNoteClientCall::get_reply() { + if (!status.ok()) { + throw std::runtime_error(status.error_message()); + } + return reply_; +} + +UpdateRequests::CreateNoteClientCall::CreateNoteClientCall( + const CreateNoteRequest &request, + CompletionQueue *cq, + const std::unique_ptr &stub +) + : responder_(stub->AsyncCreateNote(&context, request, cq)) { + context.set_deadline( + std::chrono::system_clock::now() + std::chrono::seconds(5) + ); + responder_->Finish(&reply_, &status, this); + std::cout << "[CLIENT]: CREATE NOTE REQUEST SENT\n"; +} + +void UpdateRequests::CreateNoteClientCall::Proceed(const bool ok) { + if (!ok) { + std::cout << "[CLIENT WARNING]: RPC failed\n"; + } + delete this; +} + +CreateNoteResponse UpdateRequests::CreateNoteClientCall::get_reply() { + return reply_; +} + +bool UpdateRequests::fetch_note(Note *note) const { + GetNoteRequest request; + request.set_id(note->id()); + + const auto call = new GetNoteClientCall(request, cq_, stub_); + + void *tag; + bool ok = false; + if (!cq_->Next(&tag, &ok)) { + std::cout << "[CLIENT]: Completion queue failed\n"; + return false; + } + + if (!ok || !call->get_reply().has_note()) { + std::cout << "[CLIENT WARNING]: no note in reply\n"; + return false; + } + + *note = call->get_reply().note(); + std::cout << "[CLIENT]: GOT NOTE - " << note->title() << "\n"; + return true; +} + +bool UpdateRequests::create_note(Note *note) const { + const CreateNoteRequest request; + + const auto call = new CreateNoteClientCall(request, cq_, stub_); + + void *tag; + bool ok = false; + if (!cq_->Next(&tag, &ok)) { + std::cout << "[CLIENT]: Completion queue failed\n"; + return false; + } + + if (!ok || !call->get_reply().has_note()) { + std::cout << "[CLIENT WARNING]: no note in reply\n"; + return false; + } + + *note = call->get_reply().note(); + std::cout << "[CLIENT]: CREATED NOTE id=" << note->id() << "\n"; + return true; +} + diff --git a/client/main.cpp b/client/main.cpp index 51b956c..b71b731 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -2,6 +2,8 @@ #include #include #include +#include +#include "client/include/client_implementation.h" #include "note_edit_dialog.h" int main(int argc, char *argv[]) { @@ -18,6 +20,18 @@ int main(int argc, char *argv[]) { } } + const auto channel = CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); + + std::thread requests([&] { + try { + ClientImplementation client(channel); + client.CompleteRpc(); + } catch (const std::exception &e) { + std::cout << "[CLIENT ERROR]: " << e.what() << std::endl; + } + }); + requests.detach(); + NoteEditDialog dialog(nullptr, new Note); dialog.show(); return QApplication::exec(); diff --git a/client/ui/note-widget/CMakeLists.txt b/client/ui/note-widget/CMakeLists.txt index 6d842e7..b879264 100644 --- a/client/ui/note-widget/CMakeLists.txt +++ b/client/ui/note-widget/CMakeLists.txt @@ -38,5 +38,4 @@ target_include_directories(NoteWidget $ ) -# TODO: remove "Service" and "efficio-rpc" from this list -target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto Service efficio-rpc) \ No newline at end of file +target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto Client) \ No newline at end of file diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index fe4b508..659b927 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -12,6 +12,7 @@ #include "tags_dialog.h" #include #include "efficio-rpc-proto/efficio.grpc.pb.h" +#include "update_requests.h" using grpc::Channel; using grpc::ClientContext; @@ -196,31 +197,5 @@ bool NoteEditDialog::try_save_note() const { note_->set_text(ui_->descriptionTextEdit->toPlainText().toStdString()); note_->set_date(ui_->dateEdit->date().toString("yyyy-MM-dd").toStdString()); - // TODO: move to client part - const auto channel = - CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); - - ClientContext context; - context.set_deadline( - std::chrono::system_clock::now() + std::chrono::seconds(5) - ); - - Efficio_proto::CreateNoteRequest request; - request.mutable_user()->set_login("test"); - - try { - Update::Stub stub(channel); - Efficio_proto::CreateNoteResponse response; - - Status status = stub.CreateNote(&context, request, &response); - - if (!status.ok()) { - throw std::runtime_error(status.error_message()); - } - return true; - } - catch (const std::exception& e) { - std::cerr << "Error creating note: " << e.what() << std::endl; - return false; - } + return true; } \ No newline at end of file diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 6559117..d28ba90 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -36,8 +36,11 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { GetNoteResponse response; - const auto note = NoteDao::get_note(request_.id(), request_.user().login()); + const auto note = NoteDao::get_note(request_.id()); response.mutable_note()->CopyFrom(note); + std::cout << "[SERVER]: FETCH NOTE REQUEST id=" << request_.id() + << ", title=" << response.mutable_note()->title() + << std::endl; responder_.Finish(response, grpc::Status::OK, this); status_ = FINISH; @@ -77,9 +80,13 @@ void UpdateService::CreateNoteServerCall::Proceed(const bool ok) { new CreateNoteServerCall(service_, cq_); CreateNoteResponse response; - const auto new_note = NoteDao::initialize_note_for_user(request_.user().login()); + const auto new_note = + NoteDao::initialize_note_for_user(request_.user().login()); response.mutable_note()->CopyFrom(new_note); + std::cout << "[SERVER]: CREATE NOTE REQUEST id=" << new_note.id() + << std::endl; + responder_.Finish(response, grpc::Status::OK, this); status_ = FINISH; break; diff --git a/server/database/include/note_dao.hpp b/server/database/include/note_dao.hpp index d973ae9..f73730a 100644 --- a/server/database/include/note_dao.hpp +++ b/server/database/include/note_dao.hpp @@ -10,7 +10,7 @@ class NoteDao { NoteDao() = default; static Note initialize_note_for_user(const std::string& login); static bool update_note(const Note ¬e); - [[nodiscard]] static Note get_note(int note_id, const std::string &login); + [[nodiscard]] static Note get_note(int note_id); }; #endif // NOTEDAO_HPP \ No newline at end of file diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index aaebf92..0ee8336 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -54,13 +54,12 @@ bool NoteDao::update_note(const Note ¬e) { return true; } -Note NoteDao::get_note(const int note_id, const std::string &login) { +Note NoteDao::get_note(const int note_id) { pqxx::connection connection(DatabaseManager::get_connection_string()); pqxx::work transaction(connection); const std::string query = - "SELECT * FROM notes WHERE id = " + transaction.quote(note_id) + - " AND user_id = " + transaction.quote(login); + "SELECT * FROM notes WHERE id = " + transaction.quote(note_id); const pqxx::result result = transaction.exec(query); @@ -74,7 +73,7 @@ Note NoteDao::get_note(const int note_id, const std::string &login) { note->set_id(row["id"].as()); note->set_title(row["title"].as()); - note->set_text(row["text"].as()); + note->set_text(row["content"].as()); if (!row["date"].is_null()) { note->set_date(row["date"].as()); @@ -85,16 +84,16 @@ Note NoteDao::get_note(const int note_id, const std::string &login) { } const pqxx::result tags = transaction.exec( - "SELECT * FROM notes WHERE note_id = " + transaction.quote(note->id()) + "SELECT * FROM notes WHERE id = " + transaction.quote(note->id()) ); - for (const auto &tag_row : tags) { - auto *tag = note->add_tags(); - tag->set_text(tag_row["text"].as()); - tag->set_color( - static_cast(tag_row["color"].as()) - ); - } + // for (const auto &tag_row : tags) { + // auto *tag = note->add_tags(); + // tag->set_text(tag_row["text"].as()); + // tag->set_color( + // static_cast(tag_row["color"].as()) + // ); + // } transaction.commit(); return *note; diff --git a/server/main.cpp b/server/main.cpp index 8ff247b..826ae64 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -2,8 +2,7 @@ int main() { ServerImplementation server; - std::cout << "Preparing...\n"; + std::cout << "[SERVER]: WAITING FOR REQUESTS...\n"; server.Run(50051); - std::cout << "Server started!\n"; return 0; } \ No newline at end of file From 72783d24b8cf7e8f4c1a30fa274ca790e9b42ca0 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Fri, 2 May 2025 15:50:11 +0300 Subject: [PATCH 17/47] feat: add update note endpoint --- CMakeLists.txt | 2 + client/CMakeLists.txt | 1 + client/client/CMakeLists.txt | 1 + client/client/include/client_implementation.h | 17 ++- client/client/include/common_client_call.h | 9 +- client/client/include/update_requests.h | 29 ++++- client/client/src/client_implementation.cpp | 19 +-- client/client/src/update_requests.cpp | 61 ++++++++- client/main.cpp | 12 +- .../ui/note-widget/include/note_edit_dialog.h | 15 ++- .../ui/note-widget/src/note_edit_dialog.cpp | 123 +++++++++++++----- client/ui/note-widget/src/tags_dialog.cpp | 28 ++-- proto/efficio-rpc-proto/efficio.proto | 13 ++ server/Service/include/common_server_call.h | 17 ++- .../Service/include/server_implementation.h | 7 +- server/Service/include/update_service.h | 40 ++++-- server/Service/src/server_implementation.cpp | 7 +- server/Service/src/update_service.cpp | 65 ++++++--- server/database/CMakeLists.txt | 2 +- server/database/include/database_manager.hpp | 2 +- server/database/include/note_dao.hpp | 2 +- server/database/src/database_manager.cpp | 3 +- server/database/src/note_dao.cpp | 65 ++++++--- 23 files changed, 389 insertions(+), 151 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e46327..30ee9e3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.16) project(test-server) +add_compile_options(-fsanitize=address,undefined,leak) +add_link_options(-fsanitize=address,undefined,leak) add_subdirectory(server) add_subdirectory(proto) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index c81637b..83f6dc5 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -23,4 +23,5 @@ target_link_libraries(start_client PRIVATE Database NoteWidget model-proto + Client ) diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt index 79d0467..ce52c7c 100644 --- a/client/client/CMakeLists.txt +++ b/client/client/CMakeLists.txt @@ -21,6 +21,7 @@ target_link_libraries(Client protobuf::libprotobuf gRPC::grpc gRPC::grpc++ + Service ) target_include_directories(Client diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 90f232f..29138e6 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -2,18 +2,25 @@ #define CLIENTIMPLEMENTATION_H #include +#include "update_requests.h" -using grpc::CompletionQueue; using grpc::Channel; class ClientImplementation { - CompletionQueue cq_; + grpc::CompletionQueue cq_; std::shared_ptr channel_; + UpdateRequests update_requests_; + public: - CompletionQueue *get_cq() { return &cq_; } - std::shared_ptr get_channel() { return channel_; } + std::shared_ptr get_channel() { + return channel_; + } + explicit ClientImplementation(const std::shared_ptr &channel); void CompleteRpc(); + + bool update_note(Note *note) const; + bool create_note(Note *note) const; }; -#endif //CLIENTIMPLEMENTATION_H \ No newline at end of file +#endif // CLIENTIMPLEMENTATION_H \ No newline at end of file diff --git a/client/client/include/common_client_call.h b/client/client/include/common_client_call.h index 7c52ef1..0ba1d9b 100644 --- a/client/client/include/common_client_call.h +++ b/client/client/include/common_client_call.h @@ -6,12 +6,8 @@ using grpc::ClientContext; using grpc::Status; - -class CommonClientCall -{ +class CommonClientCall { public: - - explicit CommonClientCall() = default; virtual ~CommonClientCall() = default; @@ -21,5 +17,4 @@ class CommonClientCall virtual void Proceed(bool = true) = 0; }; - -#endif //COMMON_CLIENT_CALL_H +#endif // COMMON_CLIENT_CALL_H diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 2af4e0b..181062c 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -8,18 +8,35 @@ using grpc::Channel; -using Efficio_proto::Update; using Efficio_proto::Note; using Efficio_proto::Project; using Efficio_proto::Storage; +using Efficio_proto::Update; -using Efficio_proto::GetNoteResponse; -using Efficio_proto::GetNoteRequest; -using Efficio_proto::CreateNoteResponse; using Efficio_proto::CreateNoteRequest; +using Efficio_proto::CreateNoteResponse; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; +using Efficio_proto::UpdateNoteRequest; +using Efficio_proto::UpdateNoteResponse; class UpdateRequests { public: + class UpdateNoteClientCall final : public CommonClientCall { + UpdateNoteResponse reply_; + std::unique_ptr> + responder_; + + public: + UpdateNoteClientCall( + const UpdateNoteRequest &request, + grpc::CompletionQueue *cq, + const std::unique_ptr &stub + ); + void Proceed(bool ok = true) override; + UpdateNoteResponse get_reply(); + }; + class GetNoteClientCall final : public CommonClientCall { GetNoteResponse reply_; std::unique_ptr> @@ -55,6 +72,7 @@ class UpdateRequests { class CreateProjectClientCall; class TryJoinProjectClientCall; + bool update_note(Note *note) const; bool fetch_note(Note *note) const; bool get_project(Project *project); bool create_note(Note *note) const; @@ -72,5 +90,4 @@ class UpdateRequests { std::unique_ptr stub_; }; - -#endif //UPDATE_REQUESTS_H +#endif // UPDATE_REQUESTS_H diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index 6192e8e..0e9ba13 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -1,8 +1,9 @@ #include "client_implementation.h" -#include "update_requests.h" #include #include #include +#include "update_requests.h" +#include "update_service.h" using grpc::Channel; using grpc::ClientAsyncResponseReader; @@ -13,13 +14,7 @@ using grpc::Status; ClientImplementation::ClientImplementation( const std::shared_ptr &channel ) - : channel_(channel) { - const UpdateRequests update_requests(channel, &cq_); - - // TODO: in future we should remove id setter [NOW IT'S ONLY FOR TEST] - const auto temp_note = new Note(); - update_requests.create_note(temp_note); - update_requests.fetch_note(temp_note); + : channel_(channel), update_requests_(channel, &cq_) { } void ClientImplementation::CompleteRpc() { @@ -38,3 +33,11 @@ void ClientImplementation::CompleteRpc() { delete call; } } + +bool ClientImplementation::update_note(Note *note) const { + return update_requests_.update_note(note); +} + +bool ClientImplementation::create_note(Note *note) const { + return update_requests_.create_note(note); +} diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 6a40a6d..3ee0f82 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -9,18 +9,45 @@ using grpc::ClientContext; using grpc::CompletionQueue; using grpc::Status; -using Efficio_proto::GetProjectRequest; -using Efficio_proto::GetProjectResponse; using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; +using Efficio_proto::GetProjectRequest; +using Efficio_proto::GetProjectResponse; using Efficio_proto::Update; +UpdateRequests::UpdateNoteClientCall::UpdateNoteClientCall( + const UpdateNoteRequest &request, + grpc::CompletionQueue *cq, + const std::unique_ptr &stub +) + : responder_(stub->AsyncUpdateNote(&context, request, cq)) { + context.set_deadline( + std::chrono::system_clock::now() + std::chrono::seconds(5) + ); + responder_->Finish(&reply_, &status, this); + std::cout << "[CLIENT]: UPDATE NOTE REQUEST SENT\n"; +} + +void UpdateRequests::UpdateNoteClientCall::Proceed(bool ok) { + if (!ok) { + std::cout << "[CLIENT WARNING]: RPC failed\n"; + } + delete this; +} + +UpdateNoteResponse UpdateRequests::UpdateNoteClientCall::get_reply() { + return reply_; +} + UpdateRequests::GetNoteClientCall::GetNoteClientCall( const GetNoteRequest &request, - CompletionQueue* cq, + CompletionQueue *cq, const std::unique_ptr &stub -) : responder_(stub->AsyncGetNote(&context, request, cq)) { - context.set_deadline(std::chrono::system_clock::now() + std::chrono::seconds(5)); +) + : responder_(stub->AsyncGetNote(&context, request, cq)) { + context.set_deadline( + std::chrono::system_clock::now() + std::chrono::seconds(5) + ); responder_->Finish(&reply_, &status, this); std::cout << "[CLIENT]: FETCH NOTE REQUEST SENT\n"; } @@ -63,6 +90,29 @@ CreateNoteResponse UpdateRequests::CreateNoteClientCall::get_reply() { return reply_; } +bool UpdateRequests::update_note(Note *note) const { + UpdateNoteRequest request; + request.mutable_note()->CopyFrom(*note); + + const auto call = new UpdateNoteClientCall(request, cq_, stub_); + + void *tag; + bool ok = false; + if (!cq_->Next(&tag, &ok)) { + std::cout << "[CLIENT]: Completion queue failed\n"; + return false; + } + + if (!ok) { + std::cout << "[CLIENT WARNING]: no note in reply\n"; + return false; + } + + *note = call->get_reply().note(); + std::cout << "[CLIENT]: UPDATE NOTE - " << note->title() << "\n"; + return true; +} + bool UpdateRequests::fetch_note(Note *note) const { GetNoteRequest request; request.set_id(note->id()); @@ -107,4 +157,3 @@ bool UpdateRequests::create_note(Note *note) const { std::cout << "[CLIENT]: CREATED NOTE id=" << note->id() << "\n"; return true; } - diff --git a/client/main.cpp b/client/main.cpp index b71b731..336fb1c 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -3,7 +3,7 @@ #include #include #include -#include "client/include/client_implementation.h" +#include "client_implementation.h" #include "note_edit_dialog.h" int main(int argc, char *argv[]) { @@ -20,11 +20,12 @@ int main(int argc, char *argv[]) { } } - const auto channel = CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); + const auto channel = + CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); + ClientImplementation client(channel); std::thread requests([&] { try { - ClientImplementation client(channel); client.CompleteRpc(); } catch (const std::exception &e) { std::cout << "[CLIENT ERROR]: " << e.what() << std::endl; @@ -32,7 +33,10 @@ int main(int argc, char *argv[]) { }); requests.detach(); - NoteEditDialog dialog(nullptr, new Note); + const auto test_note = new Note(); + client.create_note(test_note); + + NoteEditDialog dialog(&client, nullptr, test_note); dialog.show(); return QApplication::exec(); } \ No newline at end of file diff --git a/client/ui/note-widget/include/note_edit_dialog.h b/client/ui/note-widget/include/note_edit_dialog.h index f141a15..cea9350 100644 --- a/client/ui/note-widget/include/note_edit_dialog.h +++ b/client/ui/note-widget/include/note_edit_dialog.h @@ -4,6 +4,7 @@ #include #include #include +#include "client_implementation.h" #include "model-proto/model.pb.h" #include "tags_dialog.h" @@ -22,8 +23,9 @@ class NoteEditDialog final : public QDialog { public: explicit NoteEditDialog( - QWidget* parent = nullptr, - Note* note = nullptr + ClientImplementation *client, + QWidget *parent = nullptr, + Note *note = nullptr ); ~NoteEditDialog() override; @@ -39,7 +41,7 @@ private slots: void setup_connections(); void setup_ui(); - void add_member_avatar(const std::string& member); + void add_member_avatar(const std::string &member); void clear_member_avatars(); void update_tags_display(); @@ -47,11 +49,12 @@ private slots: [[nodiscard]] bool try_save_note() const; - Ui::NoteEditDialog* ui_{}; + Ui::NoteEditDialog *ui_{}; std::vector> member_avatars_; std::vector> tag_labels_; QList selected_tags_; - Note* note_; + Note *note_; + ClientImplementation *client_; }; -#endif // NOTE_EDIT_DIALOG_H \ No newline at end of file +#endif // NOTE_EDIT_DIALOG_H \ No newline at end of file diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index 659b927..bef7c89 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -1,4 +1,6 @@ #include "note_edit_dialog.h" +#include +#include #include #include #include @@ -6,23 +8,26 @@ #include #include #include -#include #include "./ui_note_edit_dialog.h" +#include "efficio-rpc-proto/efficio.grpc.pb.h" #include "note_edit_dialog_styles.h" #include "tags_dialog.h" -#include -#include "efficio-rpc-proto/efficio.grpc.pb.h" #include "update_requests.h" +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; +using Efficio_proto::Update; using grpc::Channel; using grpc::ClientContext; using grpc::Status; -using Efficio_proto::Update; -using Efficio_proto::GetNoteRequest; -using Efficio_proto::GetNoteResponse; -NoteEditDialog::NoteEditDialog(QWidget* parent, Note* note) +NoteEditDialog::NoteEditDialog( + ClientImplementation *client, + QWidget *parent, + Note *note +) : QDialog(parent), + client_(client), ui_(new Ui::NoteEditDialog), note_(note) { if (note_ == nullptr) { @@ -48,7 +53,9 @@ void NoteEditDialog::init_basic_fields() { void NoteEditDialog::init_additional_fields() { if (!note_->date().empty()) { - QDate date = QDate::fromString(QString::fromStdString(note_->date()), "yyyy-MM-dd"); + QDate date = QDate::fromString( + QString::fromStdString(note_->date()), "yyyy-MM-dd" + ); if (date.isValid()) { ui_->dateEdit->setDate(date); ui_->dateLabel->setVisible(true); @@ -58,7 +65,7 @@ void NoteEditDialog::init_additional_fields() { if (!note_->members().empty()) { ui_->membersLabel->setVisible(true); - for (const auto& member : note_->members()) { + for (const auto &member : note_->members()) { add_member_avatar(member); } ui_->joinButton->setText("Покинуть"); @@ -66,7 +73,7 @@ void NoteEditDialog::init_additional_fields() { if (!note_->tags().empty()) { ui_->tagsLabel->setVisible(true); - for (const auto& tag : note_->tags()) { + for (const auto &tag : note_->tags()) { TagsDialog::Tag tag_info; tag_info.is_checked = true; tag_info.color = tag.color(); @@ -82,16 +89,30 @@ void NoteEditDialog::init_additional_fields() { } void NoteEditDialog::setup_connections() { - connect(ui_->saveButton, &QPushButton::clicked, this, &NoteEditDialog::on_save_button_click); - connect(ui_->cancelButton, &QPushButton::clicked, this, &NoteEditDialog::reject); - connect(ui_->joinButton, &QPushButton::clicked, this, &NoteEditDialog::on_join_button_click); - connect(ui_->addMembersButton, &QPushButton::clicked, this, &NoteEditDialog::on_add_members_button_click); + connect( + ui_->saveButton, &QPushButton::clicked, this, + &NoteEditDialog::on_save_button_click + ); + connect( + ui_->cancelButton, &QPushButton::clicked, this, &NoteEditDialog::reject + ); + connect( + ui_->joinButton, &QPushButton::clicked, this, + &NoteEditDialog::on_join_button_click + ); + connect( + ui_->addMembersButton, &QPushButton::clicked, this, + &NoteEditDialog::on_add_members_button_click + ); connect(ui_->addDateButton, &QPushButton::clicked, this, [this]() { const bool is_visible = ui_->dateLabel->isVisible(); ui_->dateLabel->setVisible(!is_visible); ui_->dateEdit->setVisible(!is_visible); }); - connect(ui_->addTagsButton, &QPushButton::clicked, this, &NoteEditDialog::on_add_tags_button_click); + connect( + ui_->addTagsButton, &QPushButton::clicked, this, + &NoteEditDialog::on_add_tags_button_click + ); } void NoteEditDialog::setup_ui() { @@ -102,11 +123,18 @@ void NoteEditDialog::setup_ui() { void NoteEditDialog::on_save_button_click() { if (try_save_note()) { - QMessageBox::information(this, "Заметка сохранена", + QMessageBox::information( + this, "Заметка сохранена", QString("Заголовок: %1\nСодержимое: %2") - .arg(ui_->titleLineEdit->text(), ui_->descriptionTextEdit->toPlainText())); + .arg( + ui_->titleLineEdit->text(), + ui_->descriptionTextEdit->toPlainText() + ) + ); } else { - QMessageBox::information(this, "Ошибка", "Не удалось сохранить заметку"); + QMessageBox::information( + this, "Ошибка", "Не удалось сохранить заметку" + ); } } @@ -126,7 +154,7 @@ void NoteEditDialog::on_join_button_click() { } } -void NoteEditDialog::add_member_avatar(const std::string& member) { +void NoteEditDialog::add_member_avatar(const std::string &member) { QPixmap pixmap(32, 32); pixmap.fill(Qt::transparent); QPainter painter(&pixmap); @@ -142,7 +170,7 @@ void NoteEditDialog::add_member_avatar(const std::string& member) { } void NoteEditDialog::clear_member_avatars() { - for (auto& avatar : member_avatars_) { + for (auto &avatar : member_avatars_) { ui_->avatarsLayout->removeWidget(avatar.get()); } member_avatars_.clear(); @@ -161,7 +189,7 @@ void NoteEditDialog::on_add_tags_button_click() { } void NoteEditDialog::update_tags_display() { - for (auto& tag_label : tag_labels_) { + for (auto &tag_label : tag_labels_) { ui_->tagsLayout->removeWidget(tag_label.get()); } tag_labels_.clear(); @@ -179,23 +207,52 @@ void NoteEditDialog::update_tags_display() { QString NoteEditDialog::create_tag_style_sheet(const int color_code) { return QString( - "background-color: %1; " - "color: white; " - "padding: 9px 10px; " - "border-radius: 5px; " - "font-family: 'Arial'; " - "font-size: 12px; " - "font-weight: bold;" - "width: 40px;" - "height: 25px;" - ).arg(TagsDialog::get_color_by_code(color_code)); + "background-color: %1; " + "color: white; " + "padding: 9px 10px; " + "border-radius: 5px; " + "font-family: 'Arial'; " + "font-size: 12px; " + "font-weight: bold;" + "width: 40px;" + "height: 25px;" + ) + .arg(TagsDialog::get_color_by_code(color_code)); +} + +Efficio_proto::Note_tag_colors color_code_to_note_tag_colors( + const int color_code +) { + switch (color_code) { + case 0: + return Efficio_proto::Note_tag_colors_Red; + case 1: + return Efficio_proto::Note_tag_colors_Blue; + case 2: + return Efficio_proto::Note_tag_colors_Pink; + case 3: + return Efficio_proto::Note_tag_colors_Green; + case 4: + return Efficio_proto::Note_tag_colors_Yellow; + } } bool NoteEditDialog::try_save_note() const { - // TODO: add saving members and tags note_->set_title(ui_->titleLineEdit->text().toStdString()); note_->set_text(ui_->descriptionTextEdit->toPlainText().toStdString()); note_->set_date(ui_->dateEdit->date().toString("yyyy-MM-dd").toStdString()); - return true; + note_->clear_members(); + if (!member_avatars_.empty()) { + note_->add_members("TODO"); + } + + note_->clear_tags(); + for (const auto &tag : selected_tags_) { + auto *new_tag = note_->add_tags(); + new_tag->set_text(tag.name.toStdString()); + new_tag->set_color(color_code_to_note_tag_colors(tag.color)); + } + + return client_->update_note(note_); } \ No newline at end of file diff --git a/client/ui/note-widget/src/tags_dialog.cpp b/client/ui/note-widget/src/tags_dialog.cpp index 74b933b..a8b9f11 100644 --- a/client/ui/note-widget/src/tags_dialog.cpp +++ b/client/ui/note-widget/src/tags_dialog.cpp @@ -37,11 +37,11 @@ void TagsDialog::setup_ui() { tag_layout->addWidget(check_boxes_[i].get()); color_combo_boxes_[i] = std::make_unique(this); - color_combo_boxes_[i]->addItem("Красный", get_color_by_code(0)); - color_combo_boxes_[i]->addItem("Синий", get_color_by_code(1)); - color_combo_boxes_[i]->addItem("Розовый", get_color_by_code(2)); - color_combo_boxes_[i]->addItem("Зеленый", get_color_by_code(3)); - color_combo_boxes_[i]->addItem("Желтый", get_color_by_code(4)); + color_combo_boxes_[i]->addItem("Красный", 0); + color_combo_boxes_[i]->addItem("Синий", 1); + color_combo_boxes_[i]->addItem("Розовый", 2); + color_combo_boxes_[i]->addItem("Зеленый", 3); + color_combo_boxes_[i]->addItem("Желтый", 4); tag_layout->addWidget(color_combo_boxes_[i].get()); name_line_edits_[i] = std::make_unique(this); @@ -88,11 +88,17 @@ QList TagsDialog::get_selected_tags() const { QString TagsDialog::get_color_by_code(const int code) { switch (code) { - case 0: return "#e7624b"; - case 1: return "#165d7b"; - case 2: return "#bd6dab"; - case 3: return "#00b16b"; - case 4: return "#e69f00"; - default: return "ffffff"; + case 0: + return "#e7624b"; + case 1: + return "#165d7b"; + case 2: + return "#bd6dab"; + case 3: + return "#00b16b"; + case 4: + return "#e69f00"; + default: + return "ffffff"; } } \ No newline at end of file diff --git a/proto/efficio-rpc-proto/efficio.proto b/proto/efficio-rpc-proto/efficio.proto index c5a38f6..34d70fd 100644 --- a/proto/efficio-rpc-proto/efficio.proto +++ b/proto/efficio-rpc-proto/efficio.proto @@ -5,6 +5,7 @@ import "model-proto/model.proto"; package Efficio_proto; service Update { + rpc UpdateNote(UpdateNoteRequest) returns (UpdateNoteResponse) {} rpc GetNote (GetNoteRequest) returns (GetNoteResponse) {} rpc GetProject (GetProjectRequest) returns (GetProjectResponse) {} rpc CreateNote (CreateNoteRequest) returns (CreateNoteResponse) {} @@ -17,6 +18,18 @@ service Auth { rpc TryRegister1User (AuthRequest) returns (AuthResponse) {} } +message UpdateNoteRequest { + Note note = 1; +} + +message UpdateNoteResponse { + oneof response { + bool ok = 1; + Note note = 2; + string error_text = 3; + } +} + message GetNoteRequest { User user = 1; int32 id = 2; diff --git a/server/Service/include/common_server_call.h b/server/Service/include/common_server_call.h index bf9d190..a5f4fe8 100644 --- a/server/Service/include/common_server_call.h +++ b/server/Service/include/common_server_call.h @@ -2,27 +2,26 @@ #define CALL_DATA_H #include - using grpc::ServerCompletionQueue; using grpc::ServerContext; -class CommonServerCall -{ +class CommonServerCall { public: - ServerCompletionQueue* cq_; + ServerCompletionQueue *cq_; ServerContext ctx_; + enum CallStatus { CREATE, PROCESS, FINISH }; + CallStatus status_; + public: - explicit CommonServerCall(ServerCompletionQueue* cq): - cq_(cq), - status_(CREATE) - {} + explicit CommonServerCall(ServerCompletionQueue *cq) + : cq_(cq), status_(CREATE) { + } virtual ~CommonServerCall() = default; virtual void Proceed(bool = true) = 0; }; - #endif \ No newline at end of file diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h index bb88749..cee1658 100644 --- a/server/Service/include/server_implementation.h +++ b/server/Service/include/server_implementation.h @@ -10,9 +10,10 @@ using grpc::ServerCompletionQueue; class ServerImplementation final { std::unique_ptr cq_; std::unique_ptr server_; - public: + +public: void Run(uint16_t port); - void HandleRPCs(UpdateService& update_service) const; + void HandleRPCs(UpdateService &update_service) const; }; -#endif //SERVERIMPLEMENTATION_H +#endif // SERVERIMPLEMENTATION_H diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 6e971e8..42cdb86 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -4,37 +4,55 @@ #include #include #include -#include #include "common_server_call.h" -using grpc::ServerContext; using grpc::ServerAsyncResponseWriter; +using grpc::ServerContext; +using Efficio_proto::CreateNoteRequest; +using Efficio_proto::CreateNoteResponse; using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::Update; -using Efficio_proto::CreateNoteRequest; -using Efficio_proto::CreateNoteResponse; +using Efficio_proto::UpdateNoteRequest; +using Efficio_proto::UpdateNoteResponse; class UpdateService final { Update::AsyncService service_; ServerContext ctx_; std::unique_ptr cq_; std::unique_ptr server_; + public: + class UpdateNoteServerCall final : public CommonServerCall { + UpdateNoteRequest request_; + ServerAsyncResponseWriter responder_; + UpdateService &service_; + + public: + explicit UpdateNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq + ); + void Proceed(bool ok) override; + }; + class GetNoteServerCall final : public CommonServerCall { GetNoteRequest request_; ServerAsyncResponseWriter responder_; UpdateService &service_; public: - explicit GetNoteServerCall(UpdateService &service, ServerCompletionQueue *cq); + explicit GetNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq + ); void Proceed(bool ok) override; }; class GetProjectServerCall final : public CommonServerCall { public: - explicit GetProjectServerCall(UpdateService& service); + explicit GetProjectServerCall(UpdateService &service); void Proceed(bool ok) override; // TODO: finish this call @@ -47,12 +65,16 @@ class UpdateService final { CreateNoteRequest request_; ServerAsyncResponseWriter responder_; UpdateService &service_; + public: - explicit CreateNoteServerCall(UpdateService& service, ServerCompletionQueue *cq); + explicit CreateNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq + ); void Proceed(bool ok) override; }; - Update::AsyncService& get_service(); + Update::AsyncService &get_service(); }; -#endif //UPDATE_SERVICE_H +#endif // UPDATE_SERVICE_H diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index 2a63f4b..c61aae4 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -16,13 +16,14 @@ void ServerImplementation::Run(const uint16_t port) { HandleRPCs(update_service); } -void ServerImplementation::HandleRPCs(UpdateService& update_service) const { +void ServerImplementation::HandleRPCs(UpdateService &update_service) const { new UpdateService::GetNoteServerCall(update_service, cq_.get()); new UpdateService::CreateNoteServerCall(update_service, cq_.get()); + new UpdateService::UpdateNoteServerCall(update_service, cq_.get()); - void* tag; + void *tag; bool ok; while (cq_->Next(&tag, &ok)) { - static_cast(tag)->Proceed(ok); + static_cast(tag)->Proceed(ok); } } diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index d28ba90..a53ff3b 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -8,18 +8,54 @@ using Efficio_proto::Note; using grpc::ServerAsyncResponseWriter; -UpdateService::GetNoteServerCall::GetNoteServerCall(UpdateService &service, ServerCompletionQueue *cq) - : CommonServerCall(cq), - responder_(&ctx_), - service_(service) { +UpdateService::UpdateNoteServerCall::UpdateNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + service_.service_.RequestUpdateNote( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + status_ = PROCESS; +} +void UpdateService::UpdateNoteServerCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + + switch (status_) { + case PROCESS: { + new UpdateNoteServerCall(service_, cq_); + + UpdateNoteResponse response; + std::cout << "[SERVER]: PREPARING UPDATE\n"; + + NoteDao::update_note(request_.note()); + response.mutable_note()->CopyFrom(request_.note()); + std::cout << "[SERVER]: UPDATE NOTE REQUEST id=" + << request_.note().id() + << ", title=" << response.mutable_note()->title() + << std::endl; + responder_.Finish(response, grpc::Status::OK, this); + status_ = FINISH; + break; + } + case FINISH: { + delete this; + break; + } + } +} + +UpdateService::GetNoteServerCall::GetNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { service_.service_.RequestGetNote( - &ctx_, - &request_, - &responder_, - cq_, - cq_, - this + &ctx_, &request_, &responder_, cq_, cq_, this ); status_ = PROCESS; } @@ -59,12 +95,7 @@ UpdateService::CreateNoteServerCall::CreateNoteServerCall( ) : CommonServerCall(cq), responder_(&ctx_), service_(service) { service_.service_.RequestCreateNote( - &ctx_, - &request_, - &responder_, - cq_, - cq_, - this + &ctx_, &request_, &responder_, cq_, cq_, this ); status_ = PROCESS; } @@ -98,6 +129,6 @@ void UpdateService::CreateNoteServerCall::Proceed(const bool ok) { } } -Update::AsyncService& UpdateService::get_service() { +Update::AsyncService &UpdateService::get_service() { return service_; } \ No newline at end of file diff --git a/server/database/CMakeLists.txt b/server/database/CMakeLists.txt index fd87718..ce81cf2 100644 --- a/server/database/CMakeLists.txt +++ b/server/database/CMakeLists.txt @@ -18,4 +18,4 @@ target_include_directories(Database $ ) -target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql model-proto pqxx) \ No newline at end of file +target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) \ No newline at end of file diff --git a/server/database/include/database_manager.hpp b/server/database/include/database_manager.hpp index 8f3fd17..7c7956c 100644 --- a/server/database/include/database_manager.hpp +++ b/server/database/include/database_manager.hpp @@ -7,7 +7,7 @@ class DatabaseManager final { public: static DatabaseManager &get_instance(); - pqxx::connection& get_connection(); + pqxx::connection &get_connection(); [[nodiscard]] static std::string get_connection_string(); private: diff --git a/server/database/include/note_dao.hpp b/server/database/include/note_dao.hpp index f73730a..6cd8cf4 100644 --- a/server/database/include/note_dao.hpp +++ b/server/database/include/note_dao.hpp @@ -8,7 +8,7 @@ using Efficio_proto::Note; class NoteDao { public: NoteDao() = default; - static Note initialize_note_for_user(const std::string& login); + static Note initialize_note_for_user(const std::string &login); static bool update_note(const Note ¬e); [[nodiscard]] static Note get_note(int note_id); }; diff --git a/server/database/src/database_manager.cpp b/server/database/src/database_manager.cpp index e2cb9e0..994be1e 100644 --- a/server/database/src/database_manager.cpp +++ b/server/database/src/database_manager.cpp @@ -49,7 +49,8 @@ DatabaseManager &DatabaseManager::get_instance() { pqxx::connection &DatabaseManager::get_connection() { if (!connection_->is_open()) { - connection_ = std::make_unique(get_connection_string()); + connection_ = + std::make_unique(get_connection_string()); } return *connection_; } diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index 0ee8336..e14c74f 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -1,9 +1,19 @@ #include "note_dao.hpp" -#include "database_manager.hpp" #include +#include "database_manager.hpp" + +std::string color_code_to_hex(const int code) { + static const std::unordered_map color_map = { + {0, "#e7624b"}, {1, "#165d7b"}, {2, "#bd6dab"}, + {3, "#00b16b"}, {4, "#e69f00"}, + }; + + const auto it = color_map.find(code); + return it != color_map.end() ? it->second : "ffffff"; +} Note NoteDao::initialize_note_for_user(const std::string &login) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = @@ -28,30 +38,45 @@ Note NoteDao::initialize_note_for_user(const std::string &login) { } bool NoteDao::update_note(const Note ¬e) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); + std::string members_array = "{"; + for (int i = 0; i < note.members().size(); ++i) { + if (i > 0) { + members_array += ","; + } + members_array += "\"" + note.members(i) + "\""; + } + members_array += "}"; + + std::string tags_array = "{"; + for (int i = 0; i < note.tags().size(); ++i) { + if (i > 0) { + tags_array += ","; + } + const auto &tag = note.tags(i); + std::string color_code = color_code_to_hex(tag.color()); + tags_array += "\"{" + tag.text() + ":" + color_code + "}\""; + } + tags_array += "}"; + const std::string query = "UPDATE notes SET " - "title = :title, " - "content = :content, " - "members = :members, " - "date = :date, " - "tags = :tags " - "WHERE id = :id"; - - // TODO: I'm not sure, it will work correctly because note.members() and note.tags() return strange type - // const pqxx::result result = transaction.exec_params( - // query, note.title(), note.text(), note.members(), note.date(), - // note.tags(), note.id() - // ); - // - // if (result.empty()) { - // return false; - // } + "title = $1, " + "content = $2, " + "members = $3::varchar(50)[], " + "date = $4, " + "tags = $5::varchar(50)[] " + "WHERE id = $6"; + + const pqxx::result result = transaction.exec_params( + query, note.title(), note.text(), members_array, note.date(), + tags_array, note.id() + ); transaction.commit(); - return true; + return result.affected_rows() > 0; } Note NoteDao::get_note(const int note_id) { From bba73ee889919ff946758952432c0851b79aa02c Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Fri, 2 May 2025 16:33:24 +0300 Subject: [PATCH 18/47] feat: finish get_note method in DAO for parsing fields from db --- client/client/include/client_implementation.h | 5 +- client/client/include/update_requests.h | 15 +- client/client/src/client_implementation.cpp | 12 +- client/client/src/update_requests.cpp | 8 +- client/main.cpp | 6 +- .../ui/note-widget/src/note_edit_dialog.cpp | 2 +- server/Service/src/update_service.cpp | 4 +- server/database/CMakeLists.txt | 4 +- server/database/src/note_dao.cpp | 186 ++++++++++++------ 9 files changed, 157 insertions(+), 85 deletions(-) diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 29138e6..cb08f94 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -19,8 +19,9 @@ class ClientImplementation { explicit ClientImplementation(const std::shared_ptr &channel); void CompleteRpc(); - bool update_note(Note *note) const; - bool create_note(Note *note) const; + bool try_update_note(Note *note) const; + bool try_create_note(Note *note) const; + bool try_fetch_note(Note *note) const; }; #endif // CLIENTIMPLEMENTATION_H \ No newline at end of file diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 181062c..a8d1274 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -33,7 +33,7 @@ class UpdateRequests { grpc::CompletionQueue *cq, const std::unique_ptr &stub ); - void Proceed(bool ok = true) override; + void Proceed(bool ok) override; UpdateNoteResponse get_reply(); }; @@ -48,7 +48,7 @@ class UpdateRequests { grpc::CompletionQueue *cq, const std::unique_ptr &stub ); - void Proceed(bool ok = true) override; + void Proceed(bool ok) override; GetNoteResponse get_reply(); }; @@ -65,18 +65,17 @@ class UpdateRequests { grpc::CompletionQueue *cq, const std::unique_ptr &stub ); - void Proceed(bool ok = true) override; + void Proceed(bool ok) override; CreateNoteResponse get_reply(); }; class CreateProjectClientCall; class TryJoinProjectClientCall; - bool update_note(Note *note) const; - bool fetch_note(Note *note) const; - bool get_project(Project *project); - bool create_note(Note *note) const; - bool create_project(Project *project); + bool try_update_note(Note *note) const; + bool try_fetch_note(Note *note) const; + bool try_create_note(Note *note) const; + bool try_create_project(Project *project); bool try_join_project(Project *project); explicit UpdateRequests( diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index 0e9ba13..d8c5160 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -34,10 +34,14 @@ void ClientImplementation::CompleteRpc() { } } -bool ClientImplementation::update_note(Note *note) const { - return update_requests_.update_note(note); +bool ClientImplementation::try_update_note(Note *note) const { + return update_requests_.try_update_note(note); } -bool ClientImplementation::create_note(Note *note) const { - return update_requests_.create_note(note); +bool ClientImplementation::try_create_note(Note *note) const { + return update_requests_.try_create_note(note); +} + +bool ClientImplementation::try_fetch_note(Note *note) const { + return update_requests_.try_fetch_note(note); } diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 3ee0f82..368ad42 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -17,7 +17,7 @@ using Efficio_proto::Update; UpdateRequests::UpdateNoteClientCall::UpdateNoteClientCall( const UpdateNoteRequest &request, - grpc::CompletionQueue *cq, + CompletionQueue *cq, const std::unique_ptr &stub ) : responder_(stub->AsyncUpdateNote(&context, request, cq)) { @@ -90,7 +90,7 @@ CreateNoteResponse UpdateRequests::CreateNoteClientCall::get_reply() { return reply_; } -bool UpdateRequests::update_note(Note *note) const { +bool UpdateRequests::try_update_note(Note *note) const { UpdateNoteRequest request; request.mutable_note()->CopyFrom(*note); @@ -113,7 +113,7 @@ bool UpdateRequests::update_note(Note *note) const { return true; } -bool UpdateRequests::fetch_note(Note *note) const { +bool UpdateRequests::try_fetch_note(Note *note) const { GetNoteRequest request; request.set_id(note->id()); @@ -136,7 +136,7 @@ bool UpdateRequests::fetch_note(Note *note) const { return true; } -bool UpdateRequests::create_note(Note *note) const { +bool UpdateRequests::try_create_note(Note *note) const { const CreateNoteRequest request; const auto call = new CreateNoteClientCall(request, cq_, stub_); diff --git a/client/main.cpp b/client/main.cpp index 336fb1c..2666a1b 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -34,7 +34,11 @@ int main(int argc, char *argv[]) { requests.detach(); const auto test_note = new Note(); - client.create_note(test_note); + client.try_create_note(test_note); + + const auto new_note = new Note(); + new_note->set_id(6); + client.try_fetch_note(new_note); NoteEditDialog dialog(&client, nullptr, test_note); dialog.show(); diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index bef7c89..1f28e85 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -254,5 +254,5 @@ bool NoteEditDialog::try_save_note() const { new_tag->set_color(color_code_to_note_tag_colors(tag.color)); } - return client_->update_note(note_); + return client_->try_update_note(note_); } \ No newline at end of file diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index a53ff3b..e7ecafb 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -30,7 +30,6 @@ void UpdateService::UpdateNoteServerCall::Proceed(const bool ok) { new UpdateNoteServerCall(service_, cq_); UpdateNoteResponse response; - std::cout << "[SERVER]: PREPARING UPDATE\n"; NoteDao::update_note(request_.note()); response.mutable_note()->CopyFrom(request_.note()); @@ -76,7 +75,8 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { response.mutable_note()->CopyFrom(note); std::cout << "[SERVER]: FETCH NOTE REQUEST id=" << request_.id() << ", title=" << response.mutable_note()->title() - << std::endl; + << ", first tag=" << response.note().tags()[0].text() + << ":" << response.note().tags()[0].color() << std::endl; responder_.Finish(response, grpc::Status::OK, this); status_ = FINISH; diff --git a/server/database/CMakeLists.txt b/server/database/CMakeLists.txt index ce81cf2..3455970 100644 --- a/server/database/CMakeLists.txt +++ b/server/database/CMakeLists.txt @@ -5,7 +5,7 @@ project(Database LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(Qt6 COMPONENTS Core Sql REQUIRED) +find_package(Qt6 COMPONENTS Widgets Core Sql REQUIRED) file(GLOB SOURCES "src/*.cpp") file(GLOB HEADERS "include/*.hpp") @@ -18,4 +18,4 @@ target_include_directories(Database $ ) -target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) \ No newline at end of file +target_link_libraries(Database PRIVATE Qt6::Widgets Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) \ No newline at end of file diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index e14c74f..0974383 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -1,17 +1,44 @@ #include "note_dao.hpp" #include #include "database_manager.hpp" +#include "tags_dialog.h" + +namespace { + +std::string format_members_array( + const google::protobuf::RepeatedPtrField &members +) { + std::string result = "{"; + bool first = true; + for (const auto &member : members) { + if (!first) { + result += ","; + } + result += "\"" + member + "\""; + first = false; + } + return result + "}"; +} -std::string color_code_to_hex(const int code) { - static const std::unordered_map color_map = { - {0, "#e7624b"}, {1, "#165d7b"}, {2, "#bd6dab"}, - {3, "#00b16b"}, {4, "#e69f00"}, - }; - - const auto it = color_map.find(code); - return it != color_map.end() ? it->second : "ffffff"; +std::string format_tags_array( + const google::protobuf::RepeatedPtrField &tags +) { + std::string result = "{"; + bool first = true; + for (const auto &tag : tags) { + if (!first) { + result += ","; + } + std::string color_code = + TagsDialog::get_color_by_code(tag.color()).toStdString(); + result += "\"{" + tag.text() + ":" + color_code + "}\""; + first = false; + } + return result + "}"; } +} // namespace + Note NoteDao::initialize_note_for_user(const std::string &login) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); @@ -21,45 +48,31 @@ Note NoteDao::initialize_note_for_user(const std::string &login) { "VALUES ($1, $2) " "RETURNING id, title, content"; - const pqxx::result result = - transaction.exec_params(query, "Пустая заметка", ""); + pqxx::params params; + params.append("Пустая заметка"); + params.append(""); + + const pqxx::result result = transaction.exec(query, params); if (result.empty()) { return {}; } const pqxx::row row = result[0]; - const auto note = new Note(); - note->set_id(row["id"].as()); - note->set_title(row["title"].as()); + Note note; + note.set_id(row["id"].as()); + note.set_title(row["title"].as()); transaction.commit(); - return *note; + return note; } bool NoteDao::update_note(const Note ¬e) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); - std::string members_array = "{"; - for (int i = 0; i < note.members().size(); ++i) { - if (i > 0) { - members_array += ","; - } - members_array += "\"" + note.members(i) + "\""; - } - members_array += "}"; - - std::string tags_array = "{"; - for (int i = 0; i < note.tags().size(); ++i) { - if (i > 0) { - tags_array += ","; - } - const auto &tag = note.tags(i); - std::string color_code = color_code_to_hex(tag.color()); - tags_array += "\"{" + tag.text() + ":" + color_code + "}\""; - } - tags_array += "}"; + const std::string members_array = format_members_array(note.members()); + const std::string tags_array = format_tags_array(note.tags()); const std::string query = "UPDATE notes SET " @@ -70,56 +83,107 @@ bool NoteDao::update_note(const Note ¬e) { "tags = $5::varchar(50)[] " "WHERE id = $6"; - const pqxx::result result = transaction.exec_params( - query, note.title(), note.text(), members_array, note.date(), - tags_array, note.id() - ); + pqxx::params params; + params.append(note.title()); + params.append(note.text()); + params.append(members_array); + params.append(note.date()); + params.append(tags_array); + params.append(note.id()); + const pqxx::result result = transaction.exec(query, params); transaction.commit(); return result.affected_rows() > 0; } +Efficio_proto::Note_tag::colors color_hex_to_enum(const std::string &hex) { + static const std::unordered_map< + std::string, Efficio_proto::Note_tag::colors> + color_map = { + {"e7624b", Efficio_proto::Note_tag::Red}, + {"165d7b", Efficio_proto::Note_tag::Blue}, + {"bd6dab", Efficio_proto::Note_tag::Pink}, + {"00b16b", Efficio_proto::Note_tag::Green}, + {"e69f00", Efficio_proto::Note_tag::Yellow} + }; + auto it = color_map.find(hex); + return it != color_map.end() ? it->second : Efficio_proto::Note_tag::Red; +} + Note NoteDao::get_note(const int note_id) { - pqxx::connection connection(DatabaseManager::get_connection_string()); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); - const std::string query = - "SELECT * FROM notes WHERE id = " + transaction.quote(note_id); + const std::string query = "SELECT * FROM notes WHERE id = $1"; + pqxx::params params; + params.append(note_id); - const pqxx::result result = transaction.exec(query); + const pqxx::result result = transaction.exec(query, params); if (result.empty()) { return {}; } const pqxx::row row = result[0]; + Note note; - const auto note = new Note(); - - note->set_id(row["id"].as()); - note->set_title(row["title"].as()); - note->set_text(row["content"].as()); + note.set_id(row["id"].as()); + note.set_title(row["title"].as()); + note.set_text(row["content"].as()); if (!row["date"].is_null()) { - note->set_date(row["date"].as()); + note.set_date(row["date"].as()); } if (!row["members"].is_null()) { - note->add_members(row["members"].as()); + const auto members_as_string = row["members"].as(); + + if (members_as_string.size() > 2) { + std::stringstream ss( + members_as_string.substr(1, members_as_string.size() - 2) + ); + std::string member; + + while (std::getline(ss, member, ',')) { + std::erase(member, '"'); + if (!member.empty()) { + note.add_members(member); + } + } + } } - const pqxx::result tags = transaction.exec( - "SELECT * FROM notes WHERE id = " + transaction.quote(note->id()) - ); - - // for (const auto &tag_row : tags) { - // auto *tag = note->add_tags(); - // tag->set_text(tag_row["text"].as()); - // tag->set_color( - // static_cast(tag_row["color"].as()) - // ); - // } + if (!row["tags"].is_null()) { + const auto tags_as_string = row["tags"].as(); + + if (tags_as_string.size() > 2) { + std::stringstream ss( + tags_as_string.substr(1, tags_as_string.size() - 2) + ); + std::string tag_item; + + while (std::getline(ss, tag_item, ',')) { + std::erase(tag_item, '"'); + + if (tag_item.size() > 3 && tag_item[0] == '{' && + tag_item.back() == '}') { + const auto colon_position = tag_item.find(':'); + + if (colon_position != std::string::npos) { + auto *tag = note.add_tags(); + tag->set_text(tag_item.substr(1, colon_position - 1)); + + const std::string color_hex = tag_item.substr( + colon_position + 2, + tag_item.size() - colon_position - 3 + ); + tag->set_color(color_hex_to_enum(color_hex)); + } + } + } + } + } transaction.commit(); - return *note; -} + return note; +} \ No newline at end of file From b1d9cf0e90384f970a6244bdfe2aec3b5a2b6c53 Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 3 May 2025 12:16:45 +0300 Subject: [PATCH 19/47] send requests and get responses works --- client/client/include/update_requests.h | 4 +-- client/client/src/client_implementation.cpp | 29 +++++++++++++++------ client/client/src/update_requests.cpp | 11 +++++--- proto/efficio-rpc-proto/efficio.proto | 10 +++++++ server/Service/src/update_service.cpp | 1 - 5 files changed, 40 insertions(+), 15 deletions(-) diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 9ee5771..419b20d 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -30,7 +30,7 @@ class UpdateRequests { public: void Proceed(bool ok) override; - GetProjectClientCall(const GetProjectRequest& request, + GetProjectClientCall(GetProjectRequest& request, CompletionQueue* cq_, std::unique_ptr& stub_); }; @@ -50,7 +50,7 @@ class UpdateRequests { explicit UpdateRequests(std::shared_ptr channel, CompletionQueue *cq): - stub_(Update::NewStub(channel)), cq_(cq) {}; + stub_(Update::NewStub(channel)), cq_(cq) {} private: CompletionQueue* cq_; std::unique_ptr stub_; diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index b652901..fc7c7ac 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -14,11 +14,23 @@ using grpc::CompletionQueue; using grpc::Status; ClientImplementation::ClientImplementation(std::shared_ptr channel) : - channel_(channel) { + channel_(channel) + { + + // update_requests.get_project(pr); + auto pr = new Project(); + UpdateRequests update_requests(channel_, &cq_); std::thread t(&ClientImplementation::CompleteRpc, this); - UpdateRequests update_requests(channel, &cq_); - update_requests.get_project(nullptr); + std::string q; + + while (std::cin >> q) { + + update_requests.get_project(pr); + + } t.join(); + delete pr; + }; void ClientImplementation::CompleteRpc() { @@ -28,20 +40,21 @@ void ClientImplementation::CompleteRpc() { while (cq_.Next(&got_tag, &ok)) { - + std::cout << "got smth" << std::endl; CommonClientCall* call = static_cast(got_tag); - // Verify that the request was completed successfully. Note that "ok" - // corresponds solely to the request for updates introduced by Finish(). assert(ok); - if (call->status.ok()) + if (call->status.ok()) { + std::cout << "start procceed" << std::endl; call->Proceed(); + } else std::cout << "RPC failed" << std::endl; - // Once we're complete, deallocate the call object. + delete call; } + std::cout << "complete rpc ended" << std::endl; } diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 5634b0d..a58aff1 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -24,17 +24,20 @@ void UpdateRequests::GetProjectClientCall::Proceed(bool ok) { std::cout << response.project().title() << std::endl; } } + } -UpdateRequests::GetProjectClientCall::GetProjectClientCall(const GetProjectRequest& request, +UpdateRequests::GetProjectClientCall::GetProjectClientCall(GetProjectRequest& request, CompletionQueue* cq_, std::unique_ptr& stub_) : CommonClientCall() { - response_reader = stub_->AsyncGetProject(&context, request, cq_); + response_reader = stub_->PrepareAsyncGetProject(&context, request, cq_); + response_reader->StartCall(); response_reader->Finish(&response, &status, (void*)this); } bool UpdateRequests::get_project(Project *project) { - GetProjectRequest request; - new GetProjectClientCall(request, cq_, stub_); + auto request = new GetProjectRequest; + new GetProjectClientCall(*request, cq_, stub_); + return true; } diff --git a/proto/efficio-rpc-proto/efficio.proto b/proto/efficio-rpc-proto/efficio.proto index c5a38f6..28fd67a 100644 --- a/proto/efficio-rpc-proto/efficio.proto +++ b/proto/efficio-rpc-proto/efficio.proto @@ -10,6 +10,7 @@ service Update { rpc CreateNote (CreateNoteRequest) returns (CreateNoteResponse) {} rpc CreateProject (CreateProjectRequest) returns (CreateProjectResponse) {} rpc TryJoinProject (TryJoinProjectRequest) returns (TryJoinProjectResponse) {} + rpc UpdateProject (UpdateProjectRequest) returns (UpdateProjectResponse) {} } service Auth { @@ -76,6 +77,15 @@ message TryJoinProjectResponse { } } +message UpdateProjectRequest { + Project project = 1; +} + +message UpdateProjectResponse { + bool ok = 1; + optional string error_text = 2; +} + message AuthRequest { User user = 1; } diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index a1628a3..5fea4b1 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -10,7 +10,6 @@ using Efficio_proto::Project; UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq), service_() { // new GetNoteServerCall(&service_, cq_); - } void UpdateService::run() { From 0e772b49c0818a0f882e937554c9403be292582a Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 3 May 2025 14:41:54 +0300 Subject: [PATCH 20/47] rewrite main-window to protobuf --- client/CMakeLists.txt | 1 + client/ui/main-window/CMakeLists.txt | 3 +- .../main-window/include/main_window_style.hpp | 1 - client/ui/main-window/include/mainwindow.h | 10 ++-- client/ui/main-window/include/notelist.h | 6 ++- client/ui/main-window/include/notewidget.h | 11 +++-- client/ui/main-window/include/projectitem.h | 8 ++-- client/ui/main-window/include/projectlist.h | 9 ++-- client/ui/main-window/src/mainwindow.cpp | 48 +++++++------------ client/ui/main-window/src/notelist.cpp | 13 ++--- client/ui/main-window/src/notewidget.cpp | 21 ++++---- client/ui/main-window/src/projectitem.cpp | 8 +--- client/ui/main-window/src/projectlist.cpp | 9 ++-- server/CMakeLists.txt | 7 +-- 14 files changed, 71 insertions(+), 84 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 3b06231..372e7b6 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -20,6 +20,7 @@ add_subdirectory(client) add_executable(EfficioTaskTracker main.cpp) target_link_libraries(EfficioTaskTracker PRIVATE + model-proto Qt6::Widgets Qt6::Core Qt6::Gui diff --git a/client/ui/main-window/CMakeLists.txt b/client/ui/main-window/CMakeLists.txt index c8e1f85..40e192b 100644 --- a/client/ui/main-window/CMakeLists.txt +++ b/client/ui/main-window/CMakeLists.txt @@ -29,11 +29,12 @@ target_include_directories(MainWindow PUBLIC ) target_link_libraries(MainWindow PRIVATE + model-proto Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql - Scripts Database NoteWidget + ) \ No newline at end of file diff --git a/client/ui/main-window/include/main_window_style.hpp b/client/ui/main-window/include/main_window_style.hpp index 3e594c1..c6afa54 100644 --- a/client/ui/main-window/include/main_window_style.hpp +++ b/client/ui/main-window/include/main_window_style.hpp @@ -133,7 +133,6 @@ QPushButton { )"; - } // namespace Ui #endif // MAIN_WINDOW_STYLE_HPP \ No newline at end of file diff --git a/client/ui/main-window/include/mainwindow.h b/client/ui/main-window/include/mainwindow.h index e5242f7..0b38459 100644 --- a/client/ui/main-window/include/mainwindow.h +++ b/client/ui/main-window/include/mainwindow.h @@ -1,6 +1,7 @@ #ifndef MAINWINDOW_H #define MAINWINDOW_H +#include #include #include #include @@ -11,7 +12,8 @@ #include "bottombar.h" #include "notelist.h" #include "projectlist.h" -#include "storage.hpp" + +using namespace Efficio_proto; namespace Ui { class MainWindow : public QWidget { @@ -25,18 +27,18 @@ class MainWindow : public QWidget { QWidget *content_widget_; QPushButton *new_project_button_; QPushButton *new_note_button_; - project_storage_model::Storage *storage_; + Storage *storage_; friend ProjectList; private slots: - void add_project(); + void create_project(); void add_note(); public: explicit MainWindow( QWidget *parent = nullptr, std::string username = "none", - project_storage_model::Storage *storage = nullptr + Storage *storage = nullptr ); }; } // namespace Ui diff --git a/client/ui/main-window/include/notelist.h b/client/ui/main-window/include/notelist.h index f406536..c833b17 100644 --- a/client/ui/main-window/include/notelist.h +++ b/client/ui/main-window/include/notelist.h @@ -1,10 +1,12 @@ #ifndef NOTELIST_H #define NOTELIST_H +#include #include #include #include #include -#include "note.hpp" + +using namespace Efficio_proto; namespace Ui { class NoteList : public QWidget { @@ -18,7 +20,7 @@ class NoteList : public QWidget { int note_counter_ = 0; public: - void add_note_widget(const project_storage_model::Note *note); + void add_note_widget(const Note *note); void clear_note_list(); NoteList(QWidget *parent); diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h index f1f8f6a..2d9ca55 100644 --- a/client/ui/main-window/include/notewidget.h +++ b/client/ui/main-window/include/notewidget.h @@ -1,17 +1,18 @@ #ifndef NOTEWIDGET_H #define NOTEWIDGET_H +#include #include #include #include #include -#include -#include "note.hpp" + +using namespace Efficio_proto; namespace Ui { class NoteWidget : public QWidget { Q_OBJECT - const project_storage_model::Note *model_note_; + const Note *model_note_; QVBoxLayout *main_layout_; QPushButton *open_button_; QLabel *title_label_; @@ -20,12 +21,12 @@ class NoteWidget : public QWidget { public: explicit NoteWidget( QWidget *parent = nullptr, - const project_storage_model::Note *model_note = nullptr + const Note *model_note = nullptr ); private slots: - void open_note_window() const; + // void open_note_window() const; }; } // namespace Ui #endif // NOTEWIDGET_H \ No newline at end of file diff --git a/client/ui/main-window/include/projectitem.h b/client/ui/main-window/include/projectitem.h index 8b4a4a2..3979bd6 100644 --- a/client/ui/main-window/include/projectitem.h +++ b/client/ui/main-window/include/projectitem.h @@ -1,19 +1,21 @@ #ifndef PROJECTITEM_H #define PROJECTITEM_H +#include #include #include #include "notelist.h" -#include "project.hpp" + +using namespace Efficio_proto; namespace Ui { class ProjectItem : public QListWidgetItem { - project_storage_model::Project *project_; + Project *project_; friend NoteList; friend class MainWindow; public: - ProjectItem(QListWidget *listview, project_storage_model::Project *project); + ProjectItem(QListWidget *listview, Project *project); }; } // namespace Ui #endif // PROJECTITEM_H \ No newline at end of file diff --git a/client/ui/main-window/include/projectlist.h b/client/ui/main-window/include/projectlist.h index d221695..f2a470c 100644 --- a/client/ui/main-window/include/projectlist.h +++ b/client/ui/main-window/include/projectlist.h @@ -1,17 +1,18 @@ #ifndef PROJECTLIST_H #define PROJECTLIST_H +#include #include #include -#include "project.hpp" -#include "storage.hpp" + +using namespace Efficio_proto; namespace Ui { class ProjectList : public QListWidget { Q_OBJECT friend class MainWindow; - void add_project(project_storage_model::Project *project); - void load_projects(project_storage_model::Storage *storage); + void add_project(Project *project); + void load_projects(Storage *storage); public: explicit ProjectList(QWidget *parent = nullptr); diff --git a/client/ui/main-window/src/mainwindow.cpp b/client/ui/main-window/src/mainwindow.cpp index 907f902..6d0c4ec 100644 --- a/client/ui/main-window/src/mainwindow.cpp +++ b/client/ui/main-window/src/mainwindow.cpp @@ -1,30 +1,25 @@ #include "mainwindow.h" +#include #include #include #include #include #include #include +#include #include #include -#include #include #include "bottombar.h" -#include "lr_dao.hpp" #include "main_window_style.hpp" -#include "note_dao.hpp" #include "notelist.h" -#include "project.hpp" -#include "project_dao.hpp" #include "projectitem.h" #include "projectlist.h" +using namespace Efficio_proto; + namespace Ui { -MainWindow::MainWindow( - QWidget *parent, - std::string username, - project_storage_model::Storage *storage -) +MainWindow::MainWindow(QWidget *parent, std::string username, Storage *storage) : QWidget(parent), username(username), main_layout_(new QVBoxLayout(this)), @@ -49,7 +44,7 @@ MainWindow::MainWindow( right_layout->addWidget(project_list_); right_layout->addWidget(new_project_button_); right_layout->addWidget(new_note_button_); - QScrollArea* scrollArea = new QScrollArea(content_widget_); + QScrollArea *scrollArea = new QScrollArea(content_widget_); scrollArea->setWidgetResizable(true); scrollArea->setWidget(note_list_); content_layout_->addWidget(scrollArea, Qt::AlignRight); @@ -68,41 +63,34 @@ MainWindow::MainWindow( ); connect( new_project_button_, &QPushButton::clicked, this, - &Ui::MainWindow::add_project + &Ui::MainWindow::create_project ); } -void MainWindow::add_project() { +void MainWindow::create_project() { bool ok; QString name_of_project = QInputDialog::getText( nullptr, "Название проекта:", "Введите название", QLineEdit::Normal, "", &ok ); + // TODO: notificate server + // TODO: send request to server, get code if (ok) { - int id = 0; - - if (DB::ProjectDAO::create_project(name_of_project.toStdString(), id)) { - LRDao::add_project_to_user(username, id); - auto &project = storage_->add_project( - Project(id, name_of_project.toStdString(), "") - ); - project_list_->add_project(&project); - } + Project *project = storage_->add_projects(); + project->set_title(name_of_project.toStdString()); + project_list_->add_project(project); } } void MainWindow::add_note() { auto project_item = dynamic_cast(project_list_->currentItem()); + // TODO: notificate server if (project_item) { - if (int id = 0; NoteDao::initialize_note(id)) { - DB::ProjectDAO::add_note_to_project( - project_item->project_->get_id(), id - ); - auto ¬e = - project_item->project_->add_note({id, "Пустая заметка", ""}); - note_list_->add_note_widget(¬e); - } + Note *note = project_item->project_->add_notes(); + note->set_title("Пустая заметка"); + note->set_allocated_text(new std::string("")); + note_list_->add_note_widget(note); } else { QMessageBox msg; msg.setText("Проект не выбран!"); diff --git a/client/ui/main-window/src/notelist.cpp b/client/ui/main-window/src/notelist.cpp index 4af7558..2f2aefb 100644 --- a/client/ui/main-window/src/notelist.cpp +++ b/client/ui/main-window/src/notelist.cpp @@ -4,7 +4,6 @@ #include #include #include -#include "note.hpp" #include "notewidget.h" #include "projectitem.h" @@ -12,8 +11,7 @@ namespace Ui { NoteList::NoteList(QWidget *parent) : QWidget(parent), main_layout_(new QHBoxLayout(this)), - vertical_layouts_(std::vector()){ - + vertical_layouts_(std::vector()) { this->setAttribute(Qt::WA_StyledBackground); this->setObjectName("NoteList"); this->setLayout(main_layout_); @@ -25,7 +23,7 @@ NoteList::NoteList(QWidget *parent) } } -void NoteList::add_note_widget(const project_storage_model::Note *note) { +void NoteList::add_note_widget(const Note *note) { auto current_layout = vertical_layouts_[note_counter_ % 4]; if (current_layout->count() > 1) { current_layout->removeItem( @@ -42,12 +40,11 @@ void NoteList::add_note_widget(const project_storage_model::Note *note) { void NoteList::load_project_notes(QListWidgetItem *project) { ProjectItem *p = dynamic_cast(project); assert(p != nullptr); - qDebug() << "Адрес проекта" - << QString::fromStdString(p->project_->get_name()) << ":" - << p->project_; + qDebug() << "Адрес проекта" << QString::fromStdString(p->project_->title()) + << ":" << p->project_; this->clear_note_list(); note_counter_ = 0; - for (const auto ¬e : p->project_->get_notes()) { + for (const Note ¬e : p->project_->notes()) { this->add_note_widget(¬e); } } diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp index 3ced750..f43227f 100644 --- a/client/ui/main-window/src/notewidget.cpp +++ b/client/ui/main-window/src/notewidget.cpp @@ -2,14 +2,11 @@ #include #include #include -#include "note.hpp" -#include "note_edit_dialog.h" + +// #include "note_edit_dialog.h" namespace Ui { -NoteWidget::NoteWidget( - QWidget *parent, - const project_storage_model::Note *model_note -) +NoteWidget::NoteWidget(QWidget *parent, const Note *model_note) : QWidget(parent), model_note_(model_note), main_layout_(new QVBoxLayout(this)), @@ -17,12 +14,12 @@ NoteWidget::NoteWidget( this->setObjectName("NoteWidget"); this->setMinimumWidth(100); this->setFixedHeight(100); - title_label_ = new QLabel(model_note_->get_title().c_str(), this); - text_label_ = new QLabel(model_note_->get_text().c_str(), this); - + title_label_ = new QLabel(model_note_->title().c_str(), this); + text_label_ = new QLabel(model_note_->text().c_str(), this); + title_label_->setStyleSheet("color: rgb(33, 44, 50);"); text_label_->setStyleSheet("color: rgb(33, 44, 50);"); - + main_layout_->addWidget(title_label_); main_layout_->addWidget(text_label_); @@ -46,8 +43,8 @@ void NoteWidget::open_note_window() const { ); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->exec(); - text_label_->setText(model_note_->get_text().c_str()); - title_label_->setText(model_note_->get_title().c_str()); + text_label_->setText(model_note_->text().c_str()); + title_label_->setText(model_note_->title().c_str()); main_layout_->update(); } } // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/projectitem.cpp b/client/ui/main-window/src/projectitem.cpp index 4f38e85..cc5d2cb 100644 --- a/client/ui/main-window/src/projectitem.cpp +++ b/client/ui/main-window/src/projectitem.cpp @@ -2,11 +2,7 @@ #include namespace Ui { -ProjectItem::ProjectItem( - QListWidget *list_view, - project_storage_model::Project *project -) - : project_(project), - QListWidgetItem(project->get_name().c_str(), list_view) { +ProjectItem::ProjectItem(QListWidget *list_view, Project *project) + : project_(project), QListWidgetItem(project->title().c_str(), list_view) { } } // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/projectlist.cpp b/client/ui/main-window/src/projectlist.cpp index 3bd7828..fc65ebb 100644 --- a/client/ui/main-window/src/projectlist.cpp +++ b/client/ui/main-window/src/projectlist.cpp @@ -1,7 +1,6 @@ #include "projectlist.h" #include #include "mainwindow.h" -#include "project.hpp" #include "projectitem.h" namespace Ui { @@ -11,13 +10,13 @@ ProjectList::ProjectList(QWidget *parent) : QListWidget{parent} { this->setFixedWidth(200); } -void ProjectList::add_project(project_storage_model::Project *project) { +void ProjectList::add_project(Project *project) { this->addItem(new ProjectItem(static_cast(this), project)); } -void ProjectList::load_projects(project_storage_model::Storage *storage) { - for (project_storage_model::Project &pr : storage->get_projects()) { - add_project(&pr); +void ProjectList::load_projects(Storage *storage) { + for (Project &project : *storage->mutable_projects()) { + add_project(&project); } } diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index 9733a05..e4e0a12 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -1,8 +1,9 @@ cmake_minimum_required(VERSION 3.16) -project(server) +project(Server) + add_subdirectory(Service) -add_executable(main main.cpp) +add_executable(Server main.cpp) -target_link_libraries(main Service) \ No newline at end of file +target_link_libraries(Server Service) \ No newline at end of file From 2dd5161d580a18dfc1c7cc0555b717cd58590eec Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 3 May 2025 16:05:08 +0300 Subject: [PATCH 21/47] update_requests commit --- client/client/src/update_requests.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index a58aff1..8e4c0fc 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -3,7 +3,6 @@ #include #include - using grpc::Channel; using grpc::ClientAsyncResponseReader; using grpc::ClientContext; From e3ad5fc6182cc92e03aa0f65d199ff7ab0ce3299 Mon Sep 17 00:00:00 2001 From: toximu Date: Sat, 3 May 2025 17:30:51 +0300 Subject: [PATCH 22/47] add ProjectDAO::get_project --- server/database/include/project_dao.hpp | 17 ++++++ server/database/src/database_manager.cpp | 5 +- server/database/src/project_dao.cpp | 67 ++++++++++++++++++++++++ 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 server/database/include/project_dao.hpp create mode 100644 server/database/src/project_dao.cpp diff --git a/server/database/include/project_dao.hpp b/server/database/include/project_dao.hpp new file mode 100644 index 0000000..8ffa3ee --- /dev/null +++ b/server/database/include/project_dao.hpp @@ -0,0 +1,17 @@ +#ifndef PROJECT_DAO_HPP +#define PROJECT_DAO_HPP + +#include +#include + +using namespace Efficio_proto; + +class ProjectDAO { + public: + ProjectDAO() = default; + static bool get_project(const std::string &code, Project &project); +}; + + + +#endif diff --git a/server/database/src/database_manager.cpp b/server/database/src/database_manager.cpp index e2cb9e0..1d57434 100644 --- a/server/database/src/database_manager.cpp +++ b/server/database/src/database_manager.cpp @@ -28,9 +28,8 @@ DatabaseManager::DatabaseManager() { transaction.exec( "CREATE TABLE IF NOT EXISTS projects (" - "id SERIAL PRIMARY KEY, " - "name VARCHAR(50) NOT NULL, " - "owner VARCHAR(50) REFERENCES users(login), " + "code VARCHAR(6) PRIMARY KEY, " + "title VARCHAR(50) NOT NULL, " "notes INT[], " "members VARCHAR(50)[]" ")" diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp new file mode 100644 index 0000000..aaa2fe8 --- /dev/null +++ b/server/database/src/project_dao.cpp @@ -0,0 +1,67 @@ +#include "project_dao.hpp" +#include "note_dao.hpp" +#include "database_manager.hpp" +#include + + +bool ProjectDAO::get_project(const std::string &code, Project &project) { + pqxx::connection &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + + const std::string query = + "SELECT * FROM projects WHERE code = " + transaction.quote(code); + + const pqxx::result result = transaction.exec(query); + + if (result.empty()) { + return false; + } + + const pqxx::row row = result[0]; + + project.set_title(row["title"].as()); + project.set_code(row["code"].as()); + + + if (!row["members"].is_null()) { + const auto members_as_string = row["members"].as(); + + if (members_as_string.size() > 2) { + std::stringstream ss( + members_as_string.substr(1, members_as_string.size() - 2) + ); + std::string member; + + while (std::getline(ss, member, ',')) { + std::erase(member, '"'); + if (!member.empty()) { + project.add_members(member); + } + } + } + } + + if (!row["notes"].is_null()) { + const auto notes_as_string = row["notes"].as(); + + if (notes_as_string.size() > 2) { + std::stringstream ss( + notes_as_string.substr(1, notes_as_string.size() - 2) + ); + std::string note_id_string; + + while (std::getline(ss, note_id_string, ',')) { + std::erase(note_id_string, '"'); + if (!note_id_string.empty()) { + Note* note = project.add_notes(); + *note = NoteDao::get_note(std::stoi(note_id_string)); + } + } + } + } + + return true; + +} + From 7b185f43995728d98ba66368f287b513710baec0 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Sun, 4 May 2025 14:26:19 +0300 Subject: [PATCH 23/47] feat: rewrite authorization window to proto, rewrite LRDao to pqxx, complete auth service --- client/CMakeLists.txt | 6 + client/main.cpp | 26 +- .../ui/authorization-windows/CMakeLists.txt | 43 ++ .../include/login_window.h | 34 ++ .../include/login_window_style_sheet.h | 377 ++++++++++++++++++ .../include/registration_window.h | 34 ++ .../include/registration_window_style_sheet.h | 373 +++++++++++++++++ .../login_window_ru_RU.ts | 3 + .../src/login_window.cpp | 127 ++++++ .../src/registration_window.cpp | 188 +++++++++ .../authorization-windows/ui/login_window.ui | 153 +++++++ .../ui/registration_window.ui | 121 ++++++ client/ui/main-window/CMakeLists.txt | 36 ++ client/ui/main-window/MainWindow_en_US.ts | 3 + .../main-window/include/applicationwindow.h | 16 + .../ui/main-window/src/applicationwindow.cpp | 14 + client/ui/theme-manager/CMakeLists.txt | 32 ++ .../ui/theme-manager/include/theme_manager.h | 23 ++ client/ui/theme-manager/src/theme_manager.cpp | 27 ++ server/Service/CMakeLists.txt | 4 +- server/Service/include/auth_service.h | 63 +++ server/Service/src/auth_service.cpp | 92 +++++ server/database/include/lr_dao.hpp | 20 + server/database/src/lr_dao.cpp | 100 +++++ 24 files changed, 1902 insertions(+), 13 deletions(-) create mode 100644 client/ui/authorization-windows/CMakeLists.txt create mode 100644 client/ui/authorization-windows/include/login_window.h create mode 100644 client/ui/authorization-windows/include/login_window_style_sheet.h create mode 100644 client/ui/authorization-windows/include/registration_window.h create mode 100644 client/ui/authorization-windows/include/registration_window_style_sheet.h create mode 100644 client/ui/authorization-windows/login_window_ru_RU.ts create mode 100644 client/ui/authorization-windows/src/login_window.cpp create mode 100644 client/ui/authorization-windows/src/registration_window.cpp create mode 100644 client/ui/authorization-windows/ui/login_window.ui create mode 100644 client/ui/authorization-windows/ui/registration_window.ui create mode 100644 client/ui/main-window/CMakeLists.txt create mode 100644 client/ui/main-window/MainWindow_en_US.ts create mode 100644 client/ui/main-window/include/applicationwindow.h create mode 100644 client/ui/main-window/src/applicationwindow.cpp create mode 100644 client/ui/theme-manager/CMakeLists.txt create mode 100644 client/ui/theme-manager/include/theme_manager.h create mode 100644 client/ui/theme-manager/src/theme_manager.cpp create mode 100644 server/Service/include/auth_service.h create mode 100644 server/Service/src/auth_service.cpp create mode 100644 server/database/include/lr_dao.hpp create mode 100644 server/database/src/lr_dao.cpp diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 83f6dc5..58eb1a9 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -11,7 +11,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) +add_subdirectory(ui/main-window) add_subdirectory(ui/note-widget) +add_subdirectory(ui/authorization-windows) +add_subdirectory(ui/theme-manager) add_subdirectory(client) add_executable(start_client main.cpp) @@ -22,6 +25,9 @@ target_link_libraries(start_client PRIVATE Qt6::Sql Database NoteWidget + MainWindow + AuthorizationWindows + ThemeManager model-proto Client ) diff --git a/client/main.cpp b/client/main.cpp index 2666a1b..b982431 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -1,19 +1,20 @@ +#include +#include #include #include -#include +#include #include -#include #include "client_implementation.h" -#include "note_edit_dialog.h" +#include "login_window.h" +#include "applicationwindow.h" int main(int argc, char *argv[]) { QApplication application(argc, argv); QTranslator translator; const QStringList ui_languages = QLocale::system().uiLanguages(); - for (const QString &locale : ui_languages) { - const QString base_name = "NoteWidgetEfficio_" + QLocale(locale).name(); + const QString base_name = "MainWindow_" + QLocale(locale).name(); if (translator.load(":/i18n/" + base_name)) { QApplication::installTranslator(&translator); break; @@ -33,14 +34,15 @@ int main(int argc, char *argv[]) { }); requests.detach(); - const auto test_note = new Note(); - client.try_create_note(test_note); + auto *app_window = new Ui::ApplicationWindow("EFFICIO"); + auto *login_window = new LoginWindow(app_window); - const auto new_note = new Note(); - new_note->set_id(6); - client.try_fetch_note(new_note); + app_window->setCentralWidget(login_window); + const QRect screen_geometry = QApplication::primaryScreen()->availableGeometry(); + const int x = (screen_geometry.width() - login_window->width()) / 2; + const int y = (screen_geometry.height() - login_window->height()) / 2; + app_window->move(x, y); + app_window->show(); - NoteEditDialog dialog(&client, nullptr, test_note); - dialog.show(); return QApplication::exec(); } \ No newline at end of file diff --git a/client/ui/authorization-windows/CMakeLists.txt b/client/ui/authorization-windows/CMakeLists.txt new file mode 100644 index 0000000..53e3b07 --- /dev/null +++ b/client/ui/authorization-windows/CMakeLists.txt @@ -0,0 +1,43 @@ +cmake_minimum_required(VERSION 3.16) + +project(AuthorizationWindows LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(CMAKE_AUTOUIC_SEARCH_PATHS ${CMAKE_CURRENT_SOURCE_DIR}/ui) + +set(UI_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/ui/login_window.ui + ${CMAKE_CURRENT_SOURCE_DIR}/ui/registration_window.ui +) + +set(SOURCES + src/login_window.cpp + src/registration_window.cpp +) + +set(HEADERS + include/login_window.h + include/login_window_style_sheet.h + include/registration_window_style_sheet.h + include/registration_window.h +) + +file(GLOB TS_FILES "*.ts") + +add_library(AuthorizationWindows STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) + +target_include_directories(AuthorizationWindows + PUBLIC + $ + $ +) + +target_link_libraries(AuthorizationWindows PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto MainWindow ThemeManager) \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window.h b/client/ui/authorization-windows/include/login_window.h new file mode 100644 index 0000000..72509c6 --- /dev/null +++ b/client/ui/authorization-windows/include/login_window.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include "database_manager.hpp" +#include + +QT_BEGIN_NAMESPACE +namespace Ui { +class LoginWindow; +} +QT_END_NAMESPACE + +class LoginWindow final : public QWidget { + Q_OBJECT + +public: + explicit LoginWindow(QWidget *parent = nullptr); + ~LoginWindow() override; + + static const std::vector THEMES; + void handle_theme_changed(int theme); + +private slots: + void on_switch_mode_clicked(); + void on_push_enter_clicked(); + void on_switch_theme_clicked(); + +private: + Ui::LoginWindow *ui; + int counter_on_switch_theme_clicks = 0; +}; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window_style_sheet.h b/client/ui/authorization-windows/include/login_window_style_sheet.h new file mode 100644 index 0000000..4483df3 --- /dev/null +++ b/client/ui/authorization-windows/include/login_window_style_sheet.h @@ -0,0 +1,377 @@ +#pragma once + +#include "ui_login_window.h" + +namespace Ui { + QString login_window_light_autumn_theme = R"( + QWidget { + background-color: #f5f5f5; + } + + QLabel { + background-color: transparent; + font-family: 'Arial'; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#push_enter { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#push_enter:hover { + background-color: #d58745; + } + + QPushButton#switch_mode:hover { + background-color: #dadada; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid white; + background: white; + color: black; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #089083; + } + QPushButton::hover#switch_theme { + background-color: #089083; + } + QPushButton::pressed#switch_theme { + background-color:rgb(7, 110, 100); + } + + )"; + + QString login_window_dark_autumn_theme = R"( + QWidget { + background-color: #202020; + } + + QLabel { + background-color: transparent; + font-family: 'Arial'; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#push_enter { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: #263238; + padding: 5px 10px; + } + + QPushButton#push_enter:hover { + background-color:rgb(225, 133, 76); + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: #089083; + color: white; + padding: 5px 10px; + } + + QPushButton#switch_mode:hover { + background-color: #01635d; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid rgb(0, 0, 0); + background:rgb(0, 0, 0); + color: #727272; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #089083; + } + QPushButton::hover#switch_theme { + background-color: #089083; + } + QPushButton::pressed#switch_theme { + background-color:rgb(13, 93, 85); + } + )"; + + QString login_window_light_purple_theme = R"( + QWidget { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 #9882B9, stop:0.5 rgb(176, 157, 205), stop:1 rgb(103, 88, 126)); + margin: 0; + padding: 0; + border: none; + } + + QLabel { + background-color: transparent; + font-family: 'Arial'; + font-size: 13px; + color: rgb(42, 10, 25); + padding: 1px; + } + + QPushButton#push_enter { + font-family: 'Arial'; + border-radius: 10px; + background-color: #722548; + color:rgb(206, 193, 224); + padding: 5px 10px; + } + + QPushButton#push_enter:hover { + background-color:rgb(98, 27, 59) + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: rgb(42, 10, 25); + color: rgb(218, 207, 235); + padding: 5px 10px; + } + + QPushButton#switch_mode:hover { + background-color:rgb(27, 6, 16); + } + + QLineEdit { + border-radius: 10px; + border: 1px solid rgb(221, 210, 238); + background: rgb(221, 210, 238); + color: #221932; + padding: 5px; + } + + QLineEdit::placeholder { + color: white; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #060407; + } + QPushButton::hover#switch_theme { + background-color: #060407; + } + QPushButton::pressed#switch_theme { + background-color:rgb(2, 0, 2); + } + )"; + + QString login_window_dark_purple_theme = R"( + QWidget { + background-color:rgb(9, 6, 10); + } + + QLabel { + background-color: transparent; + font-family: 'Arial'; + font-size: 13px; + color: #9882B9; + padding: 1px; + } + + QPushButton#push_enter { + font-family: 'Arial'; + border-radius: 10px; + background-color: #722548; + color: #060407; + padding: 5px 10px; + } + + QPushButton#push_enter:hover { + background-color:rgb(98, 27, 59) + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: rgb(42, 10, 25); + color: #9882B9; + padding: 5px 10px; + } + + QPushButton#switch_mode:hover { + background-color:rgb(27, 6, 16); + } + + QLineEdit { + border-radius: 10px; + border: 1px solid #221932; + background: #221932; + color: #9882B9; + padding: 5px; + } + + QLineEdit::placeholder { + color: white; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #9882B9; + } + QPushButton::hover#switch_theme { + background-color: #9882B9; + } + QPushButton::pressed#switch_theme { + background-color:rgb(113, 93, 143); + } + + )"; + + QString login_window_blue_theme = R"( + QWidget { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 #173C4C, stop:0.5 #326D6C, stop:1 #07142B); + margin: 0; + padding: 0; + border: none; + } + + QLabel { + background-color: transparent; + font-family: 'Arial'; + font-size: 13px; + color: #BDD1BD; + padding: 1px; + } + + QPushButton#push_enter { + font-family: 'Arial'; + border-radius: 10px; + background-color: #568F7C; + color: #BDD1BD; + padding: 5px 10px; + border: none; + font-weight: bold; + } + + QPushButton#push_enter:hover { + background-color: #326D6C; + } + + QPushButton#push_enter:pressed { + background-color: #07142B; + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: #326D6C; + color: #BDD1BD; + padding: 5px 10px; + border: 1px solid #568F7C; + } + + QPushButton#switch_mode:hover { + background-color: #568F7C; + color: #07142B; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid #568F7C; + background: #07142B; + color: #BDD1BD; + padding: 5px; + selection-background-color: #326D6C; + } + + QLineEdit::placeholder { + color: #85B093; + opacity: 0.7; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #85B093; + } + + QPushButton#switch_theme:hover { + background-color: #85B093; + } + + QPushButton#switch_theme:pressed { + background-color:rgb(107, 141, 118); + } + )"; +} // namespace Ui \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window.h b/client/ui/authorization-windows/include/registration_window.h new file mode 100644 index 0000000..0bee3ff --- /dev/null +++ b/client/ui/authorization-windows/include/registration_window.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "database_manager.hpp" +#include + +QT_BEGIN_NAMESPACE + +namespace Ui { +class RegistrationWindow; +} + +QT_END_NAMESPACE + +class RegistrationWindow final : public QWidget { + Q_OBJECT + +public: + static const std::vector THEMES; + + explicit RegistrationWindow(QWidget *parent = nullptr); + ~RegistrationWindow() override; + bool is_strong_and_valid_password(const QString &password); + void handle_theme_changed(int theme); + +private slots: + void on_switch_mode_clicked(); + void on_push_registration_clicked(); + void on_switch_theme_clicked(); + +private: + Ui::RegistrationWindow *ui; + int counter_on_switch_theme_clicks = 0; +}; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window_style_sheet.h b/client/ui/authorization-windows/include/registration_window_style_sheet.h new file mode 100644 index 0000000..3a7c0f7 --- /dev/null +++ b/client/ui/authorization-windows/include/registration_window_style_sheet.h @@ -0,0 +1,373 @@ +#pragma once + +#include "ui_registration_window.h" + +namespace Ui { + QString registration_window_light_autumn_theme = R"( + QWidget { + background-color: #f5f5f5; + } + + QLabel { + font-family: 'Arial'; + background-color: transparent; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#push_registration { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: white; + padding: 5px 10px; + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: white; + color: #fea36b; + padding: 5px 10px; + } + + QPushButton#push_registration:hover { + background-color: #d58745; + } + + QPushButton#switch_mode:hover { + background-color: #dadada; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid white; + background: white; + color: black; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #089083; + } + QPushButton::hover#switch_theme { + background-color: #089083; + } + QPushButton::pressed#switch_theme { + background-color:rgb(7, 110, 100); + } + )"; + + QString registration_window_dark_autumn_theme = R"( + QWidget { + background-color: #202020; + } + + QLabel { + font-family: 'Arial'; + background-color: transparent; + font-size: 13px; + color: #089083; + padding: 1px; + } + + QPushButton#push_registration { + font-family: 'Arial'; + border-radius: 10px; + background-color: #fea36b; + color: #263238; + padding: 5px 10px; + } + + QPushButton#push_registration:hover { + background-color: rgb(225, 133, 76); + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: #089083; + color: white; + padding: 5px 10px; + } + + QPushButton#switch_mode:hover { + background-color: #01635d; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid rgb(0, 0, 0); + background: rgb(0, 0, 0); + color: #727272; + padding: 5px; + } + + QLineEdit::placeholder { + color: #727272; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #089083; + } + QPushButton::hover#switch_theme { + background-color: #089083; + } + QPushButton::pressed#switch_theme { + background-color:rgb(13, 93, 85); + } + )"; + + QString registration_window_light_purple_theme = R"( + QWidget { + background: qlineargradient(x1:0, y1:0, x2:0, y2:1, + stop:0 #9882B9, stop:0.5 rgb(176, 157, 205), stop:1 rgb(103, 88, 126)); + margin: 0; + padding: 0; + border: none; + } + + QLabel { + font-family: 'Arial'; + background-color: transparent; + font-size: 13px; + color: rgb(42, 10, 25); + padding: 1px; + } + + QPushButton#push_registration { + font-family: 'Arial'; + border-radius: 10px; + background-color: #722548; + color: rgb(206, 193, 224); + padding: 5px 10px; + } + + QPushButton#push_registration:hover { + background-color: rgb(98, 27, 59); + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: rgb(42, 10, 25); + color: rgb(218, 207, 235); + padding: 5px 10px; + } + + QPushButton#switch_mode:hover { + background-color: rgb(27, 6, 16); + } + + QLineEdit { + border-radius: 10px; + border: 1px solid rgb(221, 210, 238); + background: rgb(221, 210, 238); + color: #221932; + padding: 5px; + } + + QLineEdit::placeholder { + color: white; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #060407; + } + QPushButton::hover#switch_theme { + background-color: #060407; + } + QPushButton::pressed#switch_theme { + background-color:rgb(2, 0, 2); + } + )"; + + QString registration_window_dark_purple_theme = R"( + QWidget { + background-color: rgb(9, 6, 10); + } + + QLabel { + font-family: 'Arial'; + background-color: transparent; + font-size: 13px; + color: #9882B9; + padding: 1px; + } + + QPushButton#push_registration { + font-family: 'Arial'; + border-radius: 10px; + background-color: #722548; + color: #060407; + padding: 5px 10px; + } + + QPushButton#push_registration:hover { + background-color: rgb(98, 27, 59); + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: rgb(42, 10, 25); + color: #9882B9; + padding: 5px 10px; + } + + QPushButton#switch_mode:hover { + background-color: rgb(27, 6, 16); + } + + QLineEdit { + border-radius: 10px; + border: 1px solid #221932; + background: #221932; + color: #9882B9; + padding: 5px; + } + + QLineEdit::placeholder { + color: white; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #9882B9; + } + QPushButton::hover#switch_theme { + background-color: #9882B9; + } + QPushButton::pressed#switch_theme { + background-color:rgb(113, 93, 143); + } + )"; + + QString registration_window_blue_theme = R"( + QWidget { + background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, + stop:0 #173C4C, stop:0.5 #326D6C, stop:1 #07142B) !important; + margin: 0; + padding: 0; + border: none; + } + + QLabel { + font-family: 'Arial'; + background-color: transparent; + font-size: 13px; + color: #BDD1BD; + padding: 1px; + } + + QPushButton#push_registration { + font-family: 'Arial'; + border-radius: 10px; + background-color: #568F7C; + color: #BDD1BD; + padding: 5px 10px; + border: none; + font-weight: bold; + } + + QPushButton#push_registration:hover { + background-color: #326D6C; + } + + QPushButton#push_registration:pressed { + background-color: #07142B; + } + + QPushButton#switch_mode { + font-family: 'Arial'; + border-radius: 10px; + background-color: #326D6C; + color: #BDD1BD; + padding: 5px 10px; + border: 1px solid #568F7C; + } + + QPushButton#switch_mode:hover { + background-color: #568F7C; + color: #07142B; + } + + QLineEdit { + border-radius: 10px; + border: 1px solid #568F7C; + background: #07142B; + color: #BDD1BD; + padding: 5px; + selection-background-color: #326D6C; + } + + QLineEdit::placeholder { + color: #85B093; + opacity: 0.7; + } + + QPushButton#switch_theme { + width: 20px; + height: 20px; + min-width: 20px; + min-height: 20px; + max-width: 20px; + max-height: 20px; + border-radius: 7px; + padding: 0; + margin-bottom: 2px; + background-color: transparent; + border: 2px solid #85B093; + } + QPushButton#switch_theme:hover { + background-color: #85B093; + } + QPushButton#switch_theme:pressed { + background-color:rgb(107, 141, 118); + } + )"; +} // namespace Ui \ No newline at end of file diff --git a/client/ui/authorization-windows/login_window_ru_RU.ts b/client/ui/authorization-windows/login_window_ru_RU.ts new file mode 100644 index 0000000..32bf76c --- /dev/null +++ b/client/ui/authorization-windows/login_window_ru_RU.ts @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/client/ui/authorization-windows/src/login_window.cpp b/client/ui/authorization-windows/src/login_window.cpp new file mode 100644 index 0000000..0e0e297 --- /dev/null +++ b/client/ui/authorization-windows/src/login_window.cpp @@ -0,0 +1,127 @@ +#include "login_window.h" +#include +#include +#include +#include +#include +#include "model-proto/model.pb.h" +#include "login_window_style_sheet.h" +#include "lr_dao.hpp" +#include "registration_window.h" +#include "theme_manager.h" + +using Efficio_proto::Storage; + +const std::vector LoginWindow::THEMES = { + Ui::login_window_light_autumn_theme, + Ui::login_window_dark_autumn_theme, + Ui::login_window_dark_purple_theme, + Ui::login_window_light_purple_theme, + Ui::login_window_blue_theme +}; + +LoginWindow::LoginWindow(QWidget *parent) + : QWidget(parent), ui(new Ui::LoginWindow) { + ui->setupUi(this); + + setFixedSize(380, 480); + ui->input_login->setPlaceholderText("Введите логин:"); + ui->input_password->setPlaceholderText("Введите пароль:"); + + ui->input_password->setEchoMode(QLineEdit::Password); + handle_theme_changed(ThemeManager::get_instance()->get_current_theme()); + + connect( + ui->switch_theme, &QPushButton::clicked, this, + &LoginWindow::on_switch_theme_clicked + , Qt::UniqueConnection); + + connect( + ui->switch_mode, &QPushButton::clicked, this, + &LoginWindow::on_switch_mode_clicked); + connect( + ui->push_enter, &QPushButton::clicked, this, + &LoginWindow::on_push_enter_clicked); + connect(ThemeManager::get_instance(), &ThemeManager::on_theme_changed, + this, &LoginWindow::handle_theme_changed); +} + + +void LoginWindow::handle_theme_changed(int theme) { + this->setStyleSheet(THEMES[theme]); +} + +void LoginWindow::on_switch_theme_clicked() { + if (this->counter_on_switch_theme_clicks++ % 2) { + const int next_theme = + (ThemeManager::get_instance()->get_current_theme() + 1) % 5; + ThemeManager::get_instance()->apply_theme(next_theme); + } +} + +LoginWindow::~LoginWindow() { + delete ui; +} + +void LoginWindow::on_switch_mode_clicked() { + QWidget *parent = this->parentWidget(); + + auto *app_window = qobject_cast(parent); + + if (QWidget *old = app_window->centralWidget()) { + old->deleteLater(); + } + Storage storage; + auto *registration_window = + new RegistrationWindow(app_window); + + app_window->setCentralWidget(registration_window); + QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); + int x = (screenGeometry.width() - registration_window->width()) / 2; + int y = (screenGeometry.height() - registration_window->height()) / 2; + app_window->move(x, y); + + this->close(); +} + +void LoginWindow::on_push_enter_clicked() { + if (this->counter_on_switch_theme_clicks++ % 2) { + const QString login = ui->input_login->text(); + const QString password = ui->input_password->text(); + + if (!login.isEmpty() && !password.isEmpty()) { + if (login.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина логина не должна превышать пятидесяти символов" + ); + } else if (password.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина пароля не должна превышать пятидесяти символов" + ); + } else if (LRDao::validate_user(login, password)) { + QMessageBox::information( + this, "Вход", "Вы успешно вошли! Добро пожаловать :)" + ); + QWidget *parent = this->parentWidget(); + + const QMainWindow *app_window = qobject_cast(parent); + + if (QWidget *old = app_window->centralWidget()) { + old->deleteLater(); + } + + this->close(); + } else { + QMessageBox::warning( + this, "Ошибка ввода данных", "Неверный логин или пароль!" + ); + } + } else { + QMessageBox::warning( + this, "Ошибка ввода данных", "Пожалуйста, заполните все поля!" + ); + } + } +} diff --git a/client/ui/authorization-windows/src/registration_window.cpp b/client/ui/authorization-windows/src/registration_window.cpp new file mode 100644 index 0000000..d6d50d5 --- /dev/null +++ b/client/ui/authorization-windows/src/registration_window.cpp @@ -0,0 +1,188 @@ +#include "registration_window.h" +#include +#include +#include +#include +#include +#include +#include "login_window.h" +#include "lr_dao.hpp" +#include "registration_window_style_sheet.h" +#include "theme_manager.h" + +using Efficio_proto::Storage; + +const std::vector RegistrationWindow::THEMES = { + Ui::registration_window_light_autumn_theme, + Ui::registration_window_dark_autumn_theme, + Ui::registration_window_dark_purple_theme, + Ui::registration_window_light_purple_theme, + Ui::registration_window_blue_theme + }; + +RegistrationWindow::RegistrationWindow(QWidget *parent) + : QWidget(parent), ui(new Ui::RegistrationWindow) { + ui->setupUi(this); + + setFixedSize(380, 480); + + ui->create_login->setPlaceholderText("Введите логин:"); + ui->create_password->setPlaceholderText("Введите пароль:"); + ui->repeat_password->setPlaceholderText("Повторите пароль:"); + ui->create_password->setEchoMode(QLineEdit::Password); + ui->repeat_password->setEchoMode(QLineEdit::Password); + + setAttribute(Qt::WA_StyledBackground, true); + + connect( + ui->switch_theme, &QPushButton::clicked, this, + &RegistrationWindow::on_switch_theme_clicked + , Qt::UniqueConnection); + + connect( + ui->push_registration, &QPushButton::clicked, this, + &RegistrationWindow::on_push_registration_clicked + , Qt::UniqueConnection); + connect( + ui->switch_mode, &QPushButton::clicked, this, + &RegistrationWindow::on_switch_mode_clicked + , Qt::UniqueConnection); + connect(ThemeManager::get_instance(), &ThemeManager::on_theme_changed, + this, &RegistrationWindow::handle_theme_changed); + handle_theme_changed(ThemeManager::get_instance()->get_current_theme()); +} + + +void RegistrationWindow::handle_theme_changed(const int theme) { + this->setStyleSheet(THEMES[theme]); +} + +void RegistrationWindow::on_switch_theme_clicked() { + if ((this->counter_on_switch_theme_clicks++)%2){ + int next_theme = (ThemeManager::get_instance()->get_current_theme() + 1) % 5; + ThemeManager::get_instance()->apply_theme(next_theme); + } +} + + +RegistrationWindow::~RegistrationWindow() { + delete ui; +} + +void RegistrationWindow::on_switch_mode_clicked() { + QWidget *parent = this->parentWidget(); + + auto *app_window = qobject_cast(parent); + + if (QWidget *old = app_window->centralWidget()) { + old->deleteLater(); + } + Storage storage; + auto *login_window = new LoginWindow(app_window); + + app_window->setCentralWidget(login_window); + const QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); + const int x = (screenGeometry.width() - login_window->width()) / 2; + const int y = (screenGeometry.height() - login_window->height()) / 2; + app_window->move(x, y); + + this->close(); +} + +bool RegistrationWindow::is_strong_and_valid_password(const QString &password) { + if (password.length() < 8) { + QMessageBox::warning( + nullptr, "Ошибка: недостаточно надежный пароль", + "Пароль должен содержать не менее восьми символов" + ); + return false; + } + + bool has_digit = false; + bool has_latin_letter = false; + + for (const QChar symbol : password) { + if (!symbol.isLetter() && !symbol.isDigit()) { + QMessageBox::warning( + nullptr, "Ошибка", + "Пароль должен содержать только символы латиницы и цифры" + ); + return false; + } + + if (symbol.isLetter()) { + has_latin_letter = true; + } else if (symbol.isDigit()) { + has_digit = true; + } + } + + if (!has_latin_letter) { + QMessageBox::warning( + nullptr, "Ошибка: недостаточно надежный пароль", + "Пароль должен содержать хотя бы одну букву" + ); + return false; + } + if (!has_digit) { + QMessageBox::warning( + nullptr, "Ошибка: недостаточно надежный пароль", + "Пароль должен содержать хотя бы одну цифру" + ); + return false; + } + + return true; +} + +void RegistrationWindow::on_push_registration_clicked() { + if (this->counter_on_switch_theme_clicks++ % 2) { + const QString created_login = ui->create_login->text(); + const QString created_password = ui->create_password->text(); + const QString repeated_password = ui->repeat_password->text(); + + if (!created_login.isEmpty() && !created_password.isEmpty() && + !repeated_password.isEmpty()) { + if (created_password != repeated_password) { + QMessageBox::warning(this, "Ошибка", "Пароли не совпадают!"); + } else if (created_login.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина логина не должна превышать пятидесяти символов" + ); + } else if (created_password.size() > 50) { + QMessageBox::warning( + this, "Ошибка", + "Длина пароля не должна превышать пятидесяти символов" + ); + } else if (is_strong_and_valid_password(created_password)) { + const int try_register_user = + LRDao::try_register_user(created_login, created_password); + if (try_register_user == 0) { + QMessageBox::warning( + this, "Ошибка", + "Извините, внутренняя ошибка с базами данных." + ); + } else if (try_register_user == -1) { + QMessageBox::warning( + this, "Ошибка", + "Пользователь с таким именем уже существует. " + "Пожалуйста, " + "придумайте другое!" + ); + } else { + QMessageBox::information( + this, "Регистрация", + "Вы успешно зарегистрировались! Пожалуйста, выполните " + "вход." + ); + on_switch_mode_clicked(); + } + } + } else { + QMessageBox::warning( + this, "Ошибка", "Пожалуйста, заполните все поля." + ); + } + } +} diff --git a/client/ui/authorization-windows/ui/login_window.ui b/client/ui/authorization-windows/ui/login_window.ui new file mode 100644 index 0000000..a9ea57e --- /dev/null +++ b/client/ui/authorization-windows/ui/login_window.ui @@ -0,0 +1,153 @@ + + + LoginWindow + + + + 0 + 0 + 362 + 600 + + + + Dialog + + + + + 0 + 0 + 375 + 501 + + + + + + 120 + 280 + 131 + 51 + + + + font: 700 13pt "Arial"; +border-radius: 10px; + + + Войти + + + + + + 133 + 130 + 91 + 41 + + + + font: 700 21pt "Arial"; + + + Вход + + + + + + 62 + 175 + 251 + 41 + + + + border-radius: 10px; + + + Логин + + + + + + 62 + 225 + 251 + 41 + + + + border-radius: 10px; + + + Пароль + + + + + + 80 + 340 + 211 + 31 + + + + font: 7pt "Arial"; +border-radius: 10px; + + + Еще нет аккаунта? Зарегистрируйтесь! + + + + + + 350 + 455 + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + + 0 + 0 + 362 + 26 + + + + + + + 0 + 0 + 16 + 25 + + + + + + + \ No newline at end of file diff --git a/client/ui/authorization-windows/ui/registration_window.ui b/client/ui/authorization-windows/ui/registration_window.ui new file mode 100644 index 0000000..0ba3e34 --- /dev/null +++ b/client/ui/authorization-windows/ui/registration_window.ui @@ -0,0 +1,121 @@ + + + RegistrationWindow + + + + 0 + 0 + 390 + 562 + + + + Dialog + + + + + 350 + 455 + 20 + 20 + + + + + 20 + 20 + + + + + 20 + 20 + + + + + + + 85 + 290 + 210 + 51 + + + + font: 700 12pt "Arial"; +border-radius: 10px; + + + Зарегистрироваться + + + + + + 110 + 350 + 161 + 31 + + + + font: 7pt "Arial"; +border-radius: 10px; + + + Уже есть аккаунт? Войдите! + + + + + + 60 + 140 + 261 + 41 + + + + + + + 60 + 190 + 261 + 41 + + + + + + + 60 + 240 + 261 + 41 + + + + + + + 94 + 90 + 201 + 51 + + + + font: 700 19pt "Arial"; + + + Регистрация + + + + + + \ No newline at end of file diff --git a/client/ui/main-window/CMakeLists.txt b/client/ui/main-window/CMakeLists.txt new file mode 100644 index 0000000..f91a86d --- /dev/null +++ b/client/ui/main-window/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.16) + +project(MainWindow LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}) + +file(GLOB_RECURSE SOURCES CONFIGURE_DEPENDS "src/*.cpp") +file(GLOB_RECURSE HEADERS CONFIGURE_DEPENDS "include/*.h") +file(GLOB UI_FILES CONFIGURE_DEPENDS "ui/*.ui") + +add_library(MainWindow STATIC + ${SOURCES} + ${HEADERS} + ${UI_FILES} +) + +target_include_directories(MainWindow PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR} +) + +target_link_libraries(MainWindow PRIVATE + Qt6::Widgets + Qt6::Core + Qt6::Gui + Qt6::Sql +) \ No newline at end of file diff --git a/client/ui/main-window/MainWindow_en_US.ts b/client/ui/main-window/MainWindow_en_US.ts new file mode 100644 index 0000000..a3740fb --- /dev/null +++ b/client/ui/main-window/MainWindow_en_US.ts @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/client/ui/main-window/include/applicationwindow.h b/client/ui/main-window/include/applicationwindow.h new file mode 100644 index 0000000..ccb14f8 --- /dev/null +++ b/client/ui/main-window/include/applicationwindow.h @@ -0,0 +1,16 @@ +#ifndef APPLICATIONWINDOW_H +#define APPLICATIONWINDOW_H + +#include + +namespace Ui { +class ApplicationWindow : public QMainWindow { + Q_OBJECT + +public: + explicit ApplicationWindow(std::string window_name); + +signals: +}; +} // namespace Ui +#endif // APPLICATIONWINDOW_H \ No newline at end of file diff --git a/client/ui/main-window/src/applicationwindow.cpp b/client/ui/main-window/src/applicationwindow.cpp new file mode 100644 index 0000000..62c7bfe --- /dev/null +++ b/client/ui/main-window/src/applicationwindow.cpp @@ -0,0 +1,14 @@ +#include "applicationwindow.h" +#include +#include "mainwindow.h" + +namespace Ui { +ApplicationWindow::ApplicationWindow(std::string window_name_) + : QMainWindow{nullptr} { + this->setObjectName("ApplicationWindow"); + + this->setAttribute(Qt::WA_StyledBackground); + + this->setWindowTitle(window_name_.c_str()); +} +} // namespace Ui \ No newline at end of file diff --git a/client/ui/theme-manager/CMakeLists.txt b/client/ui/theme-manager/CMakeLists.txt new file mode 100644 index 0000000..9f8bc1e --- /dev/null +++ b/client/ui/theme-manager/CMakeLists.txt @@ -0,0 +1,32 @@ +cmake_minimum_required(VERSION 3.16) + +project(ThemeManager LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(Qt6 COMPONENTS Core Gui Widgets Sql REQUIRED) + +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +set(SOURCES + src/theme_manager.cpp +) + +set(HEADERS + include/theme_manager.h +) + +add_library(ThemeManager STATIC ${SOURCES} ${HEADERS}) + +target_include_directories(ThemeManager + PUBLIC + $ + $ + $ +) + +target_link_libraries(ThemeManager PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto) + diff --git a/client/ui/theme-manager/include/theme_manager.h b/client/ui/theme-manager/include/theme_manager.h new file mode 100644 index 0000000..2b8cffe --- /dev/null +++ b/client/ui/theme-manager/include/theme_manager.h @@ -0,0 +1,23 @@ +#ifndef THEME_MANAGER_H +#define THEME_MANAGER_H + +#include + +class ThemeManager final : public QObject { + Q_OBJECT + +public: + static ThemeManager* get_instance(); + void apply_theme(int theme); + [[nodiscard]] int get_current_theme() const; + +signals: + void on_theme_changed(int new_theme); + +private: + explicit ThemeManager(QObject *parent = nullptr); + static ThemeManager* instance_; + int current_theme_; +}; + +#endif // THEME_MANAGER_H \ No newline at end of file diff --git a/client/ui/theme-manager/src/theme_manager.cpp b/client/ui/theme-manager/src/theme_manager.cpp new file mode 100644 index 0000000..87a85a8 --- /dev/null +++ b/client/ui/theme-manager/src/theme_manager.cpp @@ -0,0 +1,27 @@ +#include "theme_manager.h" +#include +#include + +ThemeManager* ThemeManager::instance_ = nullptr; + +ThemeManager::ThemeManager(QObject *parent) + : QObject(parent) { + current_theme_ = 0; + emit on_theme_changed(this->current_theme_); +} + +ThemeManager* ThemeManager::get_instance() { + if (!instance_) { + instance_ = new ThemeManager(); + } + return instance_; +} + +void ThemeManager::apply_theme(const int theme) { + this->current_theme_ = theme; + emit on_theme_changed(this->current_theme_); +} + +int ThemeManager::get_current_theme() const { + return current_theme_; +} \ No newline at end of file diff --git a/server/Service/CMakeLists.txt b/server/Service/CMakeLists.txt index a8f0c79..9801363 100644 --- a/server/Service/CMakeLists.txt +++ b/server/Service/CMakeLists.txt @@ -14,7 +14,9 @@ find_package(Threads) file(GLOB SOURCES "src/*.cpp") file(GLOB HEADERS "include/*.h") -add_library(Service STATIC ${SOURCES} ${HEADERS}) +add_library(Service STATIC ${SOURCES} ${HEADERS} + include/auth_service.h + src/auth_service.cpp) target_link_libraries(Service Database diff --git a/server/Service/include/auth_service.h b/server/Service/include/auth_service.h new file mode 100644 index 0000000..276cef7 --- /dev/null +++ b/server/Service/include/auth_service.h @@ -0,0 +1,63 @@ +#ifndef AUTH_SERVICE_H +#define AUTH_SERVICE_H + +#include +#include +#include +#include "common_server_call.h" + +using grpc::ServerAsyncResponseWriter; +using grpc::ServerContext; + +using Efficio_proto::Auth; + +using Efficio_proto::AuthRequest; +using Efficio_proto::AuthResponse; + +class AuthService final { + Auth::AsyncService service_; + ServerContext ctx_; + std::unique_ptr cq_; + std::unique_ptr server_; + + class AuthOperation : public CommonServerCall { + protected: + AuthRequest request_; + ServerAsyncResponseWriter responder_; + AuthService& service_; + + explicit AuthOperation( + AuthService& service, + ServerCompletionQueue* cq + ) : CommonServerCall(cq), + responder_(&ctx_), + service_(service) {} + + public: + ~AuthOperation() override = default; + void Proceed(bool ok) override = 0; + }; + + class TryAuthenticateUserCall final : AuthOperation { + explicit TryAuthenticateUserCall( + AuthService &service, + ServerCompletionQueue *cq + ); + public: + void Proceed(bool) override; + }; + + class TryRegisterUserCall final : AuthOperation { + explicit TryRegisterUserCall( + AuthService &service, + ServerCompletionQueue *cq + ); + public: + void Proceed(bool) override; + }; + +public: + Auth::AsyncService &get_service(); +}; + +#endif //AUTH_SERVICE_H diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp new file mode 100644 index 0000000..81c47d6 --- /dev/null +++ b/server/Service/src/auth_service.cpp @@ -0,0 +1,92 @@ +#include "auth_service.h" +#include "lr_dao.hpp" + +AuthService::TryAuthenticateUserCall::TryAuthenticateUserCall( + AuthService &service, + ServerCompletionQueue *cq +) : AuthOperation(service, cq) { + service_.service_.RequestTryAuthenticateUser( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + status_ = PROCESS; +} + +void AuthService::TryAuthenticateUserCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + + switch (status_) { + case PROCESS: { + new TryAuthenticateUserCall(service_, cq_); + + AuthResponse response; + + const int query_exit_code = LRDao::validate_user( + request_.user().login(), request_.user().token() + ); + + if (query_exit_code == 1) { + response.mutable_user()->CopyFrom(request_.user()); + } else { + response.set_error_text( + "Не удалось выполнить запрос в базу данных на проверку " + "корректности логина и пароля" + ); + } + + status_ = FINISH; + break; + } + case FINISH: { + delete this; + break; + } + } +} + +AuthService::TryRegisterUserCall::TryRegisterUserCall( + AuthService &service, + ServerCompletionQueue *cq +) : AuthOperation(service, cq) { + service_.service_.RequestTryRegister1User( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + status_ = PROCESS; +} + +void AuthService::TryRegisterUserCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + + switch (status_) { + case PROCESS: { + new TryRegisterUserCall(service_, cq_); + + AuthResponse response; + + const int query_exit_code = LRDao::try_register_user( + request_.user().login(), request_.user().token() + ); + + if (query_exit_code < 1) { + response.set_error_text( + "Не удалось выполнить запрос на запись нового пользователя " + "в базу данных" + ); + } else { + response.mutable_user()->CopyFrom(request_.user()); + } + + status_ = FINISH; + break; + } + case FINISH: { + delete this; + break; + } + } +} \ No newline at end of file diff --git a/server/database/include/lr_dao.hpp b/server/database/include/lr_dao.hpp new file mode 100644 index 0000000..f98dcb6 --- /dev/null +++ b/server/database/include/lr_dao.hpp @@ -0,0 +1,20 @@ +#ifndef LRDAO_H +#define LRDAO_H + +#include +#include + +class LRDao { +public: + LRDao() = default; + static int try_register_user(const std::string &login, const std::string &password); + static bool validate_user(const std::string &login, const std::string &password); + static bool add_project_to_user(const std::string &user_login, int project_id); + static bool + get_user_projects(const std::string &login, std::vector &projects); + +private: + static std::string hash_password(const std::string &password); +}; + +#endif // LRDAO_H \ No newline at end of file diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp new file mode 100644 index 0000000..abaf97a --- /dev/null +++ b/server/database/src/lr_dao.cpp @@ -0,0 +1,100 @@ +#include "lr_dao.hpp" +#include +#include "database_manager.hpp" + +int LRDao::try_register_user(const std::string &login, const std::string &password) { + auto &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string check_query = "SELECT * FROM users WHERE login = ?"; + pqxx::params check_params; + check_params.append(login); + + const pqxx::result check_result = transaction.exec(check_query, check_params); + + if (!check_result.empty()) { + return -1; + } + + const std::string insert_query = "INSERT INTO users (login, password) VALUES (?, ?)"; + pqxx::params insert_params; + insert_params.append(login); + insert_params.append(password); + + const pqxx::result insert_result = transaction.exec(insert_query, insert_params); + + transaction.commit(); + return insert_result.affected_rows() > 0; +} + +bool LRDao::validate_user(const std::string &login, const std::string &password) { + auto &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = "SELECT * FROM users WHERE login = ? AND password = ?"; + pqxx::params params; + params.append(login); + params.append(password); + + const pqxx::result result = transaction.exec(query, params); + + transaction.commit(); + return result.affected_rows() > 0; +} + +bool LRDao::add_project_to_user( + const std::string &user_login, const int project_id) { + auto &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = "UPDATE users SET projects = array_append(projects, ?)" + "WHERE login = ?"; + pqxx::params params; + params.append(project_id); + params.append(user_login); + + const pqxx::result result = transaction.exec(query, params); + transaction.commit(); + return result.affected_rows() > 0; +} + +bool LRDao::get_user_projects( + const std::string &login, + std::vector &projects +) { + auto &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = "SELECT projects FROM users WHERE login = $1"; + pqxx::params params; + params.append(login); + + const pqxx::result result = transaction.exec(query, params); + + if (!result.empty()) { + const auto &row = result[0]; + if (!row["projects"].is_null()) { + const auto projects_as_string = row["projects"].as(); + + std::string clean_string = projects_as_string; + if (!clean_string.empty() && clean_string.front() == '{' && + clean_string.back() == '}') { + clean_string = clean_string.substr(1, clean_string.size() - 2); + } + + std::istringstream iss(clean_string); + std::string project_id; + while (std::getline(iss, project_id, ',')) { + if (!project_id.empty()) { + projects.push_back(std::stoi(project_id)); + } + } + + transaction.commit(); + return true; + } + } + + transaction.commit(); + return false; +} From ca6c59bfc8172fee932549d012dcbed8d5f93ffc Mon Sep 17 00:00:00 2001 From: toximu Date: Mon, 5 May 2025 14:53:05 +0300 Subject: [PATCH 24/47] feat: insert_project, add_member_to_project, add_note_to_project, change_project_title --- server/database/include/project_dao.hpp | 18 ++++- server/database/src/project_dao.cpp | 93 +++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 3 deletions(-) diff --git a/server/database/include/project_dao.hpp b/server/database/include/project_dao.hpp index 8ffa3ee..0e46d77 100644 --- a/server/database/include/project_dao.hpp +++ b/server/database/include/project_dao.hpp @@ -7,11 +7,23 @@ using namespace Efficio_proto; class ProjectDAO { - public: - ProjectDAO() = default; - static bool get_project(const std::string &code, Project &project); +public: + ProjectDAO() = default; + static bool get_project(const std::string &project_code, Project &project); + static bool insert_project(Project &project); + static bool add_member_to_project(const std::string& project_code, const std::string& member); + static bool add_note_to_project(const std::string& project_code, int note_id); + static bool change_project_title(const std::string& project_code, const std::string& new_title); }; +template +std::vector proto_arr_to_vector(const google::protobuf::RepeatedPtrField &array) { + std::vector vec; + for (int i = 0; i < array.size(); ++i) { + vec.push_back(array[i]); + } + return std::move(vec); +} #endif diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index aaa2fe8..c55d21c 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -65,3 +65,96 @@ bool ProjectDAO::get_project(const std::string &code, Project &project) { } + +bool ProjectDAO::insert_project(Project &project) { + auto& connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = + "INSERT INTO projects (code, title, notes, members) " + "VALUES ($1, $2, $3, $4) "; + + std::vector note_ids; + for (auto note : project.notes()) { + note_ids.push_back(note.id()); + } + + const pqxx::result result = transaction.exec_params( + query, + project.code(), + project.title(), + note_ids, + proto_arr_to_vector(project.members()) + ); + + transaction.commit(); + return true; +} + +bool ProjectDAO::add_member_to_project( + const std::string &project_code, + const std::string &member +) { + auto& connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = + "UPDATE projects " + "SET members = array_append(members, $1) " + "WHERE code = $2 " + "RETURNING 1;"; + + + const pqxx::result result = transaction.exec_params(query, member, project_code); + if (result.empty()) { + return false; + } + transaction.commit(); + return true; +} + + +bool ProjectDAO::add_note_to_project( + const std::string &project_code, + int note_id +) { + auto& connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = + "UPDATE projects " + "SET notes = array_append(notes, $1) " + "WHERE code = $2 " + "RETURNING 1;"; + + const pqxx::result result = transaction.exec_params(query,note_id, project_code); + if (result.empty()) { + return false; + } + transaction.commit(); + return true; +} + +bool ProjectDAO::change_project_title( + const std::string &project_code, + const std::string &new_title +) { + auto& connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = + "UPDATE projects " + "SET title = $1 " + "WHERE code = $2 " + "RETURNING 1;"; + + const pqxx::result result = transaction.exec_params(query, new_title, project_code); + if (result.empty()) { + return false; + } + + transaction.commit(); + return true; +} + + From 5b312d109235b7b3b90397602a81e224f5dd235c Mon Sep 17 00:00:00 2001 From: toximu Date: Tue, 6 May 2025 16:44:02 +0300 Subject: [PATCH 25/47] feat: get project and create project, start update handler --- client/client/include/client_implementation.h | 17 +++- client/client/include/common_client_call.h | 9 +- client/client/include/update_requests.h | 71 +++++++++----- client/client/src/client_implementation.cpp | 37 ++------ client/client/src/update_requests.cpp | 78 +++++++++++----- proto/efficio-rpc-proto/efficio.proto | 2 +- server/CMakeLists.txt | 3 +- server/Handlers/CMakeLists.txt | 22 +++++ server/Handlers/include/update_handler.hpp | 17 ++++ server/Handlers/src/update_handler.cpp | 34 +++++++ server/Service/CMakeLists.txt | 1 + server/Service/include/common_server_call.h | 17 ++-- .../Service/include/server_implementation.h | 5 +- server/Service/include/update_service.h | 48 +++++++--- server/Service/src/server_implementation.cpp | 3 +- server/Service/src/update_service.cpp | 92 +++++++++++++++---- server/database/CMakeLists.txt | 6 +- server/database/include/database_manager.hpp | 2 +- server/database/include/note_dao.hpp | 2 +- server/database/include/project_dao.hpp | 23 +++-- server/database/src/database_manager.cpp | 3 +- server/database/src/note_dao.cpp | 11 ++- server/database/src/project_dao.cpp | 56 ++++++----- 23 files changed, 388 insertions(+), 171 deletions(-) create mode 100644 server/Handlers/CMakeLists.txt create mode 100644 server/Handlers/include/update_handler.hpp create mode 100644 server/Handlers/src/update_handler.cpp diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 90f232f..04a895c 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -3,17 +3,24 @@ #include -using grpc::CompletionQueue; using grpc::Channel; +using grpc::CompletionQueue; class ClientImplementation { CompletionQueue cq_; std::shared_ptr channel_; + public: - CompletionQueue *get_cq() { return &cq_; } - std::shared_ptr get_channel() { return channel_; } - explicit ClientImplementation(const std::shared_ptr &channel); + CompletionQueue *get_cq() { + return &cq_; + } + + std::shared_ptr get_channel() { + return channel_; + } + + explicit ClientImplementation(std::shared_ptr channel); void CompleteRpc(); }; -#endif //CLIENTIMPLEMENTATION_H \ No newline at end of file +#endif // CLIENTIMPLEMENTATION_H \ No newline at end of file diff --git a/client/client/include/common_client_call.h b/client/client/include/common_client_call.h index 7c52ef1..0ba1d9b 100644 --- a/client/client/include/common_client_call.h +++ b/client/client/include/common_client_call.h @@ -6,12 +6,8 @@ using grpc::ClientContext; using grpc::Status; - -class CommonClientCall -{ +class CommonClientCall { public: - - explicit CommonClientCall() = default; virtual ~CommonClientCall() = default; @@ -21,5 +17,4 @@ class CommonClientCall virtual void Proceed(bool = true) = 0; }; - -#endif //COMMON_CLIENT_CALL_H +#endif // COMMON_CLIENT_CALL_H diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 419b20d..3df499c 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -10,53 +10,74 @@ using grpc::Channel; using grpc::ClientAsyncResponseReader; using grpc::CompletionQueue; -using Efficio_proto::Update; +using Efficio_proto::CreateProjectRequest; +using Efficio_proto::CreateProjectResponse; +using Efficio_proto::GetNoteRequest; +using Efficio_proto::GetNoteResponse; +using Efficio_proto::GetProjectRequest; +using Efficio_proto::GetProjectResponse; using Efficio_proto::Note; using Efficio_proto::Project; using Efficio_proto::Storage; -using Efficio_proto::GetProjectRequest; -using Efficio_proto::GetProjectResponse; -using Efficio_proto::GetNoteRequest; -using Efficio_proto::GetNoteResponse; +using Efficio_proto::Update; class UpdateRequests { +public: + class GetProjectClientCall final : public CommonClientCall { + GetProjectResponse response; + std::unique_ptr> + response_reader; + Project *save_to; + public: + void Proceed(bool ok) override; + GetProjectClientCall( + GetProjectRequest &request, + CompletionQueue *cq_, + std::unique_ptr &stub_, + Project *save_to_ + ); + }; + class CreateProjectClientCall : public CommonClientCall { + CreateProjectResponse response; + std::unique_ptr> + response_reader; + Project *save_to; - class GetProjectClientCall final : public CommonClientCall { - GetProjectResponse response; - std::unique_ptr> response_reader; - public: + public: void Proceed(bool ok) override; - GetProjectClientCall(GetProjectRequest& request, - CompletionQueue* cq_, - std::unique_ptr& stub_); + CreateProjectClientCall( + CreateProjectRequest &request, + CompletionQueue *cq_, + std::unique_ptr &stub_, + Project *save_to_ + ); }; - class GetNoteClientCall; class CreateNoteClientCall; - class CreateProjectClientCall; + class TryJoinProjectClientCall; bool get_note(Note *note); - bool get_project(Project *project); + bool get_project(Project *project, const std::string &code); bool create_note(Note *note); - bool create_project(Project *project); + bool create_project(Project *project, const std::string &project_title); bool try_join_project(Project *project); + explicit UpdateRequests( + std::shared_ptr channel, + CompletionQueue *cq + ) + : stub_(Update::NewStub(channel)), cq_(cq) { + } - - explicit UpdateRequests(std::shared_ptr channel, CompletionQueue *cq): - stub_(Update::NewStub(channel)), cq_(cq) {} - private: - CompletionQueue* cq_; +private: + CompletionQueue *cq_; std::unique_ptr stub_; - - }; - -#endif //UPDATE_REQUESTS_H +#endif // UPDATE_REQUESTS_H diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index fc7c7ac..4b6c678 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -1,11 +1,10 @@ #include "client_implementation.h" -#include "update_requests.h" #include #include -#include #include +#include #include - +#include "update_requests.h" using grpc::Channel; using grpc::ClientAsyncResponseReader; @@ -13,48 +12,30 @@ using grpc::ClientContext; using grpc::CompletionQueue; using grpc::Status; -ClientImplementation::ClientImplementation(std::shared_ptr channel) : - channel_(channel) - { - - // update_requests.get_project(pr); - auto pr = new Project(); - UpdateRequests update_requests(channel_, &cq_); +ClientImplementation::ClientImplementation(std::shared_ptr channel) + : channel_(channel) { std::thread t(&ClientImplementation::CompleteRpc, this); - std::string q; - - while (std::cin >> q) { - - update_requests.get_project(pr); - - } - t.join(); - delete pr; - + t.detach(); }; void ClientImplementation::CompleteRpc() { - - void* got_tag; + void *got_tag; bool ok = false; - while (cq_.Next(&got_tag, &ok)) { std::cout << "got smth" << std::endl; - CommonClientCall* call = static_cast(got_tag); + CommonClientCall *call = static_cast(got_tag); assert(ok); if (call->status.ok()) { std::cout << "start procceed" << std::endl; call->Proceed(); - } - else + } else { std::cout << "RPC failed" << std::endl; - + } delete call; } std::cout << "complete rpc ended" << std::endl; } - diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 8e4c0fc..a8300ce 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -1,6 +1,6 @@ -#include #include #include +#include #include using grpc::Channel; @@ -9,34 +9,68 @@ using grpc::ClientContext; using grpc::CompletionQueue; using grpc::Status; -using Efficio_proto::GetProjectRequest; -using Efficio_proto::GetProjectResponse; using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; +using Efficio_proto::GetProjectRequest; +using Efficio_proto::GetProjectResponse; using Efficio_proto::Update; +UpdateRequests::GetProjectClientCall::GetProjectClientCall( + GetProjectRequest &request, + CompletionQueue *cq_, + std::unique_ptr &stub_, + Project *save_to_ +) + : CommonClientCall(), save_to(save_to_) { + response_reader = stub_->PrepareAsyncGetProject(&context, request, cq_); + response_reader->StartCall(); + response_reader->Finish(&response, &status, (void *)this); +} + void UpdateRequests::GetProjectClientCall::Proceed(bool ok) { - if (ok && status.ok()) { - if (response.has_error_string()) { - std::cout << response.error_string() << std::endl; - } else if (response.has_project()) { - std::cout << response.project().title() << std::endl; - } + if (ok && status.ok()) { + if (response.has_project()) { + *save_to = response.project(); + } else { + std::cout << response.error_text() << std::endl; } - } +} -UpdateRequests::GetProjectClientCall::GetProjectClientCall(GetProjectRequest& request, - CompletionQueue* cq_, - std::unique_ptr& stub_) : CommonClientCall() - { - response_reader = stub_->PrepareAsyncGetProject(&context, request, cq_); - response_reader->StartCall(); - response_reader->Finish(&response, &status, (void*)this); - } +bool UpdateRequests::get_project(Project *project, const std::string &code) { + auto request = new GetProjectRequest; + request->set_code(code); + new GetProjectClientCall(*request, cq_, stub_, project); + return true; +} -bool UpdateRequests::get_project(Project *project) { - auto request = new GetProjectRequest; - new GetProjectClientCall(*request, cq_, stub_); - return true; +UpdateRequests::CreateProjectClientCall::CreateProjectClientCall( + CreateProjectRequest &request, + CompletionQueue *cq_, + std::unique_ptr &stub_, + Project *save_to_) : CommonClientCall(), save_to(save_to_) +{ + response_reader = stub_->PrepareAsyncCreateProject(&context, request, cq_); + response_reader->StartCall(); + response_reader->Finish(&response, &status, (void *)this); +} + +void UpdateRequests::CreateProjectClientCall::Proceed(bool ok) { + if (ok && status.ok()) { + if (response.has_project()) { + *save_to = response.project(); + } else { + std::cout << response.error_text() << std::endl; + } } +} + +bool UpdateRequests::create_project( + Project *project, + const std::string &project_title +) { + auto request = new CreateProjectRequest; + request->set_project_title(project_title); + new CreateProjectClientCall(*request, cq_, stub_, project); + return true; +} diff --git a/proto/efficio-rpc-proto/efficio.proto b/proto/efficio-rpc-proto/efficio.proto index 28fd67a..362acfe 100644 --- a/proto/efficio-rpc-proto/efficio.proto +++ b/proto/efficio-rpc-proto/efficio.proto @@ -37,7 +37,7 @@ message GetProjectRequest { message GetProjectResponse { oneof response { Project project = 1; - string error_string = 2; + string error_text = 2; } } diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index e4e0a12..a62b1f2 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -2,7 +2,8 @@ cmake_minimum_required(VERSION 3.16) project(Server) add_subdirectory(Service) - +add_subdirectory(database) +add_subdirectory(Handlers) add_executable(Server main.cpp) diff --git a/server/Handlers/CMakeLists.txt b/server/Handlers/CMakeLists.txt new file mode 100644 index 0000000..24e86fb --- /dev/null +++ b/server/Handlers/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.16) + +project(Handlers LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +file(GLOB SOURCES "src/*.cpp") +file(GLOB HEADERS "include/*.hpp") + +add_library(Handlers STATIC ${SOURCES} ${HEADERS}) + +target_link_libraries(Handlers + model-proto + Database +) + +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_BINARY_DIR} +) \ No newline at end of file diff --git a/server/Handlers/include/update_handler.hpp b/server/Handlers/include/update_handler.hpp new file mode 100644 index 0000000..92bbd07 --- /dev/null +++ b/server/Handlers/include/update_handler.hpp @@ -0,0 +1,17 @@ +#ifndef UPDATE_HANDLER_H +#define UPDATE_HANDLER_H + +#include + +using Efficio_proto::Project; + +class UpdateHandler { +public: + static Project create_project(const std::string& title); +private: + static std::string generate_project_code(); +}; + + + +#endif \ No newline at end of file diff --git a/server/Handlers/src/update_handler.cpp b/server/Handlers/src/update_handler.cpp new file mode 100644 index 0000000..44bcb5c --- /dev/null +++ b/server/Handlers/src/update_handler.cpp @@ -0,0 +1,34 @@ +#include "update_handler.hpp" +#include +#include +#include "project_dao.hpp" + +using Efficio_proto::Project; + +std::string UpdateHandler::generate_project_code() { + static const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, chars.size() - 1); + std::string result; + for (size_t i = 0; i < 6; ++i) { + result += chars[dis(gen)]; + } + return result; +} + + +Project UpdateHandler::create_project(const std::string& title) { + Project project; + project.set_title(title); + std::string project_code; + while (true) { + project_code = generate_project_code(); + if (ProjectDAO::code_available(project_code)) { + break; + } + } + project.set_code(project_code); + ProjectDAO::insert_project(project); + return std::move(project); +} \ No newline at end of file diff --git a/server/Service/CMakeLists.txt b/server/Service/CMakeLists.txt index a8f0c79..37b4fda 100644 --- a/server/Service/CMakeLists.txt +++ b/server/Service/CMakeLists.txt @@ -21,6 +21,7 @@ target_link_libraries(Service model-proto efficio-rpc protobuf::libprotobuf + Handlers gRPC::grpc gRPC::grpc++ pqxx diff --git a/server/Service/include/common_server_call.h b/server/Service/include/common_server_call.h index bf9d190..a5f4fe8 100644 --- a/server/Service/include/common_server_call.h +++ b/server/Service/include/common_server_call.h @@ -2,27 +2,26 @@ #define CALL_DATA_H #include - using grpc::ServerCompletionQueue; using grpc::ServerContext; -class CommonServerCall -{ +class CommonServerCall { public: - ServerCompletionQueue* cq_; + ServerCompletionQueue *cq_; ServerContext ctx_; + enum CallStatus { CREATE, PROCESS, FINISH }; + CallStatus status_; + public: - explicit CommonServerCall(ServerCompletionQueue* cq): - cq_(cq), - status_(CREATE) - {} + explicit CommonServerCall(ServerCompletionQueue *cq) + : cq_(cq), status_(CREATE) { + } virtual ~CommonServerCall() = default; virtual void Proceed(bool = true) = 0; }; - #endif \ No newline at end of file diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h index af6e563..2a4ba72 100644 --- a/server/Service/include/server_implementation.h +++ b/server/Service/include/server_implementation.h @@ -10,9 +10,10 @@ using grpc::ServerCompletionQueue; class ServerImplementation final { std::unique_ptr cq_; std::unique_ptr server_; - public: + +public: void Run(uint16_t port); void HandleRPCs() const; }; -#endif //SERVERIMPLEMENTATION_H +#endif // SERVERIMPLEMENTATION_H diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 9b4100d..55b81ea 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -7,16 +7,18 @@ #include #include "common_server_call.h" -using grpc::ServerContext; using grpc::ServerAsyncResponseWriter; +using grpc::ServerContext; +using Efficio_proto::CreateNoteRequest; +using Efficio_proto::CreateNoteResponse; using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::GetProjectRequest; using Efficio_proto::GetProjectResponse; +using Efficio_proto::CreateProjectRequest; +using Efficio_proto::CreateProjectResponse; using Efficio_proto::Update; -using Efficio_proto::CreateNoteRequest; -using Efficio_proto::CreateNoteResponse; class UpdateService final { Update::AsyncService service_; @@ -24,15 +26,18 @@ class UpdateService final { ServerCompletionQueue *cq_; public: + explicit UpdateService(ServerCompletionQueue *cq); - explicit UpdateService(ServerCompletionQueue* cq); class GetNoteServerCall final : public CommonServerCall { GetNoteRequest request_; ServerAsyncResponseWriter responder_; Update::AsyncService *service_; public: - explicit GetNoteServerCall(Update::AsyncService *service, ServerCompletionQueue *cq); + explicit GetNoteServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq + ); void Proceed(bool ok) override; }; @@ -41,26 +46,47 @@ class UpdateService final { GetProjectResponse response_; ServerAsyncResponseWriter responder_; Update::AsyncService *service_; + + public: + explicit GetProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq + ); + void Proceed(bool) override; + }; + + class CreateProjectServerCall : public CommonServerCall { + CreateProjectRequest request_; + CreateProjectResponse response_; + ServerAsyncResponseWriter responder_; + Update::AsyncService *service_; + public: - explicit GetProjectServerCall(Update::AsyncService* service, ServerCompletionQueue *cq); + explicit CreateProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq + ); void Proceed(bool) override; }; class TryJoinProjectServerCall; - class CreateProjectServerCall; + class CreateNoteServerCall final : public CommonServerCall { CreateNoteRequest request_; ServerAsyncResponseWriter responder_; UpdateService &service_; + public: - explicit CreateNoteServerCall(UpdateService& service, ServerCompletionQueue *cq); + explicit CreateNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq + ); void Proceed(bool ok) override; }; - Update::AsyncService& get_service(); + Update::AsyncService &get_service(); void run(); - }; -#endif //UPDATE_SERVICE_H +#endif // UPDATE_SERVICE_H diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index cf317bd..bf30ef9 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -10,7 +10,6 @@ void ServerImplementation::Run(const uint16_t port) { "localhost:" + std::to_string(port), grpc::InsecureServerCredentials() ); - UpdateService update_service(cq_.get()); builder.RegisterService(&update_service.get_service()); server_ = builder.BuildAndStart(); @@ -22,6 +21,6 @@ void ServerImplementation::HandleRPCs() const { void *tag; bool ok; while (cq_->Next(&tag, &ok)) { - static_cast(tag)->Proceed(ok); + static_cast(tag)->Proceed(ok); } } diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 5fea4b1..6f93962 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -1,5 +1,7 @@ #include "update_service.h" #include "common_server_call.h" +#include "project_dao.hpp" +#include "update_handler.hpp" using grpc::ServerAsyncResponseWriter; @@ -7,19 +9,20 @@ using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::Project; - UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq), service_() { // new GetNoteServerCall(&service_, cq_); } void UpdateService::run() { new GetProjectServerCall(&service_, cq_); + new CreateProjectServerCall(&service_, cq_); } -UpdateService::GetNoteServerCall::GetNoteServerCall(Update::AsyncService *service, ServerCompletionQueue *cq) - : CommonServerCall(cq), - responder_(&ctx_), - service_(service) { +UpdateService::GetNoteServerCall::GetNoteServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { } void UpdateService::GetNoteServerCall::Proceed(const bool ok) { @@ -56,8 +59,8 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { UpdateService::GetProjectServerCall::GetProjectServerCall( Update::AsyncService *service, ServerCompletionQueue *cq -) : CommonServerCall(cq), responder_(&ctx_), service_(service) -{ +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { this->Proceed(true); } @@ -65,30 +68,81 @@ void UpdateService::GetProjectServerCall::Proceed(const bool ok = true) { switch (status_) { case CREATE: { status_ = PROCESS; - service_->RequestGetProject(&ctx_,&request_,&responder_, cq_, cq_, this); - std::cout << "get project start listening" << std::endl; + service_->RequestGetProject( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + std::cout << "[SERVER] : {get project} : start listening" + << std::endl; break; } case PROCESS: { status_ = FINISH; - std::cout << "get project get request" << std::endl; + + // todo: validate user token + + std::cout << "[SERVER] : {get project} : get request, code=" + << request_.code() << std::endl; new GetProjectServerCall(service_, cq_); auto *project = new Project; - project->set_title("Yoo!"); - response_.set_allocated_project(project); - responder_.Finish(response_, grpc::Status::OK, this); - break; - } case FINISH: { - if (status_ == FINISH) { - delete this; + + // todo: go to handler, not dao + + if (ProjectDAO::get_project(request_.code(), *project)) { + response_.set_allocated_project(project); } else { - std::cout << "get project why not finish" << std::endl; + response_.set_error_text("can't get project"); } + responder_.Finish(response_, grpc::Status::OK, this); + break; + } + case FINISH: { + std::cout << "[SERVER] : {get project} : deleting call" << std::endl; + delete this; } } } +UpdateService::CreateProjectServerCall::CreateProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + this->Proceed(true); +} + +void UpdateService::CreateProjectServerCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + switch (status_) { + case CREATE: { + status_ = PROCESS; + service_->RequestCreateProject( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + std::cout << "[SERVER] : {create project} : start listening" + << std::endl; + break; + } + case PROCESS: { + status_ = FINISH; + new GetProjectServerCall(service_, cq_); + + std::cout << "[SERVER] : {create project} : get request, title=" + << request_.project_title() << std::endl; + + Project *res_project = new Project(UpdateHandler::create_project(request_.project_title())); + response_.set_allocated_project(res_project); + responder_.Finish(response_, grpc::Status::OK, this); + break; + } + case FINISH: { + delete this; + } + } +} -Update::AsyncService& UpdateService::get_service() { +Update::AsyncService &UpdateService::get_service() { return service_; } \ No newline at end of file diff --git a/server/database/CMakeLists.txt b/server/database/CMakeLists.txt index fd87718..ea3e5b3 100644 --- a/server/database/CMakeLists.txt +++ b/server/database/CMakeLists.txt @@ -18,4 +18,8 @@ target_include_directories(Database $ ) -target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql model-proto pqxx) \ No newline at end of file +target_link_libraries(Database PRIVATE Qt6::Core Qt6::Sql model-proto pqxx) + + +add_executable(test_database main.cpp) +target_link_libraries(test_database Database model-proto) \ No newline at end of file diff --git a/server/database/include/database_manager.hpp b/server/database/include/database_manager.hpp index 8f3fd17..7c7956c 100644 --- a/server/database/include/database_manager.hpp +++ b/server/database/include/database_manager.hpp @@ -7,7 +7,7 @@ class DatabaseManager final { public: static DatabaseManager &get_instance(); - pqxx::connection& get_connection(); + pqxx::connection &get_connection(); [[nodiscard]] static std::string get_connection_string(); private: diff --git a/server/database/include/note_dao.hpp b/server/database/include/note_dao.hpp index f73730a..6cd8cf4 100644 --- a/server/database/include/note_dao.hpp +++ b/server/database/include/note_dao.hpp @@ -8,7 +8,7 @@ using Efficio_proto::Note; class NoteDao { public: NoteDao() = default; - static Note initialize_note_for_user(const std::string& login); + static Note initialize_note_for_user(const std::string &login); static bool update_note(const Note ¬e); [[nodiscard]] static Note get_note(int note_id); }; diff --git a/server/database/include/project_dao.hpp b/server/database/include/project_dao.hpp index 0e46d77..86246f5 100644 --- a/server/database/include/project_dao.hpp +++ b/server/database/include/project_dao.hpp @@ -1,8 +1,8 @@ #ifndef PROJECT_DAO_HPP #define PROJECT_DAO_HPP -#include #include +#include using namespace Efficio_proto; @@ -11,13 +11,23 @@ class ProjectDAO { ProjectDAO() = default; static bool get_project(const std::string &project_code, Project &project); static bool insert_project(Project &project); - static bool add_member_to_project(const std::string& project_code, const std::string& member); - static bool add_note_to_project(const std::string& project_code, int note_id); - static bool change_project_title(const std::string& project_code, const std::string& new_title); + static bool add_member_to_project( + const std::string &project_code, + const std::string &member + ); + static bool + add_note_to_project(const std::string &project_code, int note_id); + static bool change_project_title( + const std::string &project_code, + const std::string &new_title + ); + static bool code_available(const std::string &project_code); }; -template -std::vector proto_arr_to_vector(const google::protobuf::RepeatedPtrField &array) { +template +std::vector proto_arr_to_vector( + const google::protobuf::RepeatedPtrField &array +) { std::vector vec; for (int i = 0; i < array.size(); ++i) { vec.push_back(array[i]); @@ -25,5 +35,4 @@ std::vector proto_arr_to_vector(const google::protobuf::RepeatedPtrField & return std::move(vec); } - #endif diff --git a/server/database/src/database_manager.cpp b/server/database/src/database_manager.cpp index 1d57434..09546e8 100644 --- a/server/database/src/database_manager.cpp +++ b/server/database/src/database_manager.cpp @@ -48,7 +48,8 @@ DatabaseManager &DatabaseManager::get_instance() { pqxx::connection &DatabaseManager::get_connection() { if (!connection_->is_open()) { - connection_ = std::make_unique(get_connection_string()); + connection_ = + std::make_unique(get_connection_string()); } return *connection_; } diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index 0ee8336..b02783c 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -1,9 +1,9 @@ #include "note_dao.hpp" -#include "database_manager.hpp" #include +#include "database_manager.hpp" Note NoteDao::initialize_note_for_user(const std::string &login) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = @@ -28,7 +28,7 @@ Note NoteDao::initialize_note_for_user(const std::string &login) { } bool NoteDao::update_note(const Note ¬e) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = @@ -40,8 +40,9 @@ bool NoteDao::update_note(const Note ¬e) { "tags = :tags " "WHERE id = :id"; - // TODO: I'm not sure, it will work correctly because note.members() and note.tags() return strange type - // const pqxx::result result = transaction.exec_params( + // TODO: I'm not sure, it will work correctly because note.members() and + // note.tags() return strange type const pqxx::result result = + // transaction.exec_params( // query, note.title(), note.text(), note.members(), note.date(), // note.tags(), note.id() // ); diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index c55d21c..400464f 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -1,14 +1,13 @@ #include "project_dao.hpp" -#include "note_dao.hpp" -#include "database_manager.hpp" #include - +#include "database_manager.hpp" +#include "note_dao.hpp" bool ProjectDAO::get_project(const std::string &code, Project &project) { - pqxx::connection &connection = DatabaseManager::get_instance().get_connection(); + pqxx::connection &connection = + DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); - const std::string query = "SELECT * FROM projects WHERE code = " + transaction.quote(code); @@ -23,7 +22,6 @@ bool ProjectDAO::get_project(const std::string &code, Project &project) { project.set_title(row["title"].as()); project.set_code(row["code"].as()); - if (!row["members"].is_null()) { const auto members_as_string = row["members"].as(); @@ -54,7 +52,7 @@ bool ProjectDAO::get_project(const std::string &code, Project &project) { while (std::getline(ss, note_id_string, ',')) { std::erase(note_id_string, '"'); if (!note_id_string.empty()) { - Note* note = project.add_notes(); + Note *note = project.add_notes(); *note = NoteDao::get_note(std::stoi(note_id_string)); } } @@ -62,12 +60,10 @@ bool ProjectDAO::get_project(const std::string &code, Project &project) { } return true; - } - bool ProjectDAO::insert_project(Project &project) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = @@ -80,12 +76,9 @@ bool ProjectDAO::insert_project(Project &project) { } const pqxx::result result = transaction.exec_params( - query, - project.code(), - project.title(), - note_ids, + query, project.code(), project.title(), note_ids, proto_arr_to_vector(project.members()) - ); + ); transaction.commit(); return true; @@ -95,7 +88,7 @@ bool ProjectDAO::add_member_to_project( const std::string &project_code, const std::string &member ) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = @@ -104,8 +97,8 @@ bool ProjectDAO::add_member_to_project( "WHERE code = $2 " "RETURNING 1;"; - - const pqxx::result result = transaction.exec_params(query, member, project_code); + const pqxx::result result = + transaction.exec_params(query, member, project_code); if (result.empty()) { return false; } @@ -113,12 +106,11 @@ bool ProjectDAO::add_member_to_project( return true; } - bool ProjectDAO::add_note_to_project( const std::string &project_code, int note_id ) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = @@ -127,7 +119,8 @@ bool ProjectDAO::add_note_to_project( "WHERE code = $2 " "RETURNING 1;"; - const pqxx::result result = transaction.exec_params(query,note_id, project_code); + const pqxx::result result = + transaction.exec_params(query, note_id, project_code); if (result.empty()) { return false; } @@ -139,7 +132,7 @@ bool ProjectDAO::change_project_title( const std::string &project_code, const std::string &new_title ) { - auto& connection = DatabaseManager::get_instance().get_connection(); + auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = @@ -148,7 +141,8 @@ bool ProjectDAO::change_project_title( "WHERE code = $2 " "RETURNING 1;"; - const pqxx::result result = transaction.exec_params(query, new_title, project_code); + const pqxx::result result = + transaction.exec_params(query, new_title, project_code); if (result.empty()) { return false; } @@ -158,3 +152,19 @@ bool ProjectDAO::change_project_title( } +bool ProjectDAO::code_available(const std::string &project_code) { + auto &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = + "SELECT code " + "FROM projects " + "WHERE code = $1 "; + const pqxx::result result = transaction.exec_params(query, project_code); + + if (result.empty()) { + return true; + } + return false; + +} From ea78c54f38496aca69a35fca6048d54276590eb4 Mon Sep 17 00:00:00 2001 From: toximu Date: Thu, 8 May 2025 11:09:39 +0300 Subject: [PATCH 26/47] change proto --- proto/efficio-rpc-proto/efficio.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/proto/efficio-rpc-proto/efficio.proto b/proto/efficio-rpc-proto/efficio.proto index c5a38f6..b51a52d 100644 --- a/proto/efficio-rpc-proto/efficio.proto +++ b/proto/efficio-rpc-proto/efficio.proto @@ -43,6 +43,7 @@ message GetProjectResponse { message CreateNoteRequest { User user = 1; optional string note_title = 2; // default = "Пустая заметка" + optional string project_code = 3; } message CreateNoteResponse { From b2592322bdd500dd8d10907e622313e659ddacdf Mon Sep 17 00:00:00 2001 From: toximu Date: Thu, 8 May 2025 17:14:42 +0300 Subject: [PATCH 27/47] feat: add join project, leave project endpoint, rewrite projectDAO --- proto/efficio-rpc-proto/efficio.proto | 10 ++ server/Handlers/include/update_handler.hpp | 12 ++- server/Handlers/src/update_handler.cpp | 46 +++++++- server/Service/include/update_service.h | 35 +++++- server/Service/src/update_service.cpp | 117 +++++++++++++++++++-- server/database/include/project_dao.hpp | 35 ++++++ server/database/src/project_dao.cpp | 46 +++----- 7 files changed, 252 insertions(+), 49 deletions(-) diff --git a/proto/efficio-rpc-proto/efficio.proto b/proto/efficio-rpc-proto/efficio.proto index 362acfe..ce6e203 100644 --- a/proto/efficio-rpc-proto/efficio.proto +++ b/proto/efficio-rpc-proto/efficio.proto @@ -10,6 +10,7 @@ service Update { rpc CreateNote (CreateNoteRequest) returns (CreateNoteResponse) {} rpc CreateProject (CreateProjectRequest) returns (CreateProjectResponse) {} rpc TryJoinProject (TryJoinProjectRequest) returns (TryJoinProjectResponse) {} + rpc TryLeaveProject (TryLeaveProjectRequest) returns (TryLeaveProjectResponse) {} rpc UpdateProject (UpdateProjectRequest) returns (UpdateProjectResponse) {} } @@ -77,6 +78,15 @@ message TryJoinProjectResponse { } } +message TryLeaveProjectRequest { + User user = 1; + string code = 2; +} + +message TryLeaveProjectResponse { + int32 ok = 1; +} + message UpdateProjectRequest { Project project = 1; } diff --git a/server/Handlers/include/update_handler.hpp b/server/Handlers/include/update_handler.hpp index 92bbd07..11ec329 100644 --- a/server/Handlers/include/update_handler.hpp +++ b/server/Handlers/include/update_handler.hpp @@ -7,11 +7,17 @@ using Efficio_proto::Project; class UpdateHandler { public: - static Project create_project(const std::string& title); + static Project create_project(const std::string &title); + static bool + try_join_project(const std::string &project_code, const std::string &login); + static bool try_leave_project( + const std::string &project_code, + const std::string &login + ); + static std::optional get_project(const std::string &project_code); + private: static std::string generate_project_code(); }; - - #endif \ No newline at end of file diff --git a/server/Handlers/src/update_handler.cpp b/server/Handlers/src/update_handler.cpp index 44bcb5c..e98efbf 100644 --- a/server/Handlers/src/update_handler.cpp +++ b/server/Handlers/src/update_handler.cpp @@ -17,8 +17,7 @@ std::string UpdateHandler::generate_project_code() { return result; } - -Project UpdateHandler::create_project(const std::string& title) { +Project UpdateHandler::create_project(const std::string &title) { Project project; project.set_title(title); std::string project_code; @@ -31,4 +30,45 @@ Project UpdateHandler::create_project(const std::string& title) { project.set_code(project_code); ProjectDAO::insert_project(project); return std::move(project); -} \ No newline at end of file +} + +bool UpdateHandler::try_join_project( + const std::string &project_code, + const std::string &login +) { + // todo: check if login exists + + // todo: check if users has not this project already + + if (!ProjectDAO::add_member_to_project(project_code, login)) { + return false; + } + + // todo: add project to user(UserDAO not exists yet) + + return true; +} + +bool UpdateHandler::try_leave_project( + const std::string &project_code, + const std::string &login +) { + // todo : check if login exists + + if (!ProjectDAO::delete_member_from_project(project_code, login)) { + return false; + } + + // todo : delete project from user + + return true; +} + +std::optional UpdateHandler::get_project(const std::string &project_code) { + Project project; + bool ok = ProjectDAO::get_project(project_code, project); + if (!ok) { + return std::nullopt; + } + return project; +} diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 55b81ea..5cc5481 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -12,12 +12,16 @@ using grpc::ServerContext; using Efficio_proto::CreateNoteRequest; using Efficio_proto::CreateNoteResponse; +using Efficio_proto::CreateProjectRequest; +using Efficio_proto::CreateProjectResponse; using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::GetProjectRequest; using Efficio_proto::GetProjectResponse; -using Efficio_proto::CreateProjectRequest; -using Efficio_proto::CreateProjectResponse; +using Efficio_proto::TryJoinProjectRequest; +using Efficio_proto::TryJoinProjectResponse; +using Efficio_proto::TryLeaveProjectRequest; +using Efficio_proto::TryLeaveProjectResponse; using Efficio_proto::Update; class UpdateService final { @@ -69,8 +73,33 @@ class UpdateService final { void Proceed(bool) override; }; - class TryJoinProjectServerCall; + class TryJoinProjectServerCall : public CommonServerCall { + TryJoinProjectRequest request_; + TryJoinProjectResponse response_; + ServerAsyncResponseWriter responder_; + Update::AsyncService *service_; + + public: + explicit TryJoinProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq + ); + void Proceed(bool) override; + }; + + class TryLeaveProjectServerCall : public CommonServerCall { + TryLeaveProjectRequest request_; + TryLeaveProjectResponse response_; + ServerAsyncResponseWriter responder_; + Update::AsyncService *service_; + public: + explicit TryLeaveProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq + ); + void Proceed(bool) override; + }; class CreateNoteServerCall final : public CommonServerCall { CreateNoteRequest request_; diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 6f93962..60ae93e 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -79,16 +79,14 @@ void UpdateService::GetProjectServerCall::Proceed(const bool ok = true) { status_ = FINISH; // todo: validate user token - + // todo: check if user has this project std::cout << "[SERVER] : {get project} : get request, code=" << request_.code() << std::endl; new GetProjectServerCall(service_, cq_); - auto *project = new Project; - - // todo: go to handler, not dao + auto project = UpdateHandler::get_project(request_.code()); - if (ProjectDAO::get_project(request_.code(), *project)) { - response_.set_allocated_project(project); + if (project.has_value()) { + response_.set_allocated_project(&project.value()); } else { response_.set_error_text("can't get project"); } @@ -96,7 +94,8 @@ void UpdateService::GetProjectServerCall::Proceed(const bool ok = true) { break; } case FINISH: { - std::cout << "[SERVER] : {get project} : deleting call" << std::endl; + std::cout << "[SERVER] : {get project} : deleting call" + << std::endl; delete this; } } @@ -126,23 +125,125 @@ void UpdateService::CreateProjectServerCall::Proceed(const bool ok) { break; } case PROCESS: { + // todo: validate user token status_ = FINISH; new GetProjectServerCall(service_, cq_); std::cout << "[SERVER] : {create project} : get request, title=" << request_.project_title() << std::endl; - Project *res_project = new Project(UpdateHandler::create_project(request_.project_title())); + Project *res_project = new Project( + UpdateHandler::create_project(request_.project_title()) + ); response_.set_allocated_project(res_project); responder_.Finish(response_, grpc::Status::OK, this); break; } case FINISH: { + std::cout << "[SERVER] : {create project} : deleting call" + << std::endl; + delete this; + } + } +} + +UpdateService::TryJoinProjectServerCall::TryJoinProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + this->Proceed(true); +} + +void UpdateService::TryJoinProjectServerCall::Proceed(const bool ok = true) { + if (!ok) { + delete this; + return; + } + switch (status_) { + case CREATE: { + status_ = PROCESS; + service_->RequestTryJoinProject( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + std::cout << "[SERVER] : {try join project} : start listening" + << std::endl; + break; + } + case PROCESS: { + // todo: validate user token + + status_ = FINISH; + new TryJoinProjectServerCall(service_, cq_); + std::cout << "[SERVER] : {try join project} : get request, code=" + << request_.code() << std::endl; + + if (!UpdateHandler::try_join_project( + request_.code(), request_.user().login() + )) { + response_.set_error_text("can't join project"); + responder_.Finish(response_, grpc::Status::OK, this); + break; + } + + auto project = UpdateHandler::get_project(request_.code()); + response_.set_allocated_project(&project.value()); + responder_.Finish(response_, grpc::Status::OK, this); + break; + } + case FINISH: { + std::cout << "[SERVER] : {try join project} : deleting call" + << std::endl; + delete this; + } + } +} + + +UpdateService::TryLeaveProjectServerCall::TryLeaveProjectServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + this->Proceed(true); +} + +void UpdateService::TryLeaveProjectServerCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + switch (status_) { + case CREATE: { + status_ = PROCESS; + service_->RequestTryLeaveProject( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + std::cout << "[SERVER] : {try leave project} : start listening" + << std::endl; + break; + } + case PROCESS: { + status_ = FINISH; + new TryLeaveProjectServerCall(service_, cq_); + std::cout << "[SERVER] : {try leave project} : get request, code=" + << request_.code() << std::endl; + + // todo: check if user exists, if project exists etc. + UpdateHandler::try_leave_project(request_.code(), request_.user().login()); + + response_.set_ok(1); + responder_.Finish(response_, grpc::Status::OK, this); + } + case FINISH: { + std::cout << "[SERVER] : {try leave project} : deleting call" + << std::endl; delete this; } } } + Update::AsyncService &UpdateService::get_service() { return service_; } \ No newline at end of file diff --git a/server/database/include/project_dao.hpp b/server/database/include/project_dao.hpp index 86246f5..41460c6 100644 --- a/server/database/include/project_dao.hpp +++ b/server/database/include/project_dao.hpp @@ -2,11 +2,40 @@ #define PROJECT_DAO_HPP #include +#include #include +#include "database_manager.hpp" using namespace Efficio_proto; class ProjectDAO { + template + static bool change_project_array( + const std::string &project_code, + const T ¶meter, + const std::string &field, + const std::string &operation + ) { + auto &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string query = + "UPDATE projects " + "SET " + + field + " = array_" + operation + "(" + field + + ", $1) " + "WHERE code = $2 " + "RETURNING 1;"; + + const pqxx::result result = + transaction.exec_params(query, parameter, project_code); + if (result.empty()) { + return false; + } + transaction.commit(); + return true; + } + public: ProjectDAO() = default; static bool get_project(const std::string &project_code, Project &project); @@ -15,8 +44,14 @@ class ProjectDAO { const std::string &project_code, const std::string &member ); + static bool delete_member_from_project( + const std::string &project_code, + const std::string &member + ); static bool add_note_to_project(const std::string &project_code, int note_id); + static bool + delete_note_from_project(const std::string &project_code, int note_id); static bool change_project_title( const std::string &project_code, const std::string &new_title diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index 400464f..39b993f 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -88,44 +88,21 @@ bool ProjectDAO::add_member_to_project( const std::string &project_code, const std::string &member ) { - auto &connection = DatabaseManager::get_instance().get_connection(); - pqxx::work transaction(connection); - - const std::string query = - "UPDATE projects " - "SET members = array_append(members, $1) " - "WHERE code = $2 " - "RETURNING 1;"; + return change_project_array(project_code, member, "members", "append"); +} - const pqxx::result result = - transaction.exec_params(query, member, project_code); - if (result.empty()) { - return false; - } - transaction.commit(); - return true; +bool ProjectDAO::delete_member_from_project( + const std::string &project_code, + const std::string &member +) { + return change_project_array(project_code, member, "members", "remove"); } bool ProjectDAO::add_note_to_project( const std::string &project_code, int note_id ) { - auto &connection = DatabaseManager::get_instance().get_connection(); - pqxx::work transaction(connection); - - const std::string query = - "UPDATE projects " - "SET notes = array_append(notes, $1) " - "WHERE code = $2 " - "RETURNING 1;"; - - const pqxx::result result = - transaction.exec_params(query, note_id, project_code); - if (result.empty()) { - return false; - } - transaction.commit(); - return true; + return change_project_array(project_code, note_id, "notes", "append"); } bool ProjectDAO::change_project_title( @@ -151,6 +128,12 @@ bool ProjectDAO::change_project_title( return true; } +bool ProjectDAO::delete_note_from_project( + const std::string &project_code, + int note_id +) { + return change_project_array(project_code, note_id, "notes", "remove"); +} bool ProjectDAO::code_available(const std::string &project_code) { auto &connection = DatabaseManager::get_instance().get_connection(); @@ -166,5 +149,4 @@ bool ProjectDAO::code_available(const std::string &project_code) { return true; } return false; - } From c515faedba70bded54e1e362d236d409229e3855 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Thu, 8 May 2025 21:22:39 +0300 Subject: [PATCH 28/47] feat: add auth client calls --- client/client/CMakeLists.txt | 4 +- client/client/include/auth_requests.h | 93 +++++++++++++++++++ client/client/include/client_implementation.h | 14 ++- client/client/src/auth_requests.cpp | 47 ++++++++++ client/client/src/client_implementation.cpp | 14 ++- proto/efficio-rpc-proto/efficio.proto | 2 +- server/Service/include/auth_service.h | 20 ++-- .../Service/include/server_implementation.h | 5 +- server/Service/src/auth_service.cpp | 26 +++--- server/Service/src/server_implementation.cpp | 19 ++-- server/database/CMakeLists.txt | 3 +- server/database/src/lr_dao.cpp | 28 ++++-- 12 files changed, 228 insertions(+), 47 deletions(-) create mode 100644 client/client/include/auth_requests.h create mode 100644 client/client/src/auth_requests.cpp diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt index ce52c7c..61213b3 100644 --- a/client/client/CMakeLists.txt +++ b/client/client/CMakeLists.txt @@ -13,7 +13,9 @@ find_package(Threads) file(GLOB SOURCES "src/*.cpp") file(GLOB HEADERS "include/*.h") -add_library(Client STATIC ${SOURCES} ${HEADERS}) +add_library(Client STATIC ${SOURCES} ${HEADERS} + include/auth_requests.h + src/auth_requests.cpp) target_link_libraries(Client model-proto diff --git a/client/client/include/auth_requests.h b/client/client/include/auth_requests.h new file mode 100644 index 0000000..897db91 --- /dev/null +++ b/client/client/include/auth_requests.h @@ -0,0 +1,93 @@ +#ifndef AUTH_REQUESTS_H +#define AUTH_REQUESTS_H + +#include +#include +#include +#include "common_client_call.h" + +using grpc::Channel; +using grpc::CompletionQueue; + +using Efficio_proto::User; + +using Efficio_proto::Auth; +using Efficio_proto::AuthRequest; +using Efficio_proto::AuthResponse; + +class AuthRequests { +private: + class AuthClientOperation : protected CommonClientCall { + protected: + AuthResponse reply_; + std::unique_ptr> + responder_; + + public: + AuthClientOperation( + const AuthRequest &request, + CompletionQueue *cq, + const std::unique_ptr &stub + ); + void Proceed(bool ok) override; + AuthResponse get_reply(); + }; + + template + bool try_auth_operation(User* user) const { + AuthRequest request; + request.mutable_user()->CopyFrom(*user); + + const auto call = new Call(request, cq_, stub_); + + void *tag; + bool ok = false; + if (!cq_->Next(&tag, &ok)) { + std::cout << "[CLIENT]: Completion queue failed\n"; + return false; + } + + if (!ok) { + std::cout << "[CLIENT WARNING]: no user in reply\n"; + return false; + } + + *user = call->get_reply().user(); + std::cout << "[CLIENT]: USER - " << user->login() << "\n"; + return true; + } + +public: + class TryAuthenticateUserClientCall final : public AuthClientOperation { + public: + TryAuthenticateUserClientCall( + const AuthRequest &request, + CompletionQueue *cq, + const std::unique_ptr &stub + ); + }; + + class TryRegisterUserClientCall final : public AuthClientOperation { + public: + TryRegisterUserClientCall( + const AuthRequest &request, + CompletionQueue *cq, + const std::unique_ptr &stub + ); + }; + + bool try_authenticate_user(User *user) const; + bool try_register(User *user) const; + + explicit AuthRequests( + const std::shared_ptr &channel, + CompletionQueue *cq + ) + : cq_(cq), stub_(Auth::NewStub(channel)) {}; + +private: + CompletionQueue *cq_; + std::unique_ptr stub_; +}; + +#endif // AUTH_REQUESTS_H diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index cb08f94..1fc119a 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -3,21 +3,25 @@ #include #include "update_requests.h" +#include "auth_requests.h" using grpc::Channel; +using grpc::CompletionQueue; class ClientImplementation { - grpc::CompletionQueue cq_; + CompletionQueue cq_; std::shared_ptr channel_; UpdateRequests update_requests_; + AuthRequests auth_requests_; public: - std::shared_ptr get_channel() { - return channel_; - } - explicit ClientImplementation(const std::shared_ptr &channel); + void CompleteRpc(); + std::shared_ptr get_channel(); + + bool try_authenticate_user(User* user) const; + bool try_register_user(User* user) const; bool try_update_note(Note *note) const; bool try_create_note(Note *note) const; diff --git a/client/client/src/auth_requests.cpp b/client/client/src/auth_requests.cpp new file mode 100644 index 0000000..21780fe --- /dev/null +++ b/client/client/src/auth_requests.cpp @@ -0,0 +1,47 @@ +#include "auth_requests.h" + +AuthRequests::AuthClientOperation::AuthClientOperation( + const AuthRequest &request, + CompletionQueue *cq, + const std::unique_ptr &stub +) + : responder_(stub->AsyncTryAuthenticateUser(&context, request, cq)) { + responder_->Finish(&reply_, &status, this); +} + +void AuthRequests::AuthClientOperation::Proceed(const bool ok) { + if (!ok) { + std::cout << "[CLIENT WARNING]: AUTH RPC failed\n"; + } + delete this; +} + +AuthResponse AuthRequests::AuthClientOperation::get_reply() { + return reply_; +} + +AuthRequests::TryAuthenticateUserClientCall::TryAuthenticateUserClientCall( + const AuthRequest &request, + CompletionQueue *cq, + const std::unique_ptr &stub +) + : AuthClientOperation(request, cq, stub) { + std::cout << "[CLIENT]: AUTH USER REQUEST SENT\n"; +} + +AuthRequests::TryRegisterUserClientCall::TryRegisterUserClientCall( + const AuthRequest &request, + CompletionQueue *cq, + const std::unique_ptr &stub +) + : AuthClientOperation(request, cq, stub) { + std::cout << "[CLIENT]: REGISTER USER REQUEST SENT\n"; +} + +bool AuthRequests::try_authenticate_user(User *user) const { + return try_auth_operation(user)(); +} + +bool AuthRequests::try_register(User *user) const { + return try_auth_operation(user)(); +} \ No newline at end of file diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index d8c5160..356d75f 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -14,7 +14,7 @@ using grpc::Status; ClientImplementation::ClientImplementation( const std::shared_ptr &channel ) - : channel_(channel), update_requests_(channel, &cq_) { + : channel_(channel), update_requests_(channel, &cq_), auth_requests_(channel, &cq_) { } void ClientImplementation::CompleteRpc() { @@ -34,6 +34,18 @@ void ClientImplementation::CompleteRpc() { } } +std::shared_ptr ClientImplementation::get_channel() { + return channel_; +} + +bool ClientImplementation::try_authenticate_user(User *user) const { + return auth_requests_.try_authenticate_user(user); +} + +bool ClientImplementation::try_register_user(User *user) const { + return auth_requests_.try_register(user); +} + bool ClientImplementation::try_update_note(Note *note) const { return update_requests_.try_update_note(note); } diff --git a/proto/efficio-rpc-proto/efficio.proto b/proto/efficio-rpc-proto/efficio.proto index 34d70fd..dc1ed7f 100644 --- a/proto/efficio-rpc-proto/efficio.proto +++ b/proto/efficio-rpc-proto/efficio.proto @@ -15,7 +15,7 @@ service Update { service Auth { rpc TryAuthenticateUser (AuthRequest) returns (AuthResponse) {} - rpc TryRegister1User (AuthRequest) returns (AuthResponse) {} + rpc TryRegisterUser (AuthRequest) returns (AuthResponse) {} } message UpdateNoteRequest { diff --git a/server/Service/include/auth_service.h b/server/Service/include/auth_service.h index 276cef7..18be3c7 100644 --- a/server/Service/include/auth_service.h +++ b/server/Service/include/auth_service.h @@ -20,13 +20,13 @@ class AuthService final { std::unique_ptr cq_; std::unique_ptr server_; - class AuthOperation : public CommonServerCall { + class AuthServerOperation : public CommonServerCall { protected: AuthRequest request_; ServerAsyncResponseWriter responder_; AuthService& service_; - explicit AuthOperation( + explicit AuthServerOperation( AuthService& service, ServerCompletionQueue* cq ) : CommonServerCall(cq), @@ -34,29 +34,29 @@ class AuthService final { service_(service) {} public: - ~AuthOperation() override = default; + ~AuthServerOperation() override = default; void Proceed(bool ok) override = 0; }; +public: - class TryAuthenticateUserCall final : AuthOperation { - explicit TryAuthenticateUserCall( + class TryAuthenticateUserServerCall final : public AuthServerOperation { + public: + explicit TryAuthenticateUserServerCall( AuthService &service, ServerCompletionQueue *cq ); - public: void Proceed(bool) override; }; - class TryRegisterUserCall final : AuthOperation { - explicit TryRegisterUserCall( + class TryRegisterUserServerCall final : AuthServerOperation { + public: + explicit TryRegisterUserServerCall( AuthService &service, ServerCompletionQueue *cq ); - public: void Proceed(bool) override; }; -public: Auth::AsyncService &get_service(); }; diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h index cee1658..b0f427f 100644 --- a/server/Service/include/server_implementation.h +++ b/server/Service/include/server_implementation.h @@ -2,6 +2,7 @@ #define SERVERIMPLEMENTATION_H #include +#include "auth_service.h" #include "update_service.h" using grpc::Server; @@ -10,10 +11,12 @@ using grpc::ServerCompletionQueue; class ServerImplementation final { std::unique_ptr cq_; std::unique_ptr server_; + UpdateService update_service_; + AuthService auth_service_; public: void Run(uint16_t port); - void HandleRPCs(UpdateService &update_service) const; + void HandleRPCs(); }; #endif // SERVERIMPLEMENTATION_H diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp index 81c47d6..5240a4d 100644 --- a/server/Service/src/auth_service.cpp +++ b/server/Service/src/auth_service.cpp @@ -1,17 +1,17 @@ #include "auth_service.h" #include "lr_dao.hpp" -AuthService::TryAuthenticateUserCall::TryAuthenticateUserCall( +AuthService::TryAuthenticateUserServerCall::TryAuthenticateUserServerCall( AuthService &service, ServerCompletionQueue *cq -) : AuthOperation(service, cq) { +) : AuthServerOperation(service, cq) { service_.service_.RequestTryAuthenticateUser( &ctx_, &request_, &responder_, cq_, cq_, this ); status_ = PROCESS; } -void AuthService::TryAuthenticateUserCall::Proceed(const bool ok) { +void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { if (!ok) { delete this; return; @@ -19,19 +19,19 @@ void AuthService::TryAuthenticateUserCall::Proceed(const bool ok) { switch (status_) { case PROCESS: { - new TryAuthenticateUserCall(service_, cq_); + new TryAuthenticateUserServerCall(service_, cq_); AuthResponse response; const int query_exit_code = LRDao::validate_user( - request_.user().login(), request_.user().token() + request_.user().login(), request_.user().hashed_password() ); if (query_exit_code == 1) { response.mutable_user()->CopyFrom(request_.user()); } else { response.set_error_text( - "Не удалось выполнить запрос в базу данных на проверку " + "[SERVER ERROR]: Не удалось выполнить запрос в базу данных на проверку " "корректности логина и пароля" ); } @@ -46,17 +46,17 @@ void AuthService::TryAuthenticateUserCall::Proceed(const bool ok) { } } -AuthService::TryRegisterUserCall::TryRegisterUserCall( +AuthService::TryRegisterUserServerCall::TryRegisterUserServerCall( AuthService &service, ServerCompletionQueue *cq -) : AuthOperation(service, cq) { - service_.service_.RequestTryRegister1User( +) : AuthServerOperation(service, cq) { + service_.service_.RequestTryRegisterUser( &ctx_, &request_, &responder_, cq_, cq_, this ); status_ = PROCESS; } -void AuthService::TryRegisterUserCall::Proceed(const bool ok) { +void AuthService::TryRegisterUserServerCall::Proceed(const bool ok) { if (!ok) { delete this; return; @@ -64,17 +64,17 @@ void AuthService::TryRegisterUserCall::Proceed(const bool ok) { switch (status_) { case PROCESS: { - new TryRegisterUserCall(service_, cq_); + new TryRegisterUserServerCall(service_, cq_); AuthResponse response; const int query_exit_code = LRDao::try_register_user( - request_.user().login(), request_.user().token() + request_.user().login(), request_.user().hashed_password() ); if (query_exit_code < 1) { response.set_error_text( - "Не удалось выполнить запрос на запись нового пользователя " + "[SERVER ERROR]: Не удалось выполнить запрос на запись нового пользователя " "в базу данных" ); } else { diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index c61aae4..e72e81b 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -5,21 +5,26 @@ using Efficio_proto::GetNoteRequest; void ServerImplementation::Run(const uint16_t port) { grpc::ServerBuilder builder; - UpdateService update_service; builder.AddListeningPort( "127.0.0.1:" + std::to_string(port), grpc::InsecureServerCredentials() ); - builder.RegisterService(&update_service.get_service()); + + builder.RegisterService(&update_service_.get_service()); + builder.RegisterService(&auth_service_.get_service()); + cq_ = builder.AddCompletionQueue(); server_ = builder.BuildAndStart(); - HandleRPCs(update_service); + HandleRPCs(); } -void ServerImplementation::HandleRPCs(UpdateService &update_service) const { - new UpdateService::GetNoteServerCall(update_service, cq_.get()); - new UpdateService::CreateNoteServerCall(update_service, cq_.get()); - new UpdateService::UpdateNoteServerCall(update_service, cq_.get()); +void ServerImplementation::HandleRPCs() { + new UpdateService::GetNoteServerCall(update_service_, cq_.get()); + new UpdateService::CreateNoteServerCall(update_service_, cq_.get()); + new UpdateService::UpdateNoteServerCall(update_service_, cq_.get()); + + new AuthService::TryAuthenticateUserServerCall(auth_service_, cq_.get()); + new AuthService::TryRegisterUserServerCall(auth_service_, cq_.get()); void *tag; bool ok; diff --git a/server/database/CMakeLists.txt b/server/database/CMakeLists.txt index 3455970..189ec21 100644 --- a/server/database/CMakeLists.txt +++ b/server/database/CMakeLists.txt @@ -5,6 +5,7 @@ project(Database LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) +find_package(bcrypt REQUIRED) find_package(Qt6 COMPONENTS Widgets Core Sql REQUIRED) file(GLOB SOURCES "src/*.cpp") @@ -18,4 +19,4 @@ target_include_directories(Database $ ) -target_link_libraries(Database PRIVATE Qt6::Widgets Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) \ No newline at end of file +target_link_libraries(Database PRIVATE bcrypt Qt6::Widgets Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) \ No newline at end of file diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index abaf97a..5a8b710 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -1,12 +1,13 @@ #include "lr_dao.hpp" #include #include "database_manager.hpp" +#include int LRDao::try_register_user(const std::string &login, const std::string &password) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); - const std::string check_query = "SELECT * FROM users WHERE login = ?"; + const std::string check_query = "SELECT * FROM users WHERE login = $1"; pqxx::params check_params; check_params.append(login); @@ -16,30 +17,39 @@ int LRDao::try_register_user(const std::string &login, const std::string &passwo return -1; } - const std::string insert_query = "INSERT INTO users (login, password) VALUES (?, ?)"; + const std::string hashed_password = hash_password(password); + + const std::string insert_query = "INSERT INTO users (login, password) VALUES ($1, $2)"; pqxx::params insert_params; insert_params.append(login); - insert_params.append(password); + insert_params.append(hashed_password); const pqxx::result insert_result = transaction.exec(insert_query, insert_params); transaction.commit(); - return insert_result.affected_rows() > 0; + return insert_result.affected_rows() > 0 ? 1 : 0; } bool LRDao::validate_user(const std::string &login, const std::string &password) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); - const std::string query = "SELECT * FROM users WHERE login = ? AND password = ?"; + const std::string query = "SELECT password FROM users WHERE login = $1"; pqxx::params params; params.append(login); - params.append(password); const pqxx::result result = transaction.exec(query, params); + if (result.empty()) { + transaction.commit(); + return false; + } + + const std::string stored_hash = result[0]["password"].as(); + const bool is_valid = BCrypt::validatePassword(password, stored_hash); + transaction.commit(); - return result.affected_rows() > 0; + return is_valid; } bool LRDao::add_project_to_user( @@ -98,3 +108,7 @@ bool LRDao::get_user_projects( transaction.commit(); return false; } + +std::string LRDao::hash_password(const std::string &password) { + return BCrypt::generateHash(password); +} From 6a1faf6b8812975039cacd4f2184f3f16ad8e6f4 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Fri, 9 May 2025 16:15:58 +0300 Subject: [PATCH 29/47] fix: building project --- client/client/include/auth_requests.h | 4 +- client/client/include/client_implementation.h | 6 +- client/client/src/auth_requests.cpp | 4 +- client/client/src/client_implementation.cpp | 4 +- client/main.cpp | 7 +- .../ui/authorization-windows/CMakeLists.txt | 2 +- .../include/login_window.h | 15 +++- .../include/login_window_style_sheet.h | 16 ++-- .../include/registration_window.h | 9 +- .../include/registration_window_style_sheet.h | 10 +-- .../src/login_window.cpp | 45 +++++----- .../src/registration_window.cpp | 61 ++++++++------ .../main-window/include/applicationwindow.h | 2 +- .../ui/main-window/src/applicationwindow.cpp | 3 +- .../ui/theme-manager/include/theme_manager.h | 6 +- client/ui/theme-manager/src/theme_manager.cpp | 7 +- server/Service/include/auth_service.h | 16 ++-- server/Service/src/auth_service.cpp | 16 +++- server/database/CMakeLists.txt | 4 +- server/database/include/lr_dao.hpp | 13 ++- server/database/src/lr_dao.cpp | 82 +++++++++++++++---- 21 files changed, 213 insertions(+), 119 deletions(-) diff --git a/client/client/include/auth_requests.h b/client/client/include/auth_requests.h index 897db91..ec1ec43 100644 --- a/client/client/include/auth_requests.h +++ b/client/client/include/auth_requests.h @@ -33,8 +33,8 @@ class AuthRequests { AuthResponse get_reply(); }; - template - bool try_auth_operation(User* user) const { + template + bool try_auth_operation(User *user) const { AuthRequest request; request.mutable_user()->CopyFrom(*user); diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 1fc119a..02b8522 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -2,8 +2,8 @@ #define CLIENTIMPLEMENTATION_H #include -#include "update_requests.h" #include "auth_requests.h" +#include "update_requests.h" using grpc::Channel; using grpc::CompletionQueue; @@ -20,8 +20,8 @@ class ClientImplementation { void CompleteRpc(); std::shared_ptr get_channel(); - bool try_authenticate_user(User* user) const; - bool try_register_user(User* user) const; + bool try_authenticate_user(User *user) const; + bool try_register_user(User *user) const; bool try_update_note(Note *note) const; bool try_create_note(Note *note) const; diff --git a/client/client/src/auth_requests.cpp b/client/client/src/auth_requests.cpp index 21780fe..7a2a190 100644 --- a/client/client/src/auth_requests.cpp +++ b/client/client/src/auth_requests.cpp @@ -39,9 +39,9 @@ AuthRequests::TryRegisterUserClientCall::TryRegisterUserClientCall( } bool AuthRequests::try_authenticate_user(User *user) const { - return try_auth_operation(user)(); + return try_auth_operation(user); } bool AuthRequests::try_register(User *user) const { - return try_auth_operation(user)(); + return try_auth_operation(user); } \ No newline at end of file diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index 356d75f..a0795bf 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -14,7 +14,9 @@ using grpc::Status; ClientImplementation::ClientImplementation( const std::shared_ptr &channel ) - : channel_(channel), update_requests_(channel, &cq_), auth_requests_(channel, &cq_) { + : channel_(channel), + update_requests_(channel, &cq_), + auth_requests_(channel, &cq_) { } void ClientImplementation::CompleteRpc() { diff --git a/client/main.cpp b/client/main.cpp index b982431..93b7ca5 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -4,9 +4,9 @@ #include #include #include +#include "applicationwindow.h" #include "client_implementation.h" #include "login_window.h" -#include "applicationwindow.h" int main(int argc, char *argv[]) { QApplication application(argc, argv); @@ -35,10 +35,11 @@ int main(int argc, char *argv[]) { requests.detach(); auto *app_window = new Ui::ApplicationWindow("EFFICIO"); - auto *login_window = new LoginWindow(app_window); + auto *login_window = new LoginWindow(&client, app_window); app_window->setCentralWidget(login_window); - const QRect screen_geometry = QApplication::primaryScreen()->availableGeometry(); + const QRect screen_geometry = + QApplication::primaryScreen()->availableGeometry(); const int x = (screen_geometry.width() - login_window->width()) / 2; const int y = (screen_geometry.height() - login_window->height()) / 2; app_window->move(x, y); diff --git a/client/ui/authorization-windows/CMakeLists.txt b/client/ui/authorization-windows/CMakeLists.txt index 53e3b07..79bfc2f 100644 --- a/client/ui/authorization-windows/CMakeLists.txt +++ b/client/ui/authorization-windows/CMakeLists.txt @@ -40,4 +40,4 @@ target_include_directories(AuthorizationWindows $ ) -target_link_libraries(AuthorizationWindows PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto MainWindow ThemeManager) \ No newline at end of file +target_link_libraries(AuthorizationWindows PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto MainWindow ThemeManager Client) \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window.h b/client/ui/authorization-windows/include/login_window.h index 72509c6..1d72a04 100644 --- a/client/ui/authorization-windows/include/login_window.h +++ b/client/ui/authorization-windows/include/login_window.h @@ -4,22 +4,28 @@ #include #include #include -#include "database_manager.hpp" #include +#include "client_implementation.h" +#include "database_manager.hpp" QT_BEGIN_NAMESPACE + namespace Ui { class LoginWindow; } + QT_END_NAMESPACE class LoginWindow final : public QWidget { Q_OBJECT public: - explicit LoginWindow(QWidget *parent = nullptr); + explicit LoginWindow( + ClientImplementation *client, + QWidget *parent = nullptr + ); ~LoginWindow() override; - + static const std::vector THEMES; void handle_theme_changed(int theme); @@ -29,6 +35,7 @@ private slots: void on_switch_theme_clicked(); private: - Ui::LoginWindow *ui; + Ui::LoginWindow *ui; int counter_on_switch_theme_clicks = 0; + ClientImplementation *client_; }; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window_style_sheet.h b/client/ui/authorization-windows/include/login_window_style_sheet.h index 4483df3..f8af17e 100644 --- a/client/ui/authorization-windows/include/login_window_style_sheet.h +++ b/client/ui/authorization-windows/include/login_window_style_sheet.h @@ -3,7 +3,7 @@ #include "ui_login_window.h" namespace Ui { - QString login_window_light_autumn_theme = R"( +QString login_window_light_autumn_theme = R"( QWidget { background-color: #f5f5f5; } @@ -74,7 +74,7 @@ namespace Ui { )"; - QString login_window_dark_autumn_theme = R"( +QString login_window_dark_autumn_theme = R"( QWidget { background-color: #202020; } @@ -144,7 +144,7 @@ namespace Ui { } )"; - QString login_window_light_purple_theme = R"( +QString login_window_light_purple_theme = R"( QWidget { background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #9882B9, stop:0.5 rgb(176, 157, 205), stop:1 rgb(103, 88, 126)); @@ -216,9 +216,9 @@ namespace Ui { QPushButton::pressed#switch_theme { background-color:rgb(2, 0, 2); } - )"; + )"; - QString login_window_dark_purple_theme = R"( +QString login_window_dark_purple_theme = R"( QWidget { background-color:rgb(9, 6, 10); } @@ -288,8 +288,8 @@ namespace Ui { } )"; - - QString login_window_blue_theme = R"( + +QString login_window_blue_theme = R"( QWidget { background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #173C4C, stop:0.5 #326D6C, stop:1 #07142B); @@ -373,5 +373,5 @@ namespace Ui { QPushButton#switch_theme:pressed { background-color:rgb(107, 141, 118); } - )"; + )"; } // namespace Ui \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window.h b/client/ui/authorization-windows/include/registration_window.h index 0bee3ff..1929bbe 100644 --- a/client/ui/authorization-windows/include/registration_window.h +++ b/client/ui/authorization-windows/include/registration_window.h @@ -1,8 +1,9 @@ #pragma once #include -#include "database_manager.hpp" #include +#include "client_implementation.h" +#include "database_manager.hpp" QT_BEGIN_NAMESPACE @@ -18,7 +19,10 @@ class RegistrationWindow final : public QWidget { public: static const std::vector THEMES; - explicit RegistrationWindow(QWidget *parent = nullptr); + explicit RegistrationWindow( + ClientImplementation *client, + QWidget *parent = nullptr + ); ~RegistrationWindow() override; bool is_strong_and_valid_password(const QString &password); void handle_theme_changed(int theme); @@ -31,4 +35,5 @@ private slots: private: Ui::RegistrationWindow *ui; int counter_on_switch_theme_clicks = 0; + ClientImplementation *client_; }; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window_style_sheet.h b/client/ui/authorization-windows/include/registration_window_style_sheet.h index 3a7c0f7..c907b27 100644 --- a/client/ui/authorization-windows/include/registration_window_style_sheet.h +++ b/client/ui/authorization-windows/include/registration_window_style_sheet.h @@ -3,7 +3,7 @@ #include "ui_registration_window.h" namespace Ui { - QString registration_window_light_autumn_theme = R"( +QString registration_window_light_autumn_theme = R"( QWidget { background-color: #f5f5f5; } @@ -73,7 +73,7 @@ namespace Ui { } )"; - QString registration_window_dark_autumn_theme = R"( +QString registration_window_dark_autumn_theme = R"( QWidget { background-color: #202020; } @@ -143,7 +143,7 @@ namespace Ui { } )"; - QString registration_window_light_purple_theme = R"( +QString registration_window_light_purple_theme = R"( QWidget { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #9882B9, stop:0.5 rgb(176, 157, 205), stop:1 rgb(103, 88, 126)); @@ -217,7 +217,7 @@ namespace Ui { } )"; - QString registration_window_dark_purple_theme = R"( +QString registration_window_dark_purple_theme = R"( QWidget { background-color: rgb(9, 6, 10); } @@ -287,7 +287,7 @@ namespace Ui { } )"; - QString registration_window_blue_theme = R"( +QString registration_window_blue_theme = R"( QWidget { background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 #173C4C, stop:0.5 #326D6C, stop:1 #07142B) !important; diff --git a/client/ui/authorization-windows/src/login_window.cpp b/client/ui/authorization-windows/src/login_window.cpp index 0e0e297..e192fb8 100644 --- a/client/ui/authorization-windows/src/login_window.cpp +++ b/client/ui/authorization-windows/src/login_window.cpp @@ -4,24 +4,22 @@ #include #include #include -#include "model-proto/model.pb.h" +#include "client_implementation.h" #include "login_window_style_sheet.h" -#include "lr_dao.hpp" +#include "model-proto/model.pb.h" #include "registration_window.h" #include "theme_manager.h" using Efficio_proto::Storage; const std::vector LoginWindow::THEMES = { - Ui::login_window_light_autumn_theme, - Ui::login_window_dark_autumn_theme, - Ui::login_window_dark_purple_theme, - Ui::login_window_light_purple_theme, + Ui::login_window_light_autumn_theme, Ui::login_window_dark_autumn_theme, + Ui::login_window_dark_purple_theme, Ui::login_window_light_purple_theme, Ui::login_window_blue_theme }; -LoginWindow::LoginWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::LoginWindow) { +LoginWindow::LoginWindow(ClientImplementation *client, QWidget *parent) + : QWidget(parent), ui(new Ui::LoginWindow), client_(client) { ui->setupUi(this); setFixedSize(380, 480); @@ -32,21 +30,24 @@ LoginWindow::LoginWindow(QWidget *parent) handle_theme_changed(ThemeManager::get_instance()->get_current_theme()); connect( - ui->switch_theme, &QPushButton::clicked, this, - &LoginWindow::on_switch_theme_clicked - , Qt::UniqueConnection); + ui->switch_theme, &QPushButton::clicked, this, + &LoginWindow::on_switch_theme_clicked, Qt::UniqueConnection + ); connect( ui->switch_mode, &QPushButton::clicked, this, - &LoginWindow::on_switch_mode_clicked); + &LoginWindow::on_switch_mode_clicked + ); connect( ui->push_enter, &QPushButton::clicked, this, - &LoginWindow::on_push_enter_clicked); - connect(ThemeManager::get_instance(), &ThemeManager::on_theme_changed, - this, &LoginWindow::handle_theme_changed); + &LoginWindow::on_push_enter_clicked + ); + connect( + ThemeManager::get_instance(), &ThemeManager::on_theme_changed, this, + &LoginWindow::handle_theme_changed + ); } - void LoginWindow::handle_theme_changed(int theme) { this->setStyleSheet(THEMES[theme]); } @@ -72,8 +73,7 @@ void LoginWindow::on_switch_mode_clicked() { old->deleteLater(); } Storage storage; - auto *registration_window = - new RegistrationWindow(app_window); + auto *registration_window = new RegistrationWindow(client_, app_window); app_window->setCentralWidget(registration_window); QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); @@ -89,6 +89,10 @@ void LoginWindow::on_push_enter_clicked() { const QString login = ui->input_login->text(); const QString password = ui->input_password->text(); + auto user = new User(); + user->set_login(login.toStdString()); + user->set_hashed_password(password.toStdString()); + if (!login.isEmpty() && !password.isEmpty()) { if (login.size() > 50) { QMessageBox::warning( @@ -100,13 +104,14 @@ void LoginWindow::on_push_enter_clicked() { this, "Ошибка", "Длина пароля не должна превышать пятидесяти символов" ); - } else if (LRDao::validate_user(login, password)) { + } else if (client_->try_authenticate_user(user)) { QMessageBox::information( this, "Вход", "Вы успешно вошли! Добро пожаловать :)" ); QWidget *parent = this->parentWidget(); - const QMainWindow *app_window = qobject_cast(parent); + const QMainWindow *app_window = + qobject_cast(parent); if (QWidget *old = app_window->centralWidget()) { old->deleteLater(); diff --git a/client/ui/authorization-windows/src/registration_window.cpp b/client/ui/authorization-windows/src/registration_window.cpp index d6d50d5..31ed8fb 100644 --- a/client/ui/authorization-windows/src/registration_window.cpp +++ b/client/ui/authorization-windows/src/registration_window.cpp @@ -6,22 +6,24 @@ #include #include #include "login_window.h" -#include "lr_dao.hpp" #include "registration_window_style_sheet.h" #include "theme_manager.h" using Efficio_proto::Storage; const std::vector RegistrationWindow::THEMES = { - Ui::registration_window_light_autumn_theme, - Ui::registration_window_dark_autumn_theme, - Ui::registration_window_dark_purple_theme, - Ui::registration_window_light_purple_theme, - Ui::registration_window_blue_theme - }; - -RegistrationWindow::RegistrationWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::RegistrationWindow) { + Ui::registration_window_light_autumn_theme, + Ui::registration_window_dark_autumn_theme, + Ui::registration_window_dark_purple_theme, + Ui::registration_window_light_purple_theme, + Ui::registration_window_blue_theme +}; + +RegistrationWindow::RegistrationWindow( + ClientImplementation *client, + QWidget *parent +) + : QWidget(parent), ui(new Ui::RegistrationWindow), client_(client) { ui->setupUi(this); setFixedSize(380, 480); @@ -35,36 +37,37 @@ RegistrationWindow::RegistrationWindow(QWidget *parent) setAttribute(Qt::WA_StyledBackground, true); connect( - ui->switch_theme, &QPushButton::clicked, this, - &RegistrationWindow::on_switch_theme_clicked - , Qt::UniqueConnection); + ui->switch_theme, &QPushButton::clicked, this, + &RegistrationWindow::on_switch_theme_clicked, Qt::UniqueConnection + ); connect( ui->push_registration, &QPushButton::clicked, this, - &RegistrationWindow::on_push_registration_clicked - , Qt::UniqueConnection); + &RegistrationWindow::on_push_registration_clicked, Qt::UniqueConnection + ); connect( ui->switch_mode, &QPushButton::clicked, this, - &RegistrationWindow::on_switch_mode_clicked - , Qt::UniqueConnection); - connect(ThemeManager::get_instance(), &ThemeManager::on_theme_changed, - this, &RegistrationWindow::handle_theme_changed); + &RegistrationWindow::on_switch_mode_clicked, Qt::UniqueConnection + ); + connect( + ThemeManager::get_instance(), &ThemeManager::on_theme_changed, this, + &RegistrationWindow::handle_theme_changed + ); handle_theme_changed(ThemeManager::get_instance()->get_current_theme()); } - void RegistrationWindow::handle_theme_changed(const int theme) { this->setStyleSheet(THEMES[theme]); } void RegistrationWindow::on_switch_theme_clicked() { - if ((this->counter_on_switch_theme_clicks++)%2){ - int next_theme = (ThemeManager::get_instance()->get_current_theme() + 1) % 5; + if ((this->counter_on_switch_theme_clicks++) % 2) { + int next_theme = + (ThemeManager::get_instance()->get_current_theme() + 1) % 5; ThemeManager::get_instance()->apply_theme(next_theme); } } - RegistrationWindow::~RegistrationWindow() { delete ui; } @@ -78,10 +81,11 @@ void RegistrationWindow::on_switch_mode_clicked() { old->deleteLater(); } Storage storage; - auto *login_window = new LoginWindow(app_window); + auto *login_window = new LoginWindow(client_, app_window); app_window->setCentralWidget(login_window); - const QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); + const QRect screenGeometry = + QApplication::primaryScreen()->availableGeometry(); const int x = (screenGeometry.width() - login_window->width()) / 2; const int y = (screenGeometry.height() - login_window->height()) / 2; app_window->move(x, y); @@ -156,8 +160,11 @@ void RegistrationWindow::on_push_registration_clicked() { "Длина пароля не должна превышать пятидесяти символов" ); } else if (is_strong_and_valid_password(created_password)) { - const int try_register_user = - LRDao::try_register_user(created_login, created_password); + auto user = new User(); + user->set_login(created_login.toStdString()); + user->set_hashed_password(created_password.toStdString()); + + const int try_register_user = client_->try_register_user(user); if (try_register_user == 0) { QMessageBox::warning( this, "Ошибка", diff --git a/client/ui/main-window/include/applicationwindow.h b/client/ui/main-window/include/applicationwindow.h index ccb14f8..dbbe82e 100644 --- a/client/ui/main-window/include/applicationwindow.h +++ b/client/ui/main-window/include/applicationwindow.h @@ -8,7 +8,7 @@ class ApplicationWindow : public QMainWindow { Q_OBJECT public: - explicit ApplicationWindow(std::string window_name); + explicit ApplicationWindow(const std::string &window_name); signals: }; diff --git a/client/ui/main-window/src/applicationwindow.cpp b/client/ui/main-window/src/applicationwindow.cpp index 62c7bfe..2cef620 100644 --- a/client/ui/main-window/src/applicationwindow.cpp +++ b/client/ui/main-window/src/applicationwindow.cpp @@ -1,9 +1,8 @@ #include "applicationwindow.h" #include -#include "mainwindow.h" namespace Ui { -ApplicationWindow::ApplicationWindow(std::string window_name_) +ApplicationWindow::ApplicationWindow(const std::string &window_name_) : QMainWindow{nullptr} { this->setObjectName("ApplicationWindow"); diff --git a/client/ui/theme-manager/include/theme_manager.h b/client/ui/theme-manager/include/theme_manager.h index 2b8cffe..5d014c9 100644 --- a/client/ui/theme-manager/include/theme_manager.h +++ b/client/ui/theme-manager/include/theme_manager.h @@ -7,7 +7,7 @@ class ThemeManager final : public QObject { Q_OBJECT public: - static ThemeManager* get_instance(); + static ThemeManager *get_instance(); void apply_theme(int theme); [[nodiscard]] int get_current_theme() const; @@ -16,8 +16,8 @@ class ThemeManager final : public QObject { private: explicit ThemeManager(QObject *parent = nullptr); - static ThemeManager* instance_; + static ThemeManager *instance_; int current_theme_; }; -#endif // THEME_MANAGER_H \ No newline at end of file +#endif // THEME_MANAGER_H \ No newline at end of file diff --git a/client/ui/theme-manager/src/theme_manager.cpp b/client/ui/theme-manager/src/theme_manager.cpp index 87a85a8..dc73868 100644 --- a/client/ui/theme-manager/src/theme_manager.cpp +++ b/client/ui/theme-manager/src/theme_manager.cpp @@ -2,15 +2,14 @@ #include #include -ThemeManager* ThemeManager::instance_ = nullptr; +ThemeManager *ThemeManager::instance_ = nullptr; -ThemeManager::ThemeManager(QObject *parent) - : QObject(parent) { +ThemeManager::ThemeManager(QObject *parent) : QObject(parent) { current_theme_ = 0; emit on_theme_changed(this->current_theme_); } -ThemeManager* ThemeManager::get_instance() { +ThemeManager *ThemeManager::get_instance() { if (!instance_) { instance_ = new ThemeManager(); } diff --git a/server/Service/include/auth_service.h b/server/Service/include/auth_service.h index 18be3c7..7c4ea8a 100644 --- a/server/Service/include/auth_service.h +++ b/server/Service/include/auth_service.h @@ -24,21 +24,21 @@ class AuthService final { protected: AuthRequest request_; ServerAsyncResponseWriter responder_; - AuthService& service_; + AuthService &service_; explicit AuthServerOperation( - AuthService& service, - ServerCompletionQueue* cq - ) : CommonServerCall(cq), - responder_(&ctx_), - service_(service) {} + AuthService &service, + ServerCompletionQueue *cq + ) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + } public: ~AuthServerOperation() override = default; void Proceed(bool ok) override = 0; }; -public: +public: class TryAuthenticateUserServerCall final : public AuthServerOperation { public: explicit TryAuthenticateUserServerCall( @@ -60,4 +60,4 @@ class AuthService final { Auth::AsyncService &get_service(); }; -#endif //AUTH_SERVICE_H +#endif // AUTH_SERVICE_H diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp index 5240a4d..88f934f 100644 --- a/server/Service/src/auth_service.cpp +++ b/server/Service/src/auth_service.cpp @@ -4,7 +4,8 @@ AuthService::TryAuthenticateUserServerCall::TryAuthenticateUserServerCall( AuthService &service, ServerCompletionQueue *cq -) : AuthServerOperation(service, cq) { +) + : AuthServerOperation(service, cq) { service_.service_.RequestTryAuthenticateUser( &ctx_, &request_, &responder_, cq_, cq_, this ); @@ -31,7 +32,8 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { response.mutable_user()->CopyFrom(request_.user()); } else { response.set_error_text( - "[SERVER ERROR]: Не удалось выполнить запрос в базу данных на проверку " + "[SERVER ERROR]: Не удалось выполнить запрос в базу данных " + "на проверку " "корректности логина и пароля" ); } @@ -49,7 +51,8 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { AuthService::TryRegisterUserServerCall::TryRegisterUserServerCall( AuthService &service, ServerCompletionQueue *cq -) : AuthServerOperation(service, cq) { +) + : AuthServerOperation(service, cq) { service_.service_.RequestTryRegisterUser( &ctx_, &request_, &responder_, cq_, cq_, this ); @@ -74,7 +77,8 @@ void AuthService::TryRegisterUserServerCall::Proceed(const bool ok) { if (query_exit_code < 1) { response.set_error_text( - "[SERVER ERROR]: Не удалось выполнить запрос на запись нового пользователя " + "[SERVER ERROR]: Не удалось выполнить запрос на запись " + "нового пользователя " "в базу данных" ); } else { @@ -89,4 +93,8 @@ void AuthService::TryRegisterUserServerCall::Proceed(const bool ok) { break; } } +} + +Auth::AsyncService &AuthService::get_service() { + return service_; } \ No newline at end of file diff --git a/server/database/CMakeLists.txt b/server/database/CMakeLists.txt index 189ec21..7ebd2c0 100644 --- a/server/database/CMakeLists.txt +++ b/server/database/CMakeLists.txt @@ -5,7 +5,7 @@ project(Database LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) -find_package(bcrypt REQUIRED) +find_package(OpenSSL REQUIRED) find_package(Qt6 COMPONENTS Widgets Core Sql REQUIRED) file(GLOB SOURCES "src/*.cpp") @@ -19,4 +19,4 @@ target_include_directories(Database $ ) -target_link_libraries(Database PRIVATE bcrypt Qt6::Widgets Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) \ No newline at end of file +target_link_libraries(Database PRIVATE OpenSSL::Crypto Qt6::Widgets Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) \ No newline at end of file diff --git a/server/database/include/lr_dao.hpp b/server/database/include/lr_dao.hpp index f98dcb6..9094374 100644 --- a/server/database/include/lr_dao.hpp +++ b/server/database/include/lr_dao.hpp @@ -7,9 +7,16 @@ class LRDao { public: LRDao() = default; - static int try_register_user(const std::string &login, const std::string &password); - static bool validate_user(const std::string &login, const std::string &password); - static bool add_project_to_user(const std::string &user_login, int project_id); + static bool validate_password( + const std::string &input_password, + const std::string &stored_hash + ); + static int + try_register_user(const std::string &login, const std::string &password); + static bool + validate_user(const std::string &login, const std::string &password); + static bool + add_project_to_user(const std::string &user_login, int project_id); static bool get_user_projects(const std::string &login, std::vector &projects); diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 5a8b710..9c4d690 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -1,9 +1,58 @@ #include "lr_dao.hpp" +#include +#include +#include +#include #include +#include #include "database_manager.hpp" -#include -int LRDao::try_register_user(const std::string &login, const std::string &password) { +std::string LRDao::hash_password(const std::string &password) { + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); + if (!mdctx) { + throw std::runtime_error("Failed to create EVP_MD_CTX"); + } + + if (EVP_DigestInit_ex(mdctx, EVP_sha256(), nullptr) != 1) { + EVP_MD_CTX_free(mdctx); + throw std::runtime_error("Failed to initialize digest"); + } + + if (EVP_DigestUpdate(mdctx, password.c_str(), password.size()) != 1) { + EVP_MD_CTX_free(mdctx); + throw std::runtime_error("Failed to update digest"); + } + + std::array hash{}; + unsigned int length = 0; + if (EVP_DigestFinal_ex(mdctx, hash.data(), &length) != 1) { + EVP_MD_CTX_free(mdctx); + throw std::runtime_error("Failed to finalize digest"); + } + + EVP_MD_CTX_free(mdctx); + + std::stringstream ss; + for (const auto &byte : hash) { + ss << std::hex << std::setw(2) << std::setfill('0') + << static_cast(byte); + } + + return ss.str(); +} + +bool LRDao::validate_password( + const std::string &input_password, + const std::string &stored_hash +) { + const std::string input_hash = hash_password(input_password); + return input_hash == stored_hash; +} + +int LRDao::try_register_user( + const std::string &login, + const std::string &password +) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); @@ -11,7 +60,8 @@ int LRDao::try_register_user(const std::string &login, const std::string &passwo pqxx::params check_params; check_params.append(login); - const pqxx::result check_result = transaction.exec(check_query, check_params); + const pqxx::result check_result = + transaction.exec(check_query, check_params); if (!check_result.empty()) { return -1; @@ -19,18 +69,23 @@ int LRDao::try_register_user(const std::string &login, const std::string &passwo const std::string hashed_password = hash_password(password); - const std::string insert_query = "INSERT INTO users (login, password) VALUES ($1, $2)"; + const std::string insert_query = + "INSERT INTO users (login, password) VALUES ($1, $2)"; pqxx::params insert_params; insert_params.append(login); insert_params.append(hashed_password); - const pqxx::result insert_result = transaction.exec(insert_query, insert_params); + const pqxx::result insert_result = + transaction.exec(insert_query, insert_params); transaction.commit(); return insert_result.affected_rows() > 0 ? 1 : 0; } -bool LRDao::validate_user(const std::string &login, const std::string &password) { +bool LRDao::validate_user( + const std::string &login, + const std::string &password +) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); @@ -45,19 +100,22 @@ bool LRDao::validate_user(const std::string &login, const std::string &password) return false; } - const std::string stored_hash = result[0]["password"].as(); - const bool is_valid = BCrypt::validatePassword(password, stored_hash); + const auto stored_hash = result[0]["password"].as(); + const bool is_valid = validate_password(password, stored_hash); transaction.commit(); return is_valid; } bool LRDao::add_project_to_user( - const std::string &user_login, const int project_id) { + const std::string &user_login, + const int project_id +) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); - const std::string query = "UPDATE users SET projects = array_append(projects, ?)" + const std::string query = + "UPDATE users SET projects = array_append(projects, ?)" "WHERE login = ?"; pqxx::params params; params.append(project_id); @@ -108,7 +166,3 @@ bool LRDao::get_user_projects( transaction.commit(); return false; } - -std::string LRDao::hash_password(const std::string &password) { - return BCrypt::generateHash(password); -} From ab117ca0efa372619fddf2a346dcdf9b85d9d10f Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Fri, 16 May 2025 19:49:51 +0300 Subject: [PATCH 30/47] fix: communication between the client and the server in the Auth service --- client/client/include/auth_requests.h | 102 ++++++---------- client/client/include/client_implementation.h | 1 + client/client/src/auth_requests.cpp | 110 +++++++++++++++--- client/client/src/client_implementation.cpp | 2 +- server/Service/src/auth_service.cpp | 4 + server/database/src/lr_dao.cpp | 12 +- 6 files changed, 144 insertions(+), 87 deletions(-) diff --git a/client/client/include/auth_requests.h b/client/client/include/auth_requests.h index ec1ec43..599ecd0 100644 --- a/client/client/include/auth_requests.h +++ b/client/client/include/auth_requests.h @@ -1,93 +1,63 @@ -#ifndef AUTH_REQUESTS_H -#define AUTH_REQUESTS_H +#pragma once +#include #include -#include -#include +#include #include "common_client_call.h" -using grpc::Channel; -using grpc::CompletionQueue; - -using Efficio_proto::User; - using Efficio_proto::Auth; using Efficio_proto::AuthRequest; using Efficio_proto::AuthResponse; +using Efficio_proto::User; +using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::CompletionQueue; class AuthRequests { -private: - class AuthClientOperation : protected CommonClientCall { - protected: - AuthResponse reply_; - std::unique_ptr> - responder_; +public: + explicit AuthRequests( + const std::shared_ptr &channel, + CompletionQueue *cq + ); + bool try_authenticate_user(User *user) const; + bool try_register_user(User *user) const; + +private: + class TryAuthenticateUserClientCall : public CommonClientCall { public: - AuthClientOperation( + TryAuthenticateUserClientCall( const AuthRequest &request, CompletionQueue *cq, - const std::unique_ptr &stub + Auth::Stub *stub ); void Proceed(bool ok) override; AuthResponse get_reply(); - }; - - template - bool try_auth_operation(User *user) const { - AuthRequest request; - request.mutable_user()->CopyFrom(*user); - - const auto call = new Call(request, cq_, stub_); - - void *tag; - bool ok = false; - if (!cq_->Next(&tag, &ok)) { - std::cout << "[CLIENT]: Completion queue failed\n"; - return false; - } - if (!ok) { - std::cout << "[CLIENT WARNING]: no user in reply\n"; - return false; - } - - *user = call->get_reply().user(); - std::cout << "[CLIENT]: USER - " << user->login() << "\n"; - return true; - } - -public: - class TryAuthenticateUserClientCall final : public AuthClientOperation { - public: - TryAuthenticateUserClientCall( - const AuthRequest &request, - CompletionQueue *cq, - const std::unique_ptr &stub - ); + private: + AuthResponse reply_; + ClientContext context; + Status status; + std::unique_ptr> responder_; }; - class TryRegisterUserClientCall final : public AuthClientOperation { + class TryRegisterUserClientCall : public CommonClientCall { public: TryRegisterUserClientCall( const AuthRequest &request, CompletionQueue *cq, - const std::unique_ptr &stub + Auth::Stub *stub ); - }; - - bool try_authenticate_user(User *user) const; - bool try_register(User *user) const; + void Proceed(bool ok) override; + AuthResponse get_reply(); - explicit AuthRequests( - const std::shared_ptr &channel, - CompletionQueue *cq - ) - : cq_(cq), stub_(Auth::NewStub(channel)) {}; + private: + AuthResponse reply_; + ClientContext context; + Status status; + std::unique_ptr> responder_; + }; -private: - CompletionQueue *cq_; std::unique_ptr stub_; -}; - -#endif // AUTH_REQUESTS_H + std::unique_ptr cq_; +}; \ No newline at end of file diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 02b8522..180282d 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -5,6 +5,7 @@ #include "auth_requests.h" #include "update_requests.h" +using Efficio_proto::User; using grpc::Channel; using grpc::CompletionQueue; diff --git a/client/client/src/auth_requests.cpp b/client/client/src/auth_requests.cpp index 7a2a190..04021d1 100644 --- a/client/client/src/auth_requests.cpp +++ b/client/client/src/auth_requests.cpp @@ -1,47 +1,119 @@ -#include "auth_requests.h" +#include +#include +#include -AuthRequests::AuthClientOperation::AuthClientOperation( +using grpc::Channel; +using grpc::ClientAsyncResponseReader; +using grpc::ClientContext; +using grpc::CompletionQueue; +using grpc::Status; + +using Efficio_proto::Auth; +using Efficio_proto::AuthRequest; +using Efficio_proto::AuthResponse; +using Efficio_proto::User; + +AuthRequests::TryAuthenticateUserClientCall::TryAuthenticateUserClientCall( const AuthRequest &request, CompletionQueue *cq, - const std::unique_ptr &stub + Auth::Stub *stub ) : responder_(stub->AsyncTryAuthenticateUser(&context, request, cq)) { + context.set_deadline( + std::chrono::system_clock::now() + std::chrono::seconds(5) + ); responder_->Finish(&reply_, &status, this); + std::cout << "[CLIENT]: AUTHENTICATE USER REQUEST SENT\n"; } -void AuthRequests::AuthClientOperation::Proceed(const bool ok) { +void AuthRequests::TryAuthenticateUserClientCall::Proceed(bool ok) { if (!ok) { - std::cout << "[CLIENT WARNING]: AUTH RPC failed\n"; + std::cout << "[CLIENT WARNING]: Authentication RPC failed\n"; } delete this; } -AuthResponse AuthRequests::AuthClientOperation::get_reply() { +AuthResponse AuthRequests::TryAuthenticateUserClientCall::get_reply() { return reply_; } -AuthRequests::TryAuthenticateUserClientCall::TryAuthenticateUserClientCall( +AuthRequests::TryRegisterUserClientCall::TryRegisterUserClientCall( const AuthRequest &request, CompletionQueue *cq, - const std::unique_ptr &stub + Auth::Stub *stub ) - : AuthClientOperation(request, cq, stub) { - std::cout << "[CLIENT]: AUTH USER REQUEST SENT\n"; + : responder_(stub->AsyncTryRegisterUser(&context, request, cq)) { + context.set_deadline( + std::chrono::system_clock::now() + std::chrono::seconds(5) + ); + responder_->Finish(&reply_, &status, this); + std::cout << "[CLIENT]: REGISTER USER REQUEST SENT\n"; } -AuthRequests::TryRegisterUserClientCall::TryRegisterUserClientCall( - const AuthRequest &request, - CompletionQueue *cq, - const std::unique_ptr &stub +void AuthRequests::TryRegisterUserClientCall::Proceed(bool ok) { + if (!ok) { + std::cout << "[CLIENT WARNING]: Registration RPC failed\n"; + } + delete this; +} + +AuthResponse AuthRequests::TryRegisterUserClientCall::get_reply() { + return reply_; +} + +AuthRequests::AuthRequests( + const std::shared_ptr &channel, + CompletionQueue *cq ) - : AuthClientOperation(request, cq, stub) { - std::cout << "[CLIENT]: REGISTER USER REQUEST SENT\n"; + : stub_(Auth::NewStub(channel)), cq_(cq) { } bool AuthRequests::try_authenticate_user(User *user) const { - return try_auth_operation(user); + AuthRequest request; + request.mutable_user()->set_login(user->login()); + request.mutable_user()->set_hashed_password(user->hashed_password()); + + const auto call = + new TryAuthenticateUserClientCall(request, cq_.get(), stub_.get()); + + void *tag; + bool ok = false; + if (!cq_->Next(&tag, &ok)) { + std::cout << "[CLIENT]: Completion queue failed\n"; + return false; + } + + if (!ok) { + std::cout << "[CLIENT WARNING]: Authentication failed\n"; + return false; + } + + user->CopyFrom(call->get_reply().user()); + std::cout << "[CLIENT]: AUTHENTICATED USER - " << user->login() << "\n"; + return true; } -bool AuthRequests::try_register(User *user) const { - return try_auth_operation(user); +bool AuthRequests::try_register_user(User *user) const { + AuthRequest request; + request.mutable_user()->set_login(user->login()); + request.mutable_user()->set_hashed_password(user->hashed_password()); + + const auto call = + new TryRegisterUserClientCall(request, cq_.get(), stub_.get()); + + void *tag; + bool ok = false; + if (!cq_->Next(&tag, &ok)) { + std::cout << "[CLIENT]: Completion queue failed\n"; + return false; + } + + if (!ok) { + std::cout << "[CLIENT WARNING]: Registration failed\n"; + return false; + } + + user->CopyFrom(call->get_reply().user()); + std::cout << "[CLIENT]: REGISTERED USER - " << user->login() << "\n"; + return true; } \ No newline at end of file diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index a0795bf..1e60147 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -45,7 +45,7 @@ bool ClientImplementation::try_authenticate_user(User *user) const { } bool ClientImplementation::try_register_user(User *user) const { - return auth_requests_.try_register(user); + return auth_requests_.try_register_user(user); } bool ClientImplementation::try_update_note(Note *note) const { diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp index 88f934f..37946b5 100644 --- a/server/Service/src/auth_service.cpp +++ b/server/Service/src/auth_service.cpp @@ -20,6 +20,7 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { switch (status_) { case PROCESS: { + std::cout << "[SERVER]: GOT AUTH REQUEST\n"; new TryAuthenticateUserServerCall(service_, cq_); AuthResponse response; @@ -39,6 +40,7 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { } status_ = FINISH; + responder_.Finish(response, grpc::Status::OK, this); break; } case FINISH: { @@ -67,6 +69,7 @@ void AuthService::TryRegisterUserServerCall::Proceed(const bool ok) { switch (status_) { case PROCESS: { + std::cout << "[SERVER]: GOT REGISTER REQUEST\n"; new TryRegisterUserServerCall(service_, cq_); AuthResponse response; @@ -86,6 +89,7 @@ void AuthService::TryRegisterUserServerCall::Proceed(const bool ok) { } status_ = FINISH; + responder_.Finish(response, grpc::Status::OK, this); break; } case FINISH: { diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 9c4d690..8e7fd4c 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -8,6 +8,10 @@ #include "database_manager.hpp" std::string LRDao::hash_password(const std::string &password) { + if (password.empty()) { + throw std::runtime_error("Password cannot be empty"); + } + EVP_MD_CTX *mdctx = EVP_MD_CTX_new(); if (!mdctx) { throw std::runtime_error("Failed to create EVP_MD_CTX"); @@ -38,7 +42,13 @@ std::string LRDao::hash_password(const std::string &password) { << static_cast(byte); } - return ss.str(); + std::string full_hash = ss.str(); + + if (full_hash.length() > 49) { + return full_hash.substr(0, 49); + } + + return full_hash; } bool LRDao::validate_password( From 2cac5e4a0929bd2cdba757986fcb1611246d8db5 Mon Sep 17 00:00:00 2001 From: toximu Date: Wed, 21 May 2025 23:29:09 +0300 Subject: [PATCH 31/47] rewrite projects to sync --- client/client/include/update_requests.h | 45 ++------ client/client/src/update_requests.cpp | 142 ++++++++++++++++-------- server/Service/src/update_service.cpp | 12 +- 3 files changed, 108 insertions(+), 91 deletions(-) diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 3df499c..9e7cfc0 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -23,50 +23,19 @@ using Efficio_proto::Update; class UpdateRequests { public: - class GetProjectClientCall final : public CommonClientCall { - GetProjectResponse response; - std::unique_ptr> - response_reader; - Project *save_to; - - public: - void Proceed(bool ok) override; - - GetProjectClientCall( - GetProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_ - ); - }; - - class CreateProjectClientCall : public CommonClientCall { - CreateProjectResponse response; - std::unique_ptr> - response_reader; - Project *save_to; - - public: - void Proceed(bool ok) override; - - CreateProjectClientCall( - CreateProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_ - ); - }; class GetNoteClientCall; class CreateNoteClientCall; - class TryJoinProjectClientCall; - bool get_note(Note *note); - bool get_project(Project *project, const std::string &code); bool create_note(Note *note); - bool create_project(Project *project, const std::string &project_title); - bool try_join_project(Project *project); + + bool get_project(Project &project, const std::string &code); + bool create_project(Project &project, const std::string &project_title); + bool try_leave_project(const std::string &code); + bool try_join_project(Project &project, const std::string &code); + + explicit UpdateRequests( std::shared_ptr channel, diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index a8300ce..96766d2 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -13,64 +13,112 @@ using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::GetProjectRequest; using Efficio_proto::GetProjectResponse; +using Efficio_proto::TryJoinProjectRequest; +using Efficio_proto::TryJoinProjectResponse; +using Efficio_proto::TryLeaveProjectRequest; +using Efficio_proto::TryLeaveProjectResponse; using Efficio_proto::Update; -UpdateRequests::GetProjectClientCall::GetProjectClientCall( - GetProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_ -) - : CommonClientCall(), save_to(save_to_) { - response_reader = stub_->PrepareAsyncGetProject(&context, request, cq_); - response_reader->StartCall(); - response_reader->Finish(&response, &status, (void *)this); -} -void UpdateRequests::GetProjectClientCall::Proceed(bool ok) { - if (ok && status.ok()) { - if (response.has_project()) { - *save_to = response.project(); - } else { - std::cout << response.error_text() << std::endl; - } +// todo: set user everywhere + +bool UpdateRequests::get_project(Project &project, const std::string &code) { + GetProjectRequest request; + + request.set_code(code); + + GetProjectResponse response; + + ClientContext context; + + std::cout << "CLIENT : [get project] : sending request" << std::endl; + + Status status = stub_->GetProject(&context, request, &response); + + if (status.ok() && response.has_project()) { + std::cout << "CLIENT : [get project] : got project!" << std::endl; + project = std::move(*response.mutable_project()); + return true; + } + + if (response.has_error_text()) { + std::cout << "CLIENT : [create project] : error_text : " + << response.error_text() << std::endl; + } else { + std::cout << "CLIENT [create project] : status is not OK" << std::endl; } -} -bool UpdateRequests::get_project(Project *project, const std::string &code) { - auto request = new GetProjectRequest; - request->set_code(code); - new GetProjectClientCall(*request, cq_, stub_, project); - return true; + return false; } -UpdateRequests::CreateProjectClientCall::CreateProjectClientCall( - CreateProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_) : CommonClientCall(), save_to(save_to_) -{ - response_reader = stub_->PrepareAsyncCreateProject(&context, request, cq_); - response_reader->StartCall(); - response_reader->Finish(&response, &status, (void *)this); +bool UpdateRequests::create_project( + Project &project, + const std::string &project_title +) { + CreateProjectRequest request; + request.set_project_title(project_title); + + CreateProjectResponse response; + + ClientContext context; + + std::cout << "CLIENT : [create project] : sending request" << std::endl; + Status status = stub_->CreateProject(&context, request, &response); + + if (status.ok() && response.has_project()) { + std::cout << "CLIENT : [create project] : got project!" << std::endl; + project = std::move(*response.mutable_project()); + return true; + } + + if (response.has_error_text()) { + std::cout << "CLIENT [create project] {error text} : " + << response.error_text() << std::endl; + } else { + std::cout << "CLIENT [create project] status is not OK" << std::endl; + } + return false; } -void UpdateRequests::CreateProjectClientCall::Proceed(bool ok) { - if (ok && status.ok()) { - if (response.has_project()) { - *save_to = response.project(); - } else { - std::cout << response.error_text() << std::endl; - } +bool UpdateRequests::try_leave_project(const std::string &code) { + TryLeaveProjectRequest request; + + request.set_code(code); + TryLeaveProjectResponse response; + ClientContext context; + std::cout << "CLIENT : [try leave project] : sending request" << std::endl; + Status status = stub_->TryLeaveProject(&context, request, &response); + + if (status.ok() && response.ok()) { + std::cout << "CLIENT : [try leave project] : left project" << std::endl; + return true; } + + std::cout << "CLIENT : [try leave project] : can't leave project" + << std::endl; } -bool UpdateRequests::create_project( - Project *project, - const std::string &project_title +bool UpdateRequests::try_join_project( + Project &project, + const std::string &code ) { - auto request = new CreateProjectRequest; - request->set_project_title(project_title); - new CreateProjectClientCall(*request, cq_, stub_, project); - return true; + TryJoinProjectRequest request; + request.set_code(code); + TryJoinProjectResponse response; + ClientContext context; + std::cout << "CLIENT : [try join project] : sending request" << std::endl; + Status status = stub_->TryJoinProject(&context, request, &response); + if (status.ok() && response.has_project()) { + std::cout << "CLIENT : [try join project] : got project!" << std::endl; + project = std::move(*response.mutable_project()); + return true; + } + if (response.has_error_text()) { + std::cout << "CLIENT : [try join project] : error_text : " + << response.error_text() << std::endl; + } else { + std::cout << "CLIENT : [try join project] : status is not OK" + << std::endl; + } + return false; } diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 60ae93e..98b9616 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -172,7 +172,7 @@ void UpdateService::TryJoinProjectServerCall::Proceed(const bool ok = true) { } case PROCESS: { // todo: validate user token - + // todo: check if user already has this project status_ = FINISH; new TryJoinProjectServerCall(service_, cq_); std::cout << "[SERVER] : {try join project} : get request, code=" @@ -199,7 +199,6 @@ void UpdateService::TryJoinProjectServerCall::Proceed(const bool ok = true) { } } - UpdateService::TryLeaveProjectServerCall::TryLeaveProjectServerCall( Update::AsyncService *service, ServerCompletionQueue *cq @@ -227,23 +226,24 @@ void UpdateService::TryLeaveProjectServerCall::Proceed(const bool ok) { status_ = FINISH; new TryLeaveProjectServerCall(service_, cq_); std::cout << "[SERVER] : {try leave project} : get request, code=" - << request_.code() << std::endl; + << request_.code() << std::endl; // todo: check if user exists, if project exists etc. - UpdateHandler::try_leave_project(request_.code(), request_.user().login()); + UpdateHandler::try_leave_project( + request_.code(), request_.user().login() + ); response_.set_ok(1); responder_.Finish(response_, grpc::Status::OK, this); } case FINISH: { std::cout << "[SERVER] : {try leave project} : deleting call" - << std::endl; + << std::endl; delete this; } } } - Update::AsyncService &UpdateService::get_service() { return service_; } \ No newline at end of file From fa65343fa8d0f7ed7b287ef911b3784283b481e2 Mon Sep 17 00:00:00 2001 From: toximu Date: Wed, 21 May 2025 23:46:14 +0300 Subject: [PATCH 32/47] delete old project endpoints --- client/client/include/update_requests.h | 38 --------------- client/client/src/update_requests.cpp | 63 ++----------------------- server/Service/src/update_service.cpp | 11 +++-- 3 files changed, 10 insertions(+), 102 deletions(-) diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 636b16c..f07b2aa 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -61,23 +61,6 @@ class UpdateRequests { GetNoteResponse get_reply(); }; - class GetProjectClientCall final : public CommonClientCall { - GetProjectResponse response; - std::unique_ptr> - response_reader; - Project *save_to; - - public: - void Proceed(bool ok) override; - - GetProjectClientCall( - GetProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_ - ); - }; - class CreateNoteClientCall final : public CommonClientCall { CreateNoteResponse reply_; std::unique_ptr> @@ -93,34 +76,13 @@ class UpdateRequests { CreateNoteResponse get_reply(); }; - class CreateProjectClientCall : public CommonClientCall { - CreateProjectResponse response; - std::unique_ptr> - response_reader; - Project *save_to; - - public: - void Proceed(bool ok) override; - - CreateProjectClientCall( - CreateProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_ - ); - }; - class TryJoinProjectClientCall; bool try_update_note(Note *note) const; bool try_fetch_note(Note *note) const; bool try_create_note(Note *note) const; - bool try_create_project(Project *project); bool get_note(Note *note); - bool get_project(Project *project, const std::string &code); bool create_note(Note *note); - bool create_project(Project *project, const std::string &project_title); - bool try_join_project(Project *project); explicit UpdateRequests( std::shared_ptr channel, diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index bb6b7ad..65814c8 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -1,6 +1,8 @@ +#include "update_requests.h" #include +#include +#include #include -#include #include using grpc::Channel; @@ -70,27 +72,7 @@ UpdateRequests::CreateNoteClientCall::CreateNoteClientCall( const CreateNoteRequest &request, CompletionQueue *cq, const std::unique_ptr &stub - -UpdateRequests::GetProjectClientCall::GetProjectClientCall( - GetProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_ -) - : CommonClientCall(), save_to(save_to_) { - response_reader = stub_->PrepareAsyncGetProject(&context, request, cq_); - response_reader->StartCall(); - response_reader->Finish(&response, &status, (void *)this); -} - -void UpdateRequests::GetProjectClientCall::Proceed(bool ok) { - if (ok && status.ok()) { - if (response.has_project()) { - *save_to = response.project(); - } else { - std::cout << response.error_text() << std::endl; - } - } +) { } bool UpdateRequests::try_update_note(Note *note) const { @@ -150,41 +132,4 @@ bool UpdateRequests::try_create_note(Note *note) const { std::cout << "[CLIENT]: Completion queue failed\n"; return false; } - -bool UpdateRequests::get_project(Project *project, const std::string &code) { - auto request = new GetProjectRequest; - request->set_code(code); - new GetProjectClientCall(*request, cq_, stub_, project); - return true; -} - -UpdateRequests::CreateProjectClientCall::CreateProjectClientCall( - CreateProjectRequest &request, - CompletionQueue *cq_, - std::unique_ptr &stub_, - Project *save_to_) : CommonClientCall(), save_to(save_to_) -{ - response_reader = stub_->PrepareAsyncCreateProject(&context, request, cq_); - response_reader->StartCall(); - response_reader->Finish(&response, &status, (void *)this); -} - -void UpdateRequests::CreateProjectClientCall::Proceed(bool ok) { - if (ok && status.ok()) { - if (response.has_project()) { - *save_to = response.project(); - } else { - std::cout << response.error_text() << std::endl; - } - } -} - -bool UpdateRequests::create_project( - Project *project, - const std::string &project_title -) { - auto request = new CreateProjectRequest; - request->set_project_title(project_title); - new CreateProjectClientCall(*request, cq_, stub_, project); - return true; } diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index e425264..c991cc1 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -94,7 +94,7 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { << ", title=" << response.mutable_note()->title() << ", first tag=" << response.note().tags()[0].text() << ":" << response.note().tags()[0].color() << std::endl; - + // TODO: query processing logic responder_.Finish(response, grpc::Status::OK, this); @@ -251,7 +251,6 @@ void UpdateService::TryJoinProjectServerCall::Proceed(const bool ok = true) { } } - UpdateService::TryLeaveProjectServerCall::TryLeaveProjectServerCall( Update::AsyncService *service, ServerCompletionQueue *cq @@ -279,17 +278,19 @@ void UpdateService::TryLeaveProjectServerCall::Proceed(const bool ok) { status_ = FINISH; new TryLeaveProjectServerCall(service_, cq_); std::cout << "[SERVER] : {try leave project} : get request, code=" - << request_.code() << std::endl; + << request_.code() << std::endl; // todo: check if user exists, if project exists etc. - UpdateHandler::try_leave_project(request_.code(), request_.user().login()); + UpdateHandler::try_leave_project( + request_.code(), request_.user().login() + ); response_.set_ok(1); responder_.Finish(response_, grpc::Status::OK, this); } case FINISH: { std::cout << "[SERVER] : {try leave project} : deleting call" - << std::endl; + << std::endl; delete this; } } From bdbb40e00cc030132995f35c8a0c7354966ed598 Mon Sep 17 00:00:00 2001 From: toximu Date: Thu, 22 May 2025 00:28:27 +0300 Subject: [PATCH 33/47] fix some merge troubles --- client/client/include/update_requests.h | 7 ------- client/client/src/update_requests.cpp | 4 ++++ server/Service/include/update_service.h | 2 ++ server/Service/src/update_service.cpp | 4 ++-- server/database/src/note_dao.cpp | 2 +- 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 881bdf2..2fbeb87 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -10,13 +10,6 @@ using grpc::Channel; using grpc::ClientAsyncResponseReader; using grpc::CompletionQueue; -using Efficio_proto::CreateProjectRequest; -using Efficio_proto::CreateProjectResponse; -using Efficio_proto::GetNoteRequest; -using Efficio_proto::GetNoteResponse; -using Efficio_proto::GetProjectRequest; -using Efficio_proto::GetProjectResponse; - using Efficio_proto::Note; using Efficio_proto::Project; using Efficio_proto::Storage; diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index f4da0a9..b15b7f1 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -19,6 +19,8 @@ using Efficio_proto::TryJoinProjectRequest; using Efficio_proto::TryJoinProjectResponse; using Efficio_proto::TryLeaveProjectRequest; using Efficio_proto::TryLeaveProjectResponse; +using Efficio_proto::CreateProjectRequest; +using Efficio_proto::CreateProjectResponse; using Efficio_proto::Update; @@ -110,6 +112,7 @@ bool UpdateRequests::try_join_project( ClientContext context; std::cout << "CLIENT : [try join project] : sending request" << std::endl; Status status = stub_->TryJoinProject(&context, request, &response); + if (status.ok() && response.has_project()) { std::cout << "CLIENT : [try join project] : got project!" << std::endl; project = std::move(*response.mutable_project()); @@ -123,6 +126,7 @@ bool UpdateRequests::try_join_project( << std::endl; } return false; +} UpdateRequests::UpdateNoteClientCall::UpdateNoteClientCall( const UpdateNoteRequest &request, diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 44edce4..5e8ead2 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -32,6 +32,8 @@ class UpdateService final { std::unique_ptr server_; public: + UpdateService::UpdateService(ServerCompletionQueue *cq); + class UpdateNoteServerCall final : public CommonServerCall { UpdateNoteRequest request_; ServerAsyncResponseWriter responder_; diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 89f5cd3..97c8a38 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -14,8 +14,8 @@ UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq), service_() { } void UpdateService::run() { - new GetProjectServerCall(&service_, cq_); - new CreateProjectServerCall(&service_, cq_); + new GetProjectServerCall(&service_, cq_.get()); + new CreateProjectServerCall(&service_, cq_.get()); } UpdateService::UpdateNoteServerCall::UpdateNoteServerCall( diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index 0974383..1988d63 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -1,7 +1,7 @@ #include "note_dao.hpp" #include #include "database_manager.hpp" -#include "tags_dialog.h" +// #include "tags_dialog.h" namespace { From d57c3691c99995c5c7880ef849f30b3f47fb806d Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Thu, 22 May 2025 19:04:38 +0300 Subject: [PATCH 34/47] fix: bugs int notes api after merge --- client/client/CMakeLists.txt | 11 ++--------- client/client/include/client_implementation.h | 2 +- client/client/include/update_requests.h | 6 +----- client/client/src/client_implementation.cpp | 2 +- client/client/src/update_requests.cpp | 6 ++---- client/ui/note-widget/CMakeLists.txt | 1 - server/Handlers/src/update_handler.cpp | 4 +++- server/Service/CMakeLists.txt | 1 + server/Service/include/update_service.h | 4 ++-- server/Service/src/server_implementation.cpp | 6 +----- server/Service/src/update_service.cpp | 16 +++------------- server/database/CMakeLists.txt | 5 ++--- server/database/src/note_dao.cpp | 2 +- 13 files changed, 20 insertions(+), 46 deletions(-) diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt index e9df891..ce52c7c 100644 --- a/client/client/CMakeLists.txt +++ b/client/client/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.16) -project(Client LANGUAGES CXX) +project(Service LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -13,9 +13,7 @@ find_package(Threads) file(GLOB SOURCES "src/*.cpp") file(GLOB HEADERS "include/*.h") -add_library(Client STATIC ${SOURCES} ${HEADERS} - include/auth_requests.h - src/auth_requests.cpp) +add_library(Client STATIC ${SOURCES} ${HEADERS}) target_link_libraries(Client model-proto @@ -31,9 +29,4 @@ target_include_directories(Client PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR} -) - -add_executable(client main.cpp) -target_link_libraries(client - Client ) \ No newline at end of file diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 88249af..b4f64be 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -17,7 +17,7 @@ class ClientImplementation { public: explicit ClientImplementation(const std::shared_ptr &channel); - + CompletionQueue *get_cq() { return &cq_; } diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 2fbeb87..2879ea2 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -24,9 +24,6 @@ using Efficio_proto::UpdateNoteResponse; class UpdateRequests { public: - - - class UpdateNoteClientCall final : public CommonClientCall { UpdateNoteResponse reply_; std::unique_ptr> @@ -77,13 +74,12 @@ class UpdateRequests { bool try_create_note(Note *note) const; bool get_note(Note *note); bool create_note(Note *note); - + bool get_project(Project &project, const std::string &code); bool create_project(Project &project, const std::string &project_title); bool try_leave_project(const std::string &code); bool try_join_project(Project &project, const std::string &code); - explicit UpdateRequests( std::shared_ptr channel, CompletionQueue *cq diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index cf23700..9330a7d 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -3,9 +3,9 @@ #include #include #include +#include #include "update_requests.h" #include "update_service.h" -#include using grpc::Channel; using grpc::ClientAsyncResponseReader; diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index b15b7f1..4677bd5 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -11,6 +11,8 @@ using grpc::ClientContext; using grpc::CompletionQueue; using grpc::Status; +using Efficio_proto::CreateProjectRequest; +using Efficio_proto::CreateProjectResponse; using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::GetProjectRequest; @@ -19,11 +21,8 @@ using Efficio_proto::TryJoinProjectRequest; using Efficio_proto::TryJoinProjectResponse; using Efficio_proto::TryLeaveProjectRequest; using Efficio_proto::TryLeaveProjectResponse; -using Efficio_proto::CreateProjectRequest; -using Efficio_proto::CreateProjectResponse; using Efficio_proto::Update; - // todo: set user everywhere bool UpdateRequests::get_project(Project &project, const std::string &code) { @@ -207,7 +206,6 @@ bool UpdateRequests::try_update_note(Note *note) const { *note = call->get_reply().note(); std::cout << "[CLIENT]: UPDATE NOTE - " << note->title() << "\n"; return true; - } bool UpdateRequests::try_fetch_note(Note *note) const { diff --git a/client/ui/note-widget/CMakeLists.txt b/client/ui/note-widget/CMakeLists.txt index b879264..80eeab3 100644 --- a/client/ui/note-widget/CMakeLists.txt +++ b/client/ui/note-widget/CMakeLists.txt @@ -34,7 +34,6 @@ add_library(NoteWidget STATIC ${SOURCES} ${HEADERS} ${UI_FILES}) target_include_directories(NoteWidget PUBLIC $ - $ $ ) diff --git a/server/Handlers/src/update_handler.cpp b/server/Handlers/src/update_handler.cpp index e98efbf..2bbe820 100644 --- a/server/Handlers/src/update_handler.cpp +++ b/server/Handlers/src/update_handler.cpp @@ -64,7 +64,9 @@ bool UpdateHandler::try_leave_project( return true; } -std::optional UpdateHandler::get_project(const std::string &project_code) { +std::optional UpdateHandler::get_project( + const std::string &project_code +) { Project project; bool ok = ProjectDAO::get_project(project_code, project); if (!ok) { diff --git a/server/Service/CMakeLists.txt b/server/Service/CMakeLists.txt index 421b500..811dc41 100644 --- a/server/Service/CMakeLists.txt +++ b/server/Service/CMakeLists.txt @@ -19,6 +19,7 @@ add_library(Service STATIC ${SOURCES} ${HEADERS} src/auth_service.cpp) target_link_libraries(Service + NoteWidget Database model-proto efficio-rpc diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 5e8ead2..ac99a25 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -32,7 +32,7 @@ class UpdateService final { std::unique_ptr server_; public: - UpdateService::UpdateService(ServerCompletionQueue *cq); + UpdateService(ServerCompletionQueue *cq); class UpdateNoteServerCall final : public CommonServerCall { UpdateNoteRequest request_; @@ -50,7 +50,7 @@ class UpdateService final { class GetNoteServerCall final : public CommonServerCall { GetNoteRequest request_; ServerAsyncResponseWriter responder_; - Update::AsyncService *service_; + UpdateService &service_; public: explicit GetNoteServerCall( diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index 966bf03..cdda012 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -1,8 +1,6 @@ #include "server_implementation.h" #include "common_server_call.h" -using Efficio_proto::GetNoteRequest; - void ServerImplementation::Run(const uint16_t port) { grpc::ServerBuilder builder; cq_ = builder.AddCompletionQueue(); @@ -14,8 +12,6 @@ void ServerImplementation::Run(const uint16_t port) { cq_ = builder.AddCompletionQueue(); server_ = builder.BuildAndStart(); - UpdateService update_service(cq_.get()); - update_service.run(); HandleRPCs(); } @@ -26,7 +22,7 @@ void ServerImplementation::HandleRPCs() { new AuthService::TryAuthenticateUserServerCall(auth_service_, cq_.get()); new AuthService::TryRegisterUserServerCall(auth_service_, cq_.get()); - + void *tag; bool ok; while (cq_->Next(&tag, &ok)) { diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 97c8a38..2791309 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -9,8 +9,7 @@ using Efficio_proto::GetNoteRequest; using Efficio_proto::GetNoteResponse; using Efficio_proto::Project; -UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq), service_() { - // new GetNoteServerCall(&service_, cq_); +UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq) { } void UpdateService::run() { @@ -76,16 +75,9 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { } switch (status_) { - case CREATE: { - new GetNoteServerCall(service_, cq_); - service_->RequestGetNote( - &ctx_, &request_, &responder_, cq_, cq_, this - ); - status_ = PROCESS; - break; - } case PROCESS: { - const GetNoteResponse response; + new GetNoteServerCall(service_, cq_); + GetNoteResponse response; const auto note = NoteDao::get_note(request_.id()); @@ -95,8 +87,6 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { << ", first tag=" << response.note().tags()[0].text() << ":" << response.note().tags()[0].color() << std::endl; - // TODO: query processing logic - responder_.Finish(response, grpc::Status::OK, this); status_ = FINISH; break; diff --git a/server/database/CMakeLists.txt b/server/database/CMakeLists.txt index 357586d..948ccb8 100644 --- a/server/database/CMakeLists.txt +++ b/server/database/CMakeLists.txt @@ -17,8 +17,7 @@ target_include_directories(Database PUBLIC $ $ + ${CMAKE_SOURCE_DIR}/client/ui/note-widget/include ) -target_link_libraries(Database PRIVATE OpenSSL::Crypto Qt6::Widgets Qt6::Core Qt6::Sql model-proto pqxx NoteWidget) -add_executable(test_database main.cpp) -target_link_libraries(test_database Database model-proto) +target_link_libraries(Database PRIVATE OpenSSL::Crypto Qt6::Widgets Qt6::Core Qt6::Sql NoteWidget model-proto pqxx ) diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index 1988d63..0974383 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -1,7 +1,7 @@ #include "note_dao.hpp" #include #include "database_manager.hpp" -// #include "tags_dialog.h" +#include "tags_dialog.h" namespace { From 0713253d3d3915afeca9431ba3148f71a44979e0 Mon Sep 17 00:00:00 2001 From: toximu Date: Sun, 1 Jun 2025 14:42:13 +0300 Subject: [PATCH 35/47] fix server --- CMakeLists.txt | 2 +- client/CMakeLists.txt | 6 +- client/client/CMakeLists.txt | 4 +- client/client/include/client_implementation.h | 4 - client/client/include/update_requests.h | 2 +- client/client/src/update_requests.cpp | 3 +- client/ui/main-window/include/bottombar.h | 24 +++++ client/ui/main-window/include/notewidget.h | 2 +- client/ui/main-window/src/bottombar.cpp | 31 ++++++ client/ui/main-window/src/notewidget.cpp | 2 +- server/Handlers/CMakeLists.txt | 1 + server/Handlers/include/update_handler.hpp | 29 +++-- server/Handlers/src/update_handler.cpp | 100 +++++++++++++----- .../Service/include/server_implementation.h | 2 +- server/Service/include/update_service.h | 2 +- server/Service/src/server_implementation.cpp | 14 +-- server/Service/src/update_service.cpp | 51 +++------ server/database/include/lr_dao.hpp | 7 +- server/database/src/lr_dao.cpp | 49 +++++---- server/database/src/note_dao.cpp | 17 +-- server/main.cpp | 5 +- 21 files changed, 237 insertions(+), 120 deletions(-) create mode 100644 client/ui/main-window/include/bottombar.h create mode 100644 client/ui/main-window/src/bottombar.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index aba5da5..eadccae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,4 +7,4 @@ add_link_options(-fsanitize=address,undefined,leak) add_subdirectory(server) add_subdirectory(proto) -add_subdirectory(client/client) +add_subdirectory(client) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index c23babc..4652bc5 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -11,8 +11,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) -add_subdirectory(scripts) -add_subdirectory(database) +#add_subdirectory(scripts) +#add_subdirectory(database) add_subdirectory(ui/main-window) add_subdirectory(ui/note-widget) add_subdirectory(ui/authorization-windows) @@ -28,8 +28,8 @@ target_link_libraries(EfficioTaskTracker PRIVATE Qt6::Sql Database Scripts - MainWindow NoteWidget + MainWindow AuthorizationWindows ThemeManager model-proto diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt index ce52c7c..6b0a829 100644 --- a/client/client/CMakeLists.txt +++ b/client/client/CMakeLists.txt @@ -25,8 +25,8 @@ target_link_libraries(Client ) target_include_directories(Client - PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_BINARY_DIR} -) \ No newline at end of file +) + diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index b4f64be..6bcb159 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -22,10 +22,6 @@ class ClientImplementation { return &cq_; } - std::shared_ptr get_channel() { - return channel_; - } - void CompleteRpc(); std::shared_ptr get_channel(); diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 2879ea2..bd66688 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -65,7 +65,7 @@ class UpdateRequests { grpc::CompletionQueue *cq, const std::unique_ptr &stub ); - void Proceed(bool ok) override; + // void Proceed(bool ok) override; CreateNoteResponse get_reply(); }; diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 4677bd5..9a7420f 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -183,6 +183,7 @@ UpdateRequests::CreateNoteClientCall::CreateNoteClientCall( CompletionQueue *cq, const std::unique_ptr &stub ) { + 1+1; } bool UpdateRequests::try_update_note(Note *note) const { @@ -234,7 +235,7 @@ bool UpdateRequests::try_fetch_note(Note *note) const { bool UpdateRequests::try_create_note(Note *note) const { const CreateNoteRequest request; - const auto call = new CreateNoteClientCall(request, cq_, stub_); + // const auto call = new CreateNoteClientCall(request, cq_, stub_); void *tag; bool ok = false; diff --git a/client/ui/main-window/include/bottombar.h b/client/ui/main-window/include/bottombar.h new file mode 100644 index 0000000..97f49a1 --- /dev/null +++ b/client/ui/main-window/include/bottombar.h @@ -0,0 +1,24 @@ +#ifndef BOTTOMBAR_H +#define BOTTOMBAR_H + +#include +#include +#include +#include + +namespace Ui { +class BottomBar : public QWidget { + QHBoxLayout *main_layout_; + QLabel *project_name_; + QLabel *username_; + +public: + BottomBar( + QWidget *parent_, + std::string username_, + std::string project_name_ + ); +}; +} // namespace Ui + +#endif // BOTTOMBAR_H \ No newline at end of file diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h index 2d9ca55..323d005 100644 --- a/client/ui/main-window/include/notewidget.h +++ b/client/ui/main-window/include/notewidget.h @@ -26,7 +26,7 @@ class NoteWidget : public QWidget { private slots: - // void open_note_window() const; + void open_note_window() const; }; } // namespace Ui #endif // NOTEWIDGET_H \ No newline at end of file diff --git a/client/ui/main-window/src/bottombar.cpp b/client/ui/main-window/src/bottombar.cpp new file mode 100644 index 0000000..6bc3f51 --- /dev/null +++ b/client/ui/main-window/src/bottombar.cpp @@ -0,0 +1,31 @@ +#include "bottombar.h" +#include +#include +#include +#include + +namespace Ui { +BottomBar::BottomBar( + QWidget *parent, + std::string username, + std::string project_name +) + : QWidget(parent), + main_layout_(new QHBoxLayout()), + username_(new QLabel(username.c_str())), + project_name_(new QLabel(project_name.c_str())) { + this->setObjectName("BottomBar"); + this->setLayout(main_layout_); + this->setFixedHeight(40); + + project_name_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); + username_->setAlignment(Qt::AlignVCenter | Qt::AlignRight); + + this->setSizePolicy( + QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum) + ); + + main_layout_->addWidget(project_name_); + main_layout_->addWidget(username_); +} +} // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp index f43227f..b966614 100644 --- a/client/ui/main-window/src/notewidget.cpp +++ b/client/ui/main-window/src/notewidget.cpp @@ -3,7 +3,7 @@ #include #include -// #include "note_edit_dialog.h" +#include "note_edit_dialog.h" namespace Ui { NoteWidget::NoteWidget(QWidget *parent, const Note *model_note) diff --git a/server/Handlers/CMakeLists.txt b/server/Handlers/CMakeLists.txt index 24e86fb..b14cf6d 100644 --- a/server/Handlers/CMakeLists.txt +++ b/server/Handlers/CMakeLists.txt @@ -11,6 +11,7 @@ file(GLOB HEADERS "include/*.hpp") add_library(Handlers STATIC ${SOURCES} ${HEADERS}) target_link_libraries(Handlers + efficio-rpc model-proto Database ) diff --git a/server/Handlers/include/update_handler.hpp b/server/Handlers/include/update_handler.hpp index 11ec329..befe658 100644 --- a/server/Handlers/include/update_handler.hpp +++ b/server/Handlers/include/update_handler.hpp @@ -1,20 +1,37 @@ #ifndef UPDATE_HANDLER_H #define UPDATE_HANDLER_H +#include #include +using Efficio_proto::CreateProjectRequest; +using Efficio_proto::CreateProjectResponse; +using Efficio_proto::GetProjectRequest; +using Efficio_proto::GetProjectResponse; using Efficio_proto::Project; +using Efficio_proto::TryJoinProjectRequest; +using Efficio_proto::TryJoinProjectResponse; +using Efficio_proto::TryLeaveProjectRequest; +using Efficio_proto::TryLeaveProjectResponse; +using Efficio_proto::UpdateProjectRequest; +using Efficio_proto::UpdateProjectResponse; class UpdateHandler { public: - static Project create_project(const std::string &title); - static bool - try_join_project(const std::string &project_code, const std::string &login); + static bool create_project( + const CreateProjectRequest &request, + CreateProjectResponse &response + ); + static bool try_join_project( + const TryJoinProjectRequest &request, + TryJoinProjectResponse &response + ); static bool try_leave_project( - const std::string &project_code, - const std::string &login + const TryLeaveProjectRequest &request, + TryLeaveProjectResponse &response ); - static std::optional get_project(const std::string &project_code); + static bool + get_project(const GetProjectRequest &request, GetProjectResponse &response); private: static std::string generate_project_code(); diff --git a/server/Handlers/src/update_handler.cpp b/server/Handlers/src/update_handler.cpp index 2bbe820..1461d84 100644 --- a/server/Handlers/src/update_handler.cpp +++ b/server/Handlers/src/update_handler.cpp @@ -1,15 +1,15 @@ #include "update_handler.hpp" #include #include +#include "lr_dao.hpp" #include "project_dao.hpp" using Efficio_proto::Project; std::string UpdateHandler::generate_project_code() { static const std::string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution<> dis(0, chars.size() - 1); + static std::mt19937 gen(std::random_device{}()); + static std::uniform_int_distribution dis(0, chars.size() - 1); std::string result; for (size_t i = 0; i < 6; ++i) { result += chars[dis(gen)]; @@ -17,9 +17,18 @@ std::string UpdateHandler::generate_project_code() { return result; } -Project UpdateHandler::create_project(const std::string &title) { - Project project; - project.set_title(title); +bool UpdateHandler::create_project( + const CreateProjectRequest &request, + CreateProjectResponse &response +) { + if (!LRDao::validate_user( + request.user().login(), request.user().hashed_password() + )) { + response.set_allocated_error_text(new std::string("Incorrect user")); + return false; + } + Project *project = new Project(); + project->set_title(request.project_title()); std::string project_code; while (true) { project_code = generate_project_code(); @@ -27,50 +36,87 @@ Project UpdateHandler::create_project(const std::string &title) { break; } } - project.set_code(project_code); - ProjectDAO::insert_project(project); - return std::move(project); + project->set_code(project_code); + *project->add_members() = request.user().login(); + ProjectDAO::insert_project(*project); + LRDao::add_project_to_user(request.user().login(), project_code); + response.set_allocated_project(project); + return true; } bool UpdateHandler::try_join_project( - const std::string &project_code, - const std::string &login + const TryJoinProjectRequest &request, + TryJoinProjectResponse &response ) { - // todo: check if login exists + if (!LRDao::validate_user( + request.user().login(), request.user().hashed_password() + )) { + response.set_allocated_error_text(new std::string("Incorrect user")); + return false; + } - // todo: check if users has not this project already + { + std::vector project_codes; + LRDao::get_user_projects(request.user().login(), project_codes); + for (const auto &project_code : project_codes) { + if (project_code == request.code()) { + response.set_allocated_error_text( + new std::string("Already have this project") + ); + return false; + } + } + } - if (!ProjectDAO::add_member_to_project(project_code, login)) { + if (!ProjectDAO::add_member_to_project( + request.code(), request.user().login() + )) { + response.set_allocated_error_text( + new std::string("Can't add member to project") + ); + return false; + } + if (!LRDao::add_project_to_user(request.user().login(), request.code())) { + response.set_allocated_error_text( + new std::string("Can't add project to user") + ); return false; } - // todo: add project to user(UserDAO not exists yet) + Project *project = new Project(); + ProjectDAO::get_project(request.code(), *project); + response.set_allocated_project(project); return true; } bool UpdateHandler::try_leave_project( - const std::string &project_code, - const std::string &login + const TryLeaveProjectRequest &request, + TryLeaveProjectResponse &response ) { - // todo : check if login exists - - if (!ProjectDAO::delete_member_from_project(project_code, login)) { + if (!LRDao::validate_user( + request.user().login(), request.user().hashed_password() + )) { return false; } - // todo : delete project from user + ProjectDAO::delete_member_from_project(request.code(), request.user().login()); + + LRDao::delete_project_from_user(request.user().login(), request.code()); return true; } -std::optional UpdateHandler::get_project( - const std::string &project_code +bool UpdateHandler::get_project( + const GetProjectRequest &request, + GetProjectResponse &response ) { - Project project; - bool ok = ProjectDAO::get_project(project_code, project); + Project *project = new Project(); + bool ok = ProjectDAO::get_project(request.code(), *project); if (!ok) { - return std::nullopt; + response.set_allocated_error_text(new std::string("Can't get project")); + return false; } - return project; + response.set_allocated_project(project); + return true; } diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h index b0f427f..3371051 100644 --- a/server/Service/include/server_implementation.h +++ b/server/Service/include/server_implementation.h @@ -15,7 +15,7 @@ class ServerImplementation final { AuthService auth_service_; public: - void Run(uint16_t port); + explicit ServerImplementation(const uint16_t port, grpc::ServerBuilder &builder); void HandleRPCs(); }; diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index ac99a25..008215a 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -28,7 +28,7 @@ using Efficio_proto::UpdateNoteResponse; class UpdateService final { Update::AsyncService service_; ServerContext ctx_; - std::unique_ptr cq_; + ServerCompletionQueue *cq_; std::unique_ptr server_; public: diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index cdda012..ab238a6 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -1,23 +1,25 @@ #include "server_implementation.h" #include "common_server_call.h" -void ServerImplementation::Run(const uint16_t port) { - grpc::ServerBuilder builder; +ServerImplementation::ServerImplementation( + const uint16_t port, + grpc::ServerBuilder &builder +) + : cq_(builder.AddCompletionQueue()), + update_service_(cq_.get()) { cq_ = builder.AddCompletionQueue(); builder.AddListeningPort( "localhost:" + std::to_string(port), grpc::InsecureServerCredentials() ); + builder.RegisterService(&update_service_.get_service()); builder.RegisterService(&auth_service_.get_service()); - - cq_ = builder.AddCompletionQueue(); server_ = builder.BuildAndStart(); - HandleRPCs(); } void ServerImplementation::HandleRPCs() { new UpdateService::GetNoteServerCall(update_service_, cq_.get()); - new UpdateService::CreateNoteServerCall(update_service_, cq_.get()); + // new UpdateService::CreateNoteServerCall(update_service_, cq_.get()); new UpdateService::UpdateNoteServerCall(update_service_, cq_.get()); new AuthService::TryAuthenticateUserServerCall(auth_service_, cq_.get()); diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 2791309..ff56936 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -13,8 +13,11 @@ UpdateService::UpdateService(ServerCompletionQueue *cq) : cq_(cq) { } void UpdateService::run() { - new GetProjectServerCall(&service_, cq_.get()); - new CreateProjectServerCall(&service_, cq_.get()); + new GetProjectServerCall(&service_, cq_); + new CreateProjectServerCall(&service_, cq_); + new TryJoinProjectServerCall(&service_, cq_); + new TryLeaveProjectServerCall(&service_, cq_); + } UpdateService::UpdateNoteServerCall::UpdateNoteServerCall( @@ -120,18 +123,10 @@ void UpdateService::GetProjectServerCall::Proceed(const bool ok = true) { case PROCESS: { status_ = FINISH; - // todo: validate user token - // todo: check if user has this project std::cout << "[SERVER] : {get project} : get request, code=" << request_.code() << std::endl; new GetProjectServerCall(service_, cq_); - auto project = UpdateHandler::get_project(request_.code()); - - if (project.has_value()) { - response_.set_allocated_project(&project.value()); - } else { - response_.set_error_text("can't get project"); - } + UpdateHandler::get_project(request_, response_); responder_.Finish(response_, grpc::Status::OK, this); break; } @@ -167,17 +162,14 @@ void UpdateService::CreateProjectServerCall::Proceed(const bool ok) { break; } case PROCESS: { - // todo: validate user token status_ = FINISH; new GetProjectServerCall(service_, cq_); std::cout << "[SERVER] : {create project} : get request, title=" << request_.project_title() << std::endl; - Project *res_project = new Project( - UpdateHandler::create_project(request_.project_title()) - ); - response_.set_allocated_project(res_project); + UpdateHandler::create_project(request_, response_); + responder_.Finish(response_, grpc::Status::OK, this); break; } @@ -213,32 +205,22 @@ void UpdateService::TryJoinProjectServerCall::Proceed(const bool ok = true) { break; } case PROCESS: { - // todo: validate user token - // todo: check if user already has this project status_ = FINISH; new TryJoinProjectServerCall(service_, cq_); std::cout << "[SERVER] : {try join project} : get request, code=" << request_.code() << std::endl; - if (!UpdateHandler::try_join_project( - request_.code(), request_.user().login() - )) { - response_.set_error_text("can't join project"); - responder_.Finish(response_, grpc::Status::OK, this); - break; - } + UpdateHandler::try_join_project(request_, response_); - auto project = UpdateHandler::get_project(request_.code()); - response_.set_allocated_project(&project.value()); responder_.Finish(response_, grpc::Status::OK, this); break; - } - case FINISH: { - std::cout << "[SERVER] : {try join project} : deleting call" - << std::endl; - delete this; - } } + case FINISH: { + std::cout << "[SERVER] : {try join project} : deleting call" + << std::endl; + delete this; + } +} } UpdateService::TryLeaveProjectServerCall::TryLeaveProjectServerCall( @@ -270,9 +252,8 @@ void UpdateService::TryLeaveProjectServerCall::Proceed(const bool ok) { std::cout << "[SERVER] : {try leave project} : get request, code=" << request_.code() << std::endl; - // todo: check if user exists, if project exists etc. UpdateHandler::try_leave_project( - request_.code(), request_.user().login() + request_, response_ ); response_.set_ok(1); diff --git a/server/database/include/lr_dao.hpp b/server/database/include/lr_dao.hpp index 9094374..65a9049 100644 --- a/server/database/include/lr_dao.hpp +++ b/server/database/include/lr_dao.hpp @@ -16,10 +16,11 @@ class LRDao { static bool validate_user(const std::string &login, const std::string &password); static bool - add_project_to_user(const std::string &user_login, int project_id); + add_project_to_user(const std::string &user_login, const std::string &project_code); static bool - get_user_projects(const std::string &login, std::vector &projects); - + get_user_projects(const std::string &login, std::vector &projects); + static bool + delete_project_from_user(const std::string &login, const std::string &project_code); private: static std::string hash_password(const std::string &password); }; diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 8e7fd4c..0c9fd76 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -67,11 +67,10 @@ int LRDao::try_register_user( pqxx::work transaction(connection); const std::string check_query = "SELECT * FROM users WHERE login = $1"; - pqxx::params check_params; - check_params.append(login); + const pqxx::result check_result = - transaction.exec(check_query, check_params); + transaction.exec_params(check_query, login); if (!check_result.empty()) { return -1; @@ -81,12 +80,9 @@ int LRDao::try_register_user( const std::string insert_query = "INSERT INTO users (login, password) VALUES ($1, $2)"; - pqxx::params insert_params; - insert_params.append(login); - insert_params.append(hashed_password); const pqxx::result insert_result = - transaction.exec(insert_query, insert_params); + transaction.exec_params(insert_query, login, hashed_password); transaction.commit(); return insert_result.affected_rows() > 0 ? 1 : 0; @@ -100,10 +96,8 @@ bool LRDao::validate_user( pqxx::work transaction(connection); const std::string query = "SELECT password FROM users WHERE login = $1"; - pqxx::params params; - params.append(login); - const pqxx::result result = transaction.exec(query, params); + const pqxx::result result = transaction.exec_params(query, login); if (result.empty()) { transaction.commit(); @@ -119,7 +113,7 @@ bool LRDao::validate_user( bool LRDao::add_project_to_user( const std::string &user_login, - const int project_id + const std::string &project_code ) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); @@ -128,26 +122,25 @@ bool LRDao::add_project_to_user( "UPDATE users SET projects = array_append(projects, ?)" "WHERE login = ?"; pqxx::params params; - params.append(project_id); + params.append(project_code); params.append(user_login); - const pqxx::result result = transaction.exec(query, params); + const pqxx::result result = + transaction.exec_params(query, params); transaction.commit(); return result.affected_rows() > 0; } bool LRDao::get_user_projects( const std::string &login, - std::vector &projects + std::vector &projects ) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = "SELECT projects FROM users WHERE login = $1"; - pqxx::params params; - params.append(login); - const pqxx::result result = transaction.exec(query, params); + const pqxx::result result = transaction.exec_params(query, login); if (!result.empty()) { const auto &row = result[0]; @@ -164,7 +157,7 @@ bool LRDao::get_user_projects( std::string project_id; while (std::getline(iss, project_id, ',')) { if (!project_id.empty()) { - projects.push_back(std::stoi(project_id)); + projects.push_back(project_id); } } @@ -176,3 +169,23 @@ bool LRDao::get_user_projects( transaction.commit(); return false; } + +bool LRDao::delete_project_from_user( + const std::string &login, + const std::string &project_code +) { + auto &connection = DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + const std::string query = + "UPDATE users SET projects = array_remove(projects, $1) WHERE " + "login = $2" + "RETURNING 1"; + + const pqxx::result result = + transaction.exec_params(query, project_code, login); + transaction.commit(); + if (result.empty()) { + return false; + } + return true; +} diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index 0974383..5405c39 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -48,11 +48,7 @@ Note NoteDao::initialize_note_for_user(const std::string &login) { "VALUES ($1, $2) " "RETURNING id, title, content"; - pqxx::params params; - params.append("Пустая заметка"); - params.append(""); - - const pqxx::result result = transaction.exec(query, params); + const pqxx::result result = transaction.exec_params(query, "Пустая заметка",""); if (result.empty()) { return {}; @@ -91,7 +87,14 @@ bool NoteDao::update_note(const Note ¬e) { params.append(tags_array); params.append(note.id()); - const pqxx::result result = transaction.exec(query, params); + const pqxx::result result = transaction.exec_params(query, + note.title(), + note.text(), + members_array, + note.date(), + tags_array, + note.id() + ); transaction.commit(); return result.affected_rows() > 0; } @@ -118,7 +121,7 @@ Note NoteDao::get_note(const int note_id) { pqxx::params params; params.append(note_id); - const pqxx::result result = transaction.exec(query, params); + const pqxx::result result = transaction.exec_params(query, note_id); if (result.empty()) { return {}; diff --git a/server/main.cpp b/server/main.cpp index 826ae64..8c08f88 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -1,8 +1,9 @@ #include "server_implementation.h" int main() { - ServerImplementation server; + grpc::ServerBuilder builder; + ServerImplementation server(50051, builder); std::cout << "[SERVER]: WAITING FOR REQUESTS...\n"; - server.Run(50051); + server.HandleRPCs(); return 0; } \ No newline at end of file From 58e2e7429008e4ef501badbb6a6c0323bca04b69 Mon Sep 17 00:00:00 2001 From: toximu Date: Tue, 3 Jun 2025 12:31:58 +0300 Subject: [PATCH 36/47] use-after-poison error exists --- client/client/CMakeLists.txt | 2 + client/client/include/client_implementation.h | 18 ++++++++ client/client/include/update_requests.h | 13 ++++-- client/client/src/client_implementation.cpp | 32 +++++++++++++- client/client/src/update_requests.cpp | 34 ++++++++++++--- server/Service/src/server_implementation.cpp | 5 ++- server/Service/src/update_service.cpp | 43 ++++++++++++++++++- server/database/src/database_manager.cpp | 2 +- server/database/src/lr_dao.cpp | 12 +++--- server/main.cpp | 3 ++ 10 files changed, 142 insertions(+), 22 deletions(-) diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt index 6b0a829..ab52d82 100644 --- a/client/client/CMakeLists.txt +++ b/client/client/CMakeLists.txt @@ -30,3 +30,5 @@ target_include_directories(Client ${CMAKE_CURRENT_BINARY_DIR} ) +#add_executable(test-server main.cpp) +#target_link_libraries(test-server Client) \ No newline at end of file diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 6bcb159..ed42fdc 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -2,6 +2,7 @@ #define CLIENTIMPLEMENTATION_H #include +#include #include "auth_requests.h" #include "update_requests.h" @@ -17,6 +18,7 @@ class ClientImplementation { public: explicit ClientImplementation(const std::shared_ptr &channel); + std::thread complete_rpc_thread_; CompletionQueue *get_cq() { return &cq_; @@ -31,6 +33,22 @@ class ClientImplementation { bool try_update_note(Note *note) const; bool try_create_note(Note *note) const; bool try_fetch_note(Note *note) const; + + bool create_project( + Project *project, + const std::string &title, + const User &user + ); + bool get_project(Project *project, const std::string &code); + bool try_join_project( + Project *project, + const std::string &code, + const User &user + ); + bool try_leave_project( + const std::string &code, + const User &user + ); }; #endif // CLIENTIMPLEMENTATION_H \ No newline at end of file diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index bd66688..600ebcd 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -14,6 +14,7 @@ using Efficio_proto::Note; using Efficio_proto::Project; using Efficio_proto::Storage; using Efficio_proto::Update; +using Efficio_proto::User; using Efficio_proto::CreateNoteRequest; using Efficio_proto::CreateNoteResponse; @@ -65,7 +66,7 @@ class UpdateRequests { grpc::CompletionQueue *cq, const std::unique_ptr &stub ); - // void Proceed(bool ok) override; + void Proceed(bool ok) override; CreateNoteResponse get_reply(); }; @@ -76,9 +77,13 @@ class UpdateRequests { bool create_note(Note *note); bool get_project(Project &project, const std::string &code); - bool create_project(Project &project, const std::string &project_title); - bool try_leave_project(const std::string &code); - bool try_join_project(Project &project, const std::string &code); + bool create_project( + Project &project, + const std::string &project_title, + const User &user + ); + bool try_leave_project(const std::string &code, const User &user); + bool try_join_project(Project &project, const std::string &code, const User &user); explicit UpdateRequests( std::shared_ptr channel, diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index 9330a7d..cc74058 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -19,8 +19,8 @@ ClientImplementation::ClientImplementation( : channel_(channel), update_requests_(channel, &cq_), auth_requests_(channel, &cq_) { - std::thread t(&ClientImplementation::CompleteRpc, this); - t.detach(); + complete_rpc_thread_ = + std::thread(&ClientImplementation::CompleteRpc, this); } void ClientImplementation::CompleteRpc() { @@ -68,3 +68,31 @@ bool ClientImplementation::try_create_note(Note *note) const { bool ClientImplementation::try_fetch_note(Note *note) const { return update_requests_.try_fetch_note(note); } + +bool ClientImplementation::create_project( + Project *project, + const std::string &title, + const User &user +) { + return update_requests_.create_project(*project, title, user); +} + +bool ClientImplementation::get_project( + Project *project, + const std::string &code +) { + return update_requests_.get_project(*project, code); +} + +bool ClientImplementation::try_join_project(Project *project, const std::string &code,const User &user) { + return update_requests_.try_join_project(*project, code, user); +} + +bool ClientImplementation::try_leave_project( + const std::string &code, + const User &user +) { + return update_requests_.try_leave_project(code, user); +} + + diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 9a7420f..e5739ec 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -21,9 +21,9 @@ using Efficio_proto::TryJoinProjectRequest; using Efficio_proto::TryJoinProjectResponse; using Efficio_proto::TryLeaveProjectRequest; using Efficio_proto::TryLeaveProjectResponse; +using Efficio_proto::User; using Efficio_proto::Update; -// todo: set user everywhere bool UpdateRequests::get_project(Project &project, const std::string &code) { GetProjectRequest request; @@ -56,10 +56,13 @@ bool UpdateRequests::get_project(Project &project, const std::string &code) { bool UpdateRequests::create_project( Project &project, - const std::string &project_title + const std::string &project_title, + const User& user ) { CreateProjectRequest request; request.set_project_title(project_title); + User* copy_user = new User(user); + request.set_allocated_user(copy_user); CreateProjectResponse response; @@ -83,10 +86,13 @@ bool UpdateRequests::create_project( return false; } -bool UpdateRequests::try_leave_project(const std::string &code) { +bool UpdateRequests::try_leave_project(const std::string &code, const User &user) { TryLeaveProjectRequest request; request.set_code(code); + User* copy_user = new User(user); + request.set_allocated_user(copy_user); + TryLeaveProjectResponse response; ClientContext context; std::cout << "CLIENT : [try leave project] : sending request" << std::endl; @@ -99,14 +105,18 @@ bool UpdateRequests::try_leave_project(const std::string &code) { std::cout << "CLIENT : [try leave project] : can't leave project" << std::endl; + return false; } bool UpdateRequests::try_join_project( Project &project, - const std::string &code + const std::string &code, + const User &user ) { TryJoinProjectRequest request; request.set_code(code); + User* copy_user = new User(user); + request.set_allocated_user(copy_user); TryJoinProjectResponse response; ClientContext context; std::cout << "CLIENT : [try join project] : sending request" << std::endl; @@ -182,8 +192,20 @@ UpdateRequests::CreateNoteClientCall::CreateNoteClientCall( const CreateNoteRequest &request, CompletionQueue *cq, const std::unique_ptr &stub -) { - 1+1; +) + : responder_(stub->AsyncCreateNote(&context, request, cq)) { + context.set_deadline( + std::chrono::system_clock::now() + std::chrono::seconds(5) + ); + responder_->Finish(&reply_, &status, this); + std::cout << "[CLIENT]: CREATE NOTE REQUEST SENT\n"; +} + +void UpdateRequests::CreateNoteClientCall::Proceed(const bool ok) { + if (!ok) { + std::cout << "[CLIENT WARNING]: RPC failed\n"; + } + delete this; } bool UpdateRequests::try_update_note(Note *note) const { diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index ab238a6..4d01159 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -7,19 +7,20 @@ ServerImplementation::ServerImplementation( ) : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()) { - cq_ = builder.AddCompletionQueue(); builder.AddListeningPort( "localhost:" + std::to_string(port), grpc::InsecureServerCredentials() ); builder.RegisterService(&update_service_.get_service()); builder.RegisterService(&auth_service_.get_service()); + server_ = builder.BuildAndStart(); + update_service_.run(); } void ServerImplementation::HandleRPCs() { new UpdateService::GetNoteServerCall(update_service_, cq_.get()); - // new UpdateService::CreateNoteServerCall(update_service_, cq_.get()); + new UpdateService::CreateNoteServerCall(update_service_, cq_.get()); new UpdateService::UpdateNoteServerCall(update_service_, cq_.get()); new AuthService::TryAuthenticateUserServerCall(auth_service_, cq_.get()); diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index ff56936..2ea2210 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -101,6 +101,46 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { } } +UpdateService::CreateNoteServerCall::CreateNoteServerCall( + UpdateService &service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + service_.service_.RequestCreateNote( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + status_ = PROCESS; +} + +void UpdateService::CreateNoteServerCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + + switch (status_) { + case PROCESS: { + new CreateNoteServerCall(service_, cq_); + CreateNoteResponse response; + + const auto new_note = + NoteDao::initialize_note_for_user(request_.user().login()); + response.mutable_note()->CopyFrom(new_note); + + std::cout << "[SERVER]: CREATE NOTE REQUEST id=" << new_note.id() + << std::endl; + + responder_.Finish(response, grpc::Status::OK, this); + status_ = FINISH; + break; + } + case FINISH: { + delete this; + break; + } + } +} + UpdateService::GetProjectServerCall::GetProjectServerCall( Update::AsyncService *service, ServerCompletionQueue *cq @@ -121,11 +161,12 @@ void UpdateService::GetProjectServerCall::Proceed(const bool ok = true) { break; } case PROCESS: { + new GetProjectServerCall(service_, cq_); status_ = FINISH; std::cout << "[SERVER] : {get project} : get request, code=" << request_.code() << std::endl; - new GetProjectServerCall(service_, cq_); + UpdateHandler::get_project(request_, response_); responder_.Finish(response_, grpc::Status::OK, this); break; diff --git a/server/database/src/database_manager.cpp b/server/database/src/database_manager.cpp index 09546e8..32ae8f8 100644 --- a/server/database/src/database_manager.cpp +++ b/server/database/src/database_manager.cpp @@ -22,7 +22,7 @@ DatabaseManager::DatabaseManager() { "login VARCHAR(50) PRIMARY KEY, " "password VARCHAR(50) NOT NULL, " "token VARCHAR(100), " - "projects INT[]" + "projects VARCHAR(6)[]" ")" ); diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 0c9fd76..a1eb6d6 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -119,14 +119,14 @@ bool LRDao::add_project_to_user( pqxx::work transaction(connection); const std::string query = - "UPDATE users SET projects = array_append(projects, ?)" - "WHERE login = ?"; - pqxx::params params; - params.append(project_code); - params.append(user_login); + "UPDATE users SET projects = array_append(projects, $1) " + "WHERE login = $2"; + // pqxx::params params; + // params.append(project_code); + // params.append(user_login); const pqxx::result result = - transaction.exec_params(query, params); + transaction.exec_params(query, project_code, user_login); transaction.commit(); return result.affected_rows() > 0; } diff --git a/server/main.cpp b/server/main.cpp index 8c08f88..2ab320d 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -1,6 +1,9 @@ #include "server_implementation.h" +#include int main() { + absl::lts_20240116::UTCTimeZone(); + grpc::ServerBuilder builder; ServerImplementation server(50051, builder); std::cout << "[SERVER]: WAITING FOR REQUESTS...\n"; From d6e9c87fc37303176b2dce33fef579158a35990b Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Tue, 3 Jun 2025 15:39:46 +0300 Subject: [PATCH 37/47] refactor: rewrite ClientImplementation class to singleton --- client/CMakeLists.txt | 5 +- client/client/include/client_implementation.h | 29 +++---- client/client/src/client_implementation.cpp | 40 +++++++--- client/client/src/update_requests.cpp | 2 +- client/main.cpp | 6 +- .../include/login_window.h | 8 +- .../include/registration_window.h | 7 +- .../src/login_window.cpp | 11 +-- .../src/registration_window.cpp | 13 ++- client/ui/main-window/CMakeLists.txt | 1 + client/ui/main-window/src/notewidget.cpp | 2 +- client/ui/note-widget/CMakeLists.txt | 2 +- .../ui/note-widget/include/note_edit_dialog.h | 7 +- .../ui/note-widget/src/note_edit_dialog.cpp | 13 +-- server/Handlers/src/update_handler.cpp | 4 +- server/Service/include/auth_service.h | 10 ++- .../Service/include/server_implementation.h | 7 +- server/Service/include/update_service.h | 12 +-- server/Service/src/auth_service.cpp | 13 ++- server/Service/src/server_implementation.cpp | 16 ++-- server/Service/src/update_service.cpp | 79 +++++++++++++++---- server/database/include/lr_dao.hpp | 19 +++-- server/database/src/lr_dao.cpp | 4 +- server/database/src/note_dao.cpp | 15 ++-- 24 files changed, 185 insertions(+), 140 deletions(-) diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 4652bc5..00dafc1 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -11,8 +11,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt6 COMPONENTS Widgets Core Gui Sql REQUIRED) -#add_subdirectory(scripts) -#add_subdirectory(database) add_subdirectory(ui/main-window) add_subdirectory(ui/note-widget) add_subdirectory(ui/authorization-windows) @@ -27,11 +25,10 @@ target_link_libraries(EfficioTaskTracker PRIVATE Qt6::Gui Qt6::Sql Database - Scripts + Client NoteWidget MainWindow AuthorizationWindows ThemeManager model-proto - Client ) diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 6bcb159..99f48c4 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -10,27 +10,28 @@ using grpc::Channel; using grpc::CompletionQueue; class ClientImplementation { - CompletionQueue cq_; - std::shared_ptr channel_; - UpdateRequests update_requests_; - AuthRequests auth_requests_; + static CompletionQueue cq_; + static std::shared_ptr channel_; + static UpdateRequests update_requests_; + static AuthRequests auth_requests_; -public: - explicit ClientImplementation(const std::shared_ptr &channel); + static void init(); + + ClientImplementation() = default; - CompletionQueue *get_cq() { - return &cq_; - } +public: + static ClientImplementation &get_instance(); void CompleteRpc(); std::shared_ptr get_channel(); + CompletionQueue *get_cq(); - bool try_authenticate_user(User *user) const; - bool try_register_user(User *user) const; + bool try_authenticate_user(User *user); + bool try_register_user(User *user); - bool try_update_note(Note *note) const; - bool try_create_note(Note *note) const; - bool try_fetch_note(Note *note) const; + bool try_update_note(Note *note); + bool try_create_note(Note *note); + bool try_fetch_note(Note *note); }; #endif // CLIENTIMPLEMENTATION_H \ No newline at end of file diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index 9330a7d..68a4c18 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -13,14 +13,24 @@ using grpc::ClientContext; using grpc::CompletionQueue; using grpc::Status; -ClientImplementation::ClientImplementation( - const std::shared_ptr &channel -) - : channel_(channel), - update_requests_(channel, &cq_), - auth_requests_(channel, &cq_) { - std::thread t(&ClientImplementation::CompleteRpc, this); - t.detach(); +CompletionQueue ClientImplementation::cq_; +std::shared_ptr ClientImplementation::channel_; +UpdateRequests ClientImplementation::update_requests_(nullptr, nullptr); +AuthRequests ClientImplementation::auth_requests_(nullptr, nullptr); + +void ClientImplementation::init() { + channel_ = + CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); + update_requests_ = UpdateRequests(channel_, &cq_); + auth_requests_ = AuthRequests(channel_, &cq_); +} + +ClientImplementation &ClientImplementation::get_instance() { + if (channel_ == nullptr) { + init(); + } + static ClientImplementation instance; + return instance; } void ClientImplementation::CompleteRpc() { @@ -49,22 +59,26 @@ std::shared_ptr ClientImplementation::get_channel() { return channel_; } -bool ClientImplementation::try_authenticate_user(User *user) const { +CompletionQueue *ClientImplementation::get_cq() { + return &cq_; +} + +bool ClientImplementation::try_authenticate_user(User *user) { return auth_requests_.try_authenticate_user(user); } -bool ClientImplementation::try_register_user(User *user) const { +bool ClientImplementation::try_register_user(User *user) { return auth_requests_.try_register_user(user); } -bool ClientImplementation::try_update_note(Note *note) const { +bool ClientImplementation::try_update_note(Note *note) { return update_requests_.try_update_note(note); } -bool ClientImplementation::try_create_note(Note *note) const { +bool ClientImplementation::try_create_note(Note *note) { return update_requests_.try_create_note(note); } -bool ClientImplementation::try_fetch_note(Note *note) const { +bool ClientImplementation::try_fetch_note(Note *note) { return update_requests_.try_fetch_note(note); } diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 9a7420f..637368f 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -183,7 +183,7 @@ UpdateRequests::CreateNoteClientCall::CreateNoteClientCall( CompletionQueue *cq, const std::unique_ptr &stub ) { - 1+1; + 1 + 1; } bool UpdateRequests::try_update_note(Note *note) const { diff --git a/client/main.cpp b/client/main.cpp index 93b7ca5..6e2b6c6 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -21,9 +21,7 @@ int main(int argc, char *argv[]) { } } - const auto channel = - CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()); - ClientImplementation client(channel); + auto client = ClientImplementation::get_instance(); std::thread requests([&] { try { @@ -35,7 +33,7 @@ int main(int argc, char *argv[]) { requests.detach(); auto *app_window = new Ui::ApplicationWindow("EFFICIO"); - auto *login_window = new LoginWindow(&client, app_window); + auto *login_window = new LoginWindow(app_window); app_window->setCentralWidget(login_window); const QRect screen_geometry = diff --git a/client/ui/authorization-windows/include/login_window.h b/client/ui/authorization-windows/include/login_window.h index 1d72a04..f623c18 100644 --- a/client/ui/authorization-windows/include/login_window.h +++ b/client/ui/authorization-windows/include/login_window.h @@ -20,14 +20,11 @@ class LoginWindow final : public QWidget { Q_OBJECT public: - explicit LoginWindow( - ClientImplementation *client, - QWidget *parent = nullptr - ); + explicit LoginWindow(QWidget *parent = nullptr); ~LoginWindow() override; static const std::vector THEMES; - void handle_theme_changed(int theme); + void handle_theme_changed(const int theme); private slots: void on_switch_mode_clicked(); @@ -37,5 +34,4 @@ private slots: private: Ui::LoginWindow *ui; int counter_on_switch_theme_clicks = 0; - ClientImplementation *client_; }; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window.h b/client/ui/authorization-windows/include/registration_window.h index 1929bbe..2c41255 100644 --- a/client/ui/authorization-windows/include/registration_window.h +++ b/client/ui/authorization-windows/include/registration_window.h @@ -2,7 +2,6 @@ #include #include -#include "client_implementation.h" #include "database_manager.hpp" QT_BEGIN_NAMESPACE @@ -19,10 +18,7 @@ class RegistrationWindow final : public QWidget { public: static const std::vector THEMES; - explicit RegistrationWindow( - ClientImplementation *client, - QWidget *parent = nullptr - ); + explicit RegistrationWindow(QWidget *parent = nullptr); ~RegistrationWindow() override; bool is_strong_and_valid_password(const QString &password); void handle_theme_changed(int theme); @@ -35,5 +31,4 @@ private slots: private: Ui::RegistrationWindow *ui; int counter_on_switch_theme_clicks = 0; - ClientImplementation *client_; }; \ No newline at end of file diff --git a/client/ui/authorization-windows/src/login_window.cpp b/client/ui/authorization-windows/src/login_window.cpp index e192fb8..70b328b 100644 --- a/client/ui/authorization-windows/src/login_window.cpp +++ b/client/ui/authorization-windows/src/login_window.cpp @@ -18,8 +18,8 @@ const std::vector LoginWindow::THEMES = { Ui::login_window_blue_theme }; -LoginWindow::LoginWindow(ClientImplementation *client, QWidget *parent) - : QWidget(parent), ui(new Ui::LoginWindow), client_(client) { +LoginWindow::LoginWindow(QWidget *parent) + : QWidget(parent), ui(new Ui::LoginWindow) { ui->setupUi(this); setFixedSize(380, 480); @@ -48,7 +48,7 @@ LoginWindow::LoginWindow(ClientImplementation *client, QWidget *parent) ); } -void LoginWindow::handle_theme_changed(int theme) { +void LoginWindow::handle_theme_changed(const int theme) { this->setStyleSheet(THEMES[theme]); } @@ -73,7 +73,7 @@ void LoginWindow::on_switch_mode_clicked() { old->deleteLater(); } Storage storage; - auto *registration_window = new RegistrationWindow(client_, app_window); + auto *registration_window = new RegistrationWindow(app_window); app_window->setCentralWidget(registration_window); QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); @@ -104,7 +104,8 @@ void LoginWindow::on_push_enter_clicked() { this, "Ошибка", "Длина пароля не должна превышать пятидесяти символов" ); - } else if (client_->try_authenticate_user(user)) { + } else if (ClientImplementation::get_instance() + .try_authenticate_user(user)) { QMessageBox::information( this, "Вход", "Вы успешно вошли! Добро пожаловать :)" ); diff --git a/client/ui/authorization-windows/src/registration_window.cpp b/client/ui/authorization-windows/src/registration_window.cpp index 31ed8fb..de05e3e 100644 --- a/client/ui/authorization-windows/src/registration_window.cpp +++ b/client/ui/authorization-windows/src/registration_window.cpp @@ -19,11 +19,8 @@ const std::vector RegistrationWindow::THEMES = { Ui::registration_window_blue_theme }; -RegistrationWindow::RegistrationWindow( - ClientImplementation *client, - QWidget *parent -) - : QWidget(parent), ui(new Ui::RegistrationWindow), client_(client) { +RegistrationWindow::RegistrationWindow(QWidget *parent) + : QWidget(parent), ui(new Ui::RegistrationWindow) { ui->setupUi(this); setFixedSize(380, 480); @@ -81,7 +78,7 @@ void RegistrationWindow::on_switch_mode_clicked() { old->deleteLater(); } Storage storage; - auto *login_window = new LoginWindow(client_, app_window); + auto *login_window = new LoginWindow(app_window); app_window->setCentralWidget(login_window); const QRect screenGeometry = @@ -164,7 +161,9 @@ void RegistrationWindow::on_push_registration_clicked() { user->set_login(created_login.toStdString()); user->set_hashed_password(created_password.toStdString()); - const int try_register_user = client_->try_register_user(user); + const int try_register_user = + ClientImplementation::get_instance().try_register_user(user + ); if (try_register_user == 0) { QMessageBox::warning( this, "Ошибка", diff --git a/client/ui/main-window/CMakeLists.txt b/client/ui/main-window/CMakeLists.txt index 04b7c3c..eb96399 100644 --- a/client/ui/main-window/CMakeLists.txt +++ b/client/ui/main-window/CMakeLists.txt @@ -36,4 +36,5 @@ target_link_libraries(MainWindow PRIVATE Qt6::Sql Database NoteWidget + Client ) \ No newline at end of file diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp index b966614..7768119 100644 --- a/client/ui/main-window/src/notewidget.cpp +++ b/client/ui/main-window/src/notewidget.cpp @@ -2,7 +2,6 @@ #include #include #include - #include "note_edit_dialog.h" namespace Ui { @@ -38,6 +37,7 @@ NoteWidget::NoteWidget(QWidget *parent, const Note *model_note) void NoteWidget::open_note_window() const { auto dialog = new ::NoteEditDialog( + const_cast(qobject_cast(this)), const_cast(model_note_) ); diff --git a/client/ui/note-widget/CMakeLists.txt b/client/ui/note-widget/CMakeLists.txt index 80eeab3..cefe109 100644 --- a/client/ui/note-widget/CMakeLists.txt +++ b/client/ui/note-widget/CMakeLists.txt @@ -37,4 +37,4 @@ target_include_directories(NoteWidget $ ) -target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Database model-proto Client) \ No newline at end of file +target_link_libraries(NoteWidget PRIVATE Qt6::Widgets Qt6::Core Qt6::Gui Qt6::Sql Client Database model-proto) \ No newline at end of file diff --git a/client/ui/note-widget/include/note_edit_dialog.h b/client/ui/note-widget/include/note_edit_dialog.h index cea9350..76dc91f 100644 --- a/client/ui/note-widget/include/note_edit_dialog.h +++ b/client/ui/note-widget/include/note_edit_dialog.h @@ -22,11 +22,7 @@ class NoteEditDialog final : public QDialog { Q_OBJECT public: - explicit NoteEditDialog( - ClientImplementation *client, - QWidget *parent = nullptr, - Note *note = nullptr - ); + explicit NoteEditDialog(QWidget *parent = nullptr, Note *note = nullptr); ~NoteEditDialog() override; private slots: @@ -54,7 +50,6 @@ private slots: std::vector> tag_labels_; QList selected_tags_; Note *note_; - ClientImplementation *client_; }; #endif // NOTE_EDIT_DIALOG_H \ No newline at end of file diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index 1f28e85..ed4e53c 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -21,15 +21,8 @@ using grpc::Channel; using grpc::ClientContext; using grpc::Status; -NoteEditDialog::NoteEditDialog( - ClientImplementation *client, - QWidget *parent, - Note *note -) - : QDialog(parent), - client_(client), - ui_(new Ui::NoteEditDialog), - note_(note) { +NoteEditDialog::NoteEditDialog(QWidget *parent, Note *note) + : QDialog(parent), ui_(new Ui::NoteEditDialog), note_(note) { if (note_ == nullptr) { std::cerr << "Not a valid note!\n"; } @@ -254,5 +247,5 @@ bool NoteEditDialog::try_save_note() const { new_tag->set_color(color_code_to_note_tag_colors(tag.color)); } - return client_->try_update_note(note_); + return ClientImplementation::get_instance().try_update_note(note_); } \ No newline at end of file diff --git a/server/Handlers/src/update_handler.cpp b/server/Handlers/src/update_handler.cpp index 1461d84..bd1985e 100644 --- a/server/Handlers/src/update_handler.cpp +++ b/server/Handlers/src/update_handler.cpp @@ -100,7 +100,9 @@ bool UpdateHandler::try_leave_project( return false; } - ProjectDAO::delete_member_from_project(request.code(), request.user().login()); + ProjectDAO::delete_member_from_project( + request.code(), request.user().login() + ); LRDao::delete_project_from_user(request.user().login(), request.code()); diff --git a/server/Service/include/auth_service.h b/server/Service/include/auth_service.h index 7c4ea8a..c04f014 100644 --- a/server/Service/include/auth_service.h +++ b/server/Service/include/auth_service.h @@ -24,10 +24,10 @@ class AuthService final { protected: AuthRequest request_; ServerAsyncResponseWriter responder_; - AuthService &service_; + Auth::AsyncService *service_; explicit AuthServerOperation( - AuthService &service, + Auth::AsyncService *service, ServerCompletionQueue *cq ) : CommonServerCall(cq), responder_(&ctx_), service_(service) { @@ -42,7 +42,7 @@ class AuthService final { class TryAuthenticateUserServerCall final : public AuthServerOperation { public: explicit TryAuthenticateUserServerCall( - AuthService &service, + Auth::AsyncService *service, ServerCompletionQueue *cq ); void Proceed(bool) override; @@ -51,13 +51,15 @@ class AuthService final { class TryRegisterUserServerCall final : AuthServerOperation { public: explicit TryRegisterUserServerCall( - AuthService &service, + Auth::AsyncService *service, ServerCompletionQueue *cq ); void Proceed(bool) override; }; Auth::AsyncService &get_service(); + + void run(); }; #endif // AUTH_SERVICE_H diff --git a/server/Service/include/server_implementation.h b/server/Service/include/server_implementation.h index 3371051..9ccccf1 100644 --- a/server/Service/include/server_implementation.h +++ b/server/Service/include/server_implementation.h @@ -15,8 +15,11 @@ class ServerImplementation final { AuthService auth_service_; public: - explicit ServerImplementation(const uint16_t port, grpc::ServerBuilder &builder); - void HandleRPCs(); + explicit ServerImplementation( + const uint16_t port, + grpc::ServerBuilder &builder + ); + void HandleRPCs() const; }; #endif // SERVERIMPLEMENTATION_H diff --git a/server/Service/include/update_service.h b/server/Service/include/update_service.h index 008215a..589b16b 100644 --- a/server/Service/include/update_service.h +++ b/server/Service/include/update_service.h @@ -37,11 +37,11 @@ class UpdateService final { class UpdateNoteServerCall final : public CommonServerCall { UpdateNoteRequest request_; ServerAsyncResponseWriter responder_; - UpdateService &service_; + Update::AsyncService *service_; public: explicit UpdateNoteServerCall( - UpdateService &service, + Update::AsyncService *service, ServerCompletionQueue *cq ); void Proceed(bool ok) override; @@ -50,11 +50,11 @@ class UpdateService final { class GetNoteServerCall final : public CommonServerCall { GetNoteRequest request_; ServerAsyncResponseWriter responder_; - UpdateService &service_; + Update::AsyncService *service_; public: explicit GetNoteServerCall( - UpdateService &service, + Update::AsyncService *service, ServerCompletionQueue *cq ); void Proceed(bool ok) override; @@ -119,11 +119,11 @@ class UpdateService final { class CreateNoteServerCall final : public CommonServerCall { CreateNoteRequest request_; ServerAsyncResponseWriter responder_; - UpdateService &service_; + Update::AsyncService *service_; public: explicit CreateNoteServerCall( - UpdateService &service, + Update::AsyncService *service, ServerCompletionQueue *cq ); void Proceed(bool ok) override; diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp index 37946b5..d76c704 100644 --- a/server/Service/src/auth_service.cpp +++ b/server/Service/src/auth_service.cpp @@ -2,11 +2,11 @@ #include "lr_dao.hpp" AuthService::TryAuthenticateUserServerCall::TryAuthenticateUserServerCall( - AuthService &service, + Auth::AsyncService *service, ServerCompletionQueue *cq ) : AuthServerOperation(service, cq) { - service_.service_.RequestTryAuthenticateUser( + service_->RequestTryAuthenticateUser( &ctx_, &request_, &responder_, cq_, cq_, this ); status_ = PROCESS; @@ -51,11 +51,11 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { } AuthService::TryRegisterUserServerCall::TryRegisterUserServerCall( - AuthService &service, + Auth::AsyncService *service, ServerCompletionQueue *cq ) : AuthServerOperation(service, cq) { - service_.service_.RequestTryRegisterUser( + service_->RequestTryRegisterUser( &ctx_, &request_, &responder_, cq_, cq_, this ); status_ = PROCESS; @@ -101,4 +101,9 @@ void AuthService::TryRegisterUserServerCall::Proceed(const bool ok) { Auth::AsyncService &AuthService::get_service() { return service_; +} + +void AuthService::run() { + new TryAuthenticateUserServerCall(&service_, cq_.get()); + new TryRegisterUserServerCall(&service_, cq_.get()); } \ No newline at end of file diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index ab238a6..b8a023c 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -5,8 +5,7 @@ ServerImplementation::ServerImplementation( const uint16_t port, grpc::ServerBuilder &builder ) - : cq_(builder.AddCompletionQueue()), - update_service_(cq_.get()) { + : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()) { cq_ = builder.AddCompletionQueue(); builder.AddListeningPort( "localhost:" + std::to_string(port), grpc::InsecureServerCredentials() @@ -14,17 +13,14 @@ ServerImplementation::ServerImplementation( builder.RegisterService(&update_service_.get_service()); builder.RegisterService(&auth_service_.get_service()); - server_ = builder.BuildAndStart(); -} -void ServerImplementation::HandleRPCs() { - new UpdateService::GetNoteServerCall(update_service_, cq_.get()); - // new UpdateService::CreateNoteServerCall(update_service_, cq_.get()); - new UpdateService::UpdateNoteServerCall(update_service_, cq_.get()); + update_service_.run(); + auth_service_.run(); - new AuthService::TryAuthenticateUserServerCall(auth_service_, cq_.get()); - new AuthService::TryRegisterUserServerCall(auth_service_, cq_.get()); + server_ = builder.BuildAndStart(); +} +void ServerImplementation::HandleRPCs() const { void *tag; bool ok; while (cq_->Next(&tag, &ok)) { diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index ff56936..ba4b054 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -18,16 +18,17 @@ void UpdateService::run() { new TryJoinProjectServerCall(&service_, cq_); new TryLeaveProjectServerCall(&service_, cq_); + new GetNoteServerCall(&service_, cq_); + new CreateNoteServerCall(&service_, cq_); + new UpdateNoteServerCall(&service_, cq_); } UpdateService::UpdateNoteServerCall::UpdateNoteServerCall( - UpdateService &service, + Update::AsyncService *service, ServerCompletionQueue *cq ) : CommonServerCall(cq), responder_(&ctx_), service_(service) { - service_.service_.RequestUpdateNote( - &ctx_, &request_, &responder_, cq_, cq_, this - ); + service_->RequestUpdateNote(&ctx_, &request_, &responder_, cq_, cq_, this); status_ = PROCESS; } @@ -61,13 +62,11 @@ void UpdateService::UpdateNoteServerCall::Proceed(const bool ok) { } UpdateService::GetNoteServerCall::GetNoteServerCall( - UpdateService &service, + Update::AsyncService *service, ServerCompletionQueue *cq ) : CommonServerCall(cq), responder_(&ctx_), service_(service) { - service_.service_.RequestGetNote( - &ctx_, &request_, &responder_, cq_, cq_, this - ); + service_->RequestGetNote(&ctx_, &request_, &responder_, cq_, cq_, this); status_ = PROCESS; } @@ -214,14 +213,14 @@ void UpdateService::TryJoinProjectServerCall::Proceed(const bool ok = true) { responder_.Finish(response_, grpc::Status::OK, this); break; - } - case FINISH: { - std::cout << "[SERVER] : {try join project} : deleting call" - << std::endl; - delete this; + } + case FINISH: { + std::cout << "[SERVER] : {try join project} : deleting call" + << std::endl; + delete this; + } } } -} UpdateService::TryLeaveProjectServerCall::TryLeaveProjectServerCall( Update::AsyncService *service, @@ -252,9 +251,7 @@ void UpdateService::TryLeaveProjectServerCall::Proceed(const bool ok) { std::cout << "[SERVER] : {try leave project} : get request, code=" << request_.code() << std::endl; - UpdateHandler::try_leave_project( - request_, response_ - ); + UpdateHandler::try_leave_project(request_, response_); response_.set_ok(1); responder_.Finish(response_, grpc::Status::OK, this); @@ -267,6 +264,54 @@ void UpdateService::TryLeaveProjectServerCall::Proceed(const bool ok) { } } +UpdateService::CreateNoteServerCall::CreateNoteServerCall( + Update::AsyncService *service, + ServerCompletionQueue *cq +) + : CommonServerCall(cq), responder_(&ctx_), service_(service) { + this->Proceed(true); +} + +void UpdateService::CreateNoteServerCall::Proceed(const bool ok) { + if (!ok) { + delete this; + return; + } + + switch (status_) { + case CREATE: { + status_ = PROCESS; + service_->RequestCreateNote( + &ctx_, &request_, &responder_, cq_, cq_, this + ); + std::cout << "[SERVER] : {create note} : start listening" + << std::endl; + break; + } + case PROCESS: { + status_ = FINISH; + new CreateNoteServerCall(service_, cq_); + + std::cout << "[SERVER] : {create note} : get request, title=" + << request_.note_title() << std::endl; + + CreateNoteResponse response; + const auto created_note = + NoteDao::initialize_note_for_user(request_.user().login()); + response.mutable_note()->CopyFrom(created_note); + + responder_.Finish(response, grpc::Status::OK, this); + break; + } + case FINISH: { + std::cout << "[SERVER] : {create note} : deleting call" + << std::endl; + delete this; + break; + } + } +} + Update::AsyncService &UpdateService::get_service() { return service_; } \ No newline at end of file diff --git a/server/database/include/lr_dao.hpp b/server/database/include/lr_dao.hpp index 65a9049..81b7714 100644 --- a/server/database/include/lr_dao.hpp +++ b/server/database/include/lr_dao.hpp @@ -15,12 +15,19 @@ class LRDao { try_register_user(const std::string &login, const std::string &password); static bool validate_user(const std::string &login, const std::string &password); - static bool - add_project_to_user(const std::string &user_login, const std::string &project_code); - static bool - get_user_projects(const std::string &login, std::vector &projects); - static bool - delete_project_from_user(const std::string &login, const std::string &project_code); + static bool add_project_to_user( + const std::string &user_login, + const std::string &project_code + ); + static bool get_user_projects( + const std::string &login, + std::vector &projects + ); + static bool delete_project_from_user( + const std::string &login, + const std::string &project_code + ); + private: static std::string hash_password(const std::string &password); }; diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 0c9fd76..dd9e3db 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -68,7 +68,6 @@ int LRDao::try_register_user( const std::string check_query = "SELECT * FROM users WHERE login = $1"; - const pqxx::result check_result = transaction.exec_params(check_query, login); @@ -125,8 +124,7 @@ bool LRDao::add_project_to_user( params.append(project_code); params.append(user_login); - const pqxx::result result = - transaction.exec_params(query, params); + const pqxx::result result = transaction.exec_params(query, params); transaction.commit(); return result.affected_rows() > 0; } diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index 5405c39..c82e353 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -48,7 +48,8 @@ Note NoteDao::initialize_note_for_user(const std::string &login) { "VALUES ($1, $2) " "RETURNING id, title, content"; - const pqxx::result result = transaction.exec_params(query, "Пустая заметка",""); + const pqxx::result result = + transaction.exec_params(query, "Пустая заметка", ""); if (result.empty()) { return {}; @@ -87,14 +88,10 @@ bool NoteDao::update_note(const Note ¬e) { params.append(tags_array); params.append(note.id()); - const pqxx::result result = transaction.exec_params(query, - note.title(), - note.text(), - members_array, - note.date(), - tags_array, - note.id() - ); + const pqxx::result result = transaction.exec_params( + query, note.title(), note.text(), members_array, note.date(), + tags_array, note.id() + ); transaction.commit(); return result.affected_rows() > 0; } From 514579c5c1aacdfa039d257583af06525ac96fb8 Mon Sep 17 00:00:00 2001 From: toximu Date: Wed, 4 Jun 2025 16:39:45 +0300 Subject: [PATCH 38/47] fixed --- CMakeLists.txt | 4 ++-- server/Service/src/server_implementation.cpp | 6 +++--- server/main.cpp | 3 ++- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index eadccae..af79c09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,8 @@ cmake_minimum_required(VERSION 3.16) project(test-server) -add_compile_options(-fsanitize=address,undefined,leak) -add_link_options(-fsanitize=address,undefined,leak) +#add_compile_options(-fsanitize=address,undefined,leak) +#add_link_options(-fsanitize=address,undefined,leak) add_subdirectory(server) add_subdirectory(proto) diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index 4d01159..41e099a 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -7,9 +7,9 @@ ServerImplementation::ServerImplementation( ) : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()) { - builder.AddListeningPort( - "localhost:" + std::to_string(port), grpc::InsecureServerCredentials() - ); + std::string server_address = "127.0.0.1:" + std::to_string(port); + + builder.AddListeningPort(server_address, grpc::InsecureServerCredentials()); builder.RegisterService(&update_service_.get_service()); builder.RegisterService(&auth_service_.get_service()); diff --git a/server/main.cpp b/server/main.cpp index 2ab320d..b287d07 100644 --- a/server/main.cpp +++ b/server/main.cpp @@ -1,8 +1,9 @@ #include "server_implementation.h" #include +#include int main() { - absl::lts_20240116::UTCTimeZone(); + std::cout << "gRPC version : " << GRPC_CPP_VERSION_STRING << std::endl; grpc::ServerBuilder builder; ServerImplementation server(50051, builder); From 3222f4350e96fbfa503f133c2291701c030addf8 Mon Sep 17 00:00:00 2001 From: toximu Date: Wed, 4 Jun 2025 18:14:24 +0300 Subject: [PATCH 39/47] fix after merge --- .gitignore | 1 + client/client/CMakeLists.txt | 4 +- client/client/include/client_implementation.h | 19 ++---- client/client/src/client_implementation.cpp | 15 ++--- client/client/src/update_requests.cpp | 25 +++++--- client/client/test_server.cpp | 58 +++++++++++++++++++ .../ui/note-widget/src/note_edit_dialog.cpp | 3 +- server/Service/src/server_implementation.cpp | 16 +++-- server/Service/src/update_service.cpp | 42 +------------- server/database/src/lr_dao.cpp | 4 +- 10 files changed, 102 insertions(+), 85 deletions(-) create mode 100644 client/client/test_server.cpp diff --git a/.gitignore b/.gitignore index f1a0782..ef7f65e 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ doc/api/ local.properties **/main.cpp !client/main.cpp +!client/client/test_server.cpp !server/main.cpp *pb* diff --git a/client/client/CMakeLists.txt b/client/client/CMakeLists.txt index ab52d82..fbd0623 100644 --- a/client/client/CMakeLists.txt +++ b/client/client/CMakeLists.txt @@ -30,5 +30,5 @@ target_include_directories(Client ${CMAKE_CURRENT_BINARY_DIR} ) -#add_executable(test-server main.cpp) -#target_link_libraries(test-server Client) \ No newline at end of file +add_executable(test-server test_server.cpp) +target_link_libraries(test-server Client) \ No newline at end of file diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index 0b9f101..d9ab8c6 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -11,20 +11,15 @@ using grpc::Channel; using grpc::CompletionQueue; class ClientImplementation { - static CompletionQueue cq_; - static std::shared_ptr channel_; - static UpdateRequests update_requests_; - static AuthRequests auth_requests_; - + CompletionQueue cq_; + std::shared_ptr channel_; + UpdateRequests update_requests_; + AuthRequests auth_requests_; public: explicit ClientImplementation(const std::shared_ptr &channel); std::thread complete_rpc_thread_; - -public: - static ClientImplementation &get_instance(); - void CompleteRpc(); std::shared_ptr get_channel(); CompletionQueue *get_cq(); @@ -47,11 +42,7 @@ class ClientImplementation { const std::string &code, const User &user ); - bool try_leave_project( - const std::string &code, - const User &user - ); - + bool try_leave_project(const std::string &code, const User &user); }; #endif // CLIENTIMPLEMENTATION_H \ No newline at end of file diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index 9ebfbd5..ee81652 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -21,7 +21,6 @@ ClientImplementation::ClientImplementation( auth_requests_(channel, &cq_) { complete_rpc_thread_ = std::thread(&ClientImplementation::CompleteRpc, this); - } void ClientImplementation::CompleteRpc() { @@ -62,15 +61,15 @@ bool ClientImplementation::try_register_user(User *user) { return auth_requests_.try_register_user(user); } -bool ClientImplementation::try_update_note(Note *note) { +bool ClientImplementation::try_update_note(Note *note) const { return update_requests_.try_update_note(note); } -bool ClientImplementation::try_create_note(Note *note) { +bool ClientImplementation::try_create_note(Note *note) const { return update_requests_.try_create_note(note); } -bool ClientImplementation::try_fetch_note(Note *note) { +bool ClientImplementation::try_fetch_note(Note *note) const { return update_requests_.try_fetch_note(note); } @@ -89,7 +88,11 @@ bool ClientImplementation::get_project( return update_requests_.get_project(*project, code); } -bool ClientImplementation::try_join_project(Project *project, const std::string &code,const User &user) { +bool ClientImplementation::try_join_project( + Project *project, + const std::string &code, + const User &user +) { return update_requests_.try_join_project(*project, code, user); } @@ -99,5 +102,3 @@ bool ClientImplementation::try_leave_project( ) { return update_requests_.try_leave_project(code, user); } - - diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 687a36e..29f5735 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -21,9 +21,8 @@ using Efficio_proto::TryJoinProjectRequest; using Efficio_proto::TryJoinProjectResponse; using Efficio_proto::TryLeaveProjectRequest; using Efficio_proto::TryLeaveProjectResponse; -using Efficio_proto::User; using Efficio_proto::Update; - +using Efficio_proto::User; bool UpdateRequests::get_project(Project &project, const std::string &code) { GetProjectRequest request; @@ -57,11 +56,11 @@ bool UpdateRequests::get_project(Project &project, const std::string &code) { bool UpdateRequests::create_project( Project &project, const std::string &project_title, - const User& user + const User &user ) { CreateProjectRequest request; request.set_project_title(project_title); - User* copy_user = new User(user); + User *copy_user = new User(user); request.set_allocated_user(copy_user); CreateProjectResponse response; @@ -86,11 +85,14 @@ bool UpdateRequests::create_project( return false; } -bool UpdateRequests::try_leave_project(const std::string &code, const User &user) { +bool UpdateRequests::try_leave_project( + const std::string &code, + const User &user +) { TryLeaveProjectRequest request; request.set_code(code); - User* copy_user = new User(user); + User *copy_user = new User(user); request.set_allocated_user(copy_user); TryLeaveProjectResponse response; @@ -115,7 +117,7 @@ bool UpdateRequests::try_join_project( ) { TryJoinProjectRequest request; request.set_code(code); - User* copy_user = new User(user); + User *copy_user = new User(user); request.set_allocated_user(copy_user); TryJoinProjectResponse response; ClientContext context; @@ -188,6 +190,10 @@ GetNoteResponse UpdateRequests::GetNoteClientCall::get_reply() { return reply_; } +CreateNoteResponse UpdateRequests::CreateNoteClientCall::get_reply() { + return reply_; +} + UpdateRequests::CreateNoteClientCall::CreateNoteClientCall( const CreateNoteRequest &request, CompletionQueue *cq, @@ -207,7 +213,6 @@ void UpdateRequests::CreateNoteClientCall::Proceed(const bool ok) { std::cout << "[CLIENT WARNING]: RPC failed\n"; } delete this; - } bool UpdateRequests::try_update_note(Note *note) const { @@ -259,7 +264,7 @@ bool UpdateRequests::try_fetch_note(Note *note) const { bool UpdateRequests::try_create_note(Note *note) const { const CreateNoteRequest request; - // const auto call = new CreateNoteClientCall(request, cq_, stub_); + const auto call = new CreateNoteClientCall(request, cq_, stub_); void *tag; bool ok = false; @@ -267,4 +272,6 @@ bool UpdateRequests::try_create_note(Note *note) const { std::cout << "[CLIENT]: Completion queue failed\n"; return false; } + *note = call->get_reply().note(); + return true; } diff --git a/client/client/test_server.cpp b/client/client/test_server.cpp new file mode 100644 index 0000000..b81c288 --- /dev/null +++ b/client/client/test_server.cpp @@ -0,0 +1,58 @@ +#include +#include "client_implementation.h" +#include "update_requests.h" + +using Efficio_proto::Project; +using grpc::Channel; + +int main() { + ClientImplementation client(grpc::CreateChannel( + "localhost:50051", grpc::InsecureChannelCredentials() + )); + + User user; + user.set_login("toximu"); + user.set_hashed_password("12345678"); + + // client.try_register_user(&user); ok + + Project *project = new Project; + + // client.create_project(project, "ttt", user); ok + + // std::cout << project->DebugString() << std::endl; + + // client.get_project(project, "JUSIBT"); + + // std::cout << project->DebugString() << std::endl; + + Note *note = new Note; + + // client.try_create_note(note); ok, but note should be added to project + // aswell + + note->set_id(1); + note->set_title("neww"); + note->set_text("some texttt"); + // client.try_fetch_note(note); // not ok, oshibka + // std::cout << "Note id : " << note->id() << ", title : " << note->title() + // << std::endl; + + // client.try_update_note(note); // ok + + std::cout << note->DebugString() << std::endl; + + User user2; + user2.set_login("usr"); + user2.set_hashed_password("12345678"); + + Project *project_to_join = new Project; + + // client.try_join_project(project_to_join, "ML05B3", user2); // ok + + // std::cout << project_to_join->DebugString() << std::endl; + + // client.try_leave_project("ML05B3", user2); // ok + + client.complete_rpc_thread_.join(); +} diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index ed4e53c..a3abe3c 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -247,5 +247,6 @@ bool NoteEditDialog::try_save_note() const { new_tag->set_color(color_code_to_note_tag_colors(tag.color)); } - return ClientImplementation::get_instance().try_update_note(note_); + // return ClientImplementation::get_instance().try_update_note(note_); Ilya + return true; } \ No newline at end of file diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index 9a3b0f0..fc86b36 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -4,21 +4,19 @@ ServerImplementation::ServerImplementation( const uint16_t port, grpc::ServerBuilder &builder -) : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()) { - +) + : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()) { builder.AddListeningPort( - "localhost:" + std::to_string(port), grpc::InsecureServerCredentials() + "127.0.0.1:" + std::to_string(port), grpc::InsecureServerCredentials() ); builder.RegisterService(&update_service_.get_service()); - builder.RegisterService(&auth_service_.get_service()); - - + // builder.RegisterService(&auth_service_.get_service()); server_ = builder.BuildAndStart(); - - update_service_.run(); - auth_service_.run(); + + update_service_.run(); + // auth_service_.run(); Ilya } void ServerImplementation::HandleRPCs() const { diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index b666b42..99f2838 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -100,46 +100,6 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { } } -UpdateService::CreateNoteServerCall::CreateNoteServerCall( - UpdateService &service, - ServerCompletionQueue *cq -) - : CommonServerCall(cq), responder_(&ctx_), service_(service) { - service_.service_.RequestCreateNote( - &ctx_, &request_, &responder_, cq_, cq_, this - ); - status_ = PROCESS; -} - -void UpdateService::CreateNoteServerCall::Proceed(const bool ok) { - if (!ok) { - delete this; - return; - } - - switch (status_) { - case PROCESS: { - new CreateNoteServerCall(service_, cq_); - CreateNoteResponse response; - - const auto new_note = - NoteDao::initialize_note_for_user(request_.user().login()); - response.mutable_note()->CopyFrom(new_note); - - std::cout << "[SERVER]: CREATE NOTE REQUEST id=" << new_note.id() - << std::endl; - - responder_.Finish(response, grpc::Status::OK, this); - status_ = FINISH; - break; - } - case FINISH: { - delete this; - break; - } - } -} - UpdateService::GetProjectServerCall::GetProjectServerCall( Update::AsyncService *service, ServerCompletionQueue *cq @@ -203,7 +163,7 @@ void UpdateService::CreateProjectServerCall::Proceed(const bool ok) { } case PROCESS: { status_ = FINISH; - new GetProjectServerCall(service_, cq_); + new CreateProjectServerCall(service_, cq_); std::cout << "[SERVER] : {create project} : get request, title=" << request_.project_title() << std::endl; diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 874ab7e..22cf7c1 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -174,8 +174,8 @@ bool LRDao::delete_project_from_user( auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = - "UPDATE users SET projects = array_remove(projects, $1) WHERE " - "login = $2" + "UPDATE users SET projects = array_remove(projects, $1) " + "WHERE login = $2 " "RETURNING 1"; const pqxx::result result = From 58aaadc07b87a14fd6eb2a7cbcb754f0f4e30245 Mon Sep 17 00:00:00 2001 From: toximu Date: Wed, 4 Jun 2025 18:54:35 +0300 Subject: [PATCH 40/47] return old windows --- client/main.cpp | 32 ++++++++++++------- .../include/login_window.h | 8 +++-- .../include/registration_window.h | 7 +++- .../src/login_window.cpp | 13 ++++---- .../src/registration_window.cpp | 15 +++++---- .../ui/note-widget/include/note_edit_dialog.h | 11 +++++-- .../ui/note-widget/src/note_edit_dialog.cpp | 14 +++++--- 7 files changed, 65 insertions(+), 35 deletions(-) diff --git a/client/main.cpp b/client/main.cpp index 6e2b6c6..693babd 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -6,7 +6,8 @@ #include #include "applicationwindow.h" #include "client_implementation.h" -#include "login_window.h" +// #include "login_window.h" +#include "mainwindow.h" int main(int argc, char *argv[]) { QApplication application(argc, argv); @@ -21,7 +22,9 @@ int main(int argc, char *argv[]) { } } - auto client = ClientImplementation::get_instance(); + ClientImplementation client(grpc::CreateChannel( + "localhost:50051", grpc::InsecureChannelCredentials() + )); std::thread requests([&] { try { @@ -31,17 +34,24 @@ int main(int argc, char *argv[]) { } }); requests.detach(); - auto *app_window = new Ui::ApplicationWindow("EFFICIO"); - auto *login_window = new LoginWindow(app_window); + Storage *storage = new Storage(); + User usr; + usr.set_login("toximu"); + usr.set_hashed_password("12345678"); + client.try_authenticate_user(&usr); + Ui::MainWindow window(app_window, usr.login(), usr.mutable_storage()); + window.show(); - app_window->setCentralWidget(login_window); - const QRect screen_geometry = - QApplication::primaryScreen()->availableGeometry(); - const int x = (screen_geometry.width() - login_window->width()) / 2; - const int y = (screen_geometry.height() - login_window->height()) / 2; - app_window->move(x, y); - app_window->show(); + // auto *login_window = new LoginWindow(app_window); + // + // app_window->setCentralWidget(login_window); + // const QRect screen_geometry = + // QApplication::primaryScreen()->availableGeometry(); + // const int x = (screen_geometry.width() - login_window->width()) / 2; + // const int y = (screen_geometry.height() - login_window->height()) / 2; + // app_window->move(x, y); + // app_window->show(); return QApplication::exec(); } \ No newline at end of file diff --git a/client/ui/authorization-windows/include/login_window.h b/client/ui/authorization-windows/include/login_window.h index f623c18..1d72a04 100644 --- a/client/ui/authorization-windows/include/login_window.h +++ b/client/ui/authorization-windows/include/login_window.h @@ -20,11 +20,14 @@ class LoginWindow final : public QWidget { Q_OBJECT public: - explicit LoginWindow(QWidget *parent = nullptr); + explicit LoginWindow( + ClientImplementation *client, + QWidget *parent = nullptr + ); ~LoginWindow() override; static const std::vector THEMES; - void handle_theme_changed(const int theme); + void handle_theme_changed(int theme); private slots: void on_switch_mode_clicked(); @@ -34,4 +37,5 @@ private slots: private: Ui::LoginWindow *ui; int counter_on_switch_theme_clicks = 0; + ClientImplementation *client_; }; \ No newline at end of file diff --git a/client/ui/authorization-windows/include/registration_window.h b/client/ui/authorization-windows/include/registration_window.h index 2c41255..1929bbe 100644 --- a/client/ui/authorization-windows/include/registration_window.h +++ b/client/ui/authorization-windows/include/registration_window.h @@ -2,6 +2,7 @@ #include #include +#include "client_implementation.h" #include "database_manager.hpp" QT_BEGIN_NAMESPACE @@ -18,7 +19,10 @@ class RegistrationWindow final : public QWidget { public: static const std::vector THEMES; - explicit RegistrationWindow(QWidget *parent = nullptr); + explicit RegistrationWindow( + ClientImplementation *client, + QWidget *parent = nullptr + ); ~RegistrationWindow() override; bool is_strong_and_valid_password(const QString &password); void handle_theme_changed(int theme); @@ -31,4 +35,5 @@ private slots: private: Ui::RegistrationWindow *ui; int counter_on_switch_theme_clicks = 0; + ClientImplementation *client_; }; \ No newline at end of file diff --git a/client/ui/authorization-windows/src/login_window.cpp b/client/ui/authorization-windows/src/login_window.cpp index 70b328b..59b8c61 100644 --- a/client/ui/authorization-windows/src/login_window.cpp +++ b/client/ui/authorization-windows/src/login_window.cpp @@ -18,8 +18,8 @@ const std::vector LoginWindow::THEMES = { Ui::login_window_blue_theme }; -LoginWindow::LoginWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::LoginWindow) { +LoginWindow::LoginWindow(ClientImplementation *client, QWidget *parent) + : QWidget(parent), ui(new Ui::LoginWindow), client_(client) { ui->setupUi(this); setFixedSize(380, 480); @@ -48,7 +48,7 @@ LoginWindow::LoginWindow(QWidget *parent) ); } -void LoginWindow::handle_theme_changed(const int theme) { +void LoginWindow::handle_theme_changed(int theme) { this->setStyleSheet(THEMES[theme]); } @@ -73,7 +73,7 @@ void LoginWindow::on_switch_mode_clicked() { old->deleteLater(); } Storage storage; - auto *registration_window = new RegistrationWindow(app_window); + auto *registration_window = new RegistrationWindow(client_, app_window); app_window->setCentralWidget(registration_window); QRect screenGeometry = QApplication::primaryScreen()->availableGeometry(); @@ -104,8 +104,7 @@ void LoginWindow::on_push_enter_clicked() { this, "Ошибка", "Длина пароля не должна превышать пятидесяти символов" ); - } else if (ClientImplementation::get_instance() - .try_authenticate_user(user)) { + } else if (client_->try_authenticate_user(user)) { QMessageBox::information( this, "Вход", "Вы успешно вошли! Добро пожаловать :)" ); @@ -130,4 +129,4 @@ void LoginWindow::on_push_enter_clicked() { ); } } -} +} \ No newline at end of file diff --git a/client/ui/authorization-windows/src/registration_window.cpp b/client/ui/authorization-windows/src/registration_window.cpp index de05e3e..3aaabdd 100644 --- a/client/ui/authorization-windows/src/registration_window.cpp +++ b/client/ui/authorization-windows/src/registration_window.cpp @@ -19,8 +19,11 @@ const std::vector RegistrationWindow::THEMES = { Ui::registration_window_blue_theme }; -RegistrationWindow::RegistrationWindow(QWidget *parent) - : QWidget(parent), ui(new Ui::RegistrationWindow) { +RegistrationWindow::RegistrationWindow( + ClientImplementation *client, + QWidget *parent +) + : QWidget(parent), ui(new Ui::RegistrationWindow), client_(client) { ui->setupUi(this); setFixedSize(380, 480); @@ -78,7 +81,7 @@ void RegistrationWindow::on_switch_mode_clicked() { old->deleteLater(); } Storage storage; - auto *login_window = new LoginWindow(app_window); + auto *login_window = new LoginWindow(client_, app_window); app_window->setCentralWidget(login_window); const QRect screenGeometry = @@ -161,9 +164,7 @@ void RegistrationWindow::on_push_registration_clicked() { user->set_login(created_login.toStdString()); user->set_hashed_password(created_password.toStdString()); - const int try_register_user = - ClientImplementation::get_instance().try_register_user(user - ); + const int try_register_user = client_->try_register_user(user); if (try_register_user == 0) { QMessageBox::warning( this, "Ошибка", @@ -191,4 +192,4 @@ void RegistrationWindow::on_push_registration_clicked() { ); } } -} +} \ No newline at end of file diff --git a/client/ui/note-widget/include/note_edit_dialog.h b/client/ui/note-widget/include/note_edit_dialog.h index 76dc91f..d2c6fa2 100644 --- a/client/ui/note-widget/include/note_edit_dialog.h +++ b/client/ui/note-widget/include/note_edit_dialog.h @@ -22,11 +22,15 @@ class NoteEditDialog final : public QDialog { Q_OBJECT public: - explicit NoteEditDialog(QWidget *parent = nullptr, Note *note = nullptr); + explicit NoteEditDialog( + ClientImplementation *client, + QWidget *parent = nullptr, + Note *note = nullptr + ); ~NoteEditDialog() override; -private slots: - void on_save_button_click(); + private slots: + void on_save_button_click(); void on_join_button_click(); void on_add_members_button_click(); void on_add_tags_button_click(); @@ -50,6 +54,7 @@ private slots: std::vector> tag_labels_; QList selected_tags_; Note *note_; + ClientImplementation *client_; }; #endif // NOTE_EDIT_DIALOG_H \ No newline at end of file diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index a3abe3c..1f28e85 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -21,8 +21,15 @@ using grpc::Channel; using grpc::ClientContext; using grpc::Status; -NoteEditDialog::NoteEditDialog(QWidget *parent, Note *note) - : QDialog(parent), ui_(new Ui::NoteEditDialog), note_(note) { +NoteEditDialog::NoteEditDialog( + ClientImplementation *client, + QWidget *parent, + Note *note +) + : QDialog(parent), + client_(client), + ui_(new Ui::NoteEditDialog), + note_(note) { if (note_ == nullptr) { std::cerr << "Not a valid note!\n"; } @@ -247,6 +254,5 @@ bool NoteEditDialog::try_save_note() const { new_tag->set_color(color_code_to_note_tag_colors(tag.color)); } - // return ClientImplementation::get_instance().try_update_note(note_); Ilya - return true; + return client_->try_update_note(note_); } \ No newline at end of file From 1082266e222d8b62f29a6151e5d3e7cbe1000036 Mon Sep 17 00:00:00 2001 From: toximu Date: Wed, 4 Jun 2025 20:23:10 +0300 Subject: [PATCH 41/47] sigseg --- client/client/src/auth_requests.cpp | 2 ++ client/client/src/client_implementation.cpp | 6 +++--- client/main.cpp | 11 ++++++----- client/ui/main-window/include/mainwindow.h | 10 +++++++--- client/ui/main-window/include/notelist.h | 5 ++++- client/ui/main-window/include/notewidget.h | 7 +++++-- client/ui/main-window/src/mainwindow.cpp | 10 ++++++++-- client/ui/main-window/src/notelist.cpp | 5 +++-- client/ui/main-window/src/notewidget.cpp | 12 ++++++++---- server/Service/include/auth_service.h | 6 ++++-- server/Service/src/auth_service.cpp | 10 ++++++++-- server/Service/src/server_implementation.cpp | 7 ++++--- 12 files changed, 62 insertions(+), 29 deletions(-) diff --git a/client/client/src/auth_requests.cpp b/client/client/src/auth_requests.cpp index 04021d1..1f90b1d 100644 --- a/client/client/src/auth_requests.cpp +++ b/client/client/src/auth_requests.cpp @@ -88,6 +88,8 @@ bool AuthRequests::try_authenticate_user(User *user) const { return false; } + + user->CopyFrom(call->get_reply().user()); std::cout << "[CLIENT]: AUTHENTICATED USER - " << user->login() << "\n"; return true; diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index ee81652..b7124b1 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -28,16 +28,16 @@ void ClientImplementation::CompleteRpc() { bool ok = false; while (cq_.Next(&got_tag, &ok)) { - std::cout << "got smth" << std::endl; + std::cout << "[CLIENT] : GET CALL FROM CQ" << std::endl; CommonClientCall *call = static_cast(got_tag); assert(ok); if (call->status.ok()) { - std::cout << "start procceed" << std::endl; + std::cout << "[CLIENT] : START PROCEED" << std::endl; call->Proceed(); } else { - std::cout << "RPC failed" << std::endl; + std::cout << "[CLIENT] : RPC FAILED" << std::endl; } delete call; diff --git a/client/main.cpp b/client/main.cpp index 693babd..60186d1 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -36,11 +36,12 @@ int main(int argc, char *argv[]) { requests.detach(); auto *app_window = new Ui::ApplicationWindow("EFFICIO"); Storage *storage = new Storage(); - User usr; - usr.set_login("toximu"); - usr.set_hashed_password("12345678"); - client.try_authenticate_user(&usr); - Ui::MainWindow window(app_window, usr.login(), usr.mutable_storage()); + User *usr = new User(); + usr->set_login("toximu"); + usr->set_hashed_password("12345678"); + // client.try_authenticate_user(usr); + *usr->mutable_storage()->add_projects()->mutable_code() = "ML05B3"; + Ui::MainWindow window(app_window, usr.login(), usr.mutable_storage(), &client); window.show(); // auto *login_window = new LoginWindow(app_window); diff --git a/client/ui/main-window/include/mainwindow.h b/client/ui/main-window/include/mainwindow.h index 0b38459..a47bc0b 100644 --- a/client/ui/main-window/include/mainwindow.h +++ b/client/ui/main-window/include/mainwindow.h @@ -10,6 +10,7 @@ #include #include #include "bottombar.h" +#include "client_implementation.h" #include "notelist.h" #include "projectlist.h" @@ -18,6 +19,7 @@ using namespace Efficio_proto; namespace Ui { class MainWindow : public QWidget { Q_OBJECT + ClientImplementation *client_; std::string username; QVBoxLayout *main_layout_; BottomBar *top_bar_; @@ -29,6 +31,7 @@ class MainWindow : public QWidget { QPushButton *new_note_button_; Storage *storage_; + friend ProjectList; private slots: void create_project(); @@ -36,9 +39,10 @@ private slots: public: explicit MainWindow( - QWidget *parent = nullptr, - std::string username = "none", - Storage *storage = nullptr + QWidget *parent, + std::string username, + Storage *storage, + ClientImplementation *client ); }; } // namespace Ui diff --git a/client/ui/main-window/include/notelist.h b/client/ui/main-window/include/notelist.h index c833b17..3878ce3 100644 --- a/client/ui/main-window/include/notelist.h +++ b/client/ui/main-window/include/notelist.h @@ -5,6 +5,7 @@ #include #include #include +#include "client_implementation.h" using namespace Efficio_proto; @@ -17,12 +18,14 @@ class NoteList : public QWidget { std::vector vertical_layouts_; + ClientImplementation *client_; + int note_counter_ = 0; public: void add_note_widget(const Note *note); void clear_note_list(); - NoteList(QWidget *parent); + NoteList(QWidget *parent, ClientImplementation *client); public slots: void load_project_notes(QListWidgetItem *project); diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h index 323d005..90a5d1a 100644 --- a/client/ui/main-window/include/notewidget.h +++ b/client/ui/main-window/include/notewidget.h @@ -6,6 +6,7 @@ #include #include #include +#include "client_implementation.h" using namespace Efficio_proto; @@ -17,11 +18,13 @@ class NoteWidget : public QWidget { QPushButton *open_button_; QLabel *title_label_; QLabel *text_label_; + ClientImplementation *client_; public: explicit NoteWidget( - QWidget *parent = nullptr, - const Note *model_note = nullptr + QWidget *parent, + const Note *model_note, + ClientImplementation *client ); private slots: diff --git a/client/ui/main-window/src/mainwindow.cpp b/client/ui/main-window/src/mainwindow.cpp index 6d0c4ec..46f621a 100644 --- a/client/ui/main-window/src/mainwindow.cpp +++ b/client/ui/main-window/src/mainwindow.cpp @@ -19,14 +19,20 @@ using namespace Efficio_proto; namespace Ui { -MainWindow::MainWindow(QWidget *parent, std::string username, Storage *storage) +MainWindow::MainWindow( + QWidget *parent, + std::string username, + Storage *storage, + ClientImplementation *client +) : QWidget(parent), + client_(client), username(username), main_layout_(new QVBoxLayout(this)), top_bar_(new BottomBar(this, username, "EFFICIO :: Таск-Трекер")), content_layout_(new QHBoxLayout(this)), project_list_(new ProjectList(this)), - note_list_(new NoteList(this)), + note_list_(new NoteList(this, client)), content_widget_(new QWidget(this)), new_project_button_(new QPushButton("Новый проект", this)), new_note_button_(new QPushButton("Новая заметка", this)), diff --git a/client/ui/main-window/src/notelist.cpp b/client/ui/main-window/src/notelist.cpp index 2f2aefb..dcdce98 100644 --- a/client/ui/main-window/src/notelist.cpp +++ b/client/ui/main-window/src/notelist.cpp @@ -8,8 +8,9 @@ #include "projectitem.h" namespace Ui { -NoteList::NoteList(QWidget *parent) +NoteList::NoteList(QWidget *parent, ClientImplementation *client) : QWidget(parent), + client_(client), main_layout_(new QHBoxLayout(this)), vertical_layouts_(std::vector()) { this->setAttribute(Qt::WA_StyledBackground); @@ -31,7 +32,7 @@ void NoteList::add_note_widget(const Note *note) { ); } vertical_layouts_[note_counter_ % 4]->addWidget( - new NoteWidget(this, note), 0, Qt::AlignTop + new NoteWidget(this, note, client_), 0, Qt::AlignTop ); current_layout->addStretch(); note_counter_++; diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp index 7768119..8caaec4 100644 --- a/client/ui/main-window/src/notewidget.cpp +++ b/client/ui/main-window/src/notewidget.cpp @@ -5,11 +5,16 @@ #include "note_edit_dialog.h" namespace Ui { -NoteWidget::NoteWidget(QWidget *parent, const Note *model_note) +NoteWidget::NoteWidget( + QWidget *parent, + const Note *model_note, + ClientImplementation *client +) : QWidget(parent), model_note_(model_note), main_layout_(new QVBoxLayout(this)), - open_button_(new QPushButton("Открыть")) { + open_button_(new QPushButton("Открыть")), + client_(client) { this->setObjectName("NoteWidget"); this->setMinimumWidth(100); this->setFixedHeight(100); @@ -37,8 +42,7 @@ NoteWidget::NoteWidget(QWidget *parent, const Note *model_note) void NoteWidget::open_note_window() const { auto dialog = new ::NoteEditDialog( - - const_cast(qobject_cast(this)), + client_, const_cast(qobject_cast(this)), const_cast(model_note_) ); dialog->setAttribute(Qt::WA_DeleteOnClose); diff --git a/server/Service/include/auth_service.h b/server/Service/include/auth_service.h index c04f014..5f79f0a 100644 --- a/server/Service/include/auth_service.h +++ b/server/Service/include/auth_service.h @@ -17,7 +17,7 @@ using Efficio_proto::AuthResponse; class AuthService final { Auth::AsyncService service_; ServerContext ctx_; - std::unique_ptr cq_; + ServerCompletionQueue* cq_; std::unique_ptr server_; class AuthServerOperation : public CommonServerCall { @@ -57,9 +57,11 @@ class AuthService final { void Proceed(bool) override; }; + explicit AuthService(ServerCompletionQueue* cq); + Auth::AsyncService &get_service(); void run(); }; -#endif // AUTH_SERVICE_H +#endif // AUTH_SERVICE_H \ No newline at end of file diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp index d76c704..a708ff1 100644 --- a/server/Service/src/auth_service.cpp +++ b/server/Service/src/auth_service.cpp @@ -1,6 +1,10 @@ #include "auth_service.h" #include "lr_dao.hpp" +AuthService::AuthService(ServerCompletionQueue* cq) : cq_(cq) { + +} + AuthService::TryAuthenticateUserServerCall::TryAuthenticateUserServerCall( Auth::AsyncService *service, ServerCompletionQueue *cq @@ -30,8 +34,10 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { ); if (query_exit_code == 1) { + std::cout << "[SERVER]: GOOD AUTH REQUEST" << std::endl; response.mutable_user()->CopyFrom(request_.user()); } else { + std::cout << "[SERVER]: BAD AUTH REQUEST" << std::endl; response.set_error_text( "[SERVER ERROR]: Не удалось выполнить запрос в базу данных " "на проверку " @@ -104,6 +110,6 @@ Auth::AsyncService &AuthService::get_service() { } void AuthService::run() { - new TryAuthenticateUserServerCall(&service_, cq_.get()); - new TryRegisterUserServerCall(&service_, cq_.get()); + new TryAuthenticateUserServerCall(&service_, cq_); + new TryRegisterUserServerCall(&service_, cq_); } \ No newline at end of file diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index fc86b36..dd2c788 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -5,18 +5,19 @@ ServerImplementation::ServerImplementation( const uint16_t port, grpc::ServerBuilder &builder ) - : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()) { + : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()), + auth_service_(cq_.get()) { builder.AddListeningPort( "127.0.0.1:" + std::to_string(port), grpc::InsecureServerCredentials() ); builder.RegisterService(&update_service_.get_service()); - // builder.RegisterService(&auth_service_.get_service()); + builder.RegisterService(&auth_service_.get_service()); server_ = builder.BuildAndStart(); update_service_.run(); - // auth_service_.run(); Ilya + auth_service_.run(); } void ServerImplementation::HandleRPCs() const { From 59b0752e1157b13ae0c63589561c1a9f36c73061 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Wed, 4 Jun 2025 21:50:47 +0300 Subject: [PATCH 42/47] feat: fill user projects storage after authorization --- server/Service/include/auth_service.h | 6 +-- server/Service/src/auth_service.cpp | 12 +++-- server/database/include/project_dao.hpp | 1 + server/database/src/lr_dao.cpp | 11 +++-- server/database/src/project_dao.cpp | 65 +++++++++++++++++++++++++ 5 files changed, 86 insertions(+), 9 deletions(-) diff --git a/server/Service/include/auth_service.h b/server/Service/include/auth_service.h index 5f79f0a..c38c08a 100644 --- a/server/Service/include/auth_service.h +++ b/server/Service/include/auth_service.h @@ -57,11 +57,11 @@ class AuthService final { void Proceed(bool) override; }; - explicit AuthService(ServerCompletionQueue* cq); - Auth::AsyncService &get_service(); + explicit AuthService(ServerCompletionQueue* cq); + void run(); }; -#endif // AUTH_SERVICE_H \ No newline at end of file +#endif // AUTH_SERVICE_H diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp index a708ff1..2af16b0 100644 --- a/server/Service/src/auth_service.cpp +++ b/server/Service/src/auth_service.cpp @@ -1,8 +1,10 @@ #include "auth_service.h" #include "lr_dao.hpp" +#include "project_dao.hpp" -AuthService::AuthService(ServerCompletionQueue* cq) : cq_(cq) { +using Efficio_proto::Storage; +AuthService::AuthService(ServerCompletionQueue *cq) : cq_(cq) { } AuthService::TryAuthenticateUserServerCall::TryAuthenticateUserServerCall( @@ -34,10 +36,14 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { ); if (query_exit_code == 1) { - std::cout << "[SERVER]: GOOD AUTH REQUEST" << std::endl; + Storage user_storage; + ProjectDAO::get_all_user_projects(request_.user().login(), user_storage); + response.mutable_user()->CopyFrom(request_.user()); + response.mutable_user()->mutable_storage()->CopyFrom(user_storage); + std::cout << "[SERVER]: WELCOME, " << response.mutable_user()->login() << "\n"; } else { - std::cout << "[SERVER]: BAD AUTH REQUEST" << std::endl; + std::cout << "[SERVER]: SQL QUERY ERROR\n"; response.set_error_text( "[SERVER ERROR]: Не удалось выполнить запрос в базу данных " "на проверку " diff --git a/server/database/include/project_dao.hpp b/server/database/include/project_dao.hpp index 41460c6..b4b52e0 100644 --- a/server/database/include/project_dao.hpp +++ b/server/database/include/project_dao.hpp @@ -38,6 +38,7 @@ class ProjectDAO { public: ProjectDAO() = default; + static bool get_all_user_projects(const std::string& login, Storage &storage); static bool get_project(const std::string &project_code, Project &project); static bool insert_project(Project &project); static bool add_member_to_project( diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 22cf7c1..79bc051 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -68,6 +68,7 @@ int LRDao::try_register_user( const std::string check_query = "SELECT * FROM users WHERE login = $1"; + const pqxx::result check_result = transaction.exec_params(check_query, login); @@ -93,12 +94,14 @@ bool LRDao::validate_user( ) { auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); + std::cout << "[SERVER]: AUTHENTICATING USER - " << login << "\n"; const std::string query = "SELECT password FROM users WHERE login = $1"; const pqxx::result result = transaction.exec_params(query, login); if (result.empty()) { + std::cout << "[SERVER]: IDI NAHUI" << "\n"; transaction.commit(); return false; } @@ -120,10 +123,12 @@ bool LRDao::add_project_to_user( const std::string query = "UPDATE users SET projects = array_append(projects, $1) " "WHERE login = $2"; + // pqxx::params params; + // params.append(project_code); + // params.append(user_login); const pqxx::result result = transaction.exec_params(query, project_code, user_login); - transaction.commit(); return result.affected_rows() > 0; } @@ -174,8 +179,8 @@ bool LRDao::delete_project_from_user( auto &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); const std::string query = - "UPDATE users SET projects = array_remove(projects, $1) " - "WHERE login = $2 " + "UPDATE users SET projects = array_remove(projects, $1) WHERE " + "login = $2" "RETURNING 1"; const pqxx::result result = diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index 39b993f..b4a4bbb 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -3,6 +3,71 @@ #include "database_manager.hpp" #include "note_dao.hpp" +bool ProjectDAO::get_all_user_projects(const std::string &login, Storage &storage) { + pqxx::connection &connection = + DatabaseManager::get_instance().get_connection(); + pqxx::work transaction(connection); + + const std::string projects_query = + "SELECT p.code, p.title, p.members " + "FROM projects p " + "JOIN users u ON p.code = ANY(u.projects) " + "WHERE u.login = " + transaction.quote(login); + + const pqxx::result projects_result = transaction.exec(projects_query); + + if (projects_result.empty()) { + transaction.commit(); + return false; + } + + for (const auto &project_row : projects_result) { + Project project; + project.set_code(project_row["code"].as()); + project.set_title(project_row["title"].as()); + + if (!project_row["members"].is_null()) { + auto members_array = project_row["members"].as_array(); + for (const auto &member : members_array) { + project.add_members(member.as()); + } + } + + const std::string notes_query = + "SELECT id, title, content, date, tags " + "FROM notes " + "WHERE project_code = " + transaction.quote(project.code()); + + const pqxx::result notes_result = transaction.exec(notes_query); + + for (const auto ¬e_row : notes_result) { + Note* note = project.add_notes(); + note->set_id(note_row["id"].as()); + note->set_title(note_row["title"].as()); + + if (!note_row["content"].is_null()) { + note->set_text(note_row["content"].as()); + } + + if (!note_row["date"].is_null()) { + note->set_date(note_row["date"].as()); + } + + if (!note_row["tags"].is_null()) { + auto tags_array = note_row["tags"].as_array(); + for (const auto &tag : tags_array) { + note->add_tags(tag.as()); + } + } + } + + storage.mutable_projects()->Add(std::move(project)); + } + + transaction.commit(); + return true; +} + bool ProjectDAO::get_project(const std::string &code, Project &project) { pqxx::connection &connection = DatabaseManager::get_instance().get_connection(); From ecc13a57512d41edff4c18ab0a48edf0e732c963 Mon Sep 17 00:00:00 2001 From: toximu Date: Thu, 5 Jun 2025 00:39:58 +0300 Subject: [PATCH 43/47] connect rpc methods to main window --- client/client/src/auth_requests.cpp | 2 -- client/main.cpp | 23 ++++++-------------- client/ui/main-window/include/mainwindow.h | 7 +++--- client/ui/main-window/include/notelist.h | 2 +- client/ui/main-window/include/notewidget.h | 4 ++-- client/ui/main-window/src/mainwindow.cpp | 25 +++++++++------------- client/ui/main-window/src/notelist.cpp | 7 +++--- client/ui/main-window/src/notewidget.cpp | 3 ++- client/ui/main-window/src/projectitem.cpp | 2 +- server/Handlers/src/update_handler.cpp | 1 + server/database/src/lr_dao.cpp | 8 ++++--- server/database/src/note_dao.cpp | 4 ++-- server/database/src/project_dao.cpp | 9 ++++---- 13 files changed, 42 insertions(+), 55 deletions(-) diff --git a/client/client/src/auth_requests.cpp b/client/client/src/auth_requests.cpp index 1f90b1d..04021d1 100644 --- a/client/client/src/auth_requests.cpp +++ b/client/client/src/auth_requests.cpp @@ -88,8 +88,6 @@ bool AuthRequests::try_authenticate_user(User *user) const { return false; } - - user->CopyFrom(call->get_reply().user()); std::cout << "[CLIENT]: AUTHENTICATED USER - " << user->login() << "\n"; return true; diff --git a/client/main.cpp b/client/main.cpp index 60186d1..e8cd71b 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -26,23 +26,14 @@ int main(int argc, char *argv[]) { "localhost:50051", grpc::InsecureChannelCredentials() )); - std::thread requests([&] { - try { - client.CompleteRpc(); - } catch (const std::exception &e) { - std::cout << "[CLIENT ERROR]: " << e.what() << std::endl; - } - }); - requests.detach(); auto *app_window = new Ui::ApplicationWindow("EFFICIO"); - Storage *storage = new Storage(); - User *usr = new User(); + std::unique_ptr usr = std::make_unique(); usr->set_login("toximu"); usr->set_hashed_password("12345678"); - // client.try_authenticate_user(usr); - *usr->mutable_storage()->add_projects()->mutable_code() = "ML05B3"; - Ui::MainWindow window(app_window, usr.login(), usr.mutable_storage(), &client); - window.show(); + client.try_authenticate_user(usr.get()); + Ui::MainWindow window(app_window, std::move(usr), &client); + app_window->resize(800, 600); + app_window->show(); // auto *login_window = new LoginWindow(app_window); // @@ -53,6 +44,6 @@ int main(int argc, char *argv[]) { // const int y = (screen_geometry.height() - login_window->height()) / 2; // app_window->move(x, y); // app_window->show(); - - return QApplication::exec(); + int exit_code = QApplication::exec(); + client.complete_rpc_thread_.join(); } \ No newline at end of file diff --git a/client/ui/main-window/include/mainwindow.h b/client/ui/main-window/include/mainwindow.h index a47bc0b..86075bf 100644 --- a/client/ui/main-window/include/mainwindow.h +++ b/client/ui/main-window/include/mainwindow.h @@ -20,7 +20,7 @@ namespace Ui { class MainWindow : public QWidget { Q_OBJECT ClientImplementation *client_; - std::string username; + std::unique_ptr user_; QVBoxLayout *main_layout_; BottomBar *top_bar_; QHBoxLayout *content_layout_; @@ -29,7 +29,7 @@ class MainWindow : public QWidget { QWidget *content_widget_; QPushButton *new_project_button_; QPushButton *new_note_button_; - Storage *storage_; + friend ProjectList; @@ -40,8 +40,7 @@ private slots: public: explicit MainWindow( QWidget *parent, - std::string username, - Storage *storage, + std::unique_ptr user, ClientImplementation *client ); }; diff --git a/client/ui/main-window/include/notelist.h b/client/ui/main-window/include/notelist.h index 3878ce3..399a342 100644 --- a/client/ui/main-window/include/notelist.h +++ b/client/ui/main-window/include/notelist.h @@ -23,7 +23,7 @@ class NoteList : public QWidget { int note_counter_ = 0; public: - void add_note_widget(const Note *note); + void add_note_widget(Note *note); void clear_note_list(); NoteList(QWidget *parent, ClientImplementation *client); diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h index 90a5d1a..18f1957 100644 --- a/client/ui/main-window/include/notewidget.h +++ b/client/ui/main-window/include/notewidget.h @@ -13,7 +13,7 @@ using namespace Efficio_proto; namespace Ui { class NoteWidget : public QWidget { Q_OBJECT - const Note *model_note_; + Note * const model_note_; QVBoxLayout *main_layout_; QPushButton *open_button_; QLabel *title_label_; @@ -23,7 +23,7 @@ class NoteWidget : public QWidget { public: explicit NoteWidget( QWidget *parent, - const Note *model_note, + Note *model_note, ClientImplementation *client ); diff --git a/client/ui/main-window/src/mainwindow.cpp b/client/ui/main-window/src/mainwindow.cpp index 46f621a..e3badac 100644 --- a/client/ui/main-window/src/mainwindow.cpp +++ b/client/ui/main-window/src/mainwindow.cpp @@ -21,22 +21,20 @@ using namespace Efficio_proto; namespace Ui { MainWindow::MainWindow( QWidget *parent, - std::string username, - Storage *storage, + std::unique_ptr user, ClientImplementation *client ) : QWidget(parent), client_(client), - username(username), + user_(std::move(user)), main_layout_(new QVBoxLayout(this)), - top_bar_(new BottomBar(this, username, "EFFICIO :: Таск-Трекер")), + top_bar_(new BottomBar(this, user_->login(), "EFFICIO :: Таск-Трекер")), content_layout_(new QHBoxLayout(this)), project_list_(new ProjectList(this)), note_list_(new NoteList(this, client)), content_widget_(new QWidget(this)), new_project_button_(new QPushButton("Новый проект", this)), - new_note_button_(new QPushButton("Новая заметка", this)), - storage_(storage) { + new_note_button_(new QPushButton("Новая заметка", this)) { this->setObjectName("main-window"); this->setAttribute(Qt::WA_StyledBackground); this->setMinimumSize(QSize(800, 600)); @@ -58,7 +56,7 @@ MainWindow::MainWindow( main_layout_->addWidget(content_widget_); this->setLayout(main_layout_); - this->project_list_->load_projects(storage); + this->project_list_->load_projects(user_->mutable_storage()); connect( project_list_, &QListWidget::itemClicked, note_list_, @@ -79,11 +77,10 @@ void MainWindow::create_project() { nullptr, "Название проекта:", "Введите название", QLineEdit::Normal, "", &ok ); - // TODO: notificate server - // TODO: send request to server, get code + if (ok) { - Project *project = storage_->add_projects(); - project->set_title(name_of_project.toStdString()); + Project *project = user_->mutable_storage()->add_projects(); + client_->create_project(project, name_of_project.toStdString(), *user_); project_list_->add_project(project); } } @@ -91,12 +88,10 @@ void MainWindow::create_project() { void MainWindow::add_note() { auto project_item = dynamic_cast(project_list_->currentItem()); - // TODO: notificate server if (project_item) { Note *note = project_item->project_->add_notes(); - note->set_title("Пустая заметка"); - note->set_allocated_text(new std::string("")); - note_list_->add_note_widget(note); + client_->try_create_note(note); + note_list_->add_note_widget(note); // todo : and to project! } else { QMessageBox msg; msg.setText("Проект не выбран!"); diff --git a/client/ui/main-window/src/notelist.cpp b/client/ui/main-window/src/notelist.cpp index dcdce98..750a8b7 100644 --- a/client/ui/main-window/src/notelist.cpp +++ b/client/ui/main-window/src/notelist.cpp @@ -24,7 +24,7 @@ NoteList::NoteList(QWidget *parent, ClientImplementation *client) } } -void NoteList::add_note_widget(const Note *note) { +void NoteList::add_note_widget(Note *note) { auto current_layout = vertical_layouts_[note_counter_ % 4]; if (current_layout->count() > 1) { current_layout->removeItem( @@ -41,11 +41,10 @@ void NoteList::add_note_widget(const Note *note) { void NoteList::load_project_notes(QListWidgetItem *project) { ProjectItem *p = dynamic_cast(project); assert(p != nullptr); - qDebug() << "Адрес проекта" << QString::fromStdString(p->project_->title()) - << ":" << p->project_; this->clear_note_list(); + client_->get_project(p->project_, p->project_->code()); note_counter_ = 0; - for (const Note ¬e : p->project_->notes()) { + for (Note ¬e : *p->project_->mutable_notes()) { this->add_note_widget(¬e); } } diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp index 8caaec4..123ad03 100644 --- a/client/ui/main-window/src/notewidget.cpp +++ b/client/ui/main-window/src/notewidget.cpp @@ -7,7 +7,7 @@ namespace Ui { NoteWidget::NoteWidget( QWidget *parent, - const Note *model_note, + Note *model_note, ClientImplementation *client ) : QWidget(parent), @@ -41,6 +41,7 @@ NoteWidget::NoteWidget( } void NoteWidget::open_note_window() const { + client_->try_fetch_note(model_note_); auto dialog = new ::NoteEditDialog( client_, const_cast(qobject_cast(this)), const_cast(model_note_) diff --git a/client/ui/main-window/src/projectitem.cpp b/client/ui/main-window/src/projectitem.cpp index cc5d2cb..b8f02f2 100644 --- a/client/ui/main-window/src/projectitem.cpp +++ b/client/ui/main-window/src/projectitem.cpp @@ -3,6 +3,6 @@ namespace Ui { ProjectItem::ProjectItem(QListWidget *list_view, Project *project) - : project_(project), QListWidgetItem(project->title().c_str(), list_view) { + : project_(project), QListWidgetItem(project->code().c_str(), list_view) { } } // namespace Ui \ No newline at end of file diff --git a/server/Handlers/src/update_handler.cpp b/server/Handlers/src/update_handler.cpp index bd1985e..53898d3 100644 --- a/server/Handlers/src/update_handler.cpp +++ b/server/Handlers/src/update_handler.cpp @@ -113,6 +113,7 @@ bool UpdateHandler::get_project( const GetProjectRequest &request, GetProjectResponse &response ) { + Project *project = new Project(); bool ok = ProjectDAO::get_project(request.code(), *project); if (!ok) { diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 22cf7c1..6fb74c9 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -72,18 +72,20 @@ int LRDao::try_register_user( transaction.exec_params(check_query, login); if (!check_result.empty()) { + transaction.commit(); return -1; } - + transaction.commit(); const std::string hashed_password = hash_password(password); + pqxx::work transaction_insert(connection); const std::string insert_query = "INSERT INTO users (login, password) VALUES ($1, $2)"; const pqxx::result insert_result = - transaction.exec_params(insert_query, login, hashed_password); + transaction_insert.exec_params(insert_query, login, hashed_password); - transaction.commit(); + transaction_insert.commit(); return insert_result.affected_rows() > 0 ? 1 : 0; } diff --git a/server/database/src/note_dao.cpp b/server/database/src/note_dao.cpp index c82e353..81f5545 100644 --- a/server/database/src/note_dao.cpp +++ b/server/database/src/note_dao.cpp @@ -52,6 +52,7 @@ Note NoteDao::initialize_note_for_user(const std::string &login) { transaction.exec_params(query, "Пустая заметка", ""); if (result.empty()) { + transaction.commit(); return {}; } @@ -115,12 +116,11 @@ Note NoteDao::get_note(const int note_id) { pqxx::work transaction(connection); const std::string query = "SELECT * FROM notes WHERE id = $1"; - pqxx::params params; - params.append(note_id); const pqxx::result result = transaction.exec_params(query, note_id); if (result.empty()) { + transaction.commit(); return {}; } diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index 39b993f..898b777 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -9,14 +9,15 @@ bool ProjectDAO::get_project(const std::string &code, Project &project) { pqxx::work transaction(connection); const std::string query = - "SELECT * FROM projects WHERE code = " + transaction.quote(code); + "SELECT * FROM projects WHERE code = $1"; - const pqxx::result result = transaction.exec(query); + const pqxx::result result = transaction.exec_params(query, code); if (result.empty()) { + transaction.commit(); return false; } - + transaction.commit(); const pqxx::row row = result[0]; project.set_title(row["title"].as()); @@ -144,7 +145,7 @@ bool ProjectDAO::code_available(const std::string &project_code) { "FROM projects " "WHERE code = $1 "; const pqxx::result result = transaction.exec_params(query, project_code); - + transaction.commit(); if (result.empty()) { return true; } From c47a76836dd1216798b92383469b27bf67283386 Mon Sep 17 00:00:00 2001 From: mirotvoretts Date: Thu, 5 Jun 2025 20:44:15 +0300 Subject: [PATCH 44/47] fix: log message in GetNoteServerCall --- server/Service/src/auth_service.cpp | 22 +++++-- server/Service/src/server_implementation.cpp | 5 +- server/Service/src/update_service.cpp | 13 ++++- server/database/src/lr_dao.cpp | 2 - server/database/src/project_dao.cpp | 60 ++++++++------------ 5 files changed, 56 insertions(+), 46 deletions(-) diff --git a/server/Service/src/auth_service.cpp b/server/Service/src/auth_service.cpp index 2af16b0..ece80d6 100644 --- a/server/Service/src/auth_service.cpp +++ b/server/Service/src/auth_service.cpp @@ -36,12 +36,26 @@ void AuthService::TryAuthenticateUserServerCall::Proceed(const bool ok) { ); if (query_exit_code == 1) { + response.mutable_user()->CopyFrom(request_.user()); + std::cout << "[SERVER]: WELCOME, " + << response.mutable_user()->login() << "\n"; + Storage user_storage; - ProjectDAO::get_all_user_projects(request_.user().login(), user_storage); + bool have_projects = ProjectDAO::get_all_user_projects( + request_.user().login(), user_storage + ); + + if (have_projects) { + response.mutable_user()->mutable_storage()->CopyFrom( + user_storage + ); + std::cout << "[SERVER]: DOWNLOADED YOUR PROJECT: " + << response.user().storage().projects()[0].code() + << "\n"; + } else { + std::cout << "[SERVER]: YOU DONT HAVE ANY PROJECTS\n"; + } - response.mutable_user()->CopyFrom(request_.user()); - response.mutable_user()->mutable_storage()->CopyFrom(user_storage); - std::cout << "[SERVER]: WELCOME, " << response.mutable_user()->login() << "\n"; } else { std::cout << "[SERVER]: SQL QUERY ERROR\n"; response.set_error_text( diff --git a/server/Service/src/server_implementation.cpp b/server/Service/src/server_implementation.cpp index dd2c788..ba2f5c2 100644 --- a/server/Service/src/server_implementation.cpp +++ b/server/Service/src/server_implementation.cpp @@ -5,8 +5,9 @@ ServerImplementation::ServerImplementation( const uint16_t port, grpc::ServerBuilder &builder ) - : cq_(builder.AddCompletionQueue()), update_service_(cq_.get()), - auth_service_(cq_.get()) { + : cq_(builder.AddCompletionQueue()), + update_service_(cq_.get()), + auth_service_(cq_.get()) { builder.AddListeningPort( "127.0.0.1:" + std::to_string(port), grpc::InsecureServerCredentials() ); diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 99f2838..39037ee 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -84,10 +84,17 @@ void UpdateService::GetNoteServerCall::Proceed(const bool ok) { const auto note = NoteDao::get_note(request_.id()); response.mutable_note()->CopyFrom(note); + std::cout << "[SERVER]: FETCH NOTE REQUEST id=" << request_.id() - << ", title=" << response.mutable_note()->title() - << ", first tag=" << response.note().tags()[0].text() - << ":" << response.note().tags()[0].color() << std::endl; + << ", title=" << response.note().title(); + + if (response.note().tags_size() > 0) { + std::cout << ", first tag=" << response.note().tags()[0].text() + << ":" << response.note().tags()[0].color(); + } else { + std::cout << ", no tags"; + } + std::cout << std::endl; responder_.Finish(response, grpc::Status::OK, this); status_ = FINISH; diff --git a/server/database/src/lr_dao.cpp b/server/database/src/lr_dao.cpp index 8ef3ed7..b903749 100644 --- a/server/database/src/lr_dao.cpp +++ b/server/database/src/lr_dao.cpp @@ -68,7 +68,6 @@ int LRDao::try_register_user( const std::string check_query = "SELECT * FROM users WHERE login = $1"; - const pqxx::result check_result = transaction.exec_params(check_query, login); @@ -103,7 +102,6 @@ bool LRDao::validate_user( const pqxx::result result = transaction.exec_params(query, login); if (result.empty()) { - std::cout << "[SERVER]: IDI NAHUI" << "\n"; transaction.commit(); return false; } diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index 52d34e0..e310ca3 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -3,7 +3,10 @@ #include "database_manager.hpp" #include "note_dao.hpp" -bool ProjectDAO::get_all_user_projects(const std::string &login, Storage &storage) { +bool ProjectDAO::get_all_user_projects( + const std::string &login, + Storage &storage +) { pqxx::connection &connection = DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); @@ -11,8 +14,9 @@ bool ProjectDAO::get_all_user_projects(const std::string &login, Storage &storag const std::string projects_query = "SELECT p.code, p.title, p.members " "FROM projects p " - "JOIN users u ON p.code = ANY(u.projects) " - "WHERE u.login = " + transaction.quote(login); + "JOIN users u ON p.code::text = ANY(u.projects::text[]) " + "WHERE u.login = " + + transaction.quote(login); const pqxx::result projects_result = transaction.exec(projects_query); @@ -27,36 +31,23 @@ bool ProjectDAO::get_all_user_projects(const std::string &login, Storage &storag project.set_title(project_row["title"].as()); if (!project_row["members"].is_null()) { - auto members_array = project_row["members"].as_array(); - for (const auto &member : members_array) { - project.add_members(member.as()); - } - } - - const std::string notes_query = - "SELECT id, title, content, date, tags " - "FROM notes " - "WHERE project_code = " + transaction.quote(project.code()); - - const pqxx::result notes_result = transaction.exec(notes_query); - - for (const auto ¬e_row : notes_result) { - Note* note = project.add_notes(); - note->set_id(note_row["id"].as()); - note->set_title(note_row["title"].as()); - - if (!note_row["content"].is_null()) { - note->set_text(note_row["content"].as()); - } - - if (!note_row["date"].is_null()) { - note->set_date(note_row["date"].as()); - } - - if (!note_row["tags"].is_null()) { - auto tags_array = note_row["tags"].as_array(); - for (const auto &tag : tags_array) { - note->add_tags(tag.as()); + auto members_str = project_row["members"].as(); + + if (!members_str.empty() && members_str.front() == '{' && + members_str.back() == '}') { + members_str = members_str.substr(1, members_str.size() - 2); + std::istringstream iss(members_str); + std::string member; + while (std::getline(iss, member, ',')) { + if (member.size() >= 2 && member.front() == '"' && + member.back() == '"') { + member = member.substr(1, member.size() - 2); + } + + std::erase_if(member, ::isspace); + if (!member.empty()) { + project.add_members(member); + } } } } @@ -73,8 +64,7 @@ bool ProjectDAO::get_project(const std::string &code, Project &project) { DatabaseManager::get_instance().get_connection(); pqxx::work transaction(connection); - const std::string query = - "SELECT * FROM projects WHERE code = $1"; + const std::string query = "SELECT * FROM projects WHERE code = $1"; const pqxx::result result = transaction.exec_params(query, code); From d7c44b7a2504db6ac9639a9051cfa8bc5a400fc0 Mon Sep 17 00:00:00 2001 From: toximu Date: Thu, 5 Jun 2025 21:05:57 +0300 Subject: [PATCH 45/47] connect main_window --- client/client/include/client_implementation.h | 2 +- client/client/include/update_requests.h | 2 +- client/client/src/client_implementation.cpp | 4 +- client/client/src/update_requests.cpp | 6 +- client/main.cpp | 1 + client/ui/main-window/include/mainwindow.h | 2 + client/ui/main-window/include/notelist.h | 2 + client/ui/main-window/include/notewidget.h | 7 +- client/ui/main-window/src/mainwindow.cpp | 29 +++++++- client/ui/main-window/src/notelist.cpp | 7 +- client/ui/main-window/src/notewidget.cpp | 44 +++++++++--- client/ui/main-window/src/projectitem.cpp | 2 +- client/ui/main-window/src/projectlist.cpp | 2 +- .../ui/note-widget/include/note_edit_dialog.h | 4 +- .../ui/note-widget/src/note_edit_dialog.cpp | 29 ++++---- server/Service/src/update_service.cpp | 1 + server/database/src/database_manager.cpp | 2 +- server/database/src/project_dao.cpp | 68 +++++++++---------- 18 files changed, 135 insertions(+), 79 deletions(-) diff --git a/client/client/include/client_implementation.h b/client/client/include/client_implementation.h index d9ab8c6..76c17a9 100644 --- a/client/client/include/client_implementation.h +++ b/client/client/include/client_implementation.h @@ -28,7 +28,7 @@ class ClientImplementation { bool try_register_user(User *user); bool try_update_note(Note *note) const; - bool try_create_note(Note *note) const; + bool try_create_note(Note *note, const std::string& project_code) const; bool try_fetch_note(Note *note) const; bool create_project( diff --git a/client/client/include/update_requests.h b/client/client/include/update_requests.h index 600ebcd..d661b79 100644 --- a/client/client/include/update_requests.h +++ b/client/client/include/update_requests.h @@ -72,7 +72,7 @@ class UpdateRequests { bool try_update_note(Note *note) const; bool try_fetch_note(Note *note) const; - bool try_create_note(Note *note) const; + bool try_create_note(Note *note, const std::string& project_code) const; bool get_note(Note *note); bool create_note(Note *note); diff --git a/client/client/src/client_implementation.cpp b/client/client/src/client_implementation.cpp index b7124b1..177d31f 100644 --- a/client/client/src/client_implementation.cpp +++ b/client/client/src/client_implementation.cpp @@ -65,8 +65,8 @@ bool ClientImplementation::try_update_note(Note *note) const { return update_requests_.try_update_note(note); } -bool ClientImplementation::try_create_note(Note *note) const { - return update_requests_.try_create_note(note); +bool ClientImplementation::try_create_note(Note *note, const std::string& project_code) const { + return update_requests_.try_create_note(note, project_code); } bool ClientImplementation::try_fetch_note(Note *note) const { diff --git a/client/client/src/update_requests.cpp b/client/client/src/update_requests.cpp index 29f5735..f26e879 100644 --- a/client/client/src/update_requests.cpp +++ b/client/client/src/update_requests.cpp @@ -261,9 +261,9 @@ bool UpdateRequests::try_fetch_note(Note *note) const { return true; } -bool UpdateRequests::try_create_note(Note *note) const { - const CreateNoteRequest request; - +bool UpdateRequests::try_create_note(Note *note, const std::string& project_code) const { + CreateNoteRequest request; + request.set_project_code(project_code); const auto call = new CreateNoteClientCall(request, cq_, stub_); void *tag; diff --git a/client/main.cpp b/client/main.cpp index e8cd71b..210fcc1 100644 --- a/client/main.cpp +++ b/client/main.cpp @@ -32,6 +32,7 @@ int main(int argc, char *argv[]) { usr->set_hashed_password("12345678"); client.try_authenticate_user(usr.get()); Ui::MainWindow window(app_window, std::move(usr), &client); + app_window->setCentralWidget(&window); app_window->resize(800, 600); app_window->show(); diff --git a/client/ui/main-window/include/mainwindow.h b/client/ui/main-window/include/mainwindow.h index 86075bf..6e920d9 100644 --- a/client/ui/main-window/include/mainwindow.h +++ b/client/ui/main-window/include/mainwindow.h @@ -28,6 +28,7 @@ class MainWindow : public QWidget { NoteList *note_list_; QWidget *content_widget_; QPushButton *new_project_button_; + QPushButton *join_project_button_; QPushButton *new_note_button_; @@ -35,6 +36,7 @@ class MainWindow : public QWidget { friend ProjectList; private slots: void create_project(); + void add_project_by_code(); void add_note(); public: diff --git a/client/ui/main-window/include/notelist.h b/client/ui/main-window/include/notelist.h index 399a342..1cfbc7b 100644 --- a/client/ui/main-window/include/notelist.h +++ b/client/ui/main-window/include/notelist.h @@ -22,6 +22,8 @@ class NoteList : public QWidget { int note_counter_ = 0; + const std::size_t notes_per_row = 3; + public: void add_note_widget(Note *note); void clear_note_list(); diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h index 18f1957..047c810 100644 --- a/client/ui/main-window/include/notewidget.h +++ b/client/ui/main-window/include/notewidget.h @@ -17,7 +17,8 @@ class NoteWidget : public QWidget { QVBoxLayout *main_layout_; QPushButton *open_button_; QLabel *title_label_; - QLabel *text_label_; + QHBoxLayout *tags_layout_; + std::vector tag_labels_; ClientImplementation *client_; public: @@ -26,10 +27,12 @@ class NoteWidget : public QWidget { Note *model_note, ClientImplementation *client ); +private: + void update_tags(); private slots: - void open_note_window() const; + void open_note_window() ; }; } // namespace Ui #endif // NOTEWIDGET_H \ No newline at end of file diff --git a/client/ui/main-window/src/mainwindow.cpp b/client/ui/main-window/src/mainwindow.cpp index e3badac..b718e10 100644 --- a/client/ui/main-window/src/mainwindow.cpp +++ b/client/ui/main-window/src/mainwindow.cpp @@ -33,7 +33,8 @@ MainWindow::MainWindow( project_list_(new ProjectList(this)), note_list_(new NoteList(this, client)), content_widget_(new QWidget(this)), - new_project_button_(new QPushButton("Новый проект", this)), + new_project_button_(new QPushButton("Создать", this)), + join_project_button_(new QPushButton("Добавить", this)), new_note_button_(new QPushButton("Новая заметка", this)) { this->setObjectName("main-window"); this->setAttribute(Qt::WA_StyledBackground); @@ -45,8 +46,13 @@ MainWindow::MainWindow( main_layout_->addWidget(content_widget_); content_widget_->setLayout(content_layout_); auto right_layout = new QVBoxLayout(content_widget_); + right_layout->addWidget(project_list_); - right_layout->addWidget(new_project_button_); + + auto project_button_layout = new QHBoxLayout(content_widget_); + project_button_layout->addWidget(new_project_button_); + project_button_layout->addWidget(join_project_button_); + right_layout->addLayout(project_button_layout); right_layout->addWidget(new_note_button_); QScrollArea *scrollArea = new QScrollArea(content_widget_); scrollArea->setWidgetResizable(true); @@ -69,6 +75,10 @@ MainWindow::MainWindow( new_project_button_, &QPushButton::clicked, this, &Ui::MainWindow::create_project ); + connect( + join_project_button_, &QPushButton::clicked, this, + &MainWindow::add_project_by_code + ); } void MainWindow::create_project() { @@ -85,12 +95,25 @@ void MainWindow::create_project() { } } +void MainWindow::add_project_by_code() { + bool ok; + QString code = QInputDialog::getText( + nullptr, "Код", "Введите код:", QLineEdit::Normal, "", &ok + ); + + if (ok) { + Project *project = user_->mutable_storage()->add_projects(); + client_->try_join_project(project,code.toStdString() , *user_); + project_list_->add_project(project); + } +} + void MainWindow::add_note() { auto project_item = dynamic_cast(project_list_->currentItem()); if (project_item) { Note *note = project_item->project_->add_notes(); - client_->try_create_note(note); + client_->try_create_note(note, project_item->project_->code()); note_list_->add_note_widget(note); // todo : and to project! } else { QMessageBox msg; diff --git a/client/ui/main-window/src/notelist.cpp b/client/ui/main-window/src/notelist.cpp index 750a8b7..d542848 100644 --- a/client/ui/main-window/src/notelist.cpp +++ b/client/ui/main-window/src/notelist.cpp @@ -16,7 +16,8 @@ NoteList::NoteList(QWidget *parent, ClientImplementation *client) this->setAttribute(Qt::WA_StyledBackground); this->setObjectName("NoteList"); this->setLayout(main_layout_); - vertical_layouts_.resize(4, nullptr); + + vertical_layouts_.resize(notes_per_row, nullptr); for (auto &layout : vertical_layouts_) { layout = new QVBoxLayout(this); @@ -25,13 +26,13 @@ NoteList::NoteList(QWidget *parent, ClientImplementation *client) } void NoteList::add_note_widget(Note *note) { - auto current_layout = vertical_layouts_[note_counter_ % 4]; + auto current_layout = vertical_layouts_[note_counter_ % notes_per_row]; if (current_layout->count() > 1) { current_layout->removeItem( current_layout->itemAt(current_layout->count() - 1) ); } - vertical_layouts_[note_counter_ % 4]->addWidget( + vertical_layouts_[note_counter_ % notes_per_row]->addWidget( new NoteWidget(this, note, client_), 0, Qt::AlignTop ); current_layout->addStretch(); diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp index 123ad03..30322e2 100644 --- a/client/ui/main-window/src/notewidget.cpp +++ b/client/ui/main-window/src/notewidget.cpp @@ -2,6 +2,7 @@ #include #include #include +// #include "../../../../cmake-build-just-run/client/ui/note-widget/NoteWidget_autogen/include/ui_note_edit_dialog.h" #include "note_edit_dialog.h" namespace Ui { @@ -14,22 +15,20 @@ NoteWidget::NoteWidget( model_note_(model_note), main_layout_(new QVBoxLayout(this)), open_button_(new QPushButton("Открыть")), + tags_layout_(new QHBoxLayout(this)), + tag_labels_(), client_(client) { this->setObjectName("NoteWidget"); this->setMinimumWidth(100); - this->setFixedHeight(100); + this->setFixedHeight(110); title_label_ = new QLabel(model_note_->title().c_str(), this); - text_label_ = new QLabel(model_note_->text().c_str(), this); title_label_->setStyleSheet("color: rgb(33, 44, 50);"); - text_label_->setStyleSheet("color: rgb(33, 44, 50);"); main_layout_->addWidget(title_label_); - main_layout_->addWidget(text_label_); - - text_label_->setWordWrap(false); + main_layout_->addLayout(tags_layout_); + update_tags(); title_label_->setWordWrap(false); - text_label_->setTextInteractionFlags(Qt::TextSelectableByMouse); main_layout_->addWidget(open_button_); @@ -40,16 +39,39 @@ NoteWidget::NoteWidget( this->setAttribute(Qt::WA_StyledBackground); } -void NoteWidget::open_note_window() const { - client_->try_fetch_note(model_note_); +void NoteWidget::open_note_window() { + // client_->try_fetch_note(model_note_); // todo dont work auto dialog = new ::NoteEditDialog( client_, const_cast(qobject_cast(this)), const_cast(model_note_) ); dialog->setAttribute(Qt::WA_DeleteOnClose); + update_tags(); dialog->exec(); - text_label_->setText(model_note_->text().c_str()); + title_label_->setText(model_note_->title().c_str()); + update_tags(); + resize(sizeHint()); main_layout_->update(); } -} // namespace Ui \ No newline at end of file + +void NoteWidget::update_tags() { + for (auto tag : tag_labels_) { + tags_layout_->removeWidget(tag); + delete tag; + } + tag_labels_.clear(); + + for (auto tag : model_note_->tags()) { + QLabel *tag_label = new QLabel(this); + tag_label->setText(tag.text().c_str()); + tag_label->setStyleSheet( + ::NoteEditDialog::create_tag_style_sheet(tag.color()) + ); + tag_label->resize(tag_label->sizeHint()); + tag_labels_.push_back(tag_label); + tags_layout_->addWidget(tag_label); + } +} + +} // namespace Ui diff --git a/client/ui/main-window/src/projectitem.cpp b/client/ui/main-window/src/projectitem.cpp index b8f02f2..cc5d2cb 100644 --- a/client/ui/main-window/src/projectitem.cpp +++ b/client/ui/main-window/src/projectitem.cpp @@ -3,6 +3,6 @@ namespace Ui { ProjectItem::ProjectItem(QListWidget *list_view, Project *project) - : project_(project), QListWidgetItem(project->code().c_str(), list_view) { + : project_(project), QListWidgetItem(project->title().c_str(), list_view) { } } // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/projectlist.cpp b/client/ui/main-window/src/projectlist.cpp index fc65ebb..f5591fa 100644 --- a/client/ui/main-window/src/projectlist.cpp +++ b/client/ui/main-window/src/projectlist.cpp @@ -7,7 +7,7 @@ namespace Ui { ProjectList::ProjectList(QWidget *parent) : QListWidget{parent} { this->setObjectName("ProjectList"); - this->setFixedWidth(200); + this->setFixedWidth(300); } void ProjectList::add_project(Project *project) { diff --git a/client/ui/note-widget/include/note_edit_dialog.h b/client/ui/note-widget/include/note_edit_dialog.h index d2c6fa2..f4eba97 100644 --- a/client/ui/note-widget/include/note_edit_dialog.h +++ b/client/ui/note-widget/include/note_edit_dialog.h @@ -28,7 +28,7 @@ class NoteEditDialog final : public QDialog { Note *note = nullptr ); ~NoteEditDialog() override; - + static QString create_tag_style_sheet(int color_code); private slots: void on_save_button_click(); void on_join_button_click(); @@ -45,7 +45,7 @@ class NoteEditDialog final : public QDialog { void clear_member_avatars(); void update_tags_display(); - static QString create_tag_style_sheet(int color_code); + [[nodiscard]] bool try_save_note() const; diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index 1f28e85..17bc650 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -122,20 +122,21 @@ void NoteEditDialog::setup_ui() { } void NoteEditDialog::on_save_button_click() { - if (try_save_note()) { - QMessageBox::information( - this, "Заметка сохранена", - QString("Заголовок: %1\nСодержимое: %2") - .arg( - ui_->titleLineEdit->text(), - ui_->descriptionTextEdit->toPlainText() - ) - ); - } else { - QMessageBox::information( - this, "Ошибка", "Не удалось сохранить заметку" - ); - } + try_save_note(); + // if () { \\ очень бесит когда много раз замтеку сохраняешь + // QMessageBox::information( + // this, "Заметка сохранена", + // QString("Заголовок: %1\nСодержимое: %2") + // .arg( + // ui_->titleLineEdit->text(), + // ui_->descriptionTextEdit->toPlainText() + // ) + // ); + // } else { + // QMessageBox::information( + // this, "Ошибка", "Не удалось сохранить заметку" + // ); + // } } void NoteEditDialog::on_join_button_click() { diff --git a/server/Service/src/update_service.cpp b/server/Service/src/update_service.cpp index 99f2838..8859bb6 100644 --- a/server/Service/src/update_service.cpp +++ b/server/Service/src/update_service.cpp @@ -299,6 +299,7 @@ void UpdateService::CreateNoteServerCall::Proceed(const bool ok) { CreateNoteResponse response; const auto created_note = NoteDao::initialize_note_for_user(request_.user().login()); + ProjectDAO::add_note_to_project(request_.project_code(), created_note.id()); response.mutable_note()->CopyFrom(created_note); responder_.Finish(response, grpc::Status::OK, this); diff --git a/server/database/src/database_manager.cpp b/server/database/src/database_manager.cpp index 32ae8f8..5e0b5c3 100644 --- a/server/database/src/database_manager.cpp +++ b/server/database/src/database_manager.cpp @@ -13,7 +13,7 @@ DatabaseManager::DatabaseManager() { "user_id VARCHAR(50) REFERENCES users(login), " "members VARCHAR(50)[], " "date VARCHAR(50), " - "tags VARCHAR(50)[]" + "tags VARCHAR(50)[] " ")" ); diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index 52d34e0..df4b6b2 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -26,40 +26,40 @@ bool ProjectDAO::get_all_user_projects(const std::string &login, Storage &storag project.set_code(project_row["code"].as()); project.set_title(project_row["title"].as()); - if (!project_row["members"].is_null()) { - auto members_array = project_row["members"].as_array(); - for (const auto &member : members_array) { - project.add_members(member.as()); - } - } - - const std::string notes_query = - "SELECT id, title, content, date, tags " - "FROM notes " - "WHERE project_code = " + transaction.quote(project.code()); - - const pqxx::result notes_result = transaction.exec(notes_query); - - for (const auto ¬e_row : notes_result) { - Note* note = project.add_notes(); - note->set_id(note_row["id"].as()); - note->set_title(note_row["title"].as()); - - if (!note_row["content"].is_null()) { - note->set_text(note_row["content"].as()); - } - - if (!note_row["date"].is_null()) { - note->set_date(note_row["date"].as()); - } - - if (!note_row["tags"].is_null()) { - auto tags_array = note_row["tags"].as_array(); - for (const auto &tag : tags_array) { - note->add_tags(tag.as()); - } - } - } + // if (!project_row["members"].is_null()) { + // auto members_array = project_row["members"].as>(); + // for (const auto &member : members_array) { + // project.add_members(member); + // } + // } + // + // const std::string notes_query = + // "SELECT id, title, content, date, tags " + // "FROM notes " + // "WHERE project_code = " + transaction.quote(project.code()); + // + // const pqxx::result notes_result = transaction.exec(notes_query); + // + // for (const auto ¬e_row : notes_result) { + // Note* note = project.add_notes(); + // note->set_id(note_row["id"].as()); + // note->set_title(note_row["title"].as()); + // + // if (!note_row["content"].is_null()) { + // note->set_text(note_row["content"].as()); + // } + // + // if (!note_row["date"].is_null()) { + // note->set_date(note_row["date"].as()); + // } + // + // if (!note_row["tags"].is_null()) { + // auto tags_array = note_row["tags"].as>(); + // for (const auto &tag : tags_array) { + // *note->tags() = tag; + // } + // } + // } storage.mutable_projects()->Add(std::move(project)); } From 43b0626ec2c9058c97b2f790cc841b3e7eb54cff Mon Sep 17 00:00:00 2001 From: toximu Date: Thu, 5 Jun 2025 22:35:46 +0300 Subject: [PATCH 46/47] main_window works good! --- client/ui/main-window/include/notewidget.h | 2 +- .../ui/main-window/src/applicationwindow.cpp | 3 ++ client/ui/main-window/src/mainwindow.cpp | 2 +- client/ui/main-window/src/notewidget.cpp | 20 ++++++--- .../ui/note-widget/src/note_edit_dialog.cpp | 1 + client/ui/note-widget/src/tags_dialog.cpp | 1 + server/database/src/project_dao.cpp | 42 +++++++++---------- 7 files changed, 42 insertions(+), 29 deletions(-) diff --git a/client/ui/main-window/include/notewidget.h b/client/ui/main-window/include/notewidget.h index 047c810..ad6dad5 100644 --- a/client/ui/main-window/include/notewidget.h +++ b/client/ui/main-window/include/notewidget.h @@ -31,7 +31,7 @@ class NoteWidget : public QWidget { void update_tags(); private slots: - + void good_resize(); void open_note_window() ; }; } // namespace Ui diff --git a/client/ui/main-window/src/applicationwindow.cpp b/client/ui/main-window/src/applicationwindow.cpp index 2cef620..9f7857d 100644 --- a/client/ui/main-window/src/applicationwindow.cpp +++ b/client/ui/main-window/src/applicationwindow.cpp @@ -1,4 +1,5 @@ #include "applicationwindow.h" +#include "notelist.h" #include namespace Ui { @@ -9,5 +10,7 @@ ApplicationWindow::ApplicationWindow(const std::string &window_name_) this->setAttribute(Qt::WA_StyledBackground); this->setWindowTitle(window_name_.c_str()); + + } } // namespace Ui \ No newline at end of file diff --git a/client/ui/main-window/src/mainwindow.cpp b/client/ui/main-window/src/mainwindow.cpp index b718e10..4eb708d 100644 --- a/client/ui/main-window/src/mainwindow.cpp +++ b/client/ui/main-window/src/mainwindow.cpp @@ -114,7 +114,7 @@ void MainWindow::add_note() { if (project_item) { Note *note = project_item->project_->add_notes(); client_->try_create_note(note, project_item->project_->code()); - note_list_->add_note_widget(note); // todo : and to project! + note_list_->add_note_widget(note); } else { QMessageBox msg; msg.setText("Проект не выбран!"); diff --git a/client/ui/main-window/src/notewidget.cpp b/client/ui/main-window/src/notewidget.cpp index 30322e2..40d155a 100644 --- a/client/ui/main-window/src/notewidget.cpp +++ b/client/ui/main-window/src/notewidget.cpp @@ -2,7 +2,9 @@ #include #include #include -// #include "../../../../cmake-build-just-run/client/ui/note-widget/NoteWidget_autogen/include/ui_note_edit_dialog.h" +// #include +// "../../../../cmake-build-just-run/client/ui/note-widget/NoteWidget_autogen/include/ui_note_edit_dialog.h" +#include #include "note_edit_dialog.h" namespace Ui { @@ -16,19 +18,22 @@ NoteWidget::NoteWidget( main_layout_(new QVBoxLayout(this)), open_button_(new QPushButton("Открыть")), tags_layout_(new QHBoxLayout(this)), - tag_labels_(), + tag_labels_(), client_(client) { this->setObjectName("NoteWidget"); this->setMinimumWidth(100); + this->setFixedHeight(110); title_label_ = new QLabel(model_note_->title().c_str(), this); - title_label_->setStyleSheet("color: rgb(33, 44, 50);"); + title_label_->setStyleSheet( + "color: rgb(33, 44, 50);" + ); main_layout_->addWidget(title_label_); main_layout_->addLayout(tags_layout_); update_tags(); - title_label_->setWordWrap(false); + // title_label_->setWordWrap(true); main_layout_->addWidget(open_button_); @@ -40,7 +45,7 @@ NoteWidget::NoteWidget( } void NoteWidget::open_note_window() { - // client_->try_fetch_note(model_note_); // todo dont work + client_->try_fetch_note(model_note_); auto dialog = new ::NoteEditDialog( client_, const_cast(qobject_cast(this)), const_cast(model_note_) @@ -51,7 +56,6 @@ void NoteWidget::open_note_window() { title_label_->setText(model_note_->title().c_str()); update_tags(); - resize(sizeHint()); main_layout_->update(); } @@ -74,4 +78,8 @@ void NoteWidget::update_tags() { } } +void NoteWidget::good_resize() { + setMaximumWidth(reinterpret_cast(parent())->width() / 3); +} + } // namespace Ui diff --git a/client/ui/note-widget/src/note_edit_dialog.cpp b/client/ui/note-widget/src/note_edit_dialog.cpp index 17bc650..79078f7 100644 --- a/client/ui/note-widget/src/note_edit_dialog.cpp +++ b/client/ui/note-widget/src/note_edit_dialog.cpp @@ -47,6 +47,7 @@ NoteEditDialog::~NoteEditDialog() { } void NoteEditDialog::init_basic_fields() { + ui_->titleLineEdit->setMaxLength(50); ui_->titleLineEdit->setText(QString::fromStdString(note_->title())); ui_->descriptionTextEdit->setText(QString::fromStdString(note_->text())); } diff --git a/client/ui/note-widget/src/tags_dialog.cpp b/client/ui/note-widget/src/tags_dialog.cpp index a8b9f11..89c6a7d 100644 --- a/client/ui/note-widget/src/tags_dialog.cpp +++ b/client/ui/note-widget/src/tags_dialog.cpp @@ -48,6 +48,7 @@ void TagsDialog::setup_ui() { name_line_edits_[i]->setPlaceholderText( "Имя тега " + QString::number(i + 1) ); + name_line_edits_[i]->setMaxLength(15); tag_layout->addWidget(name_line_edits_[i].get()); main_layout->addLayout(tag_layout); diff --git a/server/database/src/project_dao.cpp b/server/database/src/project_dao.cpp index e310ca3..96858cd 100644 --- a/server/database/src/project_dao.cpp +++ b/server/database/src/project_dao.cpp @@ -30,27 +30,27 @@ bool ProjectDAO::get_all_user_projects( project.set_code(project_row["code"].as()); project.set_title(project_row["title"].as()); - if (!project_row["members"].is_null()) { - auto members_str = project_row["members"].as(); - - if (!members_str.empty() && members_str.front() == '{' && - members_str.back() == '}') { - members_str = members_str.substr(1, members_str.size() - 2); - std::istringstream iss(members_str); - std::string member; - while (std::getline(iss, member, ',')) { - if (member.size() >= 2 && member.front() == '"' && - member.back() == '"') { - member = member.substr(1, member.size() - 2); - } - - std::erase_if(member, ::isspace); - if (!member.empty()) { - project.add_members(member); - } - } - } - } + // if (!project_row["members"].is_null()) { + // auto members_str = project_row["members"].as(); + // + // if (!members_str.empty() && members_str.front() == '{' && + // members_str.back() == '}') { + // members_str = members_str.substr(1, members_str.size() - 2); + // std::istringstream iss(members_str); + // std::string member; + // while (std::getline(iss, member, ',')) { + // if (member.size() >= 2 && member.front() == '"' && + // member.back() == '"') { + // member = member.substr(1, member.size() - 2); + // } + // + // std::erase_if(member, ::isspace); + // if (!member.empty()) { + // project.add_members(member); + // } + // } + // } + // } storage.mutable_projects()->Add(std::move(project)); } From 5e12772fcbf9c98e9bf7efb1cc20181ce5edafb0 Mon Sep 17 00:00:00 2001 From: Ilya Panov <89508801+mirotvoretts@users.noreply.github.com> Date: Sat, 14 Jun 2025 03:08:56 +0300 Subject: [PATCH 47/47] docs: update build and run app instruction --- README.md | 51 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index d2242e4..e1ebcb7 100644 --- a/README.md +++ b/README.md @@ -24,13 +24,7 @@ git clone git@github.com:toximu/efficio-task-tracker.git ``` -2. Start **PostgreSQL** service - -```bash -sudo service postgresql start && sudo -u postgres psql -``` - -3. Create **efficio** user +2. Create **efficio** user ```SQL CREATE USER efficio WITH PASSWORD 'admin'; @@ -39,29 +33,50 @@ GRANT ALL PRIVILEGES ON DATABASE efficio TO efficio; \q ``` -4. Enter under **efficio** profile +3. Build ```bash -psql -U efficio -d efficio -h localhost +mkdir -p build && cd build +cmake .. +make ``` -> After that run the server on address **localhost** and **port** 5432 in your pgAdmin4 - -5. Build and start app +4. Run the server and client in different terminal windows ```bash -mkdir -p build && cd build -cmake .. -make -./EfficioTaskTracker -platform xcb +build/server/Server +build/client/EfficioTaskTracker +``` + +## Recent problems + +### 1) libpqxx not installed + +```shell +CMake Error at server/service/CMakelists.txt:8 (find_package): + By not providing "Findlibpqxx.cmake" in CMAKE_MODULE_PATH this project has + asked CMake to find a package configuration file provided by "libpqxx", + but CMake did not find one. ``` +1) Clone [libpqxx repository](https://github.com/jtv/libpqxx) into `server/` +2) `cd libpqxx/` then build and install it + +```shell +cmake . +cmake --build . +cmake --install . +``` + +Now try to build **EFFICIO** one more time + ## Technologies Used - Qt 6.8.2 - PostgreSQL 17.4 - CMake 3.28.3 -- VcXsrv server (XLaunch) +- gRPC 1.72.0 +- pqxx 7.9.2-1 ## License -This project is licensed under the **MIT License**. See the [LICENSE](https://github.com/toximu/efficio-task-tracker/blob/main/LICENSE) file for details. \ No newline at end of file +This project is licensed under the **MIT License**. See the [LICENSE](https://github.com/toximu/efficio-task-tracker/blob/main/LICENSE) file for details.