mirror of
https://github.com/ProtonMail/proton-bridge.git
synced 2025-12-13 06:06:44 +00:00
feat(GODT-2261): sync progress in GUI.
This commit is contained in:
@ -49,6 +49,48 @@ qt_standard_project_setup()
|
||||
message(STATUS "Using Qt ${Qt6_VERSION}")
|
||||
|
||||
|
||||
#*****************************************************************************************************************************************************
|
||||
# bridgelib
|
||||
#*****************************************************************************************************************************************************
|
||||
find_program(GO_BIN "go")
|
||||
if (NOT GO_BIN)
|
||||
message(FATAL_ERROR "Could not location go compiler")
|
||||
endif()
|
||||
message(STATUS "go compiler is ${GO_BIN}")
|
||||
|
||||
if (APPLE) # set some env variable for go compiler on macOS. Note the CGO_ENABLED=1 is required when cross-compiling.
|
||||
if (CMAKE_OSX_ARCHITECTURES STREQUAL "arm64")
|
||||
set(GO_BIN "MACOSX_DEPLOYMENT_TARGET=11.0" "GOARCH=arm64" "CGO_CFLAGS=\"-mmacosx-version-min=11.0\"" CGO_ENABLED=1 ${GO_BIN})
|
||||
else ()
|
||||
set(GO_BIN "MACOSX_DEPLOYMENT_TARGET=10.15" "GOARCH=amd64" "CGO_CFLAGS=\"-mmacosx-version-min=10.15\"" CGO_ENABLED=1 ${GO_BIN})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
file(REAL_PATH "pkg/bridgelib" BRIDGELIB_DIR BASE_DIRECTORY "${BRIDGE_REPO_ROOT}")
|
||||
message(STATUS "bridgelib folder is ${BRIDGELIB_DIR}")
|
||||
|
||||
set(BRIDGELIB_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
|
||||
set(BRIDGELIB_BASE_NAME "bridgelib")
|
||||
if (UNIX AND NOT APPLE)
|
||||
set(BRIDGELIB_LIB_FILE "${BRIDGELIB_BASE_NAME}.so")
|
||||
endif()
|
||||
if (APPLE)
|
||||
set(BRIDGELIB_LIB_FILE "${BRIDGELIB_BASE_NAME}.dylib")
|
||||
endif()
|
||||
if (WIN32)
|
||||
set(BRIDGELIB_LIB_FILE "${BRIDGELIB_BASE_NAME}.dll")
|
||||
endif()
|
||||
|
||||
set(BRIDGELIB_OUTPUT_PATH "${BRIDGELIB_OUTPUT_DIR}/${BRIDGELIB_LIB_FILE}")
|
||||
|
||||
add_custom_target(
|
||||
bridgelib
|
||||
COMMAND ${GO_BIN} build -o ${BRIDGELIB_OUTPUT_PATH} --buildmode c-shared
|
||||
WORKING_DIRECTORY ${BRIDGELIB_DIR}
|
||||
COMMENT "Compile bridgelib library"
|
||||
)
|
||||
|
||||
|
||||
#*****************************************************************************************************************************************************
|
||||
# Source files and output
|
||||
#*****************************************************************************************************************************************************
|
||||
@ -72,6 +114,8 @@ add_executable(bridge-gui-tester
|
||||
UserTable.cpp UserTable.h
|
||||
)
|
||||
|
||||
add_dependencies(bridge-gui-tester bridgelib)
|
||||
|
||||
target_precompile_headers(bridge-gui-tester PRIVATE Pch.h)
|
||||
target_include_directories(bridge-gui-tester PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(bridge-gui-tester PRIVATE BRIDGE_APP_VERSION=\"${BRIDGE_APP_VERSION}\")
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "GRPCServerWorker.h"
|
||||
#include "Cert.h"
|
||||
#include "GRPCService.h"
|
||||
#include <bridgepp/BridgeLib.h>
|
||||
#include <bridgepp/Exception/Exception.h>
|
||||
#include <bridgepp/GRPC/GRPCUtils.h>
|
||||
#include <bridgepp/GRPC/GRPCConfig.h>
|
||||
@ -33,7 +34,6 @@ using namespace grpc;
|
||||
//****************************************************************************************************************************************************
|
||||
GRPCServerWorker::GRPCServerWorker(QObject *parent)
|
||||
: Worker(parent) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -81,7 +81,7 @@ void GRPCServerWorker::run() {
|
||||
|
||||
config.port = port;
|
||||
QString err;
|
||||
if (!config.save(grpcServerConfigPath(), &err)) {
|
||||
if (!config.save(grpcServerConfigPath(bridgelib::userConfigDir()), &err)) {
|
||||
throw Exception(QString("Could not save gRPC server config. %1").arg(err));
|
||||
}
|
||||
|
||||
|
||||
@ -192,17 +192,6 @@ Status GRPCService::IsAllMailVisible(ServerContext *, Empty const *request, Bool
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[out] response The response.
|
||||
/// \return The status for the call.
|
||||
//****************************************************************************************************************************************************
|
||||
Status GRPCService::GoOs(ServerContext *, Empty const *, StringValue *response) {
|
||||
app().log().debug(__FUNCTION__);
|
||||
response->set_value(app().mainWindow().settingsTab().os().toStdString());
|
||||
return Status::OK;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return The status for the call.
|
||||
//****************************************************************************************************************************************************
|
||||
|
||||
@ -51,7 +51,6 @@ public: // member functions.
|
||||
grpc::Status IsBetaEnabled(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::BoolValue *response) override;
|
||||
grpc::Status SetIsAllMailVisible(::grpc::ServerContext *context, ::google::protobuf::BoolValue const *request, ::google::protobuf::Empty *response) override;
|
||||
grpc::Status IsAllMailVisible(::grpc::ServerContext *context, ::google::protobuf::Empty const *request, ::google::protobuf::BoolValue *response) override;
|
||||
grpc::Status GoOs(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
||||
grpc::Status TriggerReset(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::Empty *) override;
|
||||
grpc::Status Version(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
||||
grpc::Status LogsPath(::grpc::ServerContext *, ::google::protobuf::Empty const *, ::google::protobuf::StringValue *response) override;
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
|
||||
#include "SettingsTab.h"
|
||||
#include "GRPCService.h"
|
||||
#include <bridgepp/BridgeLib.h>
|
||||
#include <bridgepp/GRPC/EventFactory.h>
|
||||
#include <bridgepp/BridgeUtils.h>
|
||||
|
||||
@ -438,7 +439,7 @@ void SettingsTab::resetUI() {
|
||||
this->setClientPlatform("Unknown");
|
||||
|
||||
ui_.editVersion->setText(BRIDGE_APP_VERSION);
|
||||
ui_.comboOS->setCurrentText(bridgepp::goos());
|
||||
ui_.comboOS->setCurrentText(bridgelib::goos());
|
||||
ui_.editCurrentEmailClient->setText("Thunderbird/102.0.3");
|
||||
ui_.checkShowOnStartup->setChecked(true);
|
||||
ui_.checkShowSplashScreen->setChecked(false);
|
||||
|
||||
@ -55,6 +55,8 @@ UsersTab::UsersTab(QWidget *parent)
|
||||
connect(ui_.buttonImapLoginFailed, &QPushButton::clicked, this, &UsersTab::onSendIMAPLoginFailedEvent);
|
||||
connect(ui_.buttonUsedBytesChanged, &QPushButton::clicked, this, &UsersTab::onSendUsedBytesChangedEvent);
|
||||
connect(ui_.checkUsernamePasswordError, &QCheckBox::toggled, this, &UsersTab::updateGUIState);
|
||||
connect(ui_.checkSync, &QCheckBox::toggled, this, &UsersTab::onCheckSyncToggled);
|
||||
connect(ui_.sliderSync, &QSlider::valueChanged, this, &UsersTab::onSliderSyncValueChanged);
|
||||
|
||||
users_.append(randomUser());
|
||||
|
||||
@ -217,9 +219,19 @@ void UsersTab::updateGUIState() {
|
||||
ui_.groupBoxUsedSpace->setEnabled(hasSelectedUser && (UserState::Connected == state));
|
||||
ui_.editUsernamePasswordError->setEnabled(ui_.checkUsernamePasswordError->isChecked());
|
||||
ui_.spinUsedBytes->setValue(user ? user->usedBytes() : 0.0);
|
||||
ui_.groupboxSync->setEnabled(user.get());
|
||||
|
||||
if (user)
|
||||
ui_.editIMAPLoginFailedUsername->setText(user->primaryEmailOrUsername());
|
||||
|
||||
QSignalBlocker b(ui_.checkSync);
|
||||
bool const syncing = user && user->isSyncing();
|
||||
ui_.checkSync->setChecked(syncing);
|
||||
b = QSignalBlocker(ui_.sliderSync);
|
||||
ui_.sliderSync->setEnabled(syncing);
|
||||
qint32 const progressPercent = syncing ? qint32(user->syncProgress() * 100.0f) : 0;
|
||||
ui_.sliderSync->setValue(progressPercent);
|
||||
ui_.labelSync->setText(syncing ? QString("%1%").arg(progressPercent) : "" );
|
||||
}
|
||||
|
||||
|
||||
@ -398,3 +410,44 @@ void UsersTab::configureUserAppleMail(QString const &userID, QString const &addr
|
||||
app().log().info(QString("Apple mail configuration was requested for user %1, address %2").arg(userID, address));
|
||||
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] checked Is the sync checkbox checked?
|
||||
//****************************************************************************************************************************************************
|
||||
void UsersTab::onCheckSyncToggled(bool checked) {
|
||||
SPUser const user = this->selectedUser();
|
||||
if ((!user) || (user->isSyncing() == checked)) {
|
||||
return;
|
||||
}
|
||||
|
||||
user->setIsSyncing(checked);
|
||||
user->setSyncProgress(0.0);
|
||||
GRPCService &grpc = app().grpc();
|
||||
|
||||
// we do not apply delay for these event.
|
||||
if (checked) {
|
||||
grpc.sendEvent(newSyncStartedEvent(user->id()));
|
||||
grpc.sendEvent(newSyncProgressEvent(user->id(), 0.0, 1, 1));
|
||||
} else {
|
||||
grpc.sendEvent(newSyncFinishedEvent(user->id()));
|
||||
}
|
||||
|
||||
this->updateGUIState();
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] value The value for the slider.
|
||||
//****************************************************************************************************************************************************
|
||||
void UsersTab::onSliderSyncValueChanged(int value) {
|
||||
SPUser const user = this->selectedUser();
|
||||
if ((!user) || (!user->isSyncing()) || user->syncProgress() == value) {
|
||||
return;
|
||||
}
|
||||
|
||||
double const progress = value / 100.0;
|
||||
user->setSyncProgress(progress);
|
||||
app().grpc().sendEvent(newSyncProgressEvent(user->id(), progress, 1, 1)); // we do not simulate elapsed & remaining.
|
||||
this->updateGUIState();
|
||||
}
|
||||
|
||||
@ -54,7 +54,6 @@ public slots:
|
||||
void logoutUser(QString const &userID); ///< slot for the logging out of a user.
|
||||
void removeUser(QString const &userID); ///< Slot for the removal of a user.
|
||||
void configureUserAppleMail(QString const &userID, QString const &address); ///< Slot for the configuration of Apple mail.
|
||||
|
||||
private slots:
|
||||
void onAddUserButton(); ///< Add a user to the user list.
|
||||
void onEditUserButton(); ///< Edit the currently selected user.
|
||||
@ -63,6 +62,8 @@ private slots:
|
||||
void onSendUserBadEvent(); ///< Slot for the 'Send Bad Event Error' button.
|
||||
void onSendUsedBytesChangedEvent(); ///< Slot for the 'Send Used Bytes Changed Event' button.
|
||||
void onSendIMAPLoginFailedEvent(); ///< Slot for the 'Send IMAP Login failure Event' button.
|
||||
void onCheckSyncToggled(bool checked); ///< Slot for the 'Synchronizing' check box.
|
||||
void onSliderSyncValueChanged(int value); ///< Slot for the sync 'Progress' slider.
|
||||
void updateGUIState(); ///< Update the GUI state.
|
||||
|
||||
private: // member functions.
|
||||
|
||||
@ -6,8 +6,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1225</width>
|
||||
<height>717</height>
|
||||
<width>1221</width>
|
||||
<height>894</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@ -66,6 +66,52 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupboxSync">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Sync</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="checkSync">
|
||||
<property name="text">
|
||||
<string>Synchronizing</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="labelSync">
|
||||
<property name="text">
|
||||
<string>0%</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSlider" name="sliderSync">
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="tickInterval">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBoxBadEvent">
|
||||
<property name="minimumSize">
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#include "MainWindow.h"
|
||||
#include "AppController.h"
|
||||
#include "GRPCServerWorker.h"
|
||||
#include <bridgepp/BridgeLib.h>
|
||||
#include <bridgepp/Exception/Exception.h>
|
||||
#include <bridgepp/Worker/Overseer.h>
|
||||
|
||||
@ -54,6 +55,8 @@ int main(int argc, char **argv) {
|
||||
QApplication::setOrganizationDomain("proton.ch");
|
||||
QApplication::setQuitOnLastWindowClosed(true);
|
||||
|
||||
bridgelib::loadLibrary();
|
||||
|
||||
Log &log = app().log();
|
||||
log.setEchoInConsole(true);
|
||||
log.setLevel(Log::Level::Debug);
|
||||
@ -85,7 +88,10 @@ int main(int argc, char **argv) {
|
||||
return exitCode;
|
||||
}
|
||||
catch (Exception const &e) {
|
||||
QTextStream(stderr) << QString("A fatal error occurred: %1\n").arg(e.qwhat());
|
||||
QString message = e.qwhat();
|
||||
if (!e.details().isEmpty())
|
||||
message += "\n\nDetails:\n" + e.details();
|
||||
QTextStream(stderr) << QString("A fatal error occurred: %1\n").arg(message);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,10 +152,9 @@ add_executable(bridge-gui
|
||||
Resources.qrc
|
||||
AppController.cpp AppController.h
|
||||
BridgeApp.cpp BridgeApp.h
|
||||
BridgeLib.cpp BridgeLib.h
|
||||
CommandLine.cpp CommandLine.h
|
||||
EventStreamWorker.cpp EventStreamWorker.h
|
||||
Log.cpp Log.h
|
||||
LogUtils.cpp LogUtils.h
|
||||
main.cpp
|
||||
Pch.h
|
||||
QMLBackend.cpp QMLBackend.h
|
||||
|
||||
@ -16,9 +16,9 @@
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#include "Log.h"
|
||||
#include "BridgeLib.h"
|
||||
#include "LogUtils.h"
|
||||
#include "BuildConfig.h"
|
||||
#include <bridgepp/BridgeLib.h>
|
||||
|
||||
|
||||
using namespace bridgepp;
|
||||
@ -16,8 +16,8 @@
|
||||
// along with Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
#ifndef BRIDGE_GUI_LOG_H
|
||||
#define BRIDGE_GUI_LOG_H
|
||||
#ifndef BRIDGE_GUI_LOG_UTILS_H
|
||||
#define BRIDGE_GUI_LOG_UTILS_H
|
||||
|
||||
|
||||
#include <bridgepp/Log/Log.h>
|
||||
@ -26,4 +26,4 @@
|
||||
bridgepp::Log &initLog(); ///< Initialize the application log.
|
||||
|
||||
|
||||
#endif //BRIDGE_GUI_LOG_H
|
||||
#endif //BRIDGE_GUI_LOG_UTILS_H
|
||||
@ -18,8 +18,8 @@
|
||||
|
||||
#include "QMLBackend.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "BridgeLib.h"
|
||||
#include "EventStreamWorker.h"
|
||||
#include <bridgepp/BridgeLib.h>
|
||||
#include <bridgepp/GRPC/GRPCClient.h>
|
||||
#include <bridgepp/Exception/Exception.h>
|
||||
#include <bridgepp/Worker/Overseer.h>
|
||||
|
||||
@ -17,12 +17,8 @@
|
||||
|
||||
#include "SentryUtils.h"
|
||||
#include "BuildConfig.h"
|
||||
#include "BridgeLib.h"
|
||||
#include <bridgepp/BridgeLib.h>
|
||||
#include <bridgepp/BridgeUtils.h>
|
||||
#include <QByteArray>
|
||||
#include <QCryptographicHash>
|
||||
#include <QString>
|
||||
#include <QSysInfo>
|
||||
|
||||
|
||||
using namespace bridgepp;
|
||||
|
||||
@ -39,6 +39,9 @@ void UserList::connectGRPCEvents() const {
|
||||
connect(&client, &GRPCClient::userChanged, this, &UserList::onUserChanged);
|
||||
connect(&client, &GRPCClient::toggleSplitModeFinished, this, &UserList::onToggleSplitModeFinished);
|
||||
connect(&client, &GRPCClient::usedBytesChanged, this, &UserList::onUsedBytesChanged);
|
||||
connect(&client, &GRPCClient::syncStarted, this, &UserList::onSyncStarted);
|
||||
connect(&client, &GRPCClient::syncFinished, this, &UserList::onSyncFinished);
|
||||
connect(&client, &GRPCClient::syncProgress, this, &UserList::onSyncProgress);
|
||||
}
|
||||
|
||||
|
||||
@ -251,3 +254,47 @@ void UserList::onUsedBytesChanged(QString const &userID, qint64 usedBytes) {
|
||||
}
|
||||
users_[index]->setUsedBytes(usedBytes);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
//****************************************************************************************************************************************************
|
||||
void UserList::onSyncStarted(QString const &userID) {
|
||||
int const index = this->rowOfUserID(userID);
|
||||
if (index < 0) {
|
||||
app().log().error(QString("Received onSyncStarted event for unknown userID %1").arg(userID));
|
||||
return;
|
||||
}
|
||||
users_[index]->setIsSyncing(true);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
//****************************************************************************************************************************************************
|
||||
void UserList::onSyncFinished(QString const &userID) {
|
||||
int const index = this->rowOfUserID(userID);
|
||||
if (index < 0) {
|
||||
app().log().error(QString("Received onSyncFinished event for unknown userID %1").arg(userID));
|
||||
return;
|
||||
}
|
||||
users_[index]->setIsSyncing(false);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
/// \param[in] progress The sync progress ratio.
|
||||
/// \param[in] elapsedMs The elapsed sync time in milliseconds.
|
||||
/// \param[in] remainingMs The remaining sync time in milliseconds.
|
||||
//****************************************************************************************************************************************************
|
||||
void UserList::onSyncProgress(QString const &userID, double progress, float elapsedMs, float remainingMs) {
|
||||
Q_UNUSED(elapsedMs)
|
||||
Q_UNUSED(remainingMs)
|
||||
int const index = this->rowOfUserID(userID);
|
||||
if (index < 0) {
|
||||
app().log().error(QString("Received onSyncFinished event for unknown userID %1").arg(userID));
|
||||
return;
|
||||
}
|
||||
users_[index]->setSyncProgress(progress);
|
||||
}
|
||||
|
||||
@ -61,6 +61,9 @@ public slots: ///< handler for signals coming from the gRPC service
|
||||
void onUserChanged(QString const &userID);
|
||||
void onToggleSplitModeFinished(QString const &userID);
|
||||
void onUsedBytesChanged(QString const &userID, qint64 usedBytes); ///< Slot for usedBytesChanged events.
|
||||
void onSyncStarted(QString const &userID); ///< Slot for syncStarted events.
|
||||
void onSyncFinished(QString const &userID); ///< Slot for syncFinished events.
|
||||
void onSyncProgress(QString const &userID, double progress, float elapsedMs, float remainingMs); ///< Slot for syncFinished events.
|
||||
|
||||
private: // data members
|
||||
QList<bridgepp::SPUser> users_; ///< The user list.
|
||||
|
||||
@ -18,12 +18,12 @@
|
||||
|
||||
#include "Pch.h"
|
||||
#include "BridgeApp.h"
|
||||
#include "BridgeLib.h"
|
||||
#include "CommandLine.h"
|
||||
#include "Log.h"
|
||||
#include "LogUtils.h"
|
||||
#include "QMLBackend.h"
|
||||
#include "SentryUtils.h"
|
||||
#include "BuildConfig.h"
|
||||
#include <bridgepp/BridgeLib.h>
|
||||
#include <bridgepp/BridgeUtils.h>
|
||||
#include <bridgepp/Exception/Exception.h>
|
||||
#include <bridgepp/FocusGRPC/FocusGRPCClient.h>
|
||||
|
||||
@ -29,14 +29,19 @@ Item {
|
||||
|
||||
property var _spacing: 12 * ProtonStyle.px
|
||||
|
||||
property color usedSpaceColor : {
|
||||
property color progressColor : {
|
||||
if (!root.enabled) return root.colorScheme.text_weak
|
||||
if (root.type == AccountDelegate.SmallView) return root.colorScheme.text_weak
|
||||
if (root.usedFraction < .50) return root.colorScheme.signal_success
|
||||
if (root.usedFraction < .75) return root.colorScheme.signal_warning
|
||||
if (root.user && root.user.isSyncing) return root.colorScheme.text_weak
|
||||
if (root.progressRatio < .50) return root.colorScheme.signal_success
|
||||
if (root.progressRatio < .75) return root.colorScheme.signal_warning
|
||||
return root.colorScheme.signal_danger
|
||||
}
|
||||
property real usedFraction: root.user ? reasonableFraction(root.user.usedBytes, root.user.totalBytes) : 0
|
||||
property real progressRatio: {
|
||||
if (!root.user)
|
||||
return 0
|
||||
return root.user.isSyncing ? root.user.syncProgress : reasonableFraction(root.user.usedBytes, root.user.totalBytes)
|
||||
}
|
||||
property string totalSpace: root.spaceWithUnits(root.user ? root.reasonableBytes(root.user.totalBytes) : 0)
|
||||
property string usedSpace: root.spaceWithUnits(root.user ? root.reasonableBytes(root.user.usedBytes) : 0)
|
||||
|
||||
@ -171,18 +176,21 @@ Item {
|
||||
case EUserState.Locked:
|
||||
return qsTr("Connecting") + dotsTimer.dots
|
||||
case EUserState.Connected:
|
||||
if (root.user.isSyncing)
|
||||
return qsTr("Synchronizing (%1%)").arg(Math.floor(root.user.syncProgress * 100)) + dotsTimer.dots
|
||||
else
|
||||
return root.usedSpace
|
||||
}
|
||||
}
|
||||
|
||||
Timer { // dots animation while connecting. 1 sec cycle, roughly similar to the webmail loading page.
|
||||
Timer { // dots animation while connecting & syncing.
|
||||
id:dotsTimer
|
||||
property string dots: ""
|
||||
interval: 250;
|
||||
interval: 500;
|
||||
repeat: true;
|
||||
running: (root.user != null) && (root.user.state === EUserState.Locked)
|
||||
running: (root.user != null) && ((root.user.state === EUserState.Locked) || (root.user.isSyncing))
|
||||
onTriggered: {
|
||||
dots = dots + "."
|
||||
dots += "."
|
||||
if (dots.length > 3)
|
||||
dots = ""
|
||||
}
|
||||
@ -191,7 +199,7 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
color: root.usedSpaceColor
|
||||
color: root.progressColor
|
||||
type: {
|
||||
switch (root.type) {
|
||||
case AccountDelegate.SmallView: return Label.Caption
|
||||
@ -202,7 +210,7 @@ Item {
|
||||
|
||||
Label {
|
||||
colorScheme: root.colorScheme
|
||||
text: root.user && root.user.state == EUserState.Connected ? " / " + root.totalSpace : ""
|
||||
text: root.user && root.user.state == EUserState.Connected && !root.user.isSyncing ? " / " + root.totalSpace : ""
|
||||
color: root.colorScheme.text_weak
|
||||
type: {
|
||||
switch (root.type) {
|
||||
@ -213,26 +221,27 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
Item { implicitHeight: root.type == AccountDelegate.LargeView ? 3 * ProtonStyle.px : 0 }
|
||||
|
||||
Rectangle {
|
||||
id: storage_bar
|
||||
id: progress_bar
|
||||
visible: root.user ? root.type == AccountDelegate.LargeView : false
|
||||
width: 140 * ProtonStyle.px
|
||||
height: 4 * ProtonStyle.px
|
||||
radius: ProtonStyle.storage_bar_radius
|
||||
radius: ProtonStyle.progress_bar_radius
|
||||
color: root.colorScheme.border_weak
|
||||
|
||||
Rectangle {
|
||||
id: storage_bar_filled
|
||||
radius: ProtonStyle.storage_bar_radius
|
||||
color: root.usedSpaceColor
|
||||
id: progress_bar_filled
|
||||
radius: ProtonStyle.progress_bar_radius
|
||||
color: root.progressColor
|
||||
visible: root.user ? parent.visible && (root.user.state == EUserState.Connected): false
|
||||
anchors {
|
||||
top : parent.top
|
||||
bottom : parent.bottom
|
||||
left : parent.left
|
||||
}
|
||||
width: Math.min(1,Math.max(0.02,root.usedFraction)) * parent.width
|
||||
width: Math.min(1,Math.max(0.02,root.progressRatio)) * parent.width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,7 +362,7 @@ QtObject {
|
||||
property real banner_radius : 12 * root.px // px
|
||||
property real dialog_radius : 12 * root.px // px
|
||||
property real card_radius : 12 * root.px // px
|
||||
property real storage_bar_radius : 3 * root.px // px
|
||||
property real progress_bar_radius : 3 * root.px // px
|
||||
property real tooltip_radius : 8 * root.px // px
|
||||
|
||||
property int heading_font_size: 28
|
||||
|
||||
@ -135,6 +135,7 @@ add_custom_command(
|
||||
|
||||
|
||||
add_library(bridgepp
|
||||
bridgepp/BridgeLib.cpp bridgepp/BridgeLib.h
|
||||
bridgepp/BridgeUtils.cpp bridgepp/BridgeUtils.h
|
||||
bridgepp/Exception/Exception.h bridgepp/Exception/Exception.cpp
|
||||
bridgepp/GRPC/GRPCClient.cpp bridgepp/GRPC/GRPCClient.h
|
||||
|
||||
@ -601,6 +601,51 @@ SPStreamEvent newIMAPLoginFailedEvent(QString const &username) {
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
/// \return The event.
|
||||
//****************************************************************************************************************************************************
|
||||
SPStreamEvent newSyncStartedEvent(QString const &userID) {
|
||||
auto event = new grpc::SyncStartedEvent;
|
||||
event->set_userid(userID.toStdString());
|
||||
auto userEvent = new grpc::UserEvent;
|
||||
userEvent->set_allocated_syncstartedevent(event);
|
||||
return wrapUserEvent(userEvent);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
/// \return The event.
|
||||
//****************************************************************************************************************************************************
|
||||
SPStreamEvent newSyncFinishedEvent(QString const &userID) {
|
||||
auto event = new grpc::SyncFinishedEvent;
|
||||
event->set_userid(userID.toStdString());
|
||||
auto userEvent = new grpc::UserEvent;
|
||||
userEvent->set_allocated_syncfinishedevent(event);
|
||||
return wrapUserEvent(userEvent);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] userID The userID.
|
||||
/// \param[in] progress The progress ratio.
|
||||
/// \param[in] elapsedMs The elapsed time in milliseconds.
|
||||
/// \param[in] remainingMs The remaining time in milliseconds.
|
||||
/// \return The event.
|
||||
//****************************************************************************************************************************************************
|
||||
SPStreamEvent newSyncProgressEvent(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs) {
|
||||
auto event = new grpc::SyncProgressEvent;
|
||||
event->set_userid(userID.toStdString());
|
||||
event->set_progress(progress);
|
||||
event->set_elapsedms(elapsedMs);
|
||||
event->set_remainingms(remainingMs);
|
||||
auto userEvent = new grpc::UserEvent;
|
||||
userEvent->set_allocated_syncprogressevent(event);
|
||||
return wrapUserEvent(userEvent);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] errorCode The error errorCode.
|
||||
/// \return The event.
|
||||
|
||||
@ -80,6 +80,9 @@ SPStreamEvent newUserChangedEvent(QString const &userID); ///< Create a new User
|
||||
SPStreamEvent newUserBadEvent(QString const &userID, QString const& errorMessage); ///< Create a new UserBadEvent event.
|
||||
SPStreamEvent newUsedBytesChangedEvent(QString const &userID, qint64 usedBytes); ///< Create a new UsedBytesChangedEvent event.
|
||||
SPStreamEvent newIMAPLoginFailedEvent(QString const &username); ///< Create a new ImapLoginFailedEvent event.
|
||||
SPStreamEvent newSyncStartedEvent(QString const &userID); ///< Create a new SyncStarted event.
|
||||
SPStreamEvent newSyncFinishedEvent(QString const &userID); ///< Create a new SyncFinished event.
|
||||
SPStreamEvent newSyncProgressEvent(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs); ///< Create a new SyncFinished event.
|
||||
|
||||
// Generic error event
|
||||
SPStreamEvent newGenericErrorEvent(grpc::ErrorCode errorCode); ///< Create a new GenericErrrorEvent event.
|
||||
|
||||
@ -1380,6 +1380,28 @@ void GRPCClient::processUserEvent(UserEvent const &event) {
|
||||
emit imapLoginFailed(username);
|
||||
break;
|
||||
}
|
||||
case UserEvent::kSyncStartedEvent: {
|
||||
SyncStartedEvent const &e = event.syncstartedevent();
|
||||
QString const &userID = QString::fromStdString(e.userid());
|
||||
this->logTrace(QString("User event received: SyncStarted (userID = %1).:").arg(userID));
|
||||
emit syncStarted(userID);
|
||||
break;
|
||||
}
|
||||
case UserEvent::kSyncFinishedEvent: {
|
||||
SyncFinishedEvent const &e = event.syncfinishedevent();
|
||||
QString const &userID = QString::fromStdString(e.userid());
|
||||
this->logTrace(QString("User event received: SyncFinished (userID = %1).:").arg(userID));
|
||||
emit syncFinished(userID);
|
||||
break;
|
||||
}
|
||||
case UserEvent::kSyncProgressEvent: {
|
||||
SyncProgressEvent const &e = event.syncprogressevent();
|
||||
QString const &userID = QString::fromStdString(e.userid());
|
||||
this->logTrace(QString("User event received SyncProgress (userID = %1, progress = %2, elapsedMs = %3, remainingMs = %4).").arg(userID)
|
||||
.arg(e.progress()).arg(e.elapsedms()).arg(e.remainingms()));
|
||||
emit syncProgress(userID, e.progress(), e.elapsedms(), e.remainingms());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this->logError("Unknown User event received.");
|
||||
}
|
||||
|
||||
@ -181,6 +181,9 @@ signals:
|
||||
void userBadEvent(QString const &userID, QString const& errorMessage);
|
||||
void usedBytesChanged(QString const &userID, qint64 usedBytes);
|
||||
void imapLoginFailed(QString const& username);
|
||||
void syncStarted(QString const &userID);
|
||||
void syncFinished(QString const &userID);
|
||||
void syncProgress(QString const &userID, double progress, qint64 elapsedMs, qint64 remainingMs);
|
||||
|
||||
public: // keychain related calls
|
||||
grpc::Status availableKeychains(QStringList &outKeychains);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -294,6 +294,48 @@ void User::setTotalBytes(float totalBytes) {
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return true iff a sync is in progress.
|
||||
//****************************************************************************************************************************************************
|
||||
bool User::isSyncing() const {
|
||||
return isSyncing_;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] syncing The new value for the sync state.
|
||||
//****************************************************************************************************************************************************
|
||||
void User::setIsSyncing(bool syncing) {
|
||||
if (isSyncing_ == syncing) {
|
||||
return;
|
||||
}
|
||||
|
||||
isSyncing_ = syncing;
|
||||
emit isSyncingChanged(syncing);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \return The sync progress ratio
|
||||
//****************************************************************************************************************************************************
|
||||
float User::syncProgress() const {
|
||||
return syncProgress_;
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] progress The progress ratio.
|
||||
//****************************************************************************************************************************************************
|
||||
void User::setSyncProgress(float progress) {
|
||||
if (qAbs(syncProgress_ - progress) < 0.00001) {
|
||||
return;
|
||||
}
|
||||
|
||||
syncProgress_ = progress;
|
||||
emit syncProgressChanged(progress);
|
||||
}
|
||||
|
||||
|
||||
//****************************************************************************************************************************************************
|
||||
/// \param[in] state The user state.
|
||||
/// \return A string describing the state.
|
||||
|
||||
@ -101,6 +101,8 @@ public:
|
||||
Q_PROPERTY(bool splitMode READ splitMode WRITE setSplitMode NOTIFY splitModeChanged)
|
||||
Q_PROPERTY(float usedBytes READ usedBytes WRITE setUsedBytes NOTIFY usedBytesChanged)
|
||||
Q_PROPERTY(float totalBytes READ totalBytes WRITE setTotalBytes NOTIFY totalBytesChanged)
|
||||
Q_PROPERTY(bool isSyncing READ isSyncing WRITE setIsSyncing NOTIFY isSyncingChanged)
|
||||
Q_PROPERTY(float syncProgress READ syncProgress WRITE setSyncProgress NOTIFY syncProgressChanged)
|
||||
|
||||
QString id() const;
|
||||
void setID(QString const &id);
|
||||
@ -120,6 +122,10 @@ public:
|
||||
void setUsedBytes(float usedBytes);
|
||||
float totalBytes() const;
|
||||
void setTotalBytes(float totalBytes);
|
||||
bool isSyncing() const;
|
||||
void setIsSyncing(bool syncing);
|
||||
float syncProgress() const;
|
||||
void setSyncProgress(float progress);
|
||||
|
||||
signals:
|
||||
// signals used for Qt properties
|
||||
@ -134,6 +140,8 @@ signals:
|
||||
void usedBytesChanged(float byteCount);
|
||||
void totalBytesChanged(float byteCount);
|
||||
void toggleSplitModeFinished();
|
||||
void isSyncingChanged(bool syncing);
|
||||
void syncProgressChanged(float syncProgress);
|
||||
|
||||
private: // member functions.
|
||||
User(QObject *parent); ///< Default constructor.
|
||||
@ -149,6 +157,8 @@ private: // data members.
|
||||
bool splitMode_ { false }; ///< Is split mode active.
|
||||
float usedBytes_ { 0.0f }; ///< The storage used by the user.
|
||||
float totalBytes_ { 1.0f }; ///< The storage quota of the user.
|
||||
bool isSyncing_ { false }; ///< Is a sync in progress for the user.
|
||||
float syncProgress_ { 0.0f }; ///< The sync progress.
|
||||
};
|
||||
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -449,6 +449,9 @@ message UserEvent {
|
||||
UserBadEvent userBadEvent = 4;
|
||||
UsedBytesChangedEvent usedBytesChangedEvent = 5;
|
||||
ImapLoginFailedEvent imapLoginFailedEvent = 6;
|
||||
SyncStartedEvent syncStartedEvent = 7;
|
||||
SyncFinishedEvent syncFinishedEvent = 8;
|
||||
SyncProgressEvent syncProgressEvent = 9;
|
||||
}
|
||||
}
|
||||
|
||||
@ -478,6 +481,21 @@ message ImapLoginFailedEvent {
|
||||
string username = 1;
|
||||
}
|
||||
|
||||
message SyncStartedEvent {
|
||||
string userID = 1;
|
||||
}
|
||||
|
||||
message SyncFinishedEvent {
|
||||
string userID = 1;
|
||||
}
|
||||
|
||||
message SyncProgressEvent {
|
||||
string userID = 1;
|
||||
double progress = 2;
|
||||
int64 elapsedMs = 3;
|
||||
int64 remainingMs = 4;
|
||||
}
|
||||
|
||||
//**********************************************************
|
||||
// Generic errors
|
||||
//**********************************************************
|
||||
|
||||
@ -185,6 +185,23 @@ func newIMAPLoginFailedEvent(username string) *StreamEvent {
|
||||
return userEvent(&UserEvent{Event: &UserEvent_ImapLoginFailedEvent{ImapLoginFailedEvent: &ImapLoginFailedEvent{Username: username}}})
|
||||
}
|
||||
|
||||
func NewSyncStartedEvent(userID string) *StreamEvent {
|
||||
return userEvent(&UserEvent{Event: &UserEvent_SyncStartedEvent{SyncStartedEvent: &SyncStartedEvent{UserID: userID}}})
|
||||
}
|
||||
|
||||
func NewSyncFinishedEvent(userID string) *StreamEvent {
|
||||
return userEvent(&UserEvent{Event: &UserEvent_SyncFinishedEvent{SyncFinishedEvent: &SyncFinishedEvent{UserID: userID}}})
|
||||
}
|
||||
|
||||
func NewSyncProgressEvent(userID string, progress float64, elapsedMs, remainingMs int64) *StreamEvent {
|
||||
return userEvent(&UserEvent{Event: &UserEvent_SyncProgressEvent{SyncProgressEvent: &SyncProgressEvent{
|
||||
UserID: userID,
|
||||
Progress: progress,
|
||||
ElapsedMs: elapsedMs,
|
||||
RemainingMs: remainingMs,
|
||||
}}})
|
||||
}
|
||||
|
||||
func NewGenericErrorEvent(errorCode ErrorCode) *StreamEvent {
|
||||
return genericErrorEvent(&GenericErrorEvent{Code: errorCode})
|
||||
}
|
||||
|
||||
@ -323,6 +323,15 @@ func (s *Service) watchEvents() {
|
||||
case events.UserBadEvent:
|
||||
_ = s.SendEvent(NewUserBadEvent(event.UserID, event.Error.Error()))
|
||||
|
||||
case events.SyncStarted:
|
||||
_ = s.SendEvent(NewSyncStartedEvent(event.UserID))
|
||||
|
||||
case events.SyncFinished:
|
||||
_ = s.SendEvent(NewSyncFinishedEvent(event.UserID))
|
||||
|
||||
case events.SyncProgress:
|
||||
_ = s.SendEvent(NewSyncProgressEvent(event.UserID, event.Progress, event.Elapsed.Milliseconds(), event.Remaining.Milliseconds()))
|
||||
|
||||
case events.UpdateLatest:
|
||||
safe.RLock(func() {
|
||||
s.latest = event.Version
|
||||
|
||||
Reference in New Issue
Block a user