latest changes

This commit is contained in:
Selim Mustafaev 2022-02-23 15:14:13 +03:00
parent 8caa12b4ce
commit 46a8b84869
17 changed files with 534 additions and 135 deletions

View File

@ -2,19 +2,35 @@ cmake_minimum_required(VERSION 3.0)
project(autocat_gnome) project(autocat_gnome)
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -g -Og")
#if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") find_package(PkgConfig REQUIRED)
# add_compile_options(-stdlib=libc++) find_package(nlohmann_json REQUIRED)
#endif() find_package(Boost 1.70 COMPONENTS thread REQUIRED)
find_package(PkgConfig)
pkg_check_modules(GTKMM REQUIRED gtkmm-4.0) pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)
pkg_check_modules(GLIBMM REQUIRED glibmm-2.68) pkg_check_modules(GLIBMM REQUIRED glibmm-2.68)
pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4) pkg_check_modules(LIBSOUP REQUIRED libsoup-2.4)
pkg_check_modules(LIBADWAITA REQUIRED libadwaita-1)
include_directories(${GTKMM_INCLUDE_DIRS} ${GLIBMM_INCLUDE_DIRS} ${LIBSOUP_INCLUDE_DIRS}) include_directories(${GTKMM_INCLUDE_DIRS}
link_directories(${GTKMM_LIBRARY_DIRS} ${GLIBMM_LIBRARY_DIRS} ${LIBSOUP_LIBRARY_DIRS}) ${GLIBMM_INCLUDE_DIRS}
${LIBSOUP_INCLUDE_DIRS}
${Boost_INCLUDE_DIR}
${LIBADWAITA_INCLUDE_DIRS})
add_executable(autocat_gnome main.cpp gui/MainWindow.cpp gui/MainWindow.h gui/LoginWindow.cpp gui/LoginWindow.h services/Api.cpp services/Api.h coro/Task.h models/User.cpp models/User.h) link_directories(${GTKMM_LIBRARY_DIRS}
target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES} ${GLIBMM_LIBRARIES} ${LIBSOUP_LIBRARIES}) ${GLIBMM_LIBRARY_DIRS}
${LIBSOUP_LIBRARY_DIRS}
${Boost_LIBRARY_DIRS}
${LIBADWAITA_LIBRARY_DIRS})
add_definitions(-DBOOST_THREAD_PROVIDES_FUTURE -DBOOST_THREAD_PROVIDES_FUTURE_CONTINUATION -DBOOST_THREAD_PROVIDES_PROMISE_LAZY -DBOOST_THREAD_VERSION=4)
add_executable(autocat_gnome main.cpp gui/MainWindow.cpp gui/MainWindow.h gui/LoginWindow.cpp gui/LoginWindow.h services/Api.cpp services/Api.h coro/Task.h models/User.cpp models/User.h services/Settings.cpp services/Settings.h app/App.cpp app/App.h gui/TitleBar.cpp gui/TitleBar.h)
target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES}
${GLIBMM_LIBRARIES}
${LIBSOUP_LIBRARIES}
nlohmann_json::nlohmann_json
${Boost_LIBRARIES}
${LIBADWAITA_LIBRARIES})

48
app/App.cpp Normal file
View File

