Files
proton-bridge/internal/frontend/bridge-gui/bridge-gui/qml/AccountDelegate.qml
2024-01-02 15:06:05 +01:00

255 lines
9.2 KiB
QML

// Copyright (c) 2024 Proton AG
// This file is part of Proton Mail Bridge.
// Proton Mail Bridge 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.
// Proton Mail Bridge 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 Proton Mail Bridge. If not, see <https://www.gnu.org/licenses/>.
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Proton
Item {
id: root
enum ViewType {
SmallView,
LargeView
}
property var _spacing: 12
property ColorScheme colorScheme
property color progressColor: {
if (!root.enabled)
return root.colorScheme.text_weak;
if (root.type === AccountDelegate.SmallView)
return root.colorScheme.text_weak;
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 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 var type: AccountDelegate.SmallView
property string usedSpace: root.spaceWithUnits(root.user ? root.reasonableBytes(root.user.usedBytes) : 0)
property var user
function primaryEmail() {
return root.user ? root.user.primaryEmailOrUsername() : "";
}
function reasonableBytes(bytes) {
const safeBytes = bytes + 0;
if (safeBytes !== bytes)
return 0;
if (safeBytes < 0)
return 0;
return Math.ceil(safeBytes);
}
function reasonableFraction(used, total) {
const usedSafe = root.reasonableBytes(used);
const totalSafe = root.reasonableBytes(total);
if (totalSafe === 0 || usedSafe === 0)
return 0;
if (totalSafe <= usedSafe)
return 1;
return usedSafe / totalSafe;
}
function spaceWithUnits(bytes) {
if (bytes * 1 !== bytes || bytes === 0)
return "0 kB";
const units = ['B', "kB", "MB", "GB", "TB"];
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return Math.round(bytes * 10 / Math.pow(1024, i)) / 10 + " " + units[i];
}
// width expected to be set by parent object
implicitHeight: children[0].implicitHeight
RowLayout {
spacing: root._spacing
anchors {
left: root.left
right: root.right
top: root.top
}
Rectangle {
id: avatar
Layout.fillHeight: true
Layout.preferredWidth: height
color: root.colorScheme.background_avatar
radius: ProtonStyle.avatar_radius
Label {
anchors.fill: parent
color: "#FFFFFF"
colorScheme: root.colorScheme
font.weight: Font.Normal
horizontalAlignment: Qt.AlignHCenter
text: root.user ? root.user.avatarText.toUpperCase() : ""
type: {
switch (root.type) {
case AccountDelegate.SmallView:
return Label.Body;
case AccountDelegate.LargeView:
return Label.Title;
}
}
verticalAlignment: Qt.AlignVCenter
}
}
ColumnLayout {
id: account
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 0
Label {
id: labelEmail
Layout.maximumWidth: root.width - (root._spacing + avatar.width)
colorScheme: root.colorScheme
elide: Text.ElideMiddle
text: primaryEmail()
type: {
switch (root.type) {
case AccountDelegate.SmallView:
return Label.Body;
case AccountDelegate.LargeView:
return Label.Title;
}
}
MouseArea {
id: labelArea
anchors.fill: parent
hoverEnabled: true
}
ToolTip {
id: toolTipEmail
delay: 1000
text: primaryEmail()
visible: labelArea.containsMouse && labelEmail.truncated
background: Rectangle {
border.color: root.colorScheme.background_strong
color: root.colorScheme.background_norm
}
contentItem: Text {
color: root.colorScheme.text_norm
text: toolTipEmail.text
}
}
}
Item {
implicitHeight: root.type === AccountDelegate.LargeView ? 6 : 0
}
RowLayout {
spacing: 0
Label {
color: root.progressColor
colorScheme: root.colorScheme
text: {
if (!root.user)
return qsTr("Signed out");
switch (root.user.state) {
case EUserState.SignedOut:
default:
return qsTr("Signed out");
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;
}
}
type: {
switch (root.type) {
case AccountDelegate.SmallView:
return Label.Caption;
case AccountDelegate.LargeView:
return Label.Body;
}
}
Timer {
// dots animation while connecting & syncing.
id: dotsTimer
property string dots: ""
interval: 500
repeat: true
running: (root.user != null) && ((root.user.state === EUserState.Locked) || (root.user.isSyncing))
onRunningChanged: {
dots = "";
}
onTriggered: {
dots += ".";
if (dots.length > 3)
dots = "";
}
}
}
Label {
color: root.colorScheme.text_weak
colorScheme: root.colorScheme
text: root.user && root.user.state === EUserState.Connected && !root.user.isSyncing ? " / " + root.totalSpace : ""
type: {
switch (root.type) {
case AccountDelegate.SmallView:
return Label.Caption;
case AccountDelegate.LargeView:
return Label.Body;
}
}
}
}
Item {
implicitHeight: root.type === AccountDelegate.LargeView ? 3 : 0
}
Rectangle {
id: progress_bar
color: root.colorScheme.border_weak
height: 4
radius: ProtonStyle.progress_bar_radius
visible: root.user ? root.type === AccountDelegate.LargeView : false
width: 140
Rectangle {
id: progress_bar_filled
color: root.progressColor
radius: ProtonStyle.progress_bar_radius
visible: root.user ? parent.visible && (root.user.state === EUserState.Connected) : false
width: Math.min(1, Math.max(0.02, root.progressRatio)) * parent.width
anchors {
bottom: parent.bottom
left: parent.left
top: parent.top
}
}
}
}
Item {
Layout.fillWidth: true
}
}
}