/* QCommsy Copyright (C) 2020 niansa This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "ui_overview.h" #include "ui_info.h" #include "ui_login.h" #include "ui_failure.h" #include "ui_offline.h" #include "ui_postView.h" #include "libcommsy.hpp" #include "libcommsyauth.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define QCommsy_NAME "QCommsy" #define QCommsy_VERSION "1.2-stable" static QSettings *settings; static QApplication *a; static QMainWindow *w; class _UILoader { libCommsy *connector = nullptr; libCommsyAuth *auther = nullptr; bool cacheInvalidate = true; public: void failureWindow(); void overviewWindow(bool catchInvalidRoomError = true); void loginWindow(const QString& failure = ""); void infoWindow(); void postViewWindow(commsyPost *thispost, QString description); void offlineWindow(); _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 downloadAndOpenFile(QNetworkRequest mNetReq, const QString& filename) { auto mNetMan = new QNetworkAccessManager(); // Set SID in request so we're authenticated TODO: think about a cleaner way mNetReq.setRawHeader("Cookie", "SID=" + settings->value("server_sid").toString().toUtf8()); // Connect to downloading w->connect(mNetMan, &QNetworkAccessManager::finished, [this, mNetMan, filename] (QNetworkReply *mNetReply) { // Save and open downloaded file { // Initialise QString destPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + '/' + filename; QFile destFile(destPath); // Write downloaded data to file if (!destFile.open(QIODevice::WriteOnly)) { return failureWindow(); } destFile.write(mNetReply->readAll()); destFile.close(); // Open file in some application QDesktopServices::openUrl(QUrl::fromLocalFile(destPath)); } // Clean up mNetMan->deleteLater(); }); // Start download mNetMan->get(mNetReq); } }; void _UILoader::infoWindow() { 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 "
" "Git version: " GIT_CURRENT_SHA1 "
" "Build ID: " + QString::number(std::hash{}(__DATE__ __TIME__)) + "
" ); thisui->buildDateInfo->setText( "Build date:\n" __DATE__ " " __TIME__ ); // Add button handlers w->connect(thisui->backButton, &QPushButton::pressed, [this] () { loginWindow(); }); } void _UILoader::failureWindow() { Ui::failureWindow *thisui = new Ui::failureWindow; // Show initial UI thisui->setupUi(w); w->show(); // Show error message thisui->descriptionText->setText("Verbindung fehlgeschlagen
" "Ein interner Fehler ist aufgetreten
" "Dies ist sehr wahrscheinlich ein Programmfehler. Sollte das zu häufig passieren, kontaktiere bitte den Entwickler"); // Add button handlers w->connect(thisui->retryButton, &QPushButton::pressed, [this] () { loginWindow(); }); w->connect(thisui->logoutButton, &QPushButton::pressed, [this] () { logout(); loginWindow(); }); } void _UILoader::offlineWindow() { Ui::offlineWindow *thisui = new Ui::offlineWindow; // Show initial UI thisui->setupUi(w); w->show(); // Add button handlers w->connect(thisui->logoutButton, &QPushButton::pressed, [this] () { logout(); loginWindow(); }); } void _UILoader::loginWindow(const QString& failure) { Ui::loginWindow *thisui = new Ui::loginWindow; // Check if logged in already if (settings->contains("server_sid")) { return overviewWindow(); } // 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); thisui->adressLine->setText(settings->value("server_url", thisui->adressLine->text()).toString()); thisui->roomLine->setText(settings->value("server_room", "").toString()); thisui->usernameLine->setText(settings->value("server_username", "").toString()); thisui->passwordLine->setText(settings->value("server_password", "").toString()); } // Set failure text thisui->failureText->setText(failure); // Connect connection handlers w->connect(auther, &libCommsyAuth::authDone, [thisui] (QNetworkReply *) { // Finished authentication; reenable login button thisui->loginButton->setDisabled(false); }); w->connect(auther, &libCommsyAuth::authDone, [this, thisui] (QNetworkReply *netReply) { // Finished authentication; try to login // Login try { settings->setValue("server_sid", auther->getSID(netReply)); cacheInvalidate = true; overviewWindow(false); auther->deleteLater(); } catch (authFailureError&) { thisui->failureText->setText("Die eingegebenen Anmeldedaten sind ungültig"); } catch (libCommsy::invalidRoomError&) { thisui->failureText->setText("Der angegebene Raum existiert nicht"); } }); w->connect(auther, &libCommsyAuth::connectionDone, [this, thisui] (QNetworkReply *netReply) { // Finished test connection auther->sendAuth(auther->getAuthUrl(netReply), thisui->usernameLine->text(), thisui->passwordLine->text()); }); // Add button handlers w->connect(thisui->loginButton, &QPushButton::pressed, [this, thisui] () { // Check input lines if (thisui->adressLine->text().isEmpty() or thisui->usernameLine->text().isEmpty() or thisui->roomLine->text().isEmpty() or thisui->passwordLine->text().isEmpty()) { return; } // Make sure the adressLine doesn't end with / if (thisui->adressLine->text().endsWith("/")) { thisui->adressLine->backspace(); } // Save basic server credentials bool saveAllData = thisui->saveBox->checkState() == Qt::Checked; settings->setValue("server_save", saveAllData); settings->setValue("server_url", thisui->adressLine->text()); settings->setValue("server_room", thisui->roomLine->text()); if (saveAllData) { settings->setValue("server_username", thisui->usernameLine->text()); settings->setValue("server_password", thisui->passwordLine->text()); } // Disable login button thisui->loginButton->setDisabled(true); // Connect auther->connectionInit(thisui->adressLine->text()); }); } void _UILoader::postViewWindow(commsyPost *thispost, QString description) { Ui::postViewWindow *thisui = new Ui::postViewWindow; // Show initial UI thisui->setupUi(w); w->show(); thisui->statusText->setVisible(false); // Remove trailing space from description if (description.size() != 0 and description.back() == '\n') { description.chop(1); } // Update informations thisui->titleText->setText(QString::fromStdString(thispost->name)); thisui->metaText->setText(QString::fromStdString(thispost->meta)); thisui->descriptionText->setText(description); // Update file list if required if (thispost->files.size() != 0) { for (const auto& thisfile : thispost->files) { thisui->fileList->addItem(QString::fromStdString(thisfile.name)); } } else { // If no files exist, hide the list thisui->filesInfo->setHidden(true); thisui->fileList->setHidden(true); } // Add button handlers w->connect(thisui->fileList, &QListWidget::activated, [this, thisui, thispost] (QModelIndex index) { // Get full file URL commsyFile *thisfile = &thispost->files[static_cast(index.row())]; QUrl fullFileUrl(settings->value("server_url").toString() + QString::fromStdString(thisfile->url)); // Start download downloadAndOpenFile(QNetworkRequest(fullFileUrl), QString::fromStdString(thisfile->name)); // Show download status für 5 seconds thisui->statusText->setVisible(true); QTimer::singleShot(5 * 1000, thisui->statusText, &QLabel::hide); }); w->connect(thisui->backButton, &QPushButton::pressed, [this] () { loginWindow(); }); } void _UILoader::overviewWindow(bool catchInvalidRoomError) { Ui::overviewWindow *thisui = new Ui::overviewWindow; // Following code is blocking a->processEvents(); // Connect if required try { reconnect(); } catch (libCommsy::invalidSIDError&) { logout(); return loginWindow("Die Sitzung ist abgelaufen - bitte erneut anmelden"); } catch (libCommsy::invalidRoomError& exception) { if (catchInvalidRoomError) { logout(); return loginWindow("Der Raum existiert nicht mehr"); } else { throw exception; } } catch (libCommsy::connectionFailError&) { return offlineWindow(); } catch (std::exception&) { return failureWindow(); } // Show initial UI thisui->setupUi(w); w->show(); // Add button handlers w->connect(thisui->postList, &QListWidget::activated, [this, thisui] (QModelIndex index) { auto postID = static_cast(index.row()); commsyPost *thispost = connector->getPost(postID); thisui->centralwidget->setDisabled(true); thispost->unread = false; // Mark post as read locally // Run blocking code a->processEvents(); postViewWindow(thispost, QString::fromStdString(*connector->getDescription(postID))); }); w->connect(thisui->logoutButton, &QPushButton::pressed, [this] () { logout(); loginWindow(); }); w->connect(thisui->infoButton, &QPushButton::pressed, [this] () { infoWindow(); }); // Get font used for unread posts QFont unreadFont = QFont(); unreadFont.setBold(true); // Refresh view thisui->postCounter->setText(QString::number(connector->numposts) + " Einträge"); thisui->postList->clear(); for (const auto& thispost : connector->posts) { QListWidgetItem *thisitem = new QListWidgetItem(QString::fromStdString(thispost.name), thisui->postList); if (thispost.unread) { thisitem->setFont(unreadFont); } QString postIcon; switch(thispost.taskState) { case taskState::done: postIcon = "checkbox-checked-symbolic"; break; case taskState::inProgress: postIcon = "clock"; break; case taskState::todo: postIcon = "emblem-important-symbolic"; break; default: postIcon = "arrow-right"; break; } thisitem->setIcon(QIcon::fromTheme(postIcon)); } } 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 a = new QApplication(argc, argv); w = new QMainWindow; _UILoader UILoader; // Set theme //a->setStyle(QStyleFactory::create("android")); // Load initial window UILoader.loginWindow(); // Run forever return a->exec(); }