@ -0,0 +1,48 @@
//
// Created by selim on 02.02.2022.
//
#include "App.h"
#include "../gui/MainWindow.h"
static void activateApp(GtkApplication *gtkApp, void* data) {
auto app = reinterpret_cast<App*>(data);
int i = 0;
auto win = std::make_shared<MainWindow>();
app->setWindow(win);
// GtkWidget *window = gtk_application_window_new (gtkApp);
// GtkWidget *label = gtk_label_new ("Hello World");
//
// gtk_window_set_title (GTK_WINDOW (window), "Hello");
// gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
// gtk_window_set_child (GTK_WINDOW (window), label);
// gtk_window_present (GTK_WINDOW (window));
}
App::App(): _app(nullptr), _window(nullptr) {
_app = adw_application_new("pro.aliencat.aliencat", G_APPLICATION_FLAGS_NONE);
g_signal_connect_data(_app, "activate", G_CALLBACK(activateApp), this, nullptr, G_CONNECT_AFTER);
}
App::~App() {
glib_autoptr_cleanup_AdwApplication(&_app);
}
int App::run(int argc, char **argv) {
return g_application_run(G_APPLICATION(_app), argc, argv);
}
void App::setWindow(std::shared_ptr<Gtk::Window> window) {
gtk_application_add_window(GTK_APPLICATION(_app), window->gobj());
if(_window) {
_window->hide();
gtk_application_remove_window(GTK_APPLICATION(_app), _window->gobj());
}
window->show();
_window = window;
}

25
app/App.h Normal file
View File

@ -0,0 +1,25 @@
//
// Created by selim on 02.02.2022.
//
#ifndef AUTOCAT_GNOME_APP_H
#define AUTOCAT_GNOME_APP_H
#include <gtkmm/window.h>
#include <adwaita.h>
#include <memory>
class App {
private:
AdwApplication* _app;
std::shared_ptr<Gtk::Window> _window;
public:
explicit App();
~App();
int run(int argc, char** argv);
void setWindow(std::shared_ptr<Gtk::Window> window);
};
#endif //AUTOCAT_GNOME_APP_H

View File

