From 1d26801a266d3b2d93632660df130cc884e9fd8d Mon Sep 17 00:00:00 2001 From: niansa Date: Sun, 9 Aug 2020 11:03:52 +0200 Subject: [PATCH] Initial commit --- .gitignore | 73 +++++++++ QCommsy.pro | 39 +++++ failure.ui | 98 ++++++++++++ info.ui | 66 ++++++++ libcommsy.hpp | 389 ++++++++++++++++++++++++++++++++++++++++++++++ libcommsyauth.hpp | 56 +++++++ login.ui | 130 ++++++++++++++++ main.cpp | 225 +++++++++++++++++++++++++++ offline.ui | 84 ++++++++++ overview.ui | 71 +++++++++ 10 files changed, 1231 insertions(+) create mode 100644 .gitignore create mode 100644 QCommsy.pro create mode 100644 failure.ui create mode 100644 info.ui create mode 100644 libcommsy.hpp create mode 100644 libcommsyauth.hpp create mode 100644 login.ui create mode 100644 main.cpp create mode 100644 offline.ui create mode 100644 overview.ui diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fab7372 --- /dev/null +++ b/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/QCommsy.pro b/QCommsy.pro new file mode 100644 index 0000000..fd4aa17 --- /dev/null +++ b/QCommsy.pro @@ -0,0 +1,39 @@ +QT += core gui network + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +CONFIG += c++17 + +# The following define makes your compiler emit warnings if you use +# any Qt feature that has been marked deprecated (the exact warnings +# depend on your compiler). Please consult the documentation of the +# deprecated API in order to know how to port your code away from it. +DEFINES += QT_DEPRECATED_WARNINGS + +# You can also make your code fail to compile if it uses deprecated APIs. +# In order to do so, uncomment the following line. +# You can also select to disable deprecated APIs only up to a certain version of Qt. +#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + main.cpp + +HEADERS += \ + libcommsy.hpp \ + libcommsyauth.hpp + +FORMS += \ + failure.ui \ + info.ui \ + login.ui \ + offline.ui \ + overview.ui + +LIBS += -lcurlpp -lcurl +LIBS += -lfuse +LIBS += -lgumbo + +# Default rules for deployment. +qnx: target.path = /tmp/$${TARGET}/bin +else: unix:!android: target.path = /opt/$${TARGET}/bin +!isEmpty(target.path): INSTALLS += target diff --git a/failure.ui b/failure.ui new file mode 100644 index 0000000..3e86ba1 --- /dev/null +++ b/failure.ui @@ -0,0 +1,98 @@ + + + failureWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + + 72 + + + + 🙈 + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::AlignCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Abmelden + + + + + + + nochmal versuchen + + + true + + + + + + + + + + + diff --git a/info.ui b/info.ui new file mode 100644 index 0000000..e324507 --- /dev/null +++ b/info.ui @@ -0,0 +1,66 @@ + + + infoWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Okay + + + + + + + + + diff --git a/libcommsy.hpp b/libcommsy.hpp new file mode 100644 index 0000000..fb6999a --- /dev/null +++ b/libcommsy.hpp @@ -0,0 +1,389 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +using json = nlohmann::json; + +#include + +class invalidSIDError : public std::exception {}; +class invalidRoomError : public std::exception {}; +class invalidPostError : public std::exception {}; +class connectionFailError : public std::exception {}; +class parsingNoSuchIDError : public std::exception {}; +class parsingNoSuchTagError : public std::exception {}; +class descDownloadError : public std::exception {}; + + + +static std::string server_url; +static std::string server_sid; +static std::string room; + + + +std::string ltrim(const std::string& s) { + static const std::regex lws{"^[[:space:]]*", std::regex_constants::extended}; + return std::regex_replace(s, lws, ""); +} +std::string rtrim(const std::string& s) { + static const std::regex tws{"[[:space:]]*$", std::regex_constants::extended}; + return std::regex_replace(s, tws, ""); +} +std::string trim(const std::string& s) { + return ltrim(rtrim(s)); +} + +std::string get_filename(const std::string& path) { + return path.substr(path.find_last_of("/\\") + 1); +} + +std::string clean_spaces(const std::string& s) { + static const std::regex tws{"[ ]{2,}", std::regex_constants::extended}; + std::string newstr = std::regex_replace(s, tws, ""); + std::replace(newstr.begin(), newstr.end(), '\n', ' '); + newstr.erase(0, 4); + return newstr; +} + +std::vector merge_strvects(std::vector base, const std::vector &addition) { + base.insert(base.end(), addition.begin(), addition.end()); + return base; +} + +static long curlreq(std::stringstream &responsebuffer, std::string SID, std::string URL) { + // Initialise variables + curlpp::Cleanup cleaner; + curlpp::Easy request; + // Set the writer callback to enable cURL to write result in a memory area + request.setOpt(new curlpp::options::WriteStream(&responsebuffer)); + // Setting the URL to retrive. + request.setOpt(new curlpp::options::Url(URL)); + // Set SID cookie + std::list header; + header.push_back("Cookie: SID=" + SID); + request.setOpt(new curlpp::options::HttpHeader(header)); + // Perform request + request.perform(); + // Return result + return curlpp::infos::ResponseCode::get(request); +} + +void gumbo_search_by_attr(std::vector &elemvect, GumboNode* node, std::string attrname, std::string searchword, GumboTag expectedtag) { + if (node->type != GUMBO_NODE_ELEMENT) { + return; + } + GumboAttribute* hclass; + if (node->v.element.tag == expectedtag && + (hclass = gumbo_get_attribute(&node->v.element.attributes, attrname.c_str()))) { + if (hclass->value == searchword) { + elemvect.push_back(node); + } + } + GumboVector* children = &node->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { + gumbo_search_by_attr(elemvect, static_cast(children->data[i]), attrname, searchword, expectedtag); + } +} +void gumbo_search_by_class(std::vector &elemvect, GumboNode* node, std::string searchword, GumboTag expectedtag) { + return gumbo_search_by_attr(elemvect, node, "class", searchword, expectedtag); +} +GumboNode *gumbo_search_by_id(GumboNode* node, std::string searchword, GumboTag expectedtag) { + std::vector elemvect; + gumbo_search_by_attr(elemvect, node, "id", searchword, expectedtag); + // Use first node found + if (elemvect.size() > 0) { + return elemvect[0]; + } + // If no nodes were found, panic() + throw parsingNoSuchIDError(); + return new GumboNode; +} + +void gumbo_search_by_tag(std::vector &elemvect, GumboNode* node, GumboTag searchedtag) { + if (node->type != GUMBO_NODE_ELEMENT) { + return; + } + if (node->v.element.tag == searchedtag) { + elemvect.push_back(node); + } + GumboVector* children = &node->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { + gumbo_search_by_tag(elemvect, static_cast(children->data[i]), searchedtag); + } +} + +static std::string gumbo_cleantext(GumboNode* node) { + if (node->type == GUMBO_NODE_TEXT) { + return std::string(node->v.text.text); + } else if (node->type == GUMBO_NODE_ELEMENT && + node->v.element.tag != GUMBO_TAG_SCRIPT && + node->v.element.tag != GUMBO_TAG_STYLE) { + std::string contents = ""; + GumboVector* children = &node->v.element.children; + for (unsigned int i = 0; i < children->length; ++i) { + const std::string text = gumbo_cleantext(reinterpret_cast (children->data[i])); + if (i != 0 && !text.empty()) { + contents.append(" "); + } + contents.append(text); + } + return contents; + } else { + return ""; + } +} + +std::vector gumbo_get_attr(GumboNode *node, std::string attrkey, GumboTag expected_tag) { + std::vector attrvals; + GumboNode *childnode; + GumboVector* children = &node->v.element.children; + std::vector toappend; + // Check if current element is already the right one + if (node->v.element.tag == expected_tag) { + // Return this elements wanted attribute key + return {gumbo_get_attribute(&node->v.element.attributes, attrkey.c_str())->value}; + } + // Check if This is a node element + else if (node->type != GUMBO_NODE_ELEMENT) { + return {}; + } + // Iterate through child nodes + for (unsigned int it = 0; it < children->length; ++it) { + childnode = reinterpret_cast (children->data[it]); + if (childnode->v.element.tag == expected_tag) { // If node is the expected tag; use it + attrvals.push_back(gumbo_get_attribute(&childnode->v.element.attributes, attrkey.c_str())->value); + } else if (childnode->type == GUMBO_NODE_ELEMENT) { // Else; iterate through its child nodes + toappend = gumbo_get_attr(childnode, attrkey, expected_tag); + attrvals = merge_strvects(attrvals, toappend); + } + } + // Return the final result + return attrvals; +} + +std::string gumbo_find_text_by_tag(GumboNode *node, GumboTag searchtag) { + GumboNode *childnode; + GumboVector* children = &node->v.element.children; + // Iterate through childs + for (unsigned int it = 0; it < children->length; ++it) { + childnode = reinterpret_cast (children->data[it]); + if (childnode->v.element.tag == searchtag) { // If node is the expected tag; check content + return trim(gumbo_cleantext(childnode)); + } + } + throw parsingNoSuchTagError(); + return ""; +} + + +auto get_posts(GumboNode *node) { + std::vector posts; + gumbo_search_by_class(posts, node, "uk-comment", GUMBO_TAG_ARTICLE); + return posts; +} + +std::string get_post_name(GumboNode *node) { + std::vector titlenodes; + gumbo_search_by_class(titlenodes, node, "uk-comment-title", GUMBO_TAG_H4); + return trim(gumbo_cleantext(titlenodes[0])); +} + +std::string get_post_id(GumboNode *node) { + return gumbo_get_attr(node, "data-item-id", GUMBO_TAG_ARTICLE)[0]; +} + +std::string get_post_meta(GumboNode *node) { + std::vector metanodes; + gumbo_search_by_class(metanodes, node, "uk-comment-meta", GUMBO_TAG_DIV); + return clean_spaces(trim(gumbo_cleantext(metanodes[1]))); +} + +std::string get_post_url(GumboNode *node) { + std::vector titlenodes; + gumbo_search_by_class(titlenodes, node, "uk-comment-title", GUMBO_TAG_H4); + return gumbo_get_attr(titlenodes[0], "href", GUMBO_TAG_A)[0]; +} + +std::vector> get_post_files(GumboNode *node) { + std::vector metanodes; + std::vector fileurls; + std::vector filenames; + std::vector> filenameurlmap; + std::map tmpmap; + // Get meta nodes + gumbo_search_by_class(metanodes, node, "uk-comment-meta", GUMBO_TAG_DIV); + // Get URLs + fileurls = gumbo_get_attr(metanodes[2], "href", GUMBO_TAG_A); + // Get filenames + filenames = gumbo_get_attr(metanodes[2], "title", GUMBO_TAG_A); + // Generate map + auto urlit = fileurls.begin(); + auto nameit = filenames.begin(); + while (true) { + // Break if last item was reached + if (urlit == fileurls.end() or nameit == filenames.end()) { + break; + } + // Generate temporary map + tmpmap = {}; + tmpmap[*nameit] = *urlit; + // Append it to the result vector map + filenameurlmap.push_back(tmpmap); + // Get next item in both vectors + urlit++; nameit++; + } + return filenameurlmap; +} + +std::string get_post_desc(std::string post_url) { + std::string material_id; + std::stringstream httpcontent; + GumboOutput *post_document; + GumboNode *desc_node; + std::vector results; + // Get material ID + material_id = get_filename(post_url); + // Download post + long statuscode = curlreq(httpcontent, server_sid, post_url); + // Check statuscode + if (statuscode != 200) { + throw descDownloadError(); + } + // Parse post + post_document = gumbo_parse(httpcontent.str().c_str()); + // Get description element + desc_node = gumbo_search_by_id(post_document->root, "description" + material_id, GUMBO_TAG_DIV); + // Extract description + gumbo_search_by_tag(results, desc_node, GUMBO_TAG_P); + // Cencenate occurencies + std::string result_string; + for (auto it = results.begin(); it != results.end(); it++) { + result_string.append(trim(gumbo_cleantext(*it)) + "\n"); + } + // Return first occurence + return result_string; +} + + +struct commsyPost { + std::string name; + std::string id; + std::string description; + std::string meta; + std::string url; + std::map files; +}; + + +#define libCommsy_NAME "libcommsy" +#define libCommsy_VERSION "1.0" +class libCommsy { +public: + std::vector posts; + unsigned long numposts; + + bool postExists(unsigned long postID) { + return postID < numposts; + } + + commsyPost *getPost(unsigned long postID) { + // Check if post exists + if (not postExists(postID)) { + throw invalidPostError(); + } + // Return post pointer + return &posts[postID]; + } + + std::string *getDescription(unsigned long postID) { + // Get post + commsyPost *thispost = getPost(postID); + // Check if post description was downloaded already + if (thispost->description.empty()) { + // Download post + thispost->description = get_post_desc(server_url + thispost->url); + } + // Return it + return &thispost->description; + } + + libCommsy(std::string _server_url, std::string _server_sid, std::string _room) { + // Define required variables + server_url = _server_url; + server_sid = _server_sid; + room = _room; + std::string lastID; + std::stringstream httpcontent; + GumboOutput *document; + long statuscode; + numposts = 0; + while (1) { + // Check connection and download feed + try { + statuscode = curlreq(httpcontent, server_sid, server_url + "/room/" + room + "/feed/10/date?lastId=" + lastID); + } catch (std::exception&) { + throw connectionFailError(); + } + if (statuscode == 302) { + throw invalidSIDError(); + } else if (statuscode == 500) { + throw invalidRoomError(); + } else if (statuscode != 200) { + throw connectionFailError(); + } + // Do some stuff + document = gumbo_parse(httpcontent.str().c_str()); + httpcontent.str(std::string()); // Clear buffer just in case we need it later + // Get posts + auto gumboPosts = get_posts(document->root); + if (gumboPosts.size() == 0) { + // Stop fetching more data + break; + } + // Map posts and their corresponding URL to a number + for (auto it = gumboPosts.begin(); it != gumboPosts.end(); it++) { + // Create post struct + commsyPost thispost; + { + // Get posts name + thispost.name = get_post_name(*it); + // Get posts ID + thispost.id = get_post_id(*it); + // Get posts meta string + thispost.meta = get_post_meta(*it); + // Get posts URL + thispost.url = get_post_url(*it); + // Get posts files + auto files = get_post_files(*it); + for (const auto& filemap : files) { + thispost.files.insert(filemap.begin(), filemap.end()); + } + } + // Append to posts vector + posts.push_back(thispost); + // Increment post counter + numposts++; + // Get lastID + lastID = posts.back().id; + } + } + } +}; diff --git a/libcommsyauth.hpp b/libcommsyauth.hpp new file mode 100644 index 0000000..87354bc --- /dev/null +++ b/libcommsyauth.hpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define libCommsyAuth_NAME "libcommsyauth" +#define libCommsyAuth_VERSION "0.1-alpha" + +class authFailureError : public std::exception {}; + + +class libCommsyAuth : QObject { +public: + QNetworkReply *connect(const QUrl& serverBaseUrl) { + // Initialise variables + QNetworkAccessManager mNetMan(this); + QNetworkRequest mNetReq; + // Initialise request + //mNetReq->setTransferTimeout(15000); + mNetReq.setUrl(serverBaseUrl); + mNetReq.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + return mNetMan.get(mNetReq); + } + + QString getAuthUrl(const QNetworkReply *reply) { + return reply->url().toString(); + } + + QNetworkReply *sendAuth(const QString& authUrl, const QString& username, const QString& password) { + QNetworkAccessManager mNetMan(this); + QNetworkRequest mNetReq; + QUrlQuery mNetQuery; + // Set auth credentials + mNetQuery.addQueryItem("user_id", username); + mNetQuery.addQueryItem("password", password); + // Create request + mNetReq.setUrl(authUrl + "&mod=context&fct=login"); + mNetReq.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded"); + // Perform the request + return mNetMan.post(mNetReq, mNetQuery.toString(QUrl::FullyEncoded).toUtf8()); + } + + QString getSID(QNetworkReply *reply) { + auto cookies = QNetworkCookie::parseCookies(reply->rawHeader("Set-Cookie")); + for (const auto& cookie : cookies) { + if (cookie.name() == "SID") { + return cookie.value(); + } + } + throw authFailureError(); + } +}; diff --git a/login.ui b/login.ui new file mode 100644 index 0000000..648481d --- /dev/null +++ b/login.ui @@ -0,0 +1,130 @@ + + + loginWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 8 + + + + color: red; + + + + + + + + + + Qt::ImhNoAutoUppercase|Qt::ImhNoPredictiveText|Qt::ImhPreferLowercase|Qt::ImhUrlCharactersOnly + + + https://unterricht.sh.schulcommsy.de + + + Server Adresse + + + + + + + + + + Kennung + + + + + + + Qt::ImhDigitsOnly + + + + + + 7 + + + Raum + + + + + + + + + + QLineEdit::Password + + + SID + + + + + + + Daten speichern + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Anmelden + + + + + + + + + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..fe64568 --- /dev/null +++ b/main.cpp @@ -0,0 +1,225 @@ +#include "ui_overview.h" +#include "ui_info.h" +#include "ui_login.h" +#include "ui_failure.h" +#include "ui_offline.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libcommsy.hpp" +#include "libcommsyauth.hpp" + +#define QCommsy_NAME "QCommsy" +#define QCommsy_VERSION "0.1-alpha" + +QSettings *settings; + + +class _UILoader { + libCommsy *connector = nullptr; + libCommsyAuth *auther; + bool cacheInvalidate = true; + +public: + _UILoader() { + auther = new libCommsyAuth(); + } + + void reconnect() { + // Destroy old connector + if (connector != nullptr) { + if (not cacheInvalidate) { + return; + } + delete connector; + connector = nullptr; + } + cacheInvalidate = false; + // Connect to commsy + connector = new libCommsy(settings->value("server_url").toString().toStdString(), settings->value("server_sid").toString().toStdString(), settings->value("server_room").toString().toStdString()); + } + + void logout() { + settings->remove("server_sid"); + } + + void overviewWindow(QMainWindow *w, bool catchInvalidRoomError = true); + void failureWindow(QMainWindow *w); + void loginWindow(QMainWindow *w, const QString& failure = ""); + void infoWindow(QMainWindow *w); + void offlineWindow(QMainWindow *w); +}; + +void _UILoader::infoWindow(QMainWindow *w) { + Ui::infoWindow *thisui = new Ui::infoWindow; + // Show initial UI + thisui->setupUi(w); + w->show(); + // Update version info + thisui->versionInfo->setText( + "App frontend: " QCommsy_NAME " version " QCommsy_VERSION "
" + "Authentication backend: " libCommsyAuth_NAME " version " libCommsyAuth_VERSION "
" + "Scrapping backend: " libCommsy_NAME " version " libCommsy_VERSION + ); + // Add button handlers + w->connect(thisui->backButton, &QPushButton::pressed, [this, w] () { + loginWindow(w); + }); +} + +void _UILoader::failureWindow(QMainWindow *w) { + Ui::failureWindow *thisui = new Ui::failureWindow; + QString reason; + // Show initial UI + thisui->setupUi(w); + w->show(); + // Get error message + reason = "Ein interner Fehler ist aufgetreten
" + "Dies ist sehr wahrscheinlich ein Programmfehler. Sollte das zu häufig passieren, kontaktiere bitte den Entwickler"; + // Show error message + thisui->descriptionText->setText("Verbindung fehlgeschlagen
" + reason); + // Add button handlers + w->connect(thisui->retryButton, &QPushButton::pressed, [this, w] () { + loginWindow(w); + }); + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { + logout(); + loginWindow(w); + }); +} +void _UILoader::offlineWindow(QMainWindow *w) { + Ui::offlineWindow *thisui = new Ui::offlineWindow; + QString reason; + // Show initial UI + thisui->setupUi(w); + w->show(); + // Add button handlers + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { + logout(); + loginWindow(w); + }); +} + +void _UILoader::loginWindow(QMainWindow *w, const QString& failure) { + Ui::loginWindow *thisui = new Ui::loginWindow; + // Check if logged in already + if (settings->contains("server_sid")) { + return overviewWindow(w); + } + // Show initial UI + thisui->setupUi(w); + w->show(); + // Restore login data if wanted + bool restoreForm = settings->value("server_save", false).toBool(); + if (restoreForm) { + thisui->saveBox->setCheckState(Qt::Checked); + //... TODO + } + // Set failure text + thisui->failureText->setText(failure); + // Add button handlers + w->connect(thisui->loginButton, &QPushButton::pressed, [this, thisui, w] () { + // Check input lines + if (thisui->adressLine->text().isEmpty() or thisui->usernameLine->text().isEmpty() or thisui->roomLine->text().isEmpty() or thisui->passwordLine->text().isEmpty()) { + return; + } + // Save basic server credentials + settings->setValue("server_save", thisui->saveBox->checkState() == Qt::Checked); + settings->setValue("server_url", thisui->adressLine->text()); + settings->setValue("server_room", thisui->roomLine->text()); + // Connect + QNetworkReply *netReply = auther->connect(thisui->adressLine->text()); + w->connect(netReply, &QNetworkReply::readyRead, [this, thisui, w, &netReply] () { // SIGSEGV + // Get SID + netReply = auther->sendAuth(auther->getAuthUrl(netReply), thisui->usernameLine->text(), thisui->passwordLine->text()); + w->connect(netReply, &QNetworkReply::readyRead, [this, thisui, w, netReply] () { + // Login + try { + settings->setValue("server_sid", auther->getSID(netReply)); + cacheInvalidate = true; + overviewWindow(w, false); + } catch (authFailureError&) { + thisui->failureText->setText("Die eingegebenen Anmeldedaten sind ungültig"); + } catch (invalidRoomError&) { + thisui->failureText->setText("Der angegebene Raum existiert nicht"); + } + }); + }); + }); +} + +void _UILoader::overviewWindow(QMainWindow *w, bool catchInvalidRoomError) { + Ui::overviewWindow *thisui = new Ui::overviewWindow; + // Connect if required + try { + reconnect(); + } catch (invalidSIDError&) { + logout(); + return loginWindow(w, "Die Sitzung ist abgelaufen - bitte erneut anmelden"); + } catch (invalidRoomError& exception) { + if (catchInvalidRoomError) { + logout(); + return loginWindow(w, "Der Raum existiert nicht mehr"); + } else { + throw exception; + } + } catch (connectionFailError&) { + return offlineWindow(w); + } catch (std::exception&) { + return failureWindow(w); + } + // Show initial UI + thisui->setupUi(w); + w->show(); + // Refresh view + thisui->postCounter->setText(QString::number(connector->numposts) + " Einträge"); + thisui->postList->clear(); + for (const auto& thispost : connector->posts) { + thisui->postList->addItem(QString::fromStdString(thispost.name)); + } + // Add button handlers + w->connect(thisui->postList, &QListWidget::activated, [this, thisui] (QModelIndex index) { + QDesktopServices::openUrl(QUrl(settings->value("server_url").toString() + QString::fromStdString(connector->getPost(index.row())->url))); + }); + w->connect(thisui->logoutButton, &QPushButton::pressed, [this, w] () { + logout(); + loginWindow(w); + }); + w->connect(thisui->infoButton, &QPushButton::pressed, [this, w] () { + infoWindow(w); + }); +} + + + + +int main(int argc, char *argv[]) { + // Initalise settings storage + QCoreApplication::setOrganizationName("QCommsy"); + QCoreApplication::setOrganizationDomain("qcommsy.github.io"); + QCoreApplication::setApplicationName("QCommsy"); + settings = new QSettings(); + + //Initialise variables + QApplication a(argc, argv); + QMainWindow w; + _UILoader UILoader; + + // Set theme + settings->clear(); + a.setStyle(QStyleFactory::create("windows")); + + // Load initial window + UILoader.loginWindow(&w); + + // Run forever + return a.exec(); +} diff --git a/offline.ui b/offline.ui new file mode 100644 index 0000000..3b98621 --- /dev/null +++ b/offline.ui @@ -0,0 +1,84 @@ + + + offlineWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + + 72 + + + + 📴 + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">Entweder dein Handy oder der Server ist offline<br/></span>Wenn du dir ganz sicher bist, dass du eine funktionierende Internet Verbindung hast, ist sehr wahrscheinlich der Server wegen Wartungsarbeiten zurzeit offline.</p></body></html> + + + Qt::AlignCenter + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Abmelden + + + + + + + + + diff --git a/overview.ui b/overview.ui new file mode 100644 index 0000000..80e8b49 --- /dev/null +++ b/overview.ui @@ -0,0 +1,71 @@ + + + overviewWindow + + + + 0 + 0 + 323 + 489 + + + + QCommsy + + + + + + + QAbstractItemView::NoEditTriggers + + + + + + + + + QCommsy + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Abmelden + + + + + + + Info + + + true + + + + + + + + + + +