Other: fix bug in login screen <-> main window transition. [skip ci]

Other: fixed bug with split mode toggle. [skip ci]

Other: fix QML warnings. [skip ci]

Other: fix showMainWindow gRPC event binding. [skip ci].

QML Fixes [skip ci]

Other: wait for EventStreamReader thread to finish on exit.

Other: made BridgeMonitor generic, as ProcessMonitor. [skip ci]
This commit is contained in:
Xavier Michelon
2022-08-11 11:27:05 +02:00
committed by Jakub
parent 4ed9625959
commit ed904c2bdd
25 changed files with 149 additions and 125 deletions

View File

@ -19,7 +19,6 @@
#include "MainWindow.h"
#include "AppController.h"
#include "GRPCServerWorker.h"
#include <bridgepp/BridgeUtils.h>
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/Worker/Overseer.h>
@ -58,35 +57,30 @@ int main(int argc, char **argv)
QApplication::setOrganizationDomain("proton.ch");
QApplication::setQuitOnLastWindowClosed(true);
app().log().setEchoInConsole(true);
app().log().info(QString("%1 started.").arg(applicationName));
Log& log = app().log();
log.setEchoInConsole(true);
log.info(QString("%1 started.").arg(applicationName));
MainWindow window(nullptr);
app().setMainWindow(&window);
window.setWindowTitle(QApplication::applicationName());
window.show();
GRPCServerWorker *serverWorker = new GRPCServerWorker(nullptr);
auto *serverWorker = new GRPCServerWorker(nullptr);
QObject::connect(serverWorker, &Worker::started, []() { app().log().info("Server worker started."); });
QObject::connect(serverWorker, &Worker::finished, []() { app().log().info("Server worker finished."); });
QObject::connect(serverWorker, &Worker::error, [](QString const &message) {
throw Exception(QString("gRPC Server encountered an error: %1").arg(message));
});
QObject::connect(serverWorker, &Worker::error, [&](QString const &message) { app().log().error(message); qApp->exit(EXIT_FAILURE); });
UPOverseer overseer = std::make_unique<Overseer>(serverWorker, nullptr);
overseer->startWorker(true);
qint32 const exitCode = QApplication::exec();
serverWorker->stop();
while (!overseer->isFinished())
{
QThread::msleep(10);
}
if (!overseer->wait(5000))
log.warn("gRPC server took too long to finish.");
app().log().info(QString("%1 exiting with code %2.").arg(applicationName).arg(exitCode));
return exitCode;
}
catch (Exception const &e)
{

View File

@ -18,9 +18,9 @@
#include "AppController.h"
#include "QMLBackend.h"
#include "BridgeMonitor.h"
#include <bridgepp/GRPC/GRPCClient.h>
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/ProcessMonitor.h>
#include <bridgepp/Log/Log.h>
@ -51,14 +51,14 @@ AppController::AppController()
//****************************************************************************************************************************************************
/// \return The bridge worker, which can be null if the application was run in 'attach' mode (-a command-line switch).
//****************************************************************************************************************************************************
BridgeMonitor *AppController::bridgeMonitor() const
ProcessMonitor *AppController::bridgeMonitor() const
{
if (!bridgeOverseer_)
return nullptr;
// null bridgeOverseer is OK, it means we run in 'attached' mode (app attached to an already runnning instance of Bridge).
// but if bridgeOverseer is not null, its attached worker must be a valid BridgeMonitor instance.
auto *monitor = dynamic_cast<BridgeMonitor*>(bridgeOverseer_->worker());
// but if bridgeOverseer is not null, its attached worker must be a valid ProcessMonitor instance.
auto *monitor = dynamic_cast<ProcessMonitor*>(bridgeOverseer_->worker());
if (!monitor)
throw Exception("Could not retrieve bridge monitor");

View File

@ -21,7 +21,6 @@
class QMLBackend;
class BridgeMonitor;
namespace bridgepp
@ -29,6 +28,7 @@ namespace bridgepp
class Log;
class Overseer;
class GRPCClient;
class ProcessMonitor;
}
@ -50,7 +50,7 @@ public: // member functions.
bridgepp::GRPCClient& grpc() { return *grpc_; } ///< Return a reference to the GRPC client.
bridgepp::Log& log() { return *log_; } ///< Return a reference to the log.
std::unique_ptr<bridgepp::Overseer>& bridgeOverseer() { return bridgeOverseer_; }; ///< Returns a reference the bridge overseer
BridgeMonitor* bridgeMonitor() const; ///< Return the bridge worker.
bridgepp::ProcessMonitor* bridgeMonitor() const; ///< Return the bridge worker.
private: // member functions
AppController(); ///< Default constructor.

View File

@ -82,7 +82,6 @@ endif()
add_executable(bridge-gui
Resources.qrc
AppController.cpp AppController.h
BridgeMonitor.cpp BridgeMonitor.h
EventStreamWorker.cpp EventStreamWorker.h
main.cpp
Pch.h

View File

@ -31,8 +31,8 @@ using namespace bridgepp;
EventStreamReader::EventStreamReader(QObject *parent)
: Worker(parent)
{
connect(this, &EventStreamReader::started, [&]() { app().log().debug("EventStreamReader started");});
connect(this, &EventStreamReader::finished, [&]() { app().log().debug("EventStreamReader finished");});
connect(this, &EventStreamReader::started, [&]() { app().log().debug("EventStreamReader started"); });
connect(this, &EventStreamReader::finished, [&]() { app().log().debug("EventStreamReader finished"); });
connect(this, &EventStreamReader::error, &app().log(), &Log::error);
}

View File

@ -73,6 +73,18 @@ void QMLBackend::init()
this->retrieveUserList();
}
//****************************************************************************************************************************************************
/// \param timeoutMs The timeout after which the function should return false if the event stream reader is not finished. if -1 one, the function
/// never times out.
/// \return false if and only if the timeout delay was reached.
//****************************************************************************************************************************************************
bool QMLBackend::waitForEventStreamReaderToFinish(qint32 timeoutMs)
{
return eventStreamOverseer_->wait(timeoutMs);
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
@ -87,6 +99,7 @@ void QMLBackend::connectGrpcEvents()
connect(client, &GRPCClient::reportBugFinished, this, &QMLBackend::reportBugFinished);
connect(client, &GRPCClient::reportBugSuccess, this, &QMLBackend::bugReportSendSuccess);
connect(client, &GRPCClient::reportBugError, this, &QMLBackend::bugReportSendError);
connect(client, &GRPCClient::showMainWindow, this, &QMLBackend::showMainWindow);
// cache events
connect(client, &GRPCClient::isCacheOnDiskEnabledChanged, this, &QMLBackend::isDiskCacheEnabledChanged);
@ -168,15 +181,6 @@ void QMLBackend::retrieveUserList()
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void QMLBackend::clearUserList()
{
users_->reset();
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
@ -348,4 +352,4 @@ void QMLBackend::onResetFinished()
{
emit resetFinished();
this->restart();
}
}

View File

@ -42,7 +42,7 @@ public: // member functions.
QMLBackend &operator=(QMLBackend const &) = delete; ///< Disabled assignment operator.
QMLBackend &operator=(QMLBackend &&) = delete; ///< Disabled move assignment operator.
void init(); ///< Initialize the backend.
void clearUserList(); ///< Clear the user list.
bool waitForEventStreamReaderToFinish(qint32 timeoutMs); ///< Wait for the event stream reader to finish.
// invokable methods can be called from QML. They generally return a value, which slots cannot do.
Q_INVOKABLE static QPoint getCursorPos(); // _ func() *core.QPoint `slot:"getCursorPos"`

View File

@ -91,6 +91,7 @@ void UserList::reset()
this->beginResetModel();
users_.clear();
this->endResetModel();
emit countChanged(0);
}
@ -102,6 +103,7 @@ void UserList::reset(QList<SPUser> const &users)
this->beginResetModel();
users_ = users;
this->endResetModel();
emit countChanged(users_.size());
}
@ -114,6 +116,7 @@ void UserList::appendUser(SPUser const &user)
this->beginInsertRows(QModelIndex(), size, size);
users_.append(user);
this->endInsertRows();
emit countChanged(users_.size());
}
@ -127,6 +130,7 @@ void UserList::removeUserAt(int row)
this->beginRemoveRows(QModelIndex(), row, row);
users_.removeAt(row);
this->endRemoveRows();
emit countChanged(users_.size());
}