@ -8,94 +8,140 @@
#include <coroutine> #include <coroutine>
#include <iostream> #include <iostream>
#include <optional> #include <optional>
#include <exception>
#include <boost/thread/future.hpp>
#include <boost/exception/errinfo_nested_exception.hpp>
#include <utility>
#include <glibmm.h>
class FutureException: public virtual std::exception, public virtual boost::exception {
private:
std::string _message;
template<typename T>
class Task {
public: public:
explicit FutureException(const char* msg): _message(msg) {}
explicit FutureException(std::string msg): _message(std::move(msg)) {}
const char* what() const noexcept {
return _message.c_str();
}
};
// Enable the use of std::future<T> as a coroutine type
// by using a std::promise<T> as the promise type.
template <typename T, typename... Args>
requires(!std::is_void_v<T> && !std::is_reference_v<T>)
struct std::coroutine_traits<boost::future<T>, Args...> {
struct promise_type : boost::promise<T> {
boost::future<T> get_return_object() noexcept {
return this->get_future();
}
std::suspend_never initial_suspend() const noexcept {
return {};
}
std::suspend_never final_suspend() const noexcept {
return {};
}
void return_value(const T &value)
noexcept(std::is_nothrow_copy_constructible_v<T>) {
this->set_value(value);
}
void return_value(T &&value)
noexcept(std::is_nothrow_move_constructible_v<T>) {
this->set_value(std::move(value));
}
void unhandled_exception() noexcept {
this->set_exception(boost::current_exception());
}
};
};
// Same for std::future<void>.
template <typename... Args>
struct std::coroutine_traits<boost::future<void>, Args...> {
struct promise_type : boost::promise<void> {
boost::future<void> get_return_object() noexcept {
return this->get_future();
}
std::suspend_never initial_suspend() const noexcept {
return {};
}
std::suspend_never final_suspend() const noexcept {
return {};
}
void return_void() noexcept {
this->set_value();
}
void unhandled_exception() noexcept {
this->set_exception(boost::current_exception());
}
};
};
template<typename... Args>
struct std::coroutine_traits<void, Args...> {
struct promise_type { struct promise_type {
auto initial_suspend() const noexcept { void get_return_object() noexcept {}
std::cout << "initial_suspend" << std::endl;
return std::suspend_never(); std::suspend_never initial_suspend() const noexcept {
return {};
}
std::suspend_never final_suspend() const noexcept {
return {};
} }
auto final_suspend() const noexcept { void return_void() noexcept {}
std::cout << "final_suspend" << std::endl;
return std::suspend_never(); void unhandled_exception() noexcept {
try {
boost::rethrow_exception(boost::current_exception());
} catch (std::exception& ex) {
std::cout << "Unhandled exception (in void coroutine) detected: " << ex.what() << std::endl;
} }
Task<T> get_return_object() {
std::cout << "get_return_object" << std::endl;
return Task<T>{ std::coroutine_handle<promise_type>::from_promise(*this) };
}
void return_value(const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>) {
std::cout << "return_value: " << value << std::endl;
_value = value;
}
// void return_void() {
// std::cout << "return_void" << std::endl;
// }
void unhandled_exception() {
std::cout << "unhandled_exception" << std::endl;
throw;
} }
}; };
private:
std::optional<T> _value = std::nullopt;
std::coroutine_handle<promise_type> _handle;
//public:
// bool await_ready() const noexcept {
// std::cout << "Awaiter::await_ready" << std::endl;
// return false;
// }
//
// void await_suspend(std::coroutine_handle<promise_type> handle) const {
// std::cout << "Awaiter::await_suspend" << std::endl;
// _handle = handle;
// }
//
// T await_resume() {
// std::cout << "Awaiter::await_resume" << std::endl;
// return _value.value();
// }
public:
void set_result(T result) {
_value = result;
_handle();
}
};
template<typename U> struct Awaiter {
public:
bool await_ready() const noexcept {
std::cout << "Awaiter::await_ready" << std::endl;
return false;
}
void await_suspend(std::coroutine_handle<typename Task<U>::promise_type> cont) const {
std::cout << "Awaiter::await_suspend" << std::endl;
}
U await_resume() {
std::cout << "Awaiter::await_resume" << std::endl;
return _task.value();
}
private:
Task<U> _task;
public:
explicit Awaiter(Task<U> task): _task(task) {}
}; };
template <typename T> template <typename T>
auto operator co_await(Task<T> task) noexcept /*requires(!std::is_reference_v<T>)*/ { auto operator co_await(boost::future<T> future) noexcept requires(!std::is_reference_v<T>) {
return Awaiter<T>(task);
struct awaiter : public boost::future<T> {
bool await_ready() const noexcept {
return this->is_ready();
}
void await_suspend(std::coroutine_handle<> cont) {
this->then([this, cont](boost::future<T>&& fut){
// future was moved out of awaiter to this lambda
// so, we need to bring it back
this->swap(fut);
// Resume coroutine on the main thread (i.e. GMainContext)
g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, awaiter::resumeContinuation, (gpointer)&cont, nullptr);
});
}
T await_resume() {
return this->get();
}
static int resumeContinuation(void* data) {
auto handler = reinterpret_cast<std::coroutine_handle<>*>(data);
if(*handler) {
handler->resume();
}
return G_SOURCE_REMOVE;
}
};
return awaiter{std::move(future)};
} }
#endif //AUTOCAT_GNOME_TASK_H #endif //AUTOCAT_GNOME_TASK_H

View File

