latest changes
This commit is contained in:
parent
8caa12b4ce
commit
46a8b84869
@ -2,19 +2,35 @@ cmake_minimum_required(VERSION 3.0)
|
||||
project(autocat_gnome)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
#set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -g -Og")
|
||||
|
||||
#if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# add_compile_options(-stdlib=libc++)
|
||||
#endif()
|
||||
|
||||
find_package(PkgConfig)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
find_package(nlohmann_json REQUIRED)
|
||||
find_package(Boost 1.70 COMPONENTS thread REQUIRED)
|
||||
|
||||
pkg_check_modules(GTKMM REQUIRED gtkmm-4.0)
|
||||
pkg_check_modules(GLIBMM REQUIRED glibmm-2.68)
|
||||
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})
|
||||
link_directories(${GTKMM_LIBRARY_DIRS} ${GLIBMM_LIBRARY_DIRS} ${LIBSOUP_LIBRARY_DIRS})
|
||||
include_directories(${GTKMM_INCLUDE_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)
|
||||
target_link_libraries(autocat_gnome ${GTKMM_LIBRARIES} ${GLIBMM_LIBRARIES} ${LIBSOUP_LIBRARIES})
|
||||
link_directories(${GTKMM_LIBRARY_DIRS}
|
||||
${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
48
app/App.cpp
Normal 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
25
app/App.h
Normal 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
|
||||
194
coro/Task.h
194
coro/Task.h
@ -8,94 +8,140 @@
|
||||
#include <coroutine>
|
||||
#include <iostream>
|
||||
#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:
|
||||
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 {
|
||||
auto initial_suspend() const noexcept {
|
||||
std::cout << "initial_suspend" << std::endl;
|
||||
return std::suspend_never();
|
||||
void get_return_object() noexcept {}
|
||||
|
||||
std::suspend_never initial_suspend() const noexcept {
|
||||
return {};
|
||||
}
|
||||
std::suspend_never final_suspend() const noexcept {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto final_suspend() const noexcept {
|
||||
std::cout << "final_suspend" << std::endl;
|
||||
return std::suspend_never();
|
||||
void return_void() noexcept {}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
auto operator co_await(boost::future<T> future) noexcept requires(!std::is_reference_v<T>) {
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
Task<T> get_return_object() {
|
||||
std::cout << "get_return_object" << std::endl;
|
||||
return Task<T>{ std::coroutine_handle<promise_type>::from_promise(*this) };
|
||||
T await_resume() {
|
||||
return this->get();
|
||||
}
|
||||
|
||||
void return_value(const T& value) noexcept(std::is_nothrow_copy_constructible_v<T>) {
|
||||
std::cout << "return_value: " << value << std::endl;
|
||||
_value = value;
|
||||
static int resumeContinuation(void* data) {
|
||||
auto handler = reinterpret_cast<std::coroutine_handle<>*>(data);
|
||||
if(*handler) {
|
||||
handler->resume();
|
||||
}
|
||||
|
||||
// void return_void() {
|
||||
// std::cout << "return_void" << std::endl;
|
||||
// }
|
||||
|
||||
void unhandled_exception() {
|
||||
std::cout << "unhandled_exception" << std::endl;
|
||||
throw;
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
};
|
||||
|
||||
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>
|
||||
auto operator co_await(Task<T> task) noexcept /*requires(!std::is_reference_v<T>)*/ {
|
||||
return Awaiter<T>(task);
|
||||
return awaiter{std::move(future)};
|
||||
}
|
||||
|
||||
#endif //AUTOCAT_GNOME_TASK_H
|
||||
@ -3,8 +3,10 @@
|
||||
//
|
||||
|
||||
#include "LoginWindow.h"
|
||||
#include "MainWindow.h"
|
||||
#include "../services/Api.h"
|
||||
|
||||
#include <gtkmm/application.h>
|
||||
#include <gtkmm/box.h>
|
||||
#include <iostream>
|
||||
|
||||
@ -17,13 +19,13 @@ LoginWindow::LoginWindow() {
|
||||
_passwordField.set_input_purpose(Gtk::InputPurpose::PASSWORD);
|
||||
_passwordField.set_visibility(false);
|
||||
|
||||
_emailField.signal_changed().connect(sigc::mem_fun(*this, &LoginWindow::ValidateFields));
|
||||
_passwordField.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));
|
||||
|
||||
_loginButton.set_margin_top(8);
|
||||
_loginButton.set_margin_bottom(8);
|
||||
_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);
|
||||
|
||||
Gtk::Box box(Gtk::Orientation::VERTICAL, 8);
|
||||
@ -38,32 +40,48 @@ LoginWindow::LoginWindow() {
|
||||
set_child(box);
|
||||
}
|
||||
|
||||
//Task<int> foo() {
|
||||
// co_return 42;
|
||||
//}
|
||||
//
|
||||
//Task<int> bar() {
|
||||
// auto xxx = co_await foo();
|
||||
// std::cout << "xxx: " << xxx << std::endl;
|
||||
//}
|
||||
|
||||
void LoginWindow::LoginClicked() {
|
||||
void LoginWindow::loginClicked() {
|
||||
auto email = _emailField.get_text();
|
||||
auto password = _passwordField.get_text();
|
||||
|
||||
_loginButton.set_sensitive(false);
|
||||
_emailField.set_sensitive(false);
|
||||
_passwordField.set_sensitive(false);
|
||||
enableControls(false);
|
||||
_spinner.start();
|
||||
|
||||
std::cout << "Login clicked" << std::endl;
|
||||
std::cout << "Login: " << email << std::endl;
|
||||
std::cout << "Password: " << password << std::endl;
|
||||
|
||||
User user = co_await Api::login(email, password);
|
||||
try {
|
||||
auto user = co_await Api::login(email, password);
|
||||
auto app = this->get_application();
|
||||
auto mainWindow = new MainWindow();
|
||||
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;
|
||||
_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);
|
||||
}
|
||||
|
||||
@ -9,6 +9,8 @@
|
||||
#include <gtkmm/entry.h>
|
||||
#include <gtkmm/button.h>
|
||||
#include <gtkmm/spinner.h>
|
||||
#include <gtkmm/messagedialog.h>
|
||||
#include <memory>
|
||||
|
||||
class LoginWindow: public Gtk::Window {
|
||||
private:
|
||||
@ -16,13 +18,16 @@ private:
|
||||
Gtk::Entry _passwordField;
|
||||
Gtk::Button _loginButton;
|
||||
Gtk::Spinner _spinner;
|
||||
std::unique_ptr<Gtk::MessageDialog> _dialog;
|
||||
|
||||
public:
|
||||
LoginWindow();
|
||||
~LoginWindow() override = default;
|
||||
|
||||
void LoginClicked();
|
||||
void ValidateFields();
|
||||
void loginClicked();
|
||||
void validateFields();
|
||||
void showError(const std::string& message);
|
||||
void enableControls(bool enable);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -3,8 +3,34 @@
|
||||
//
|
||||
|
||||
#include "MainWindow.h"
|
||||
#include <gdkmm.h>
|
||||
|
||||
MainWindow::MainWindow() {
|
||||
set_title("Main window");
|
||||
//set_title("Main window");
|
||||
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);
|
||||
}
|
||||
|
||||
@ -5,9 +5,24 @@
|
||||
#ifndef AUTOCAT_GNOME_MAINWINDOW_H
|
||||
#define AUTOCAT_GNOME_MAINWINDOW_H
|
||||
|
||||
#include "TitleBar.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 {
|
||||
private:
|
||||
TitleBar _titleBar;
|
||||
Gtk::Paned _paned;
|
||||
Gtk::Label _masterLabel;
|
||||
Gtk::Label _detailLabel;
|
||||
Gtk::Box _masterBox;
|
||||
Gtk::Box _detailBox;
|
||||
Glib::RefPtr<Gtk::SizeGroup> _sizeGroup;
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
};
|
||||
|
||||
21
gui/TitleBar.cpp
Normal file
21
gui/TitleBar.cpp
Normal 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
23
gui/TitleBar.h
Normal 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
|
||||
23
main.cpp
23
main.cpp
@ -1,8 +1,29 @@
|
||||
#include "gui/MainWindow.h"
|
||||
#include "gui/LoginWindow.h"
|
||||
#include "services/Settings.h"
|
||||
#include "app/App.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[]) {
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@ -4,6 +4,6 @@
|
||||
|
||||
#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) {
|
||||
|
||||
}
|
||||
|
||||
@ -7,16 +7,21 @@
|
||||
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
class User {
|
||||
public:
|
||||
std::string login;
|
||||
std::string email;
|
||||
std::string token;
|
||||
std::optional<std::string> googleIdToken;
|
||||
std::optional<std::string> googleRefreshToken;
|
||||
|
||||
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)
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -3,47 +3,76 @@
|
||||
//
|
||||
|
||||
#include "Api.h"
|
||||
#include "Settings.h"
|
||||
#include <memory>
|
||||
|
||||
template<class...Args>
|
||||
struct callback {
|
||||
struct Callback {
|
||||
void(*function)(Args..., void*) = nullptr;
|
||||
std::unique_ptr<void, void(*)(void*)> state;
|
||||
void* state = nullptr;
|
||||
};
|
||||
|
||||
template<typename... Args, typename Lambda>
|
||||
callback<Args...> voidify(Lambda&& l) {
|
||||
Callback<Args...> voidify(Lambda&& l) {
|
||||
using Func = typename std::decay<Lambda>::type;
|
||||
std::unique_ptr<void, void(*)(void*)> data(
|
||||
new Func(std::forward<Lambda>(l)),
|
||||
+[](void* ptr){
|
||||
std::cout << "!!!!!!!!!!!!!!!!! Deleting data" << std::endl;
|
||||
delete (Func*)ptr;
|
||||
}
|
||||
);
|
||||
auto data = new Func(std::forward<Lambda>(l));
|
||||
return {
|
||||
+[](Args... args, void* v)->void {
|
||||
Func* f = static_cast< Func* >(v);
|
||||
(*f)(std::forward<Args>(args)...);
|
||||
delete f;
|
||||
},
|
||||
std::move(data)
|
||||
data
|
||||
};
|
||||
}
|
||||
|
||||
const std::string Api::_baseUrl = "https://vps.aliencat.pro:8443/";
|
||||
SoupSession* Api::_session = soup_session_new();
|
||||
|
||||
Task<User> Api::login(std::string email, std::string password) {
|
||||
std::string url = _baseUrl + "user/login";
|
||||
template<typename T>
|
||||
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 task = Task<User>();
|
||||
|
||||
auto callback = voidify<SoupSession*, SoupMessage*>([&](SoupSession* session, SoupMessage* message) {
|
||||
std::cout << "Callback called" << std::endl;
|
||||
task.set_result(User("qwe", "asdf"));
|
||||
auto promise = std::make_shared<boost::promise<T>>();
|
||||
auto callback = voidify<SoupSession*, SoupMessage*>([&, promise](SoupSession* session, SoupMessage* message) {
|
||||
if(message->status_code >= 200 && message->status_code < 300) {
|
||||
auto responseString = std::string(message->response_body->data, message->response_body->length);
|
||||
auto json = nlohmann::json::parse(responseString);
|
||||
if(json["success"].get<bool>()) {
|
||||
std::cout << "response: " << responseString << std::endl;
|
||||
auto user = json["data"].get<T>();
|
||||
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));
|
||||
}
|
||||
});
|
||||
soup_session_queue_message(_session, msg, callback.function, callback.state.get());
|
||||
|
||||
return task;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include <string>
|
||||
#include <libsoup/soup.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include "../models/User.h"
|
||||
#include "../coro/Task.h"
|
||||
|
||||
@ -15,8 +17,12 @@ private:
|
||||
static const std::string _baseUrl;
|
||||
static SoupSession* _session;
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static boost::future<T> post(const std::string& method, const nlohmann::json& params);
|
||||
|
||||
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
71
services/Settings.cpp
Normal 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
24
services/Settings.h
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user