View File

@ -19,6 +19,6 @@
#ifndef BRIDGE_GUI_VERSION_H
#define BRIDGE_GUI_VERSION_H
#define PROJECT_VER "2.2.1+git"
#define PROJECT_VER "2.2.2+git"
#endif // BRIDGE_GUI_VERSION_H

View File

@ -17,11 +17,11 @@
#include "QMLBackend.h"
#include "BridgeMonitor.h"
#include "Version.h"
#include <bridgepp/Log/Log.h>
#include <bridgepp/BridgeUtils.h>
#include <bridgepp/Exception/Exception.h>
#include <bridgepp/ProcessMonitor.h>
using namespace bridgepp;
@ -29,11 +29,30 @@ using namespace bridgepp;
namespace
{
/// \brief The file extension for the bridge executable file.
#ifdef Q_OS_WIN32
QString const exeSuffix = ".exe";
#else
QString const exeSuffix;
#endif
QString const launcherFlag = "--launcher"; ///< launcher flag parameter used for bridge.
QString const bridgeLock = "bridge-gui.lock"; ///< file name used for the lock file.
QString const exeName = "proton-bridge" + exeSuffix; ///< The bridge executable file name.*
}
//****************************************************************************************************************************************************
/// \return The path of the bridge executable.
/// \return A null string if the executable could not be located.
//****************************************************************************************************************************************************
QString locateBridgeExe()
{
QFileInfo const fileInfo(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(exeName));
return (fileInfo.exists() && fileInfo.isFile() && fileInfo.isExecutable()) ? fileInfo.absoluteFilePath() : QString();
}
//****************************************************************************************************************************************************
/// // initialize the Qt application.
//****************************************************************************************************************************************************
@ -93,7 +112,7 @@ QQmlComponent *createRootQmlComponent(QQmlApplicationEngine &engine)
//****************************************************************************************************************************************************
/// \param[in] lock The lock file to be checked.
/// \return True if the lock can be taken, false otherwize.
/// \return True if the lock can be taken, false otherwise.
//****************************************************************************************************************************************************
bool checkSingleInstance(QLockFile &lock)
{
@ -168,14 +187,14 @@ void launchBridge(QStringList const &args)
UPOverseer& overseer = app().bridgeOverseer();
overseer.reset();
const QString bridgeExePath = BridgeMonitor::locateBridgeExe();
const QString bridgeExePath = locateBridgeExe();
if (bridgeExePath.isEmpty())
throw Exception("Could not locate the bridge executable path");
else
app().log().debug(QString("Bridge executable path: %1").arg(QDir::toNativeSeparators(bridgeExePath)));
overseer = std::make_unique<Overseer>(new BridgeMonitor(bridgeExePath, args, nullptr), nullptr);
overseer = std::make_unique<Overseer>(new ProcessMonitor(bridgeExePath, args, nullptr), nullptr);
overseer->startWorker(true);
}
@ -234,16 +253,16 @@ int main(int argc, char *argv[])
if (!rootObject)
throw Exception("Could not create root object.");
BridgeMonitor *bridgeMonitor = app().bridgeMonitor();
ProcessMonitor *bridgeMonitor = app().bridgeMonitor();
bool bridgeExited = false;
bool startError = false;
QMetaObject::Connection connection;
if (bridgeMonitor)
{
const BridgeMonitor::MonitorStatus& status = bridgeMonitor->getStatus();
const ProcessMonitor::MonitorStatus& status = bridgeMonitor->getStatus();
if (!status.running && !attach)
{
// BridgeMonitor already stopped meaning we are attached to an orphan Bridge.
// ProcessMonitor already stopped meaning we are attached to an orphan Bridge.
// Restart the full process to be sure there is no more bridge orphans
app().log().error("Found orphan bridge, need to restart.");
app().backend().forceLauncher(launcher);
@ -254,7 +273,7 @@ int main(int argc, char *argv[])
else
{
app().log().debug(QString("Monitoring Bridge PID : %1").arg(status.pid));
connection = QObject::connect(bridgeMonitor, &BridgeMonitor::processExited, [&](int returnCode) {
connection = QObject::connect(bridgeMonitor, &ProcessMonitor::processExited, [&](int returnCode) {
bridgeExited = true;// clazy:exclude=lambda-in-connect
qGuiApp->exit(returnCode);
});
@ -267,6 +286,8 @@ int main(int argc, char *argv[])
QObject::disconnect(connection);
app().grpc().stopEventStream();
if (!app().backend().waitForEventStreamReaderToFinish(5000))
log.warn("Event stream reader took too long to finish.");
// We manually delete the QML components to avoid warnings error due to order of deletion of C++ / JS objects and singletons.
rootObject.reset();
@ -280,7 +301,7 @@ int main(int argc, char *argv[])
}
catch (Exception const &e)
{
app().log().error(e.qwhat());
QTextStream(stderr) << e.qwhat() << "\n";
return EXIT_FAILURE;
}
}

View File

@ -258,7 +258,7 @@ Item {
signIn.username = this.user.username
rightContent.showSignIn()
}
onShowSetupGuide: {
onShowSetupGuide: function(user, address) {
root.showSetupGuide(user,address)
}
}

View File

@ -45,7 +45,6 @@ SettingsView {
type: Label.Body
color: root.colorScheme.text_weak
Layout.fillWidth: true
Layout.maximumWidth: this.parent.Layout.maximumWidth
wrapMode: Text.WordWrap
}

View File

@ -52,7 +52,7 @@ ApplicationWindow {
target: Backend.users
function onRowsInserted(parent, first, last) {
// considerring that users are added one-by-one
// considering that users are added one-by-one
var user = Backend.users.get(first)
if (!user.loggedIn) {
@ -130,7 +130,7 @@ ApplicationWindow {
Layout.fillHeight: true
Layout.fillWidth: true
onShowSetupGuide: {
onShowSetupGuide: function(user, address) {
root.showSetup(user,address)
}
}

View File

@ -40,7 +40,6 @@ SettingsView {
type: Label.Body
color: root.colorScheme.text_weak
Layout.fillWidth: true
Layout.maximumWidth: this.parent.Layout.maximumWidth
wrapMode: Text.WordWrap
}

View File

@ -108,6 +108,7 @@ add_library(bridgepp
bridgepp/GRPC/GRPCUtils.cpp bridgepp/GRPC/GRPCUtils.h
${PROTO_CPP_FILE} ${PROTO_H_FILE} ${GRPC_CPP_FILE} ${GRPC_H_FILE}
bridgepp/Log/Log.h bridgepp/Log/Log.cpp
bridgepp/ProcessMonitor.cpp bridgepp/ProcessMonitor.h
bridgepp/User/User.cpp bridgepp/User/User.h
bridgepp/Worker/Worker.h bridgepp/Worker/Overseer.h bridgepp/Worker/Overseer.cpp
)

View File

@ -16,14 +16,15 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_BRIDGE_UTILS_H
#define BRIDGE_GUI_TESTER_BRIDGE_UTILS_H
#ifndef BRIDGE_PP_TESTER_BRIDGE_UTILS_H
#define BRIDGE_PP_TESTER_BRIDGE_UTILS_H
#include <bridgepp/User/User.h>
namespace bridgepp {
namespace bridgepp
{
QString userConfigDir(); ///< Get the path of the user configuration folder.
@ -34,8 +35,8 @@ QString randomFirstName(); ///< Get a random first name from a pre-determined li
QString randomLastName(); ///< Get a random first name from a pre-determined list.
SPUser randomUser(); ///< Get a random user.
} // namespace
#endif // BRIDGE_GUI_TESTER_BRIDGE_UTILS_H
#endif // BRIDGE_PP_TESTER_BRIDGE_UTILS_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_TESTER_EVENT_FACTORY_H
#define BRIDGE_GUI_TESTER_EVENT_FACTORY_H
#ifndef BRIDGE_PP_EVENT_FACTORY_H
#define BRIDGE_PP_EVENT_FACTORY_H
#include "bridge.grpc.pb.h"
@ -85,4 +85,4 @@ SPStreamEvent newUserChangedEvent(QString const &userID); ///< Create a new User
} // namespace bridgepp
#endif //BRIDGE_GUI_TESTER_EVENT_FACTORY_H
#endif //BRIDGE_PP_EVENT_FACTORY_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_RPC_CLIENT_H
#define BRIDGE_GUI_RPC_CLIENT_H
#ifndef BRIDGE_PP_RPC_CLIENT_H
#define BRIDGE_PP_RPC_CLIENT_H
#include "../User/User.h"
@ -233,4 +233,4 @@ private: // data members.
}
#endif // BRIDGE_GUI_RPC_CLIENT_H
#endif // BRIDGE_PP_RPC_CLIENT_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_GRPC_UTILS_H
#define BRIDGE_GUI_GRPC_UTILS_H
#ifndef BRIDGE_PP_GRPC_UTILS_H
#define BRIDGE_PP_GRPC_UTILS_H
#include "../User/User.h"
@ -42,4 +42,4 @@ SPUser userFromGRPC(grpc::User const &grpcUser); ///< Create a bridgepp::User fr
}
#endif // BRIDGE_GUI_GRPC_UTILS_H
#endif // BRIDGE_PP_GRPC_UTILS_H

View File

@ -16,62 +16,35 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#include "BridgeMonitor.h"
#include <bridgepp/Exception/Exception.h>
#include "ProcessMonitor.h"
#include "Exception/Exception.h"
using namespace bridgepp;
namespace
namespace bridgepp
{
/// \brief The file extension for the bridge executable file.
#ifdef Q_OS_WIN32
QString const exeSuffix = ".exe";
#else
QString const exeSuffix;
#endif
QString const exeName = "proton-bridge" + exeSuffix; ///< The bridge executable file name.*
}
//****************************************************************************************************************************************************
/// \return The path of the bridge executable.
/// \return A null string if the executable could not be located.
//****************************************************************************************************************************************************
QString BridgeMonitor::locateBridgeExe()
{
QFileInfo const fileInfo(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(exeName));
return (fileInfo.exists() && fileInfo.isFile() && fileInfo.isExecutable()) ? fileInfo.absoluteFilePath() : QString();
}
//****************************************************************************************************************************************************
/// \param[in] exePath The path of the Bridge executable.
/// \param[in] exePath The path of the executable.
/// \param[in] parent The parent object of the worker.
//****************************************************************************************************************************************************
BridgeMonitor::BridgeMonitor(QString const &exePath, QStringList const &args, QObject *parent)
ProcessMonitor::ProcessMonitor(QString const &exePath, QStringList const &args, QObject *parent)
: Worker(parent)
, exePath_(exePath)
, args_(args)
{
QFileInfo fileInfo(exePath);
if (!fileInfo.exists())
throw Exception("Could not locate Bridge executable.");
throw Exception(QString("Could not locate %1 executable.").arg(fileInfo.baseName()));
if ((!fileInfo.isFile()) || (!fileInfo.isExecutable()))
throw Exception("Invalid bridge executable");
throw Exception(QString("Invalid %1 executable").arg(fileInfo.baseName()));
}
//****************************************************************************************************************************************************
//
//****************************************************************************************************************************************************
void BridgeMonitor::run()
void ProcessMonitor::run()
{
try
{
@ -92,9 +65,9 @@ void BridgeMonitor::run()
}
status_.running = false;
status_.returnCode = p.exitCode();
status_.returnCode = p.exitCode();
emit processExited(status_.returnCode );
emit processExited(status_.returnCode);
emit finished();
}
catch (Exception const &e)
@ -103,10 +76,14 @@ void BridgeMonitor::run()
}
}
//****************************************************************************************************************************************************
/// \return status of the monitored process
//****************************************************************************************************************************************************
const BridgeMonitor::MonitorStatus& BridgeMonitor::getStatus()
const ProcessMonitor::MonitorStatus &ProcessMonitor::getStatus()
{
return status_;
}
} // namespace bridgepp

View File

@ -16,48 +16,52 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_BRIDGE_MONITOR_H
#define BRIDGE_GUI_BRIDGE_MONITOR_H
#ifndef BRIDGE_PP_PROCESS_MONITOR_H
#define BRIDGE_PP_PROCESS_MONITOR_H
#include <bridgepp/Worker/Worker.h>
#include "Worker/Worker.h"
//**********************************************************************************************************************
/// \brief Bridge process launcher and monitor class.
//**********************************************************************************************************************
class BridgeMonitor: public bridgepp::Worker
namespace bridgepp
{
Q_OBJECT
public: // static member functions
static QString locateBridgeExe(); ///< Try to find the bridge executable path.
struct MonitorStatus {
//**********************************************************************************************************************
/// \brief Process launcher and monitor class.
//**********************************************************************************************************************
class ProcessMonitor : public Worker
{
Q_OBJECT
public: // static member functions
struct MonitorStatus
{
bool running = false;
int returnCode = 0;
qint64 pid = 0;
};
public: // member functions.
BridgeMonitor(QString const& exePath, QStringList const &args, QObject *parent); ///< Default constructor.
BridgeMonitor(BridgeMonitor const&) = delete; ///< Disabled copy-constructor.
BridgeMonitor(BridgeMonitor&&) = delete; ///< Disabled assignment copy-constructor.
~BridgeMonitor() override = default; ///< Destructor.
BridgeMonitor& operator=(BridgeMonitor const&) = delete; ///< Disabled assignment operator.
BridgeMonitor& operator=(BridgeMonitor&&) = delete; ///< Disabled move assignment operator.
ProcessMonitor(QString const &exePath, QStringList const &args, QObject *parent); ///< Default constructor.
ProcessMonitor(ProcessMonitor const &) = delete; ///< Disabled copy-constructor.
ProcessMonitor(ProcessMonitor &&) = delete; ///< Disabled assignment copy-constructor.
~ProcessMonitor() override = default; ///< Destructor.
ProcessMonitor &operator=(ProcessMonitor const &) = delete; ///< Disabled assignment operator.
ProcessMonitor &operator=(ProcessMonitor &&) = delete; ///< Disabled move assignment operator.
void run() override; ///< Run the worker.
MonitorStatus const &getStatus();
const MonitorStatus& getStatus();
signals:
void processExited(int code); ///< Slot for the exiting of the process.
private: // data members
QString const exePath_; ///< The path to the bridge executable.
QString const exePath_; ///< The path to the executable.
QStringList args_; ///< arguments to be passed to the brigde.
MonitorStatus status_; ///< Status of the monitoring.
};
}
#endif // BRIDGE_GUI_BRIDGE_MONITOR_H
#endif //BRIDGE_PP_PROCESS_MONITOR_H

View File

@ -16,8 +16,8 @@
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
#ifndef BRIDGE_GUI_USER_H
#define BRIDGE_GUI_USER_H
#ifndef BRIDGE_PP_USER_H
#define BRIDGE_PP_USER_H
namespace bridgepp
@ -127,4 +127,4 @@ private: // data members.
} // namespace bridgepp
#endif // BRIDGE_GUI_USER_H
#endif // BRIDGE_PP_USER_H

View File

@ -108,6 +108,26 @@ bool Overseer::isFinished() const
}
//****************************************************************************************************************************************************
/// \param timeoutMs The timeout after which the function should return false if the event stream reader is not finished. if -1 one, the function
/// never times out.
/// \return false if and only if the timeout delay was reached.
//****************************************************************************************************************************************************
bool Overseer::wait(qint32 timeoutMs) const
{
QElapsedTimer timer;
timer.start();
while (!this->isFinished()) {
if ((timeoutMs >= 0) && (timer.elapsed() > timeoutMs))
return false;
QThread::msleep(10);
}
return true;
}
//****************************************************************************************************************************************************
/// \return The worker.
//****************************************************************************************************************************************************

View File

@ -41,6 +41,7 @@ public: // member functions.
Overseer &operator=(Overseer const &) = delete; ///< Disabled assignment operator.
Overseer &operator=(Overseer &&) = delete; ///< Disabled move assignment operator.
bool isFinished() const; ///< Check if the worker is finished.
bool wait(qint32 timeoutMs) const; ///< Wait for the worker to finish.
Worker *worker() const; ///< Return worker.
public slots:

View File

@ -64,7 +64,7 @@ func grpcUserFromBridge(user types.User) *User {
Username: user.Username(),
AvatarText: getInitials(user.Username()),
LoggedIn: user.IsConnected(),
SplitMode: user.IsCombinedAddressMode(),
SplitMode: !user.IsCombinedAddressMode(),
SetupGuideSeen: true, // users listed have already seen the setup guide.
UsedBytes: user.UsedBytes(),
TotalBytes: user.TotalBytes(),