@ -3,8 +3,10 @@
// //
#include "LoginWindow.h" #include "LoginWindow.h"
#include "MainWindow.h"
#include "../services/Api.h" #include "../services/Api.h"
#include <gtkmm/application.h>
#include <gtkmm/box.h> #include <gtkmm/box.h>
#include <iostream> #include <iostream>
@ -17,13 +19,13 @@ LoginWindow::LoginWindow() {
_passwordField.set_input_purpose(Gtk::InputPurpose::PASSWORD); _passwordField.set_input_purpose(Gtk::InputPurpose::PASSWORD);
_passwordField.set_visibility(false); _passwordField.set_visibility(false);
_emailField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::ValidateFields)); _emailField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::validateFields));
_passwordField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::ValidateFields)); _passwordField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::validateFields));
_loginButton.set_margin_top(8); _loginButton.set_margin_top(8);
_loginButton.set_margin_bottom(8); _loginButton.set_margin_bottom(8);
_loginButton.set_label("Log in"); _loginButton.set_label("Log in");
_loginButton.signal_clicked().connect(sigc::mem_fun(*this, &LoginWindow::LoginClicked)); _loginButton.signal_clicked().connect(sigc::mem_fun(*this, &LoginWindow::loginClicked));
_loginButton.set_sensitive(false); _loginButton.set_sensitive(false);
Gtk::Box box(Gtk::Orientation::VERTICAL, 8); Gtk::Box box(Gtk::Orientation::VERTICAL, 8);
@ -38,32 +40,48 @@ LoginWindow::LoginWindow() {
set_child(box); set_child(box);
} }
//Task<int> foo() { void LoginWindow::loginClicked() {
// co_return 42;
//}
//
//Task<int> bar() {
// auto xxx = co_await foo();
// std::cout << "xxx: " << xxx << std::endl;
//}
void LoginWindow::LoginClicked() {
auto email = _emailField.get_text(); auto email = _emailField.get_text();
auto password = _passwordField.get_text(); auto password = _passwordField.get_text();
_loginButton.set_sensitive(false); enableControls(false);
_emailField.set_sensitive(false);
_passwordField.set_sensitive(false);
_spinner.start(); _spinner.start();
std::cout << "Login clicked" << std::endl; try {
std::cout << "Login: " << email << std::endl; auto user = co_await Api::login(email, password);
std::cout << "Password: " << password << std::endl; auto app = this->get_application();
auto mainWindow = new MainWindow();
User user = co_await Api::login(email, password); mainWindow->show();
hide();
app->add_window(*mainWindow);
app->remove_window(*this);
} catch (std::exception& ex) {
enableControls(true);
_spinner.stop();
showError(ex.what());
}
} }
void LoginWindow::ValidateFields() { void LoginWindow::validateFields() {
bool buttonEnabled = _emailField.get_text_length() > 0 && _passwordField.get_text_length() > 0; bool buttonEnabled = _emailField.get_text_length() > 0 && _passwordField.get_text_length() > 0;
_loginButton.set_sensitive(buttonEnabled); _loginButton.set_sensitive(buttonEnabled);
} }
void LoginWindow::showError(const std::string& message) {
_dialog = std::make_unique<Gtk::MessageDialog>("Error",
false,
Gtk::MessageType::ERROR,
Gtk::ButtonsType::OK,
true);
_dialog->set_secondary_text(message);
_dialog->set_transient_for(*this);
_dialog->set_hide_on_close(true);
_dialog->signal_response().connect(sigc::hide(sigc::mem_fun(*_dialog, &Gtk::Widget::hide)));
_dialog->show();
}
void LoginWindow::enableControls(bool enable) {
_loginButton.set_sensitive(enable);
_emailField.set_sensitive(enable);
_passwordField.set_sensitive(enable);
}

View File

@ -9,6 +9,8 @@
#include <gtkmm/entry.h> #include <gtkmm/entry.h>
#include <gtkmm/button.h> #include <gtkmm/button.h>
#include <gtkmm/spinner.h> #include <gtkmm/spinner.h>
#include <gtkmm/messagedialog.h>
#include <memory>
class LoginWindow: public Gtk::Window { class LoginWindow: public Gtk::Window {
private: private:
@ -16,13 +18,16 @@ private:
Gtk::Entry _passwordField; Gtk::Entry _passwordField;
Gtk::Button _loginButton; Gtk::Button _loginButton;
Gtk::Spinner _spinner; Gtk::Spinner _spinner;
std::unique_ptr<Gtk::MessageDialog> _dialog;
public: public:
LoginWindow(); LoginWindow();
~LoginWindow() override = default; ~LoginWindow() override = default;
void LoginClicked(); void loginClicked();
void ValidateFields(); void validateFields();
void showError(const std::string& message);
void enableControls(bool enable);
}; };

View File

@ -3,8 +3,34 @@
// //
#include "MainWindow.h" #include "MainWindow.h"
#include <gdkmm.h>
MainWindow::MainWindow() { MainWindow::MainWindow() {
set_title("Main window"); //set_title("Main window");
set_default_size(640, 480); set_default_size(640, 480);
//set_decorated(false);
set_titlebar(_titleBar);
_masterLabel.set_text("master");
_masterBox.set_orientation(Gtk::Orientation::VERTICAL);
_masterBox.append(_masterLabel);
_detailLabel.set_text("detail");
_detailBox.set_orientation(Gtk::Orientation::VERTICAL);
_detailBox.append(_detailLabel);
_paned.set_start_child(_masterBox);
_paned.set_end_child(_detailBox);
_paned.set_resize_start_child(true);
_paned.set_shrink_end_child(true);
_paned.set_resize_end_child(true);
set_child(_paned);
_sizeGroup = Gtk::SizeGroup::create(Gtk::SizeGroup::Mode::HORIZONTAL);
_sizeGroup->add_widget(_titleBar.masterHeader());
_sizeGroup->add_widget(_masterBox);
_titleBar.masterHeader().set_size_request(50, -1);
_masterBox.set_size_request(50, -1);
} }

View File

@ -5,9 +5,24 @@
#ifndef AUTOCAT_GNOME_MAINWINDOW_H #ifndef AUTOCAT_GNOME_MAINWINDOW_H
#define AUTOCAT_GNOME_MAINWINDOW_H #define AUTOCAT_GNOME_MAINWINDOW_H
#include "TitleBar.h"
#include <gtkmm/window.h> #include <gtkmm/window.h>
#include <gtkmm/paned.h>
#include <gtkmm/label.h>
#include <gtkmm/box.h>
#include <gtkmm/sizegroup.h>
class MainWindow: public Gtk::Window { class MainWindow: public Gtk::Window {
private:
TitleBar _titleBar;
Gtk::Paned _paned;
Gtk::Label _masterLabel;
Gtk::Label _detailLabel;
Gtk::Box _masterBox;
Gtk::Box _detailBox;
Glib::RefPtr<Gtk::SizeGroup> _sizeGroup;
public: public:
MainWindow(); MainWindow();
}; };

21
gui/TitleBar.cpp Normal file
View File

@ -0,0 +1,21 @@
//
// Created by selim on 13.02.2022.
//
#include "TitleBar.h"
TitleBar::TitleBar() {
set_start_child(_masterHeader);
set_end_child(_detailHeader);
set_resize_start_child(true);
set_shrink_end_child(true);
set_resize_end_child(true);
}
Gtk::HeaderBar &TitleBar::masterHeader() {
return _masterHeader;
}
Gtk::HeaderBar &TitleBar::detailHeader() {
return _detailHeader;
}

23
gui/TitleBar.h Normal file
View File

@ -0,0 +1,23 @@
//
// Created by selim on 13.02.2022.
//
#ifndef AUTOCAT_GNOME_TITLEBAR_H
#define AUTOCAT_GNOME_TITLEBAR_H
#include <gtkmm/paned.h>
#include <gtkmm/headerbar.h>
class TitleBar: public Gtk::Paned {
private:
Gtk::HeaderBar _masterHeader;
Gtk::HeaderBar _detailHeader;
public:
TitleBar();
Gtk::HeaderBar& masterHeader();
Gtk::HeaderBar& detailHeader();
};
#endif //AUTOCAT_GNOME_TITLEBAR_H

View File

@ -1,8 +1,29 @@
#include "gui/MainWindow.h" #include "gui/MainWindow.h"
#include "gui/LoginWindow.h" #include "gui/LoginWindow.h"
#include "services/Settings.h"
#include "app/App.h"
#include <gtkmm/application.h> #include <gtkmm/application.h>
#include <glibmm.h>
#include <gtkmm.h>
#include <memory>
std::unique_ptr<Gtk::Window> createStartWindow() {
auto settings = Settings::instance();
if(settings.user().token.empty()) {
return std::make_unique<LoginWindow>();
} else {
return std::make_unique<MainWindow>();
}
}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
auto app = Gtk::Application::create("pro.aliencat.aliencat"); auto app = Gtk::Application::create("pro.aliencat.aliencat");
return app->make_window_and_run<LoginWindow>(argc, argv); auto window = createStartWindow();
app->signal_activate().connect([&](){
app->add_window(*window);
window->show();
});
return app->run(argc, argv);
} }

View File

@ -4,6 +4,6 @@
#include "User.h" #include "User.h"
User::User(std::string_view login, std::string_view token): login(login), token(token) { User::User(std::string_view email, std::string_view token): email(email), token(token) {
} }

View File

@ -7,16 +7,21 @@
#include <string> #include <string>
#include <optional> #include <optional>
#include <nlohmann/json.hpp>
class User { class User {
public: public:
std::string login; std::string email;
std::string token; std::string token;
std::optional<std::string> googleIdToken; std::optional<std::string> googleIdToken;
std::optional<std::string> googleRefreshToken; std::optional<std::string> googleRefreshToken;
public: public:
User(std::string_view login, std::string_view token); User() = default;
User(const User& user) = default;
User(std::string_view email, std::string_view token);
NLOHMANN_DEFINE_TYPE_INTRUSIVE(User, email, token)
}; };

View File

@ -3,47 +3,76 @@
// //
#include "Api.h" #include "Api.h"
#include "Settings.h"
#include <memory> #include <memory>
template<class...Args> template<class...Args>
struct callback { struct Callback {
void(*function)(Args..., void*) = nullptr; void(*function)(Args..., void*) = nullptr;
std::unique_ptr<void, void(*)(void*)> state; void* state = nullptr;
}; };
template<typename... Args, typename Lambda> template<typename... Args, typename Lambda>
callback<Args...> voidify(Lambda&& l) { Callback<Args...> voidify(Lambda&& l) {
using Func = typename std::decay<Lambda>::type; using Func = typename std::decay<Lambda>::type;
std::unique_ptr<void, void(*)(void*)> data( auto data = new Func(std::forward<Lambda>(l));
new Func(std::forward<Lambda>(l)),
+[](void* ptr){
std::cout << "!!!!!!!!!!!!!!!!! Deleting data" << std::endl;
delete (Func*)ptr;
}
);
return { return {
+[](Args... args, void* v)->void { +[](Args... args, void* v)->void {
Func* f = static_cast< Func* >(v); Func* f = static_cast< Func* >(v);
(*f)(std::forward<Args>(args)...); (*f)(std::forward<Args>(args)...);
delete f;
}, },
std::move(data) data
}; };
} }
const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/"; const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/";
SoupSession* Api::_session = soup_session_new(); SoupSession* Api::_session = soup_session_new();
Task<User> Api::login(std::string email, std::string password) { template<typename T>
std::string url = _baseUrl + "user/login"; boost::future<T> Api::post(const std::string &method, const nlohmann::json& params) {
std::string url = _baseUrl + method;
auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str()); auto msg = soup_message_new(SOUP_METHOD_POST, url.c_str());
auto task = Task<User>(); auto promise = std::make_shared<boost::promise<T>>();
auto callback = voidify<SoupSession*, SoupMessage*>([&, promise](SoupSession* session, SoupMessage* message) {
auto callback = voidify<SoupSession*, SoupMessage*>([&](SoupSession* session, SoupMessage* message) { if(message->status_code >= 200 && message->status_code < 300) {
std::cout << "Callback called" << std::endl; auto responseString = std::string(message->response_body->data, message->response_body->length);
task.set_result(User("qwe", "asdf")); auto json = nlohmann::json::parse(responseString);
}); if(json["success"].get<bool>()) {
soup_session_queue_message(_session, msg, callback.function, callback.state.get()); std::cout << "response: " << responseString << std::endl;
auto user = json["data"].get<T>();
return task; promise->set_value(user);
} else {
auto error = json["error"].get<std::string>();
promise->set_exception(FutureException(error));
}
} else {
promise->set_exception(FutureException(message->reason_phrase));
}
});
auto jsonStr = params.dump();
soup_message_set_request(msg, "application/json", SOUP_MEMORY_COPY, jsonStr.c_str(), jsonStr.size());
soup_session_queue_message(_session, msg, callback.function, callback.state);
return promise->get_future();
}
boost::future<User> Api::login(std::string email, std::string password) {
nlohmann::json params = {
{ "email", email },
{ "password", password }
};
auto user = Settings::instance().user();
auto result = co_await post<User>("user/login", params);
user.email = result.email;
user.token = result.token;
Settings::instance().setUser(user);
co_return user;
} }

View File

@ -7,6 +7,8 @@
#include <string> #include <string>
#include <libsoup/soup.h> #include <libsoup/soup.h>
#include <nlohmann/json.hpp>
#include "../models/User.h" #include "../models/User.h"
#include "../coro/Task.h" #include "../coro/Task.h"
@ -15,8 +17,12 @@ private:
static const std::string _baseUrl; static const std::string _baseUrl;
static SoupSession* _session; static SoupSession* _session;
private:
template<typename T>
static boost::future<T> post(const std::string& method, const nlohmann::json& params);
public: public:
static Task<User> login(std::string email, std::string password); static boost::future<User> login(std::string email, std::string password);
}; };

71
services/Settings.cpp Normal file
View File

@ -0,0 +1,71 @@
//
// Created by selim on 09.01.2022.
//
#include "Settings.h"
#include <nlohmann/json.hpp>
#include <cstdlib>
#include <iostream>
#include <fstream>
namespace fs = std::filesystem;
fs::path getDataPath() {
auto dataDir = std::getenv("XDG_DATA_HOME");
if(dataDir) {
return fs::path(dataDir);
}
auto home = std::getenv("HOME");
if(home) {
return fs::path(home) / ".local" / "share";
}
throw std::runtime_error("Failed to find data home directory");
}
std::string readString(const fs::path& path) {
std::ifstream stream(path);
stream.seekg(0, std::ios::end);
auto size = stream.tellg();
std::string buffer(size, ' ');
stream.seekg(0);
stream.read(&buffer[0], size);
return buffer;
}
void writeString(const fs::path& path, const std::string& text) {
std::ofstream stream(path);
stream << text;
}
Settings Settings::instance() {
static Settings instance;
return instance;
}
Settings::Settings() {
_dataPath = getDataPath() / "autocat";
if(!fs::exists(_dataPath)) {
fs::create_directories(_dataPath);
}
}
User Settings::user() const {
auto userFile = _dataPath / "user.json";
if(fs::exists(userFile)) {
auto jsonText = readString(userFile);
std::cout << "User json text: " << jsonText << std::endl;
return nlohmann::json::parse(jsonText).get<User>();
} else {
return {};
}
}
void Settings::setUser(const User &user) {
auto userFile = _dataPath / "user.json";
nlohmann::json json = user;
auto userString = json.dump(4);
writeString(userFile, userString);
}

24
services/Settings.h Normal file
View File

@ -0,0 +1,24 @@
//
// Created by selim on 09.01.2022.
//
#ifndef AUTOCAT_GNOME_SETTINGS_H
#define AUTOCAT_GNOME_SETTINGS_H
#include "../models/User.h"
#include <filesystem>
class Settings {
private:
std::filesystem::path _dataPath;
public:
static Settings instance();
Settings();
[[nodiscard]] User user() const;
void setUser(const User& user);
};
#endif //AUTOCAT_GNOME_SETTINGS_H