GODT-1554 / 1555: Implement gRPC go service and Qt 5 frontend C++ app.

WIP: updates

WIP: cache on disk and autostart.

WIP: mail, keychain and more.

WIP: updated grpc version in go mod file.

WIP: user list.

WIP: RPC service placeholder

WIP: test C++ RPC client skeleton.

Other: missing license script update.

WIP: use Qt test framework.

WIP: test for app and login calls.

WIP: test for update & cache on disk calls.

WIP: tests for mail settings calls.

WIP: all client tests.

WIP: linter fixes.

WIP: fix missing license link.

WIP: update dependency_license script for gRPC and protobuf.

WIP: removed unused file.

WIP: app & login event streaming tests.

WIP: update event stream tests.

WIP: completed event streaming tests.

GODT-1554: qt C++ frontend skeleton.

WIP: C++ backend declaration.

wip: started drafting user model.

WIP: users. not functional.

WIP: invokable methods

WIP: Exception class + backend 'injection' into QML.

WIP: switch to VCPKG to ease multi-arch compilation,  C++ RPC client skeleton.

WIP: Renaming and reorganisation

WIP:introduced new 'grpc' go frontend.

WIP: Worker & Oveerseer for thread management.

WIP: added log to C++ app.

WIP: event stream architecture on Go side.

WIP: event parsing and streamer stopping.

WIP: Moved grpc to frontend subfolder + use vcpkg for gRPC and protobuf.

WIP: windows building ok

WIP: wired a few messages

WIP: more wiring.

WIP: Fixed imports after rebase on top of devel.

WIP: wired some bool and string properties.

WIP: more properties.

WIP: wired cache on disk stuff

WIP: connect event watcher.

WIP: login

WIP: fix showSplashScreen

WIP: Wired login calls.

WIP: user list.

WIP: Refactored main().

WIP: User retrieval .

WIP: no shared pointer in user model.

WIP: fixed user count.

WIP: cached goos.

WIP: Wired autostart

WIP: beta channel toggle wired.

WIP: User removal

WIP: wired theme

WIP: implemented configure apple mail.

WIP: split mode.

WIP: fixed user updates.

WIP: fixed Quit from tray icon

WIP: wired CurrentEmailClient

WIP: wired UseSSLForSMTP

WIP: wired change ports .

WIP: wired DoH. .

WIP: wired keychain calls.

WIP: wired autoupdate option.

WIP: QML Backend clean-up.

WIP: cleanup.

WIP: moved user related files in subfolder. .

WIP: User are managed using smart pointers.

WIP: cleanup.

WIP: more cleanup.

WIP: mail events forwarding

WIP: code inspection tweaks from CLion.

WIP: moved QML, cleanup, and missing copyright notices.

WIP: Backend is not QMLBackend.

Other: fixed issues reported by Leander. [skip ci]
This commit is contained in:
Xavier Michelon
2022-05-16 10:59:45 +02:00
committed by Jakub
parent a4e54f063d
commit c11fe3e1ab
183 changed files with 53334 additions and 10851 deletions

View File

@ -0,0 +1,190 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
Item {
id: root
property ColorScheme colorScheme
property var user
property var _spacing: 12 * ProtonStyle.px
property color usedSpaceColor : {
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
return root.colorScheme.signal_danger
}
property real usedFraction: root.user ? reasonableFracion(root.user.usedBytes, root.user.totalBytes) : 0
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)
function reasonableFracion(used, total){
var usedSafe = root.reasonableBytes(used)
var totalSafe = root.reasonableBytes(total)
if (totalSafe == 0 || usedSafe == 0) return 0
if (totalSafe <= usedSafe) return 1
return usedSafe / totalSafe
}
function reasonableBytes(bytes){
var safeBytes = bytes+0
if (safeBytes != bytes) return 0
if (safeBytes < 0) return 0
return Math.ceil(safeBytes)
}
function spaceWithUnits(bytes){
if (bytes*1 !== bytes || bytes == 0 ) return "0 kB"
var units = ['B',"kB", "MB", "GB", "TB"];
var i = parseInt(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
enum ViewType{
SmallView, LargeView
}
property var type : AccountDelegate.SmallView
RowLayout {
spacing: root._spacing
anchors {
top: root.top
left: root.left
right: root.rigth
}
Rectangle {
id: avatar
Layout.fillHeight: true
Layout.preferredWidth: height
radius: ProtonStyle.avatar_radius
color: root.colorScheme.background_avatar
Label {
colorScheme: root.colorScheme
anchors.fill: parent
text: root.user ? root.user.avatarText.toUpperCase(): ""
type: {
switch (root.type) {
case AccountDelegate.SmallView: return Label.Body
case AccountDelegate.LargeView: return Label.Title
}
}
font.weight: Font.Normal
color: "#FFFFFF"
horizontalAlignment: Qt.AlignHCenter
verticalAlignment: Qt.AlignVCenter
}
}
ColumnLayout {
id: account
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 0
Label {
Layout.maximumWidth: root.width - (
root._spacing + avatar.width
)
colorScheme: root.colorScheme
text: root.user ? user.username : ""
type: {
switch (root.type) {
case AccountDelegate.SmallView: return Label.Body
case AccountDelegate.LargeView: return Label.Title
}
}
elide: Text.ElideMiddle
}
Item { implicitHeight: root.type == AccountDelegate.LargeView ? 6 * ProtonStyle.px : 0 }
RowLayout {
spacing: 0
Label {
colorScheme: root.colorScheme
text: root.user && root.user.loggedIn ? root.usedSpace : qsTr("Signed out")
color: root.usedSpaceColor
type: {
switch (root.type) {
case AccountDelegate.SmallView: return Label.Caption
case AccountDelegate.LargeView: return Label.Body
}
}
}
Label {
colorScheme: root.colorScheme
text: root.user && root.user.loggedIn ? " / " + root.totalSpace : ""
color: root.colorScheme.text_weak
type: {
switch (root.type) {
case AccountDelegate.SmallView: return Label.Caption
case AccountDelegate.LargeView: return Label.Body
}
}
}
}
Rectangle {
id: storage_bar
visible: root.user ? root.type == AccountDelegate.LargeView : false
width: 140 * ProtonStyle.px
height: 4 * ProtonStyle.px
radius: ProtonStyle.storage_bar_radius
color: root.colorScheme.border_weak
Rectangle {
id: storage_bar_filled
radius: ProtonStyle.storage_bar_radius
color: root.usedSpaceColor
visible: root.user ? parent.visible && root.user.loggedIn : false
anchors {
top : parent.top
bottom : parent.bottom
left : parent.left
}
width: Math.min(1,Math.max(0.02,root.usedFraction)) * parent.width
}
}
}
Item {
Layout.fillWidth: true
}
}
}

View File

@ -0,0 +1,251 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
Item {
id: root
property ColorScheme colorScheme
property var backend
property var notifications
property var user
signal showSignIn()
signal showSetupGuide(var user, string address)
property int _leftMargin: 64
property int _rightMargin: 64
property int _topMargin: 32
property int _detailsTopMargin: 25
property int _bottomMargin: 12
property int _spacing: 20
property int _lineWidth: 1
ScrollView {
id: scrollView
clip: true
anchors.fill: parent
Item {
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
width: scrollView.availableWidth
height: scrollView.availableHeight
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
// do not set implicitWidth because implicit width of ColumnLayout will be equal to maximum implicit width of
// internal items. And if one of internal items would be a Text or Label - implicit width of those is always
// equal to non-wrapped text (i.e. one line only). That will lead to enabling horizontal scroll when not needed
implicitWidth: width
ColumnLayout {
spacing: 0
anchors.fill: parent
Rectangle {
id: topRectangle
color: root.colorScheme.background_norm
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
Layout.fillWidth: true
ColumnLayout {
spacing: root._spacing
anchors.fill: parent
anchors.leftMargin: root._leftMargin
anchors.rightMargin: root._rightMargin
anchors.topMargin: root._topMargin
anchors.bottomMargin: root._bottomMargin
RowLayout { // account delegate with action buttons
Layout.fillWidth: true
AccountDelegate {
Layout.fillWidth: true
colorScheme: root.colorScheme
user: root.user
type: AccountDelegate.LargeView
enabled: root.user ? root.user.loggedIn : false
}
Button {
Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
text: qsTr("Sign out")
secondary: true
visible: root.user ? root.user.loggedIn : false
onClicked: {
if (!root.user) return
root.user.logout()
}
}
Button {
Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
text: qsTr("Sign in")
secondary: true
visible: root.user ? !root.user.loggedIn : false
onClicked: {
if (!root.user) return
root.showSignIn()
}
}
Button {
Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
icon.source: "icons/ic-trash.svg"
secondary: true
onClicked: {
if (!root.user) return
root.notifications.askDeleteAccount(root.user)
}
}
}
Rectangle {
Layout.fillWidth: true
height: root._lineWidth
color: root.colorScheme.border_weak
}
SettingsItem {
colorScheme: root.colorScheme
text: qsTr("Email clients")
actionText: qsTr("Configure")
description: qsTr("Using the mailbox details below (re)configure your client.")
type: SettingsItem.Button
enabled: root.user ? root.user.loggedIn : false
visible: root.user ? !root.user.splitMode || root.user.addresses.length==1 : false
showSeparator: splitMode.visible
onClicked: {
if (!root.user) return
root.showSetupGuide(root.user, user.addresses[0])
}
Layout.fillWidth: true
}
SettingsItem {
id: splitMode
colorScheme: root.colorScheme
text: qsTr("Split addresses")
description: qsTr("Setup multiple email addresses individually.")
type: SettingsItem.Toggle
checked: root.user ? root.user.splitMode : false
visible: root.user ? root.user.addresses.length > 1 : false
enabled: root.user ? root.user.loggedIn : false
showSeparator: addressSelector.visible
onClicked: {
if (!splitMode.checked){
root.notifications.askEnableSplitMode(user)
} else {
addressSelector.currentIndex = 0
root.user.toggleSplitMode(!splitMode.checked)
}
}
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
enabled: root.user ? root.user.loggedIn : false
visible: root.user ? root.user.splitMode : false
ComboBox {
id: addressSelector
colorScheme: root.colorScheme
Layout.fillWidth: true
model: root.user ? root.user.addresses : null
}
Button {
colorScheme: root.colorScheme
text: qsTr("Configure")
secondary: true
onClicked: {
if (!root.user) return
root.showSetupGuide(root.user, addressSelector.displayText)
}
}
}
}
}
Rectangle {
color: root.colorScheme.background_weak
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
Layout.fillWidth: true
ColumnLayout {
id: configuration
anchors.fill: parent
anchors.leftMargin: root._leftMargin
anchors.rightMargin: root._rightMargin
anchors.topMargin: root._detailsTopMargin
anchors.bottomMargin: root._spacing
spacing: root._spacing
visible: root.user ? root.user.loggedIn : false
property string currentAddress: addressSelector.displayText
Label {
colorScheme: root.colorScheme
text: qsTr("Mailbox details")
type: Label.Body_semibold
}
Configuration {
colorScheme: root.colorScheme
title: qsTr("IMAP")
hostname: root.backend.hostname
port: root.backend.portIMAP.toString()
username: configuration.currentAddress
password: root.user ? root.user.password : ""
security: "STARTTLS"
}
Configuration {
colorScheme: root.colorScheme
title: qsTr("SMTP")
hostname : root.backend.hostname
port : root.backend.portSMTP.toString()
username : configuration.currentAddress
password : root.user ? root.user.password : ""
security : root.backend.useSSLforSMTP ? "SSL" : "STARTTLS"
}
}
}
}
}
}
}

View File

@ -0,0 +1,234 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import Proton 4.0
import Notifications 1.0
Popup {
id: root
property ColorScheme colorScheme
property Notification notification
property var mainWindow
topMargin: 37
leftMargin: (mainWindow.width - root.implicitWidth)/2
implicitHeight: contentLayout.implicitHeight + contentLayout.anchors.topMargin + contentLayout.anchors.bottomMargin
implicitWidth: 600 // contentLayout.implicitWidth + contentLayout.anchors.leftMargin + contentLayout.anchors.rightMargin
popupType: ApplicationWindow.PopupType.Banner
shouldShow: notification ? (notification.active && !notification.dismissed) : false
modal: false
Action {
id: defaultDismissAction
text: qsTr("OK")
onTriggered: {
if (!root.notification) {
return
}
root.notification.dismissed = true
}
}
RowLayout {
id: contentLayout
anchors.fill: parent
spacing: 0
Item {
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.left: parent.left
width: parent.width + 10
radius: ProtonStyle.banner_radius
color: {
if (!root.notification) {
return "transparent"
}
switch (root.notification.type) {
case Notification.NotificationType.Info:
return root.colorScheme.signal_info
case Notification.NotificationType.Success:
return root.colorScheme.signal_success
case Notification.NotificationType.Warning:
return root.colorScheme.signal_warning
case Notification.NotificationType.Danger:
return root.colorScheme.signal_danger
}
}
}
RowLayout {
anchors.fill: parent
anchors.topMargin: 14
anchors.bottomMargin: 14
anchors.leftMargin: 16
spacing: 8
ColorImage {
color: root.colorScheme.text_invert
width: 24
height: 24
sourceSize.width: 24
sourceSize.height: 24
Layout.preferredHeight: 24
Layout.preferredWidth: 24
source: {
if (!root.notification) {
return ""
}
switch (root.notification.type) {
case Notification.NotificationType.Info:
return "./icons/ic-info-circle-filled.svg"
case Notification.NotificationType.Success:
return "./icons/ic-info-circle-filled.svg"
case Notification.NotificationType.Warning:
return "./icons/ic-exclamation-circle-filled.svg"
case Notification.NotificationType.Danger:
return "./icons/ic-exclamation-circle-filled.svg"
}
}
}
Label {
colorScheme: root.colorScheme
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: 16
color: root.colorScheme.text_invert
text: root.notification ? root.notification.description : ""
wrapMode: Text.WordWrap
}
}
}
Rectangle {
Layout.fillHeight: true
width: 1
color: {
if (!root.notification) {
return "transparent"
}
switch (root.notification.type) {
case Notification.NotificationType.Info:
return root.colorScheme.signal_info_active
case Notification.NotificationType.Success:
return root.colorScheme.signal_success_active
case Notification.NotificationType.Warning:
return root.colorScheme.signal_warning_active
case Notification.NotificationType.Danger:
return root.colorScheme.signal_danger_active
}
}
}
Button {
colorScheme: root.colorScheme
Layout.fillHeight: true
id: actionButton
action: (root.notification && root.notification.action.length > 0) ? root.notification.action[0] : defaultDismissAction
background: Item {
clip: true
Rectangle {
anchors.top: parent.top
anchors.bottom: parent.bottom
anchors.right: parent.right
width: parent.width + 10
radius: ProtonStyle.banner_radius
color: {
if (!root.notification) {
return "transparent"
}
var norm
var hover
var active
switch (root.notification.type) {
case Notification.NotificationType.Info:
norm = root.colorScheme.signal_info
hover = root.colorScheme.signal_info_hover
active = root.colorScheme.signal_info_active
break;
case Notification.NotificationType.Success:
norm = root.colorScheme.signal_success
hover = root.colorScheme.signal_success_hover
active = root.colorScheme.signal_success_active
break;
case Notification.NotificationType.Warning:
norm = root.colorScheme.signal_warning
hover = root.colorScheme.signal_warning_hover
active = root.colorScheme.signal_warning_active
break;
case Notification.NotificationType.Danger:
norm = root.colorScheme.signal_danger
hover = root.colorScheme.signal_danger_hover
active = root.colorScheme.signal_danger_active
break;
}
if (actionButton.down) {
return active
}
if (actionButton.enabled && (actionButton.highlighted || actionButton.hovered || actionButton.checked)) {
return hover
}
if (actionButton.loading) {
return hover
}
return norm
}
}
}
}
}
}

View File

@ -0,0 +1,294 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Window 2.13
import Qt.labs.platform 1.1
import Proton 4.0
import Notifications 1.0
QtObject {
id: root
function isInInterval(num, lower_limit, upper_limit) {
return lower_limit <= num && num <= upper_limit
}
function bound(num, lower_limit, upper_limit) {
return Math.max(lower_limit, Math.min(upper_limit, num))
}
property var backend
property var title: "Proton Mail Bridge"
property Notifications _notifications: Notifications {
id: notifications
backend: root.backend
frontendMain: mainWindow
frontendStatus: statusWindow
frontendTray: trayIcon
}
property MainWindow _mainWindow: MainWindow {
id: mainWindow
visible: false
title: root.title
backend: root.backend
notifications: root._notifications
onVisibleChanged: {
backend.dockIconVisible = visible
}
Connections {
target: root.backend
onCacheUnavailable: {
mainWindow.showAndRise()
}
onColorSchemeNameChanged: root.setColorScheme()
}
}
property StatusWindow _statusWindow: StatusWindow {
id: statusWindow
visible: false
title: root.title
backend: root.backend
notifications: root._notifications
onShowMainWindow: {
mainWindow.showAndRise()
}
onShowHelp: {
mainWindow.showHelp()
mainWindow.showAndRise()
}
onShowSettings: {
mainWindow.showSettings()
mainWindow.showAndRise()
}
onShowSignIn: {
mainWindow.showSignIn(username)
mainWindow.showAndRise()
}
onQuit: {
backend.quit()
}
property rect screenRect
property rect iconRect
// use binding from function with width and height as arguments so it will be recalculated every time width and height are changed
property point position: getPosition(width, height)
x: position.x
y: position.y
function getPosition(_width, _height) {
if (screenRect.width === 0 || screenRect.height === 0) {
return Qt.point(0, 0)
}
var _x = 0
var _y = 0
// fit above
_y = iconRect.top - height
if (isInInterval(_y, screenRect.top, screenRect.bottom - height)) {
// position preferebly in the horizontal center but bound to the screen rect
_x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
return Qt.point(_x, _y)
}
// fit below
_y = iconRect.bottom
if (isInInterval(_y, screenRect.top, screenRect.bottom - height)) {
// position preferebly in the horizontal center but bound to the screen rect
_x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
return Qt.point(_x, _y)
}
// fit to the left
_x = iconRect.left - width
if (isInInterval(_x, screenRect.left, screenRect.right - width)) {
// position preferebly in the vertical center but bound to the screen rect
_y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
return Qt.point(_x, _y)
}
// fit to the right
_x = iconRect.right
if (isInInterval(_x, screenRect.left, screenRect.right - width)) {
// position preferebly in the vertical center but bound to the screen rect
_y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
return Qt.point(_x, _y)
}
// Fallback: position satatus window right above icon and let window manager decide.
console.warn("Can't position status window: screenRect =", screenRect, "iconRect =", iconRect)
_x = bound(iconRect.left + (iconRect.width - width)/2, screenRect.left, screenRect.right - width)
_y = bound(iconRect.top + (iconRect.height - height)/2, screenRect.top, screenRect.bottom - height)
return Qt.point(_x, _y)
}
}
property SystemTrayIcon _trayIcon: SystemTrayIcon {
id: trayIcon
visible: true
icon.source: getTrayIconPath()
icon.mask: true // make sure that systems like macOS will use proper color
tooltip: `${root.title} v${backend.version}`
onActivated: {
function calcStatusWindowPosition() {
// On some platforms (X11 / Plasma) Qt does not provide icon position and geometry info.
// In this case we rely on cursor position
var iconRect = Qt.rect(geometry.x, geometry.y, geometry.width, geometry.height)
if (geometry.width == 0 && geometry.height == 0) {
var mousePos = backend.getCursorPos()
iconRect.x = mousePos.x
iconRect.y = mousePos.y
iconRect.width = 0
iconRect.height = 0
}
// Find screen
var screen
for (var i in Qt.application.screens) {
var _screen = Qt.application.screens[i]
if (
isInInterval(iconRect.x, _screen.virtualX, _screen.virtualX + _screen.width) &&
isInInterval(iconRect.y, _screen.virtualY, _screen.virtualY + _screen.height)
) {
screen = _screen
break
}
}
if (!screen) {
// Fallback to primary screen
screen = Qt.application.screens[0]
}
// In case we used mouse to detect icon position - we want to make a fake icon rectangle from a point
if (iconRect.width == 0 && iconRect.height == 0) {
iconRect.x = bound(iconRect.x - 16, screen.virtualX, screen.virtualX + screen.width - 32)
iconRect.y = bound(iconRect.y - 16, screen.virtualY, screen.virtualY + screen.height - 32)
iconRect.width = 32
iconRect.height = 32
}
statusWindow.screenRect = Qt.rect(screen.virtualX, screen.virtualY, screen.width, screen.height)
statusWindow.iconRect = iconRect
}
function toggleWindow(win) {
if (win.visible) {
win.close()
} else {
win.showAndRise()
}
}
switch (reason) {
case SystemTrayIcon.Unknown:
break;
case SystemTrayIcon.Context:
case SystemTrayIcon.Trigger:
case SystemTrayIcon.DoubleClick:
case SystemTrayIcon.MiddleClick:
calcStatusWindowPosition()
toggleWindow(statusWindow)
break;
default:
break;
}
}
property NotificationFilter _systrayfilter: NotificationFilter {
source: root._notifications ? root._notifications.all : undefined
}
function getTrayIconPath() {
var color = backend.goos == "darwin" ? "mono" : "color"
var level = "norm"
if (_systrayfilter.topmost) {
switch (_systrayfilter.topmost.type) {
case Notification.NotificationType.Danger:
level = "error"
break;
case Notification.NotificationType.Warning:
level = "warn"
break;
case Notification.NotificationType.Info:
level = "update"
break;
}
}
return `./icons/systray-${color}-${level}.png`
}
}
Component.onCompleted: {
if (!root.backend) {
console.log("backend not loaded")
}
root.setColorScheme()
if (!root.backend.users) {
console.log("users not loaded")
}
var c = root.backend.users.count
var u = root.backend.users.get(0)
// DEBUG
if (c != 0) {
console.log("users non zero", c)
console.log("first user", u )
}
if (c === 0) {
mainWindow.showAndRise()
}
if (u) {
if (c === 1 && u.loggedIn === false) {
mainWindow.showAndRise()
}
}
if (root.backend.showOnStartup) {
mainWindow.showAndRise()
}
root.backend.guiReady()
}
function setColorScheme() {
if (root.backend.colorSchemeName == "light") ProtonStyle.currentStyle = ProtonStyle.lightStyle
if (root.backend.colorSchemeName == "dark") ProtonStyle.currentStyle = ProtonStyle.darkStyle
}
}

View File

@ -0,0 +1,316 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import Proton 4.0
ColumnLayout {
id: root
property var user
property var userIndex
property var backend
spacing : 5
Layout.fillHeight: true
//Layout.fillWidth: true
property ColorScheme colorScheme
TextField {
colorScheme: root.colorScheme
Layout.fillWidth: true
text: user !== undefined ? user.username : ""
onEditingFinished: {
user.username = text
}
}
ColumnLayout {
Layout.fillWidth: true
Switch {
id: userLoginSwitch
colorScheme: root.colorScheme
text: "LoggedIn"
enabled: user !== undefined && user.username.length > 0
checked: user ? user.loggedIn : false
onCheckedChanged: {
if (!user) {
return
}
if (checked) {
if (user === backend.loginUser) {
var newUserObject = backend.userComponent.createObject(backend, {username: user.username, loggedIn: true, setupGuideSeen: user.setupGuideSeen})
backend.users.append( { object: newUserObject } )
user.username = ""
user.resetLoginRequests()
return
}
user.loggedIn = true
user.resetLoginRequests()
return
} else {
user.loggedIn = false
user.resetLoginRequests()
}
}
}
Switch {
colorScheme: root.colorScheme
text: "Setup guide seen"
enabled: user !== undefined && user.username.length > 0
checked: user ? user.setupGuideSeen : false
onCheckedChanged: {
if (!user) {
return
}
user.setupGuideSeen = checked
}
}
}
RowLayout {
Layout.fillWidth: true
Label {
colorScheme: root.colorScheme
id: loginLabel
text: "Login:"
Layout.preferredWidth: Math.max(loginLabel.implicitWidth, faLabel.implicitWidth, passLabel.implicitWidth)
}
Button {
colorScheme: root.colorScheme
text: "name/pass error"
enabled: user !== undefined //&& user.isLoginRequested && !user.isLogin2FARequested && !user.isLogin2PasswordProvided
onClicked: {
root.backend.loginUsernamePasswordError("")
user.resetLoginRequests()
}
}
Button {
colorScheme: root.colorScheme
text: "free user error"
enabled: user !== undefined //&& user.isLoginRequested
onClicked: {
root.backend.loginFreeUserError()
user.resetLoginRequests()
}
}
Button {
colorScheme: root.colorScheme
text: "connection error"
enabled: user !== undefined //&& user.isLoginRequested
onClicked: {
root.backend.loginConnectionError("")
user.resetLoginRequests()
}
}
}
RowLayout {
Layout.fillWidth: true
Label {
colorScheme: root.colorScheme
id: faLabel
text: "2FA:"
Layout.preferredWidth: Math.max(loginLabel.implicitWidth, faLabel.implicitWidth, passLabel.implicitWidth)
}
Button {
colorScheme: root.colorScheme
text: "request"
enabled: user !== undefined //&& user.isLoginRequested && !user.isLogin2FARequested && !user.isLogin2PasswordRequested
onClicked: {
root.backend.login2FARequested(user.username)
user.isLogin2FARequested = true
}
}
Button {
colorScheme: root.colorScheme
text: "error"
enabled: user !== undefined //&& user.isLogin2FAProvided && !(user.isLogin2PasswordRequested && !user.isLogin2PasswordProvided)
onClicked: {
root.backend.login2FAError("")
user.isLogin2FAProvided = false
}
}
Button {
colorScheme: root.colorScheme
text: "Abort"
enabled: user !== undefined //&& user.isLogin2FAProvided && !(user.isLogin2PasswordRequested && !user.isLogin2PasswordProvided)
onClicked: {
root.backend.login2FAErrorAbort("")
user.resetLoginRequests()
}
}
}
RowLayout {
Layout.fillWidth: true
Label {
colorScheme: root.colorScheme
id: passLabel
text: "2 Password:"
Layout.preferredWidth: Math.max(loginLabel.implicitWidth, faLabel.implicitWidth, passLabel.implicitWidth)
}
Button {
colorScheme: root.colorScheme
text: "request"
enabled: user !== undefined //&& user.isLoginRequested && !user.isLogin2PasswordRequested && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
onClicked: {
root.backend.login2PasswordRequested("")
user.isLogin2PasswordRequested = true
}
}
Button {
colorScheme: root.colorScheme
text: "error"
enabled: user !== undefined //&& user.isLogin2PasswordProvided && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
onClicked: {
root.backend.login2PasswordError("")
user.isLogin2PasswordProvided = false
}
}
Button {
colorScheme: root.colorScheme
text: "Abort"
enabled: user !== undefined //&& user.isLogin2PasswordProvided && !(user.isLogin2FARequested && !user.isLogin2FAProvided)
onClicked: {
root.backend.login2PasswordErrorAbort("")
user.resetLoginRequests()
}
}
}
RowLayout {
Button {
colorScheme: root.colorScheme
text: "Login Finished"
onClicked: {
root.backend.loginFinished(0+loginFinishedIndex.text)
user.resetLoginRequests()
}
}
TextField {
id: loginFinishedIndex
colorScheme: root.colorScheme
label: "Index:"
text: root.userIndex
}
}
RowLayout {
Button {
colorScheme: root.colorScheme
text: "Already logged in"
onClicked: {
root.backend.loginAlreadyLoggedIn(0+loginAlreadyLoggedInIndex.text)
user.resetLoginRequests()
}
}
TextField {
id: loginAlreadyLoggedInIndex
colorScheme: root.colorScheme
label: "Index:"
text: root.userIndex
}
}
RowLayout {
TextField {
colorScheme: root.colorScheme
label: "used:"
text: user && user.usedBytes ? user.usedBytes : 0
onEditingFinished: {
user.usedBytes = parseFloat(text)
}
implicitWidth: 200
}
TextField {
colorScheme: root.colorScheme
label: "total:"
text: user && user.totalBytes ? user.totalBytes : 0
onEditingFinished: {
user.totalBytes = parseFloat(text)
}
implicitWidth: 200
}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Split mode"}
Toggle { colorScheme: root.colorScheme; checked: user ? user.splitMode : false; onClicked: {user.splitMode = !user.splitMode}}
Button { colorScheme: root.colorScheme; text: "Toggle Finished"; onClicked: {user.toggleSplitModeFinished()}}
}
TextArea { // TODO: this is causing binding loop on imlicitWidth
colorScheme: root.colorScheme
text: user && user.addresses ? user.addresses.join("\n") : "user@protonmail.com"
Layout.fillWidth: true
onEditingFinished: {
user.addresses = text.split("\n")
}
}
Item {
Layout.fillHeight: true
}
}

View File

@ -0,0 +1,102 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import Proton 4.0
ColumnLayout {
id: root
property ColorScheme colorScheme
property var backend
property alias currentIndex: usersListView.currentIndex
ListView {
id: usersListView
Layout.fillHeight: true
Layout.preferredWidth: 200
model: backend.usersTest
highlightFollowsCurrentItem: true
delegate: Item {
implicitHeight: children[0].implicitHeight + anchors.topMargin + anchors.bottomMargin
implicitWidth: children[0].implicitWidth + anchors.leftMargin + anchors.rightMargin
width: usersListView.width
anchors.margins: 10
Label {
colorScheme: root.colorScheme
text: modelData.username
anchors.margins: 10
anchors.fill: parent
MouseArea {
anchors.fill: parent
onClicked: {
usersListView.currentIndex = index
}
}
}
}
highlight: Rectangle {
color: root.colorScheme.interaction_default_active
}
}
RowLayout {
Layout.fillWidth: true
Button {
colorScheme: root.colorScheme
text: "+"
onClicked: {
var newUserObject = backend.userComponent.createObject(backend)
newUserObject.username = backend.loginUser.username.length > 0 ? backend.loginUser.username : "test@protonmail.com"
newUserObject.loggedIn = true
newUserObject.setupGuideSeen = true // backend.loginUser.setupGuideSeen
backend.loginUser.username = ""
backend.loginUser.loggedIn = false
backend.loginUser.setupGuideSeen = false
backend.users.append( { object: newUserObject } )
}
}
Button {
colorScheme: root.colorScheme
text: "-"
enabled: usersListView.currentIndex != 0
onClicked: {
// var userObject = backend.users.get(usersListView.currentIndex - 1)
backend.users.remove(usersListView.currentIndex - 1)
// userObject.deleteLater()
}
}
}
}

View File

@ -0,0 +1,28 @@
// Copyright (c) 2022 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 QtQml.Models 2.12
ListModel {
// overriding get method to ignore any role and return directly object itself
function get(row) {
if (row < 0 || row >= count) {
return undefined
}
return data(index(row, 0), Qt.DisplayRole)
}
}

View File

@ -0,0 +1,982 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Window 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQml.Models 2.12
import Qt.labs.platform 1.1
import Proton 4.0
import "./BridgeTest"
import BridgePreview 1.0
import Notifications 1.0
Window {
id: root
x: 10
y: 10
width: 800
height: 800
property ColorScheme colorScheme: ProtonStyle.darkStyle
flags : Qt.Window | Qt.Dialog
visible : true
title : "Bridge Test GUI"
// This is needed because on MacOS if first window shown is not transparent -
// all other windows of application will not have transparent background (black
// instead of transparency). In our case that mean that if BridgeTest will be
// shown before StatusWindow - StatusWindow will not have transparent corners.
color: "transparent"
function getCursorPos() {
return BridgePreview.getCursorPos()
}
function restart() {
root.quit()
console.log("Restarting....")
root.openBridge()
}
function openBridge() {
bridge = bridgeComponent.createObject()
var showSetupGuide = false
if (showSetupGuide) {
var newUserObject = root.userComponent.createObject(root)
newUserObject.username = "LerooooyJenkins@protonmail.com"
newUserObject.loggedIn = true
newUserObject.setupGuideSeen = false
root.users.append( { object: newUserObject } )
}
}
function quit() {
if (bridge !== undefined && bridge !== null) {
bridge.destroy()
}
}
function guiReady() {
console.log("Gui Ready")
}
function _log(msg, color) {
logTextArea.text += "<p style='color: " + color + ";'>" + msg + "</p>"
logTextArea.text += "\n"
}
function log(msg) {
console.log(msg)
_log(msg, root.colorScheme.signal_info)
}
function error(msg) {
console.error(msg)
_log(msg, root.colorScheme.signal_danger)
}
// No user object should be put in this list until a successful login
property var users: UserModel {
id: _users
onRowsInserted: {
for (var i = first; i <= last; i++) {
_usersTest.insert(i + 1, { object: get(i) } )
}
}
onRowsRemoved: {
_usersTest.remove(first + 1, first - last + 1)
}
onRowsMoved: {
_usersTest.move(start + 1, row + 1, end - start + 1)
}
onDataChanged: {
for (var i = topLeft.row; i <= bottomRight.row; i++) {
_usersTest.set(i + 1, { object: get(i) } )
}
}
}
// this list is used on test gui: it contains same users list as users above + fake user to represent login request of new user on pos 0
property var usersTest: UserModel {
id: _usersTest
}
property var userComponent: Component {
id: _userComponent
QtObject {
property string username: ""
property bool loggedIn: false
property bool splitMode: false
property bool setupGuideSeen: true
property var usedBytes: 5350*1024*1024
property var totalBytes: 20*1024*1024*1024
property string avatarText: "jd"
property string password: "SMj975NnEYYsqu55GGmlpv"
property var addresses: [
"jaanedoe@protonmail.com",
"jane@pm.me",
"jdoe@pm.me"
]
signal loginUsernamePasswordError()
signal loginFreeUserError()
signal loginConnectionError()
signal login2FARequested()
signal login2FAError()
signal login2FAErrorAbort()
signal login2PasswordRequested()
signal login2PasswordError()
signal login2PasswordErrorAbort()
// Test purpose only:
property bool isFakeUser: this === root.loginUser
function userSignal(msg) {
if (isFakeUser) {
return
}
root.log("<- User (" + username + "): " + msg)
}
function toggleSplitMode(makeActive) {
userSignal("toggle split mode "+makeActive)
}
signal toggleSplitModeFinished()
function configureAppleMail(address){
userSignal("confugure apple mail "+address)
}
function logout(){
userSignal("logout")
loggedIn = false
}
function remove(){
console.log("remove this", users.count)
for (var i=0; i<users.count; i++) {
if (users.get(i) === this) {
users.remove(i,1)
return
}
}
}
onLoginUsernamePasswordError: {
userSignal("loginUsernamePasswordError")
}
onLoginFreeUserError: {
userSignal("loginFreeUserError")
}
onLoginConnectionError: {
userSignal("loginConnectionError")
}
onLogin2FARequested: {
userSignal("login2FARequested")
}
onLogin2FAError: {
userSignal("login2FAError")
}
onLogin2FAErrorAbort: {
userSignal("login2FAErrorAbort")
}
onLogin2PasswordRequested: {
userSignal("login2PasswordRequested")
}
onLogin2PasswordError: {
userSignal("login2PasswordError")
}
onLogin2PasswordErrorAbort: {
userSignal("login2PasswordErrorAbort")
}
function resetLoginRequests() {
isLoginRequested = false
isLogin2FARequested = false
isLogin2FAProvided = false
isLogin2PasswordRequested = false
isLogin2PasswordProvided = false
}
property bool isLoginRequested: false
property bool isLogin2FARequested: false
property bool isLogin2FAProvided: false
property bool isLogin2PasswordRequested: false
property bool isLogin2PasswordProvided: false
}
}
// this it fake user used only for representing first login request
property var loginUser
Component.onCompleted: {
var newLoginUser = _userComponent.createObject()
root.loginUser = newLoginUser
root.loginUser.setupGuideSeen = false
_usersTest.append({object: newLoginUser})
newLoginUser.loginUsernamePasswordError.connect(root.loginUsernamePasswordError)
newLoginUser.loginFreeUserError.connect(root.loginFreeUserError)
newLoginUser.loginConnectionError.connect(root.loginConnectionError)
newLoginUser.login2FARequested.connect(root.login2FARequested)
newLoginUser.login2FAError.connect(root.login2FAError)
newLoginUser.login2FAErrorAbort.connect(root.login2FAErrorAbort)
newLoginUser.login2PasswordRequested.connect(root.login2PasswordRequested)
newLoginUser.login2PasswordError.connect(root.login2PasswordError)
newLoginUser.login2PasswordErrorAbort.connect(root.login2PasswordErrorAbort)
// add one user on start
var hasUserOnStart = false
if (hasUserOnStart) {
var newUserObject = root.userComponent.createObject(root)
newUserObject.username = "LerooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooyJenkins@protonmail.com"
newUserObject.loggedIn = true
newUserObject.setupGuideSeen = true
root.users.append( { object: newUserObject } )
}
}
TabBar {
id: tabBar
anchors.left: parent.left
anchors.right: parent.right
TabButton {
text: "Global settings"
}
TabButton {
text: "User control"
}
TabButton {
text: "Notifications"
}
TabButton {
text: "Log"
}
TabButton {
text: "Settings signals"
}
}
Rectangle {
color: root.colorScheme.background_norm
anchors.top: tabBar.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
implicitHeight: children[0].contentHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].contentWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
StackLayout {
anchors.fill: parent
currentIndex: tabBar.currentIndex
anchors.margins: 10
RowLayout {
id: globalTab
spacing : 5
ColumnLayout {
spacing : 5
Label {
colorScheme: root.colorScheme
text: "Global settings"
}
ButtonGroup {
id: styleRadioGroup
}
RadioButton {
colorScheme: root.colorScheme
Layout.fillWidth: true
text: "Light UI"
checked: ProtonStyle.currentStyle === ProtonStyle.lightStyle
ButtonGroup.group: styleRadioGroup
onCheckedChanged: {
if (checked && ProtonStyle.currentStyle !== ProtonStyle.lightStyle) {
root.colorSchemeName = "light"
}
}
}
RadioButton {
colorScheme: root.colorScheme
Layout.fillWidth: true
text: "Dark UI"
checked: ProtonStyle.currentStyle === ProtonStyle.darkStyle
ButtonGroup.group: styleRadioGroup
onCheckedChanged: {
if (checked && ProtonStyle.currentStyle !== ProtonStyle.darkStyle) {
root.colorSchemeName = "dark"
}
}
}
CheckBox {
id: showOnStartupCheckbox
colorScheme: root.colorScheme
text: "Show on startup"
checked: root.showOnStartup
onCheckedChanged: {
root.showOnStartup = checked
}
}
CheckBox {
id: showSplashScreen
colorScheme: root.colorScheme
text: "Show splash screen"
checked: root.showSplashScreen
onCheckedChanged: {
root.showSplashScreen = checked
}
}
Button {
colorScheme: root.colorScheme
//Layout.fillWidth: true
text: "Open Bridge"
enabled: bridge === undefined || bridge === null
onClicked: root.openBridge()
}
Button {
colorScheme: root.colorScheme
//Layout.fillWidth: true
text: "Close Bridge"
enabled: bridge !== undefined && bridge !== null
onClicked: {
bridge.destroy()
}
}
Item {
Layout.fillHeight: true
}
}
ColumnLayout {
spacing : 5
Label {
colorScheme: root.colorScheme
text: "Notifications"
}
Button {
colorScheme: root.colorScheme
text: "Notify: danger"
enabled: bridge !== undefined && bridge !== null
onClicked: {
bridge.mainWindow.notifyOnlyPaidUsers()
}
}
Button {
colorScheme: root.colorScheme
text: "Notify: warning"
enabled: bridge !== undefined && bridge !== null
onClicked: {
bridge.mainWindow.notifyUpdateManually()
}
}
Button {
colorScheme: root.colorScheme
text: "Notify: success"
enabled: bridge !== undefined && bridge !== null
onClicked: {
bridge.mainWindow.notifyUserAdded()
}
}
Item {
Layout.fillHeight: true
}
}
}
RowLayout {
id: usersTab
UserList {
id: usersListView
Layout.fillHeight: true
colorScheme: root.colorScheme
backend: root
}
UserControl {
colorScheme: root.colorScheme
backend: root
user: ((root.usersTest.count > usersListView.currentIndex) && usersListView.currentIndex != -1) ? root.usersTest.get(usersListView.currentIndex) : undefined
userIndex: usersListView.currentIndex - 1 // -1 because 0 index is fake user
}
}
RowLayout {
id: notificationsTab
spacing: 5
ColumnLayout {
spacing: 5
Switch {
text: "Internet connection"
colorScheme: root.colorScheme
checked: true
onCheckedChanged: {
checked ? root.internetOn() : root.internetOff()
}
}
Button {
text: "Update manual ready"
colorScheme: root.colorScheme
onClicked: {
root.updateManualReady("3.14.1592")
}
}
Button {
text: "Update manual done"
colorScheme: root.colorScheme
onClicked: {
root.updateManualRestartNeeded()
}
}
Button {
text: "Update manual error"
colorScheme: root.colorScheme
onClicked: {
root.updateManualError()
}
}
Button {
text: "Update force"
colorScheme: root.colorScheme
onClicked: {
root.updateForce("3.14.1592")
}
}
Button {
text: "Update force error"
colorScheme: root.colorScheme
onClicked: {
root.updateForceError()
}
}
Button {
text: "Update silent done"
colorScheme: root.colorScheme
onClicked: {
root.updateSilentRestartNeeded()
}
}
Button {
text: "Update silent error"
colorScheme: root.colorScheme
onClicked: {
root.updateSilentError()
}
}
Button {
text: "Update is latest version"
colorScheme: root.colorScheme
onClicked: {
root.updateIsLatestVersion()
}
}
Button {
text: "Bug report send OK"
colorScheme: root.colorScheme
onClicked: {
root.reportBugFinished()
root.bugReportSendSuccess()
}
}
}
ColumnLayout {
spacing: 5
Button {
text: "Bug report send error"
colorScheme: root.colorScheme
onClicked: {
root.reportBugFinished()
root.bugReportSendError()
}
}
Button {
text: "Cache anavailable"
colorScheme: root.colorScheme
onClicked: {
root.cacheUnavailable()
}
}
Button {
text: "Cache can't move"
colorScheme: root.colorScheme
onClicked: {
root.cacheCantMove()
}
}
Button {
text: "Cache location change success"
onClicked: {
root.cacheLocationChangeSuccess()
}
colorScheme: root.colorScheme
}
Button {
text: "Disk full"
colorScheme: root.colorScheme
onClicked: {
root.diskFull()
}
}
Button {
text: "No keychain"
colorScheme: root.colorScheme
onClicked: {
root.notifyHasNoKeychain()
}
}
Button {
text: "Rebuild keychain"
colorScheme: root.colorScheme
onClicked: {
root.notifyRebuildKeychain()
}
}
Button {
text: "Address changed"
colorScheme: root.colorScheme
onClicked: {
root.addressChanged("p@v.el")
}
}
Button {
text: "Address changed + Logout"
colorScheme: root.colorScheme
onClicked: {
root.addressChangedLogout("p@v.el")
}
}
}
}
TextArea {
id: logTextArea
colorScheme: root.colorScheme
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: 400
Layout.preferredHeight: 200
textFormat: TextEdit.RichText
//readOnly: true
}
ScrollView {
id: settingsTab
ColumnLayout {
RowLayout {
Label {colorScheme : root.colorScheme ; text : "GOOS : "}
Button {colorScheme : root.colorScheme ; text : "Linux" ; onClicked : root.goos = "linux" ; enabled: root.goos != "linux"}
Button {colorScheme : root.colorScheme ; text : "Windows" ; onClicked : root.goos = "windows" ; enabled: root.goos != "windows"}
Button {colorScheme : root.colorScheme ; text : "macOS" ; onClicked : root.goos = "darwin" ; enabled: root.goos != "darwin"}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Automatic updates:"}
Toggle {colorScheme: root.colorScheme; checked: root.isAutomaticUpdateOn; onClicked: root.isAutomaticUpdateOn = !root.isAutomaticUpdateOn}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Autostart:"}
Toggle {colorScheme: root.colorScheme; checked: root.isAutostartOn; onClicked: root.isAutostartOn = !root.isAutostartOn}
Button {colorScheme: root.colorScheme; text: "Toggle finished"; onClicked: root.toggleAutostartFinished()}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Beta:"}
Toggle {colorScheme: root.colorScheme; checked: root.isBetaEnabled; onClicked: root.isBetaEnabled = !root.isBetaEnabled}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "DoH:"}
Toggle {colorScheme: root.colorScheme; checked: root.isDoHEnabled; onClicked: root.isDoHEnabled = !root.isDoHEnabled}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "All Mail disabled:"}
Toggle {colorScheme: root.colorScheme; checked: root.isAllMailVisible; onClicked: root.isAllMailVisible = !root.isAllMailVisible}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Ports:"}
TextField {
colorScheme:root.colorScheme
label: "IMAP"
text: root.portIMAP
onEditingFinished: root.portIMAP = this.text*1
validator: IntValidator {bottom: 1; top: 65536}
}
TextField {
colorScheme:root.colorScheme
label: "SMTP"
text: root.portSMTP
onEditingFinished: root.portSMTP = this.text*1
validator: IntValidator {bottom: 1; top: 65536}
}
Button {colorScheme: root.colorScheme; text: "Change finished"; onClicked: root.changePortFinished()}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "SMTP using SSL:"}
Toggle {colorScheme: root.colorScheme; checked: root.useSSLforSMTP; onClicked: root.useSSLforSMTP = !root.useSSLforSMTP}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Local cache:"}
Toggle {colorScheme: root.colorScheme; checked: root.isDiskCacheEnabled; onClicked: root.isDiskCacheEnabled = !root.isDiskCacheEnabled}
TextField {
colorScheme:root.colorScheme
label: "Path"
text: root.diskCachePath.toString().replace("file://", "")
implicitWidth: 160
onEditingFinished: {
root.diskCachePath = Qt.resolvedUrl("file://"+text)
}
}
Button {colorScheme: root.colorScheme; text: "Change finished"; onClicked: root.changeLocalCacheFinished()}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Reset:"}
Button {colorScheme: root.colorScheme; text: "Finished"; onClicked: root.resetFinished()}
}
RowLayout {
Label {colorScheme: root.colorScheme; text: "Check update:"}
Button {colorScheme: root.colorScheme; text: "Finished"; onClicked: root.checkUpdatesFinished()}
}
}
}
}
}
property Bridge bridge
property string goos: "darwin"
property bool showOnStartup: true // this actually needs to be false, but since we use Bridge_test for testing purpose - lets default this to true just for convenience
property bool dockIconVisible: false
// this signals are used only when trying to login with new user (i.e. not in users model)
signal loginUsernamePasswordError(string errorMsg)
signal loginFreeUserError()
signal loginConnectionError(string errorMsg)
signal login2FARequested(string username)
signal login2FAError(string errorMsg)
signal login2FAErrorAbort(string errorMsg)
signal login2PasswordRequested()
signal login2PasswordError(string errorMsg)
signal login2PasswordErrorAbort(string errorMsg)
signal loginFinished(int index)
signal loginAlreadyLoggedIn(int index)
signal internetOff()
signal internetOn()
signal updateManualReady(var version)
signal updateManualRestartNeeded()
signal updateManualError()
signal updateForce(var version)
signal updateForceError()
signal updateSilentRestartNeeded()
signal updateSilentError()
signal updateIsLatestVersion()
function checkUpdates(){
console.log("check updates")
}
signal checkUpdatesFinished()
function installUpdate() {
console.log("manuall install update triggered")
}
property bool isDiskCacheEnabled: true
// Qt.resolvedUrl("file:///C:/Users/user/AppData/Roaming/protonmail/bridge/cache/c11/messages")
property url diskCachePath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
signal cacheUnavailable()
signal cacheCantMove()
signal cacheLocationChangeSuccess()
signal diskFull()
function changeLocalCache(enableDiskCache, diskCachePath) {
console.debug("-> disk cache", enableDiskCache, diskCachePath)
}
signal changeLocalCacheFinished()
// Settings
property bool isAutomaticUpdateOn : true
function toggleAutomaticUpdate(makeItActive) {
console.debug("-> silent updates", makeItActive, root.isAutomaticUpdateOn)
var callback = function () {
root.isAutomaticUpdateOn = makeItActive;
console.debug("-> CHANGED silent updates", makeItActive, root.isAutomaticUpdateOn)
}
atimer.onTriggered.connect(callback)
atimer.restart()
}
Timer {
id: atimer
interval: 2000
running: false
repeat: false
}
property bool isAutostartOn : true // Example of settings with loading state
function toggleAutostart(makeItActive) {
console.debug("-> autostart", makeItActive, root.isAutostartOn)
}
signal toggleAutostartFinished()
property bool isBetaEnabled : false
function toggleBeta(makeItActive){
console.debug("-> beta", makeItActive, root.isBetaEnabled)
root.isBetaEnabled = makeItActive
}
property bool isDoHEnabled : true
function toggleDoH(makeItActive){
console.debug("-> DoH", makeItActive, root.isDoHEnabled)
root.isDoHEnabled = makeItActive
}
property bool isAllMailVisible : true
function changeIsAllMailVisible(isVisible){
console.debug("-> All Mail Visible", isVisible, root.isAllMailVisible)
root.isAllMailVisible = isVisible
}
property bool useSSLforSMTP: false
function toggleUseSSLforSMTP(makeItActive){
console.debug("-> SMTP SSL", makeItActive, root.useSSLforSMTP)
}
signal toggleUseSSLFinished()
property string hostname: "127.0.0.1"
property int portIMAP: 1143
property int portSMTP: 1025
function changePorts(imapPort, smtpPort){
console.debug("-> ports", imapPort, smtpPort)
}
function isPortFree(port){
if (port == portIMAP) return false
if (port == portSMTP) return false
if (port == 12345) return false
return true
}
signal changePortFinished()
signal portIssueIMAP()
signal portIssueSMTP()
function triggerReset() {
console.debug("-> trigger reset")
}
signal resetFinished()
property string version: "2.0.X-BridePreview"
property url logsPath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
property url licensePath: StandardPaths.standardLocations(StandardPaths.HomeLocation)[0]
property url releaseNotesLink: Qt.resolvedUrl("https://protonmail.com/download/bridge/early_releases.html")
property url dependencyLicensesLink: Qt.resolvedUrl("https://github.com/ProtonMail/proton-bridge/v2/blob/master/COPYING_NOTES.md#dependencies")
property url landingPageLink: Qt.resolvedUrl("https://protonmail.com/bridge")
property string colorSchemeName: "light"
function changeColorScheme(newScheme){
root.colorSchemeName = newScheme
}
property string currentEmailClient: "" // "Apple Mail 14.0"
function updateCurrentMailClient(){
currentEmailClient = "Apple Mail 14.0"
}
function reportBug(description,address,emailClient,includeLogs){
console.log("report bug")
console.log(" description",description)
console.log(" address",address)
console.log(" emailClient",emailClient)
console.log(" includeLogs",includeLogs)
}
signal reportBugFinished()
signal bugReportSendSuccess()
signal bugReportSendError()
property var availableKeychain: ["gnome-keyring", "pass", "macos-keychain", "windows-credentials"]
property string currentKeychain: availableKeychain[0]
function changeKeychain(wantedKeychain){
console.log("Changing keychain from", root.currentKeychain, "to", wantedKeychain)
root.currentKeychain = wantedKeychain
root.changeKeychainFinished()
}
signal changeKeychainFinished()
signal notifyHasNoKeychain()
signal notifyRebuildKeychain()
signal noActiveKeyForRecipient(string email)
signal showMainWindow()
signal addressChanged(string address)
signal addressChangedLogout(string address)
signal userDisconnected(string username)
signal apiCertIssue()
property bool showSplashScreen: false
function login(username, password) {
root.log("-> login(" + username + ", " + password + ")")
loginUser.username = username
loginUser.isLoginRequested = true
}
function login2FA(username, code) {
root.log("-> login2FA(" + username + ", " + code + ")")
loginUser.isLogin2FAProvided = true
}
function login2Password(username, password) {
root.log("-> login2FA(" + username + ", " + password + ")")
loginUser.isLogin2PasswordProvided = true
}
function loginAbort(username) {
root.log("-> loginAbort(" + username + ")")
loginUser.resetLoginRequests()
}
onLoginUsernamePasswordError: {
console.debug("<- loginUsernamePasswordError")
}
onLoginFreeUserError: {
console.debug("<- loginFreeUserError")
}
onLoginConnectionError: {
console.debug("<- loginConnectionError")
}
onLogin2FARequested: {
console.debug("<- login2FARequested", username)
}
onLogin2FAError: {
console.debug("<- login2FAError")
}
onLogin2FAErrorAbort: {
console.debug("<- login2FAErrorAbort")
}
onLogin2PasswordRequested: {
console.debug("<- login2PasswordRequested")
}
onLogin2PasswordError: {
console.debug("<- login2PasswordError")
}
onLogin2PasswordErrorAbort: {
console.debug("<- login2PasswordErrorAbort")
}
onLoginFinished: {
console.debug("<- loginFinished", index)
}
onLoginAlreadyLoggedIn: {
console.debug("<- loginAlreadyLoggedIn", index)
}
onInternetOff: {
console.debug("<- internetOff")
}
onInternetOn: {
console.debug("<- internetOn")
}
Component {
id: bridgeComponent
Bridge {
backend: root
}
}
onClosing: {
Qt.quit()
}
}

View File

@ -0,0 +1,192 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
SettingsView {
id: root
fillHeight: true
property var selectedAddress
Label {
text: qsTr("Report a problem")
colorScheme: root.colorScheme
type: Label.Heading
}
TextArea {
id: description
property int _minLength: 150
property int _maxLength: 800
label: qsTr("Description")
colorScheme: root.colorScheme
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: heightForLinesVisible(4)
hint: description.text.length + "/" + _maxLength
placeholderText: qsTr("Tell us what went wrong or isn't working (min. %1 characters).").arg(_minLength)
validator: function(text) {
if (description.text.length < description._minLength) {
return qsTr("Enter a problem description (min. %1 characters).").arg(_minLength)
}
if (description.text.length > description._maxLength) {
return qsTr("Enter a problem description (max. %1 characters).").arg(_maxLength)
}
return
}
onTextChanged: {
// Rise max length error imidiatly while typing
if (description.text.length > description._maxLength) {
validate()
}
}
KeyNavigation.priority: KeyNavigation.BeforeItem
KeyNavigation.tab: address
// set implicitHeight to explicit height because se don't
// want TextArea implicitHeight (which is height of all text)
// to be considered in SettingsView internal scroll view
implicitHeight: height
}
TextField {
id: address
label: qsTr("Your contact email")
colorScheme: root.colorScheme
Layout.fillWidth: true
placeholderText: qsTr("e.g. jane.doe@protonmail.com")
validator: function(str) {
if (!isValidEmail(str)) {
return qsTr("Enter valid email address")
}
return
}
}
TextField {
id: emailClient
label: qsTr("Your email client (including version)")
colorScheme: root.colorScheme
Layout.fillWidth: true
placeholderText: qsTr("e.g. Apple Mail 14.0")
validator: function(str) {
if (str.length === 0) {
return qsTr("Enter an email client name and version")
}
return
}
}
RowLayout {
CheckBox {
id: includeLogs
text: qsTr("Include my recent logs")
colorScheme: root.colorScheme
checked: true
}
Button {
Layout.leftMargin: 12
text: qsTr("View logs")
secondary: true
colorScheme: root.colorScheme
onClicked: Qt.openUrlExternally(root.backend.logsPath)
}
}
TextEdit {
text: qsTr("Reports are not end-to-end encrypted, please do not send any sensitive information.")
readOnly: true
Layout.fillWidth: true
color: root.colorScheme.text_weak
font.family: ProtonStyle.font_family
font.weight: ProtonStyle.fontWeight_400
font.pixelSize: ProtonStyle.caption_font_size
font.letterSpacing: ProtonStyle.caption_letter_spacing
// No way to set lineHeight: Style.caption_line_height
selectionColor: root.colorScheme.interaction_norm
selectedTextColor: root.colorScheme.text_invert
wrapMode: Text.WordWrap
selectByMouse: true
}
Button {
id: sendButton
text: qsTr("Send")
colorScheme: root.colorScheme
onClicked: {
description.validate()
address.validate()
emailClient.validate()
if (description.error || address.error || emailClient.error) {
return
}
submit()
}
Connections {target: root.backend; onReportBugFinished: sendButton.loading = false }
}
function setDefaultValue() {
description.text = ""
address.text = root.selectedAddress
emailClient.text = root.backend.currentEmailClient
includeLogs.checked = true
}
function isValidEmail(text){
var reEmail = /^[^@]+@[^@]+\.[A-Za-z]+\s*$/
return reEmail.test(text)
}
function submit() {
sendButton.loading = true
root.backend.reportBug(
description.text,
address.text,
emailClient.text,
includeLogs.checked
)
}
onVisibleChanged: {
root.setDefaultValue()
}
}

View File

@ -0,0 +1,73 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import Proton 4.0
Rectangle {
id: root
property ColorScheme colorScheme
property string title
property string hostname
property string port
property string username
property string password
property string security
implicitWidth: 304
implicitHeight: content.height + 2*root._margin
color: root.colorScheme.background_norm
radius: ProtonStyle.card_radius
property int _margin: 24
ColumnLayout {
id: content
width: root.width - 2*root._margin
anchors{
top: root.top
left: root.left
leftMargin : root._margin
rightMargin : root._margin
topMargin : root._margin
bottomMargin : root._margin
}
spacing: 12
Label {
colorScheme: root.colorScheme
text: root.title
type: Label.Body_semibold
}
Item{}
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Hostname") ; value: root.hostname }
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Port") ; value: root.port }
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Username") ; value: root.username }
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Password") ; value: root.password }
ConfigurationItem{ colorScheme: root.colorScheme; label: qsTr("Security") ; value: root.security }
}
}

View File

@ -0,0 +1,89 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import Proton 4.0
Item {
id: root
Layout.fillWidth: true
property var colorScheme
property string label
property string value
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
ColumnLayout {
width: root.width
RowLayout {
Layout.fillWidth: true
ColumnLayout {
Label {
colorScheme: root.colorScheme
text: root.label
type: Label.Body
}
TextEdit {
id: valueText
text: root.value
color: root.colorScheme.text_weak
readOnly: true
selectByMouse: true
selectByKeyboard: true
selectionColor: root.colorScheme.text_weak
}
}
Item {
Layout.fillWidth: true
}
ColorImage {
source: "icons/ic-copy.svg"
color: root.colorScheme.text_norm
height: root.colorScheme.body_font_size
sourceSize.height: root.colorScheme.body_font_size
MouseArea {
anchors.fill: parent
onClicked : {
valueText.select(0, valueText.length)
valueText.copy()
valueText.deselect()
}
onPressed: parent.scale = 0.90
onReleased: parent.scale = 1
}
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: root.colorScheme.border_norm
}
}
}

View File

@ -0,0 +1,408 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
import Notifications 1.0
Item {
id: root
property ColorScheme colorScheme
property var backend
property var notifications
signal showSetupGuide(var user, string address)
RowLayout {
anchors.fill: parent
spacing: 0
Rectangle {
id: leftBar
property ColorScheme colorScheme: root.colorScheme.prominent
Layout.minimumWidth: 264
Layout.maximumWidth: 320
Layout.preferredWidth: 320
Layout.fillHeight: true
color: colorScheme.background_norm
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
id:topLeftBar
Layout.fillWidth: true
Layout.minimumHeight: 60
Layout.maximumHeight: 60
Layout.preferredHeight: 60
spacing: 0
Status {
Layout.leftMargin: 16
Layout.topMargin: 24
Layout.bottomMargin: 17
Layout.alignment: Qt.AlignHCenter
colorScheme: leftBar.colorScheme
backend: root.backend
notifications: root.notifications
notificationWhitelist: Notifications.Group.Connection | Notifications.Group.ForceUpdate
}
// just a placeholder
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
Button {
colorScheme: leftBar.colorScheme
Layout.minimumHeight: 36
Layout.maximumHeight: 36
Layout.preferredHeight: 36
Layout.minimumWidth: 36
Layout.maximumWidth: 36
Layout.preferredWidth: 36
Layout.topMargin: 16
Layout.bottomMargin: 9
Layout.rightMargin: 4
horizontalPadding: 0
icon.source: "./icons/ic-question-circle.svg"
onClicked: rightContent.showHelpView()
}
Button {
colorScheme: leftBar.colorScheme
Layout.minimumHeight: 36
Layout.maximumHeight: 36
Layout.preferredHeight: 36
Layout.minimumWidth: 36
Layout.maximumWidth: 36
Layout.preferredWidth: 36
Layout.topMargin: 16
Layout.bottomMargin: 9
Layout.rightMargin: 16
horizontalPadding: 0
icon.source: "./icons/ic-cog-wheel.svg"
onClicked: rightContent.showGeneralSettings()
}
}
Item {implicitHeight:10}
// Separator line
Rectangle {
Layout.fillWidth: true
Layout.minimumHeight: 1
Layout.maximumHeight: 1
color: leftBar.colorScheme.border_weak
}
ListView {
id: accounts
property var _topBottomMargins: 24
property var _leftRightMargins: 16
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: accounts._leftRightMargins
Layout.rightMargin: accounts._leftRightMargins
Layout.topMargin: accounts._topBottomMargins
Layout.bottomMargin: accounts._topBottomMargins
spacing: 12
clip: true
boundsBehavior: Flickable.StopAtBounds
header: Rectangle {
height: headerLabel.height+16
// color: ProtonStyle.transparent
Label{
colorScheme: leftBar.colorScheme
id: headerLabel
text: qsTr("Accounts")
type: Label.LabelType.Body
}
}
highlight: Rectangle {
color: leftBar.colorScheme.interaction_default_active
radius: ProtonStyle.account_row_radius
}
model: root.backend.users
delegate: Item {
width: leftBar.width - 2*accounts._leftRightMargins
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
AccountDelegate {
id: accountDelegate
anchors.fill: parent
anchors.topMargin: 8
anchors.bottomMargin: 8
anchors.leftMargin: 12
anchors.rightMargin: 12
colorScheme: leftBar.colorScheme
user: root.backend.users.get(index)
}
MouseArea {
anchors.fill: parent
onClicked: {
var user = root.backend.users.get(index)
accounts.currentIndex = index
if (!user) return
if (user.loggedIn) {
rightContent.showAccount()
} else {
signIn.username = user.username
rightContent.showSignIn()
}
}
}
}
}
// Separator
Rectangle {
Layout.fillWidth: true
Layout.minimumHeight: 1
Layout.maximumHeight: 1
color: leftBar.colorScheme.border_weak
}
Item {
id: bottomLeftBar
Layout.fillWidth: true
Layout.minimumHeight: 52
Layout.maximumHeight: 52
Layout.preferredHeight: 52
Button {
colorScheme: leftBar.colorScheme
width: 36
height: 36
anchors.left: parent.left
anchors.top: parent.top
anchors.leftMargin: 16
anchors.topMargin: 7
horizontalPadding: 0
icon.source: "./icons/ic-plus.svg"
onClicked: {
signIn.username = ""
rightContent.showSignIn()
}
}
}
}
}
Rectangle { // right content background
Layout.fillWidth: true
Layout.fillHeight: true
color: colorScheme.background_norm
StackLayout {
id: rightContent
anchors.fill: parent
AccountView { // 0
colorScheme: root.colorScheme
backend: root.backend
notifications: root.notifications
user: {
if (accounts.currentIndex < 0) return undefined
if (root.backend.users.count == 0) return undefined
return root.backend.users.get(accounts.currentIndex)
}
onShowSignIn: {
signIn.username = this.user.username
rightContent.showSignIn()
}
onShowSetupGuide: {
root.showSetupGuide(user,address)
}
}
GridLayout { // 1 Sign In
columns: 2
Button {
id: backButton
Layout.leftMargin: 18
Layout.topMargin: 10
Layout.alignment: Qt.AlignTop
colorScheme: root.colorScheme
onClicked: {
signIn.abort()
rightContent.showAccount()
}
icon.source: "icons/ic-arrow-left.svg"
secondary: true
horizontalPadding: 8
}
SignIn {
id: signIn
Layout.topMargin: 68
Layout.leftMargin: 80 - backButton.width - 18
Layout.rightMargin: 80
Layout.bottomMargin: 68
Layout.preferredWidth: 320
Layout.fillWidth: true
Layout.fillHeight: true
colorScheme: root.colorScheme
backend: root.backend
}
}
GeneralSettings { // 2
colorScheme: root.colorScheme
backend: root.backend
notifications: root.notifications
onBack: {
rightContent.showAccount()
}
}
KeychainSettings { // 3
colorScheme: root.colorScheme
backend: root.backend
onBack: {
rightContent.showGeneralSettings()
}
}
PortSettings { // 4
colorScheme: root.colorScheme
backend: root.backend
onBack: {
rightContent.showGeneralSettings()
}
}
SMTPSettings { // 5
colorScheme: root.colorScheme
backend: root.backend
onBack: {
rightContent.showGeneralSettings()
}
}
LocalCacheSettings { // 6
colorScheme: root.colorScheme
backend: root.backend
notifications: root.notifications
onBack: {
rightContent.showGeneralSettings()
}
}
HelpView { // 7
colorScheme: root.colorScheme
backend: root.backend
onBack: {
rightContent.showAccount()
}
}
BugReportView { // 8
colorScheme: root.colorScheme
backend: root.backend
selectedAddress: {
if (accounts.currentIndex < 0) return ""
if (root.backend.users.count == 0) return ""
var user = root.backend.users.get(accounts.currentIndex)
if (!user) return ""
return user.addresses[0]
}
onBack: {
rightContent.showHelpView()
}
}
function showAccount(index) {
if (index !== undefined && index >= 0){
accounts.currentIndex = index
}
rightContent.currentIndex = 0
}
function showSignIn () { rightContent.currentIndex = 1 }
function showGeneralSettings () { rightContent.currentIndex = 2 }
function showKeychainSettings () { rightContent.currentIndex = 3 }
function showPortSettings () { rightContent.currentIndex = 4 }
function showSMTPSettings () { rightContent.currentIndex = 5 }
function showLocalCacheSettings () { rightContent.currentIndex = 6 }
function showHelpView () { rightContent.currentIndex = 7 }
function showBugReport () { rightContent.currentIndex = 8 }
Connections {
target: root.backend
onLoginFinished: rightContent.showAccount(index)
onLoginAlreadyLoggedIn: rightContent.showAccount(index)
}
}
}
}
function showLocalCacheSettings(){rightContent.showLocalCacheSettings() }
function showSettings(){rightContent.showGeneralSettings() }
function showHelp(){rightContent.showHelpView() }
function showSignIn(username){
signIn.username = username
rightContent.showSignIn()
}
}

View File

@ -0,0 +1,55 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Controls 2.12
import "."
import "./Proton"
Rectangle {
property var target: parent
x: target.x
y: target.y
width: target.width
height: target.height
color: "transparent"
border.color: "red"
border.width: 1
//z: parent.z - 1
z: 10000000
Label {
text: parent.width + "x" + parent.height
anchors.centerIn: parent
color: "black"
colorScheme: ProtonStyle.currentStyle
}
Rectangle {
width: target.implicitWidth
height: target.implicitHeight
color: "transparent"
border.color: "green"
border.width: 1
//z: parent.z - 1
z: 10000000
}
}

View File

@ -0,0 +1,225 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQuick.Controls.impl 2.13
import Proton 4.0
SettingsView {
id: root
property bool _isAdvancedShown: false
property var notifications
fillHeight: false
Label {
colorScheme: root.colorScheme
text: qsTr("Settings")
type: Label.Heading
Layout.fillWidth: true
}
SettingsItem {
id: autoUpdate
colorScheme: root.colorScheme
text: qsTr("Automatic updates")
description: qsTr("Bridge will automatically update in the background.")
type: SettingsItem.Toggle
checked: root.backend.isAutomaticUpdateOn
onClicked: root.backend.toggleAutomaticUpdate(!autoUpdate.checked)
Layout.fillWidth: true
}
SettingsItem {
id: autostart
colorScheme: root.colorScheme
text: qsTr("Open on startup")
description: qsTr("Bridge will open upon startup.")
type: SettingsItem.Toggle
checked: root.backend.isAutostartOn
onClicked: {
autostart.loading = true
root.backend.toggleAutostart(!autostart.checked)
}
Connections{
target: root.backend
onToggleAutostartFinished: {
autostart.loading = false
}
}
Layout.fillWidth: true
}
SettingsItem {
id: beta
colorScheme: root.colorScheme
text: qsTr("Beta access")
description: qsTr("Be among the first to try new features.")
type: SettingsItem.Toggle
checked: root.backend.isBetaEnabled
onClicked: {
if (!beta.checked) {
root.notifications.askEnableBeta()
} else {
root.backend.toggleBeta(false)
}
}
Layout.fillWidth: true
}
RowLayout {
ColorImage {
Layout.alignment: Qt.AlignTop
source: root._isAdvancedShown ? "icons/ic-chevron-up.svg" : "icons/ic-chevron-down.svg"
color: root.colorScheme.interaction_norm
height: root.colorScheme.body_font_size
sourceSize.height: root.colorScheme.body_font_size
MouseArea {
anchors.fill: parent
onClicked: root._isAdvancedShown = !root._isAdvancedShown
}
}
Label {
id: advSettLabel
colorScheme: root.colorScheme
text: qsTr("Advanced settings")
color: root.colorScheme.interaction_norm
type: Label.Body
MouseArea {
anchors.fill: parent
onClicked: root._isAdvancedShown = !root._isAdvancedShown
}
}
}
SettingsItem {
id: keychains
visible: root._isAdvancedShown && root.backend.availableKeychain.length > 1
colorScheme: root.colorScheme
text: qsTr("Change keychain")
description: qsTr("Change which keychain Bridge uses as default")
actionText: qsTr("Change")
type: SettingsItem.Button
checked: root.backend.isDoHEnabled
onClicked: root.parent.showKeychainSettings()
Layout.fillWidth: true
}
SettingsItem {
id: doh
visible: root._isAdvancedShown
colorScheme: root.colorScheme
text: qsTr("Alternative routing")
description: qsTr("If Protons servers are blocked in your location, alternative network routing will be used to reach Proton.")
type: SettingsItem.Toggle
checked: root.backend.isDoHEnabled
onClicked: root.backend.toggleDoH(!doh.checked)
Layout.fillWidth: true
}
SettingsItem {
id: darkMode
visible: root._isAdvancedShown
colorScheme: root.colorScheme
text: qsTr("Dark mode")
description: qsTr("Choose dark color theme.")
type: SettingsItem.Toggle
checked: root.backend.colorSchemeName == "dark"
onClicked: root.backend.changeColorScheme( darkMode.checked ? "light" : "dark")
Layout.fillWidth: true
}
SettingsItem {
id: allMail
visible: root._isAdvancedShown
colorScheme: root.colorScheme
text: qsTr("Show All Mail")
description: qsTr("Choose to list the All Mail folder in your local client.")
type: SettingsItem.Toggle
checked: root.backend.isAllMailVisible
onClicked: root.notifications.askChangeAllMailVisibility(root.backend.isAllMailVisible)
Layout.fillWidth: true
}
SettingsItem {
id: ports
visible: root._isAdvancedShown
colorScheme: root.colorScheme
text: qsTr("Default ports")
actionText: qsTr("Change")
description: qsTr("Choose which ports are used by default.")
type: SettingsItem.Button
onClicked: root.parent.showPortSettings()
Layout.fillWidth: true
}
SettingsItem {
id: smtp
visible: root._isAdvancedShown
colorScheme: root.colorScheme
text: qsTr("SMTP connection mode")
actionText: qsTr("Change")
description: qsTr("Change the protocol Bridge and your client use to connect.")
type: SettingsItem.Button
onClicked: root.parent.showSMTPSettings()
Layout.fillWidth: true
}
SettingsItem {
id: cache
visible: root._isAdvancedShown
colorScheme: root.colorScheme
text: qsTr("Local cache")
actionText: qsTr("Configure")
description: qsTr("Configure Bridge's local cache.")
type: SettingsItem.Button
onClicked: root.parent.showLocalCacheSettings()
Layout.fillWidth: true
}
SettingsItem {
id: reset
visible: root._isAdvancedShown
colorScheme: root.colorScheme
text: qsTr("Reset Bridge")
actionText: qsTr("Reset")
description: qsTr("Remove all accounts, clear cached data, and restore the original settings.")
type: SettingsItem.Button
onClicked: {
root.notifications.askResetBridge()
}
Layout.fillWidth: true
}
}

View File

@ -0,0 +1,116 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
SettingsView {
id: root
fillHeight: true
Label {
colorScheme: root.colorScheme
text: qsTr("Help")
type: Label.Heading
Layout.fillWidth: true
}
SettingsItem {
id: setupPage
colorScheme: root.colorScheme
text: qsTr("Installation and setup")
actionText: qsTr("Go to help topics")
actionIcon: "./icons/ic-external-link.svg"
description: qsTr("Get help setting up your client with our instructions and FAQs.")
type: SettingsItem.PrimaryButton
onClicked: {Qt.openUrlExternally("https://protonmail.com/support/categories/bridge/")}
Layout.fillWidth: true
}
SettingsItem {
id: checkUpdates
colorScheme: root.colorScheme
text: qsTr("Updates")
actionText: qsTr("Check now")
description: qsTr("Check that you're using the latest version of Bridge. To stay up to date, enable auto-updates in settings.")
type: SettingsItem.Button
onClicked: {
checkUpdates.loading = true
root.backend.checkUpdates()
}
Connections {target: root.backend; onCheckUpdatesFinished: checkUpdates.loading = false}
Layout.fillWidth: true
}
SettingsItem {
id: logs
colorScheme: root.colorScheme
text: qsTr("Logs")
actionText: qsTr("View logs")
description: qsTr("Open and review logs to troubleshoot.")
type: SettingsItem.Button
onClicked: Qt.openUrlExternally(root.backend.logsPath)
Layout.fillWidth: true
}
SettingsItem {
id: reportBug
colorScheme: root.colorScheme
text: qsTr("Report a problem")
actionText: qsTr("Report a problem")
description: qsTr("Something not working as expected? Let us know.")
type: SettingsItem.Button
onClicked: {
root.backend.updateCurrentMailClient()
root.parent.showBugReport()
}
Layout.fillWidth: true
}
// fill height so the footer label will be allways attached to the bottom
Item {
Layout.fillHeight: true
Layout.fillWidth: true
}
Label {
Layout.alignment: Qt.AlignHCenter
colorScheme: root.colorScheme
type: Label.Caption
color: root.colorScheme.text_weak
textFormat: Text.StyledText
horizontalAlignment: Text.AlignHCenter
text: qsTr("Proton Mail Bridge v%1<br>© 2021 Proton AG<br>%2 %3<br>%4").
arg(root.backend.version).
arg(link(root.backend.licensePath, qsTr("License"))).
arg(link(root.backend.dependencyLicensesLink, qsTr("Dependencies"))).
arg(link(root.backend.releaseNotesLink, qsTr("Release notes")))
onLinkActivated: Qt.openUrlExternally(link)
}
}

View File

@ -0,0 +1,116 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQuick.Controls.impl 2.13
import Proton 4.0
SettingsView {
id: root
fillHeight: false
property bool _valuesChanged: keychainSelection.checkedButton && keychainSelection.checkedButton.text != root.backend.currentKeychain
Label {
colorScheme: root.colorScheme
text: qsTr("Default keychain")
type: Label.Heading
Layout.fillWidth: true
}
Label {
colorScheme: root.colorScheme
text: qsTr("Change which keychain Bridge uses as default")
type: Label.Body
color: root.colorScheme.text_weak
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
ColumnLayout {
spacing: 16
ButtonGroup{ id: keychainSelection }
Repeater {
model: root.backend.availableKeychain
RadioButton {
colorScheme: root.colorScheme
ButtonGroup.group: keychainSelection
text: modelData
}
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: root.colorScheme.border_weak
}
RowLayout {
spacing: 12
Button {
id: submitButton
colorScheme: root.colorScheme
text: qsTr("Save and restart")
enabled: root._valuesChanged
onClicked: {
root.backend.changeKeychain(keychainSelection.checkedButton.text)
}
}
Button {
colorScheme: root.colorScheme
text: qsTr("Cancel")
onClicked: root.back()
secondary: true
}
Connections {
target: root.backend
onChangeKeychainFinished: {
submitButton.loading = false
root.back()
}
}
}
onBack: {
root.setDefaultValues()
}
function setDefaultValues(){
for (var bi in keychainSelection.buttons){
var button = keychainSelection.buttons[bi]
if (button.text == root.backend.currentKeychain) {
button.checked = true
break;
}
}
}
Component.onCompleted: root.setDefaultValues()
}

View File

@ -0,0 +1,149 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQuick.Controls.impl 2.13
import QtQuick.Dialogs 1.1
import Proton 4.0
SettingsView {
id: root
fillHeight: false
property var notifications
property bool _diskCacheEnabled: true
property url _diskCachePath: pathDialog.shortcuts.home
Label {
colorScheme: root.colorScheme
text: qsTr("Local cache")
type: Label.Heading
Layout.fillWidth: true
}
Label {
colorScheme: root.colorScheme
text: qsTr("Bridge stores your encrypted messages locally to optimize communication with your client.")
type: Label.Body
color: root.colorScheme.text_weak
Layout.fillWidth: true
Layout.maximumWidth: this.parent.Layout.maximumWidth
wrapMode: Text.WordWrap
}
SettingsItem {
colorScheme: root.colorScheme
text: qsTr("Enable local cache")
description: qsTr("Recommended for optimal performance.")
type: SettingsItem.Toggle
checked: root._diskCacheEnabled
onClicked: root._diskCacheEnabled = !root._diskCacheEnabled
Layout.fillWidth: true
}
SettingsItem {
colorScheme: root.colorScheme
text: qsTr("Current cache location")
actionText: qsTr("Change location")
description: root.backend.goos === "windows" ?
root._diskCachePath.toString().replace("file:///", "").replace(new RegExp("/", 'g'), "\\") + "\\" :
root._diskCachePath.toString().replace("file://", "") + "/"
descriptionWrap: Text.WrapAnywhere
type: SettingsItem.Button
enabled: root._diskCacheEnabled
onClicked: {
pathDialog.open()
}
Layout.fillWidth: true
FileDialog {
id: pathDialog
title: qsTr("Select cache location")
folder: root._diskCachePath
onAccepted: root._diskCachePath = pathDialog.fileUrl
selectFolder: true
}
}
RowLayout {
spacing: 12
Button {
id: submitButton
colorScheme: root.colorScheme
text: qsTr("Save and restart")
enabled: (
root.backend.diskCachePath != root._diskCachePath ||
root.backend.isDiskCacheEnabled != root._diskCacheEnabled
)
onClicked: {
root.submit()
}
}
Button {
colorScheme: root.colorScheme
text: qsTr("Cancel")
onClicked: root.back()
secondary: true
}
Connections {
target: root.backend
onChangeLocalCacheFinished: {
submitButton.loading = false
root.setDefaultValues()
}
}
}
onBack: {
root.setDefaultValues()
}
function submit(){
if (!root._diskCacheEnabled && root.backend.isDiskCacheEnabled) {
root.notifications.askDisableLocalCache()
return
}
if (root._diskCacheEnabled && !root.backend.isDiskCacheEnabled) {
root.notifications.askEnableLocalCache(root._diskCachePath)
return
}
// Not asking, only changing path
submitButton.loading = true
root.backend.changeLocalCache(root.backend.isDiskCacheEnabled, root._diskCachePath)
}
function setDefaultValues(){
root._diskCacheEnabled = root.backend.isDiskCacheEnabled
root._diskCachePath = root.backend.diskCachePath
}
onVisibleChanged: {
root.setDefaultValues()
}
}

View File

@ -0,0 +1,212 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Window 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
import Notifications 1.0
import CppBackend 1.0
import "tests"
ApplicationWindow {
id: root
width: 960
height: 576
visible: true
minimumHeight: contentLayout.implicitHeight
minimumWidth: contentLayout.implicitWidth
colorScheme: ProtonStyle.currentStyle
property var backend
property var notifications
// This is needed because on MacOS if first window shown is not transparent -
// all other windows of application will not have transparent background (black
// instead of transparency). In our case that mean that if MainWindow will be
// shown before StatusWindow - StatusWindow will not have transparent corners.
color: "transparent"
// show Setup Guide on every new user
Connections {
target: root.backend.users
onRowsInserted: {
// considerring that users are added one-by-one
var user = root.backend.users.get(first)
if (!user.loggedIn) {
return
}
if (user.setupGuideSeen) {
return
}
root.showSetup(user,user.addresses[0])
}
onRowsAboutToBeRemoved: {
for (var i = first; i <= last; i++ ) {
var user = root.backend.users.get(i)
if (setupGuide.user === user) {
setupGuide.user = null
contentLayout._showSetup = false
return
}
}
}
}
Connections {
target: root.backend
onShowMainWindow: {
root.showAndRise()
}
onLoginFinished: {
console.debug("Login finished", index)
}
}
StackLayout {
id: contentLayout
anchors.fill: parent
property bool _showSetup: false
currentIndex: {
// show welcome when there are no users or only one non-logged-in user is present
if (backend.users.count === 0) {
return 1
}
var u = backend.users.get(0)
if (!u) {
console.trace()
console.log("empty user")
return 1
}
if (backend.users.count === 1 && u.loggedIn === false) {
return 1
}
if (contentLayout._showSetup) {
return 2
}
return 0
}
ContentWrapper { // 0
id: contentWrapper
colorScheme: root.colorScheme
backend: root.backend
notifications: root.notifications
Layout.fillHeight: true
Layout.fillWidth: true
onShowSetupGuide: {
root.showSetup(user,address)
}
}
WelcomeGuide { // 1
colorScheme: root.colorScheme
backend: root.backend
Layout.fillHeight: true
Layout.fillWidth: true
}
SetupGuide { // 2
id: setupGuide
colorScheme: root.colorScheme
backend: root.backend
Layout.fillHeight: true
Layout.fillWidth: true
onDismissed: {
root.showSetup(null,"")
}
onFinished: {
// TODO: Do not close window. Trigger backend to check that
// there is a successfully connected client. Then backend
// should send another signal to close the setup guide.
root.showSetup(null,"")
}
}
}
NotificationPopups {
colorScheme: root.colorScheme
notifications: root.notifications
mainWindow: root
backend: root.backend
}
SplashScreen {
id: splashScreen
colorScheme: root.colorScheme
backend: root.backend
}
function showLocalCacheSettings() { contentWrapper.showLocalCacheSettings() }
function showSettings() { contentWrapper.showSettings() }
function showHelp() { contentWrapper.showHelp() }
function showSignIn(username) {
if (contentLayout.currentIndex == 1) return
contentWrapper.showSignIn(username)
}
function showSetup(user, address) {
setupGuide.user = user
setupGuide.address = address
setupGuide.reset()
if (setupGuide.user) {
contentLayout._showSetup = true
} else {
contentLayout._showSetup = false
}
}
function showAndRise() {
root.show()
root.raise()
if (!root.active) {
root.requestActivate()
}
}
}

View File

@ -0,0 +1,119 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
import Notifications 1.0
Dialog {
id: root
property var notification
shouldShow: notification && notification.active && !notification.dismissed
modal: true
default property alias data: additionalChildrenContainer.children
ColumnLayout {
spacing: 0
Image {
Layout.alignment: Qt.AlignHCenter
sourceSize.width: 64
sourceSize.height: 64
Layout.preferredHeight: 64
Layout.preferredWidth: 64
Layout.bottomMargin: 16
visible: source != ""
source: {
if (!root.notification) {
return ""
}
switch (root.notification.type) {
case Notification.NotificationType.Info:
return "./icons/ic-info.svg"
case Notification.NotificationType.Success:
return "./icons/ic-success.svg"
case Notification.NotificationType.Warning:
case Notification.NotificationType.Danger:
return "./icons/ic-alert.svg"
}
}
}
Label {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 8
colorScheme: root.colorScheme
text: root.notification.title
type: Label.LabelType.Title
}
Label {
Layout.fillWidth: true
Layout.preferredWidth: 240
Layout.bottomMargin: 16
colorScheme: root.colorScheme
text: root.notification.description
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
type: Label.LabelType.Body
onLinkActivated: Qt.openUrlExternally(link)
}
Item {
id: additionalChildrenContainer
Layout.fillWidth: true
Layout.bottomMargin: 16
visible: children.length > 0
implicitHeight: additionalChildrenContainer.childrenRect.height
implicitWidth: additionalChildrenContainer.childrenRect.width
}
ColumnLayout {
spacing: 8
Repeater {
model: root.notification.action
delegate: Button {
Layout.fillWidth: true
colorScheme: root.colorScheme
action: modelData
secondary: index > 0
loading: modelData.loading
}
}
}
}
}

View File

@ -0,0 +1,132 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.12
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
import Notifications 1.0
Item {
id: root
property var backend
property ColorScheme colorScheme
property var notifications
property var mainWindow
property int notificationWhitelist: NotificationFilter.FilterConsts.All
property int notificationBlacklist: NotificationFilter.FilterConsts.None
NotificationFilter {
id: bannerNotificationFilter
source: root.notifications.all
blacklist: Notifications.Group.Dialogs
}
Banner {
colorScheme: root.colorScheme
notification: bannerNotificationFilter.topmost
mainWindow: root.mainWindow
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.updateManualReady
Switch {
id:autoUpdate
colorScheme: root.colorScheme
text: qsTr("Update automatically in the future")
checked: root.backend.isAutomaticUpdateOn
onClicked: root.backend.toggleAutomaticUpdate(autoUpdate.checked)
}
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.updateForce
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.updateForceError
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.enableBeta
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.cacheUnavailable
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.cacheCantMove
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.diskFull
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.enableSplitMode
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.disableLocalCache
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.enableLocalCache
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.resetBridge
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.changeAllMailVisibility
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.deleteAccount
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.noKeychain
}
NotificationDialog {
colorScheme: root.colorScheme
notification: root.notifications.rebuildKeychain
}
}

View File

@ -0,0 +1,54 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick.Controls 2.12
QtObject {
id: root
default property var children
enum NotificationType {
Info = 0,
Success = 1,
Warning = 2,
Danger = 3
}
// title is used in dialogs only
property string title
// description is used in banners and in dialogs as description
property string description
// brief is used in status view only
property string brief
property string icon
property list<Action> action
property int type
property int group
property bool dismissed: false
property bool active: false
readonly property var occurred: active ? new Date() : undefined
property var data
onActiveChanged: {
dismissed = false
}
}

View File

@ -0,0 +1,114 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQml.Models 2.12
// contains notifications that satisfy black- and whitelist and are sorted in time-occurred order
ListModel {
id: root
enum FilterConsts {
None = 0,
All = 255
}
property int whitelist: NotificationFilter.FilterConsts.All
property int blacklist: NotificationFilter.FilterConsts.None
property Notification topmost
property var source
property bool componentCompleted: false
Component.onCompleted: {
root.componentCompleted = true
root.rebuildList()
}
// overriding get method to ignore any role and return directly object itself
function get(row) {
if (row < 0 || row >= count) {
return undefined
}
return data(index(row, 0), Qt.DisplayRole)
}
function rebuildList() {
// avoid evaluation of the list before Component.onCompleted
if (!root.componentCompleted) {
return
}
for (var i = 0; i < root.count; i++) {
root.get(i).onActiveChanged.disconnect( root.updateList )
}
root.clear()
if (!root.source) {
return
}
for (i = 0; i < root.source.length; i++) {
var obj = root.source[i]
if (obj.group & root.blacklist) {
continue
}
if (!(obj.group & root.whitelist)) {
continue
}
root.append({obj})
obj.onActiveChanged.connect( root.updateList )
}
}
function updateList() {
var topmost = null
for (var i = 0; i < root.count; i++) {
var obj = root.get(i)
if (!obj.active) {
continue
}
if (topmost && (topmost.type > obj.type)) {
continue
}
if (topmost && (topmost.type === obj.type) && (topmost.occurred > obj.occurred)) {
continue
}
topmost = obj
}
root.topmost = topmost
}
onWhitelistChanged: {
root.rebuildList()
}
onBlacklistChanged: {
root.rebuildList()
}
onSourceChanged: {
root.rebuildList()
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,6 @@
module Notifications
depends QtQml 2.12
Notifications 1.0 Notifications.qml
NotificationFilter 1.0 NotificationFilter.qml
Notification 1.0 Notification.qml

View File

@ -0,0 +1,164 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQuick.Controls.impl 2.13
import Proton 4.0
SettingsView {
id: root
fillHeight: false
property bool _valuesChanged: (
imapField.text*1 !== root.backend.portIMAP ||
smtpField.text*1 !== root.backend.portSMTP
)
Label {
colorScheme: root.colorScheme
text: qsTr("Default ports")
type: Label.Heading
Layout.fillWidth: true
}
Label {
colorScheme: root.colorScheme
text: qsTr("Changes require reconfiguration of your email client. Bridge will automatically restart.")
type: Label.Body
color: root.colorScheme.text_weak
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
RowLayout {
spacing: 16
TextField {
id: imapField
colorScheme: root.colorScheme
label: qsTr("IMAP port")
Layout.preferredWidth: 160
validator: root.validate
}
TextField {
id: smtpField
colorScheme: root.colorScheme
label: qsTr("SMTP port")
Layout.preferredWidth: 160
validator: root.validate
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: root.colorScheme.border_weak
}
RowLayout {
spacing: 12
Button {
id: submitButton
colorScheme: root.colorScheme
text: qsTr("Save and restart")
enabled: root._valuesChanged
onClicked: {
// removing error here because we may have set it manually (port occupied)
imapField.error = false
smtpField.error = false
// checking errors seperatly because we want to display "same port" error only once
imapField.validate()
if (imapField.error) {
return
}
smtpField.validate()
if (smtpField.error) {
return
}
submitButton.loading = true
// check both ports before returning an error
var err = false
err |= !isPortFree(imapField)
err |= !isPortFree(smtpField)
if (err) {
submitButton.loading = false
return
}
root.backend.changePorts(imapField.text, smtpField.text)
}
}
Button {
colorScheme: root.colorScheme
text: qsTr("Cancel")
onClicked: root.back()
secondary: true
}
Connections {
target: root.backend
onChangePortFinished: submitButton.loading = false
}
}
onBack: {
root.setDefaultValues()
}
function validate(port) {
var num = port*1
if (! (num > 1 && num < 65536) ) {
return qsTr("Invalid port number")
}
if (imapField.text == smtpField.text) {
return qsTr("Port numbers must be different")
}
return
}
function isPortFree(field) {
var num = field.text*1
if (num === root.backend.portIMAP) return true
if (num === root.backend.portSMTP) return true
if (!root.backend.isPortFree(num)) {
field.error = true
field.errorString = qsTr("Port occupied")
return false
}
return true
}
function setDefaultValues(){
imapField.text = backend.portIMAP
smtpField.text = backend.portSMTP
}
Component.onCompleted: root.setDefaultValues()
}

View File

@ -0,0 +1,23 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Templates 2.12 as T
T.Action {
property bool loading
}

View File

@ -0,0 +1,134 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
T.ApplicationWindow {
id: root
property ColorScheme colorScheme
// popup priority based on types
enum PopupType {
Banner = 0,
Dialog = 1
}
// contains currently visible popup
property var popupVisible: null
// list of all popups within ApplicationWindow
property ListModel popups: ListModel {
// overriding get method to ignore any role and return directly object itself
function get(row) {
if (row < 0 || row >= count) {
return undefined
}
return data(index(row, 0), Qt.DisplayRole)
}
onRowsInserted: {
for (var i = first; i <= last; i++) {
var obj = popups.get(i)
obj.onShouldShowChanged.connect( root.processPopups )
}
processPopups()
}
onRowsAboutToBeRemoved: {
for (var i = first; i <= last; i++ ) {
var obj = popups.get(i)
obj.onShouldShowChanged.disconnect( root.processPopups )
// if currently visible popup was removed
if (root.popupVisible === obj) {
root.popupVisible.visible = false
root.popupVisible = null
}
}
processPopups()
}
}
function processPopups() {
if ((root.popupVisible) && (!root.popupVisible.shouldShow)) {
root.popupVisible.visible = false
}
var topmost = null
for (var i = 0; i < popups.count; i++) {
var obj = popups.get(i)
if (obj.shouldShow === false) {
continue
}
if (topmost && (topmost.popupType > obj.popupType)) {
continue
}
if (topmost && (topmost.popupType === obj.popupType) && (topmost.occurred > obj.occurred)) {
continue
}
topmost = obj
}
if (root.popupVisible !== topmost) {
if (root.popupVisible) {
root.popupVisible.visible = false
}
root.popupVisible = topmost
}
if (!root.popupVisible) {
return
}
root.popupVisible.visible = true
}
Connections {
target: root.popupVisible
onVisibleChanged: {
if (root.popupVisible.visible) {
return
}
root.popupVisible = null
root.processPopups()
}
}
color: root.colorScheme.background_norm
overlay.modal: Rectangle {
color: root.colorScheme.backdrop_norm
}
overlay.modeless: Rectangle {
color: "transparent"
}
}

View File

@ -0,0 +1,245 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
import QtQuick.Layouts 1.12
import "." as Proton
T.Button {
property ColorScheme colorScheme
property alias secondary: control.flat
readonly property bool primary: !secondary
readonly property bool isIcon: control.text === ""
property bool loading: false
property bool borderless: false
property int labelType: Proton.Label.LabelType.Body
property alias textVerticalAlignment: label.verticalAlignment
property alias textHorizontalAlignment: label.horizontalAlignment
// TODO: store previous enabled state and restore it?
// For now assuming that only enabled buttons could have loading state
onLoadingChanged: {
if (loading) {
enabled = false
} else {
enabled = true
}
}
id: control
implicitWidth: Math.max(
implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding
)
implicitHeight: Math.max(
implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding
)
padding: 8
horizontalPadding: 16
spacing: 10
font: label.font
icon.width: 16
icon.height: 16
icon.color: {
if (primary && !isIcon) {
return "#FFFFFF"
} else {
return control.colorScheme.text_norm
}
}
contentItem: RowLayout {
id: _contentItem
spacing: control.spacing
Proton.Label {
colorScheme: root.colorScheme
id: label
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
elide: Text.ElideRight
horizontalAlignment: Qt.AlignHCenter
visible: !control.isIcon
text: control.text
color: {
if (primary && !isIcon) {
return "#FFFFFF"
} else {
return control.colorScheme.text_norm
}
}
opacity: control.enabled || control.loading ? 1.0 : 0.5
type: labelType
}
ColorImage {
id: iconImage
Layout.alignment: Qt.AlignCenter
width: {
// special case for loading since we want icon to be square for rotation animation
if (control.loading) {
return Math.min(control.icon.width, availableWidth, control.icon.height, availableHeight)
}
return Math.min(control.icon.width, availableWidth)
}
height: {
if (control.loading) {
return width
}
Math.min(control.icon.height, availableHeight)
}
sourceSize.width: control.icon.width
sourceSize.height: control.icon.height
color: control.icon.color
source: control.loading ? "../icons/Loader_16.svg" : control.icon.source
visible: control.loading || control.icon.source
RotationAnimation {
target: iconImage
loops: Animation.Infinite
duration: 1000
from: 0
to: 360
direction: RotationAnimation.Clockwise
running: control.loading
}
}
}
background: Rectangle {
implicitWidth: 36
implicitHeight: 36
radius: Style.button_radius
visible: true
color: {
if (!isIcon) {
if (primary) {
// Primary colors
if (control.down) {
return control.colorScheme.interaction_norm_active
}
if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
return control.colorScheme.interaction_norm_hover
}
if (control.loading) {
return control.colorScheme.interaction_norm_hover
}
return control.colorScheme.interaction_norm
} else {
// Secondary colors
if (control.down) {
return control.colorScheme.interaction_default_active
}
if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
return control.colorScheme.interaction_default_hover
}
if (control.loading) {
return control.colorScheme.interaction_default_hover
}
return control.colorScheme.interaction_default
}
} else {
if (primary) {
// Primary icon colors
if (control.down) {
return control.colorScheme.interaction_default_active
}
if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
return control.colorScheme.interaction_default_hover
}
if (control.loading) {
return control.colorScheme.interaction_default_hover
}
return control.colorScheme.interaction_default
} else {
// Secondary icon colors
if (control.down) {
return control.colorScheme.interaction_default_active
}
if (control.enabled && (control.highlighted || control.hovered || control.checked || control.activeFocus)) {
return control.colorScheme.interaction_default_hover
}
if (control.loading) {
return control.colorScheme.interaction_default_hover
}
return control.colorScheme.interaction_default
}
}
}
border.color: {
return control.colorScheme.border_norm
}
border.width: secondary && !borderless ? 1 : 0
opacity: control.enabled || control.loading ? 1.0 : 0.5
}
Component.onCompleted: {
if (!control.colorScheme) {
console.trace()
var next = root
for (var i = 0; i<1000; i++) {
console.log(i, next, "colorscheme", next.colorScheme)
next = next.parent
if (!next) break
}
console.error("ColorScheme not defined")
}
}
}

View File

@ -0,0 +1,134 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
T.CheckBox {
property ColorScheme colorScheme
property bool error: false
id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
padding: 0
spacing: 8
indicator: Rectangle {
implicitWidth: 20
implicitHeight: 20
radius: Style.checkbox_radius
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
y: control.topPadding + (control.availableHeight - height) / 2
color: {
if (!checked) {
return control.colorScheme.background_norm
}
if (!control.enabled) {
return control.colorScheme.field_disabled
}
if (control.error) {
return control.colorScheme.signal_danger
}
if (control.hovered || control.activeFocus) {
return control.colorScheme.interaction_norm_hover
}
return control.colorScheme.interaction_norm
}
border.width: control.checked ? 0 : 1
border.color: {
if (!control.enabled) {
return control.colorScheme.field_disabled
}
if (control.error) {
return control.colorScheme.signal_danger
}
if (control.hovered || control.activeFocus) {
return control.colorScheme.interaction_norm_hover
}
return control.colorScheme.field_norm
}
ColorImage {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: parent.width - 4
height: parent.height - 4
sourceSize.width: parent.width - 4
sourceSize.height: parent.height - 4
color: "#FFFFFF"
source: "../icons/ic-check.svg"
visible: control.checkState === Qt.Checked
}
// TODO: do we need PartiallyChecked state?
// Rectangle {
// x: (parent.width - width) / 2
// y: (parent.height - height) / 2
// width: 16
// height: 3
// color: control.palette.text
// visible: control.checkState === Qt.PartiallyChecked
//}
}
contentItem: CheckLabel {
leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
text: control.text
color: {
if (!enabled) {
return control.colorScheme.text_disabled
}
if (error) {
return control.colorScheme.signal_danger
}
return control.colorScheme.text_norm
}
font.family: Style.font_family
font.weight: Style.fontWeight_400
font.pixelSize: Style.body_font_size
lineHeight: Style.body_line_height
lineHeightMode: Text.FixedHeight
font.letterSpacing: Style.body_letter_spacing
}
}

View File

@ -0,0 +1,92 @@
// Copyright (c) 2022 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 QtQml 2.13
QtObject {
// should be a pointer to ColorScheme object
property var prominent
// Primary
property color primay_norm
// Interaction-norm
property color interaction_norm
property color interaction_norm_hover
property color interaction_norm_active
// Text
property color text_norm
property color text_weak
property color text_hint
property color text_disabled
property color text_invert
// Field
property color field_norm
property color field_hover
property color field_disabled
// Border
property color border_norm
property color border_weak
// Background
property color background_norm
property color background_weak
property color background_strong
property color background_avatar
// Interaction-weak
property color interaction_weak
property color interaction_weak_hover
property color interaction_weak_active
// Interaction-default
property color interaction_default
property color interaction_default_hover
property color interaction_default_active
// Scrollbar
property color scrollbar_norm
property color scrollbar_hover
// Signal
property color signal_danger
property color signal_danger_hover
property color signal_danger_active
property color signal_warning
property color signal_warning_hover
property color signal_warning_active
property color signal_success
property color signal_success_hover
property color signal_success_active
property color signal_info
property color signal_info_hover
property color signal_info_active
// Shadows
property color shadow_norm
property color shadow_lifted
// Backdrop
property color backdrop_norm
// Images
property string welcome_img
property string logo_img
}

View File

@ -0,0 +1,195 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
T.ComboBox {
id: root
property ColorScheme colorScheme
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
leftPadding: 12 + (!root.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
rightPadding: 12 + (root.mirrored || !indicator || !indicator.visible ? 0 : indicator.width + spacing)
topPadding: 5
bottomPadding: 5
spacing: 8
font.family: Style.font_family
font.weight: Style.fontWeight_400
font.pixelSize: Style.body_font_size
font.letterSpacing: Style.body_letter_spacing
contentItem: T.TextField {
padding: 5
text: root.editable ? root.editText : root.displayText
font: root.font
enabled: root.editable
autoScroll: root.editable
readOnly: root.down
inputMethodHints: root.inputMethodHints
validator: root.validator
verticalAlignment: TextInput.AlignVCenter
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
selectionColor: root.colorScheme.interaction_norm
selectedTextColor: root.colorScheme.text_invert
placeholderTextColor: root.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
background: Rectangle {
radius: Style.context_item_radius
visible: root.enabled && root.editable && !root.flat
border.color: {
if (root.activeFocus) {
return root.colorScheme.interaction_norm
}
if (root.hovered || root.activeFocus) {
return root.colorScheme.field_hover
}
return root.colorScheme.field_norm
}
border.width: 1
color: root.colorScheme.background_norm
}
}
background: Rectangle {
implicitWidth: 140
implicitHeight: 36
radius: Style.context_item_radius
color: {
if (root.down) {
return root.colorScheme.interaction_default_active
}
if (root.enabled && root.hovered || root.activeFocus) {
return root.colorScheme.interaction_default_hover
}
if (!root.enabled) {
return root.colorScheme.interaction_default
}
return root.colorScheme.background_norm
}
border.color: root.colorScheme.border_norm
border.width: 1
}
indicator: ColorImage {
x: root.mirrored ? 12 : root.width - width - 12
y: root.topPadding + (root.availableHeight - height) / 2
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
source: popup.visible ? "../icons/ic-chevron-up.svg" : "../icons/ic-chevron-down.svg"
sourceSize.width: 16
sourceSize.height: 16
}
delegate: ItemDelegate {
width: parent.width
text: root.textRole ? (Array.isArray(root.model) ? modelData[root.textRole] : model[root.textRole]) : modelData
palette.text: {
if (!root.enabled) {
return root.colorScheme.text_disabled
}
if (selected) {
return root.colorScheme.text_invert
}
return root.colorScheme.text_norm
}
font: root.font
hoverEnabled: root.hoverEnabled
property bool selected: root.currentIndex === index
highlighted: root.highlightedIndex === index
palette.highlightedText: selected ? root.colorScheme.text_invert : root.colorScheme.text_norm
background: PaddedRectangle {
radius: Style.context_item_radius
color: {
if (parent.down) {
return root.colorScheme.interaction_default_active
}
if (parent.selected) {
return root.colorScheme.interaction_norm
}
if (parent.hovered || parent.highlighted) {
return root.colorScheme.interaction_default_hover
}
return root.colorScheme.interaction_default
}
}
}
popup: T.Popup {
y: root.height
width: root.width
height: Math.min(contentItem.implicitHeight, root.Window.height - topMargin - bottomMargin)
topMargin: 8
bottomMargin: 8
contentItem: Item {
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
ListView {
anchors.fill: parent
anchors.margins: 8
implicitHeight: contentHeight
model: root.delegateModel
currentIndex: root.highlightedIndex
spacing: 4
T.ScrollIndicator.vertical: ScrollIndicator { }
}
}
background: Rectangle {
color: root.colorScheme.background_norm
radius: Style.dialog_radius
border.color: root.colorScheme.border_weak
border.width: 1
}
}
}

View File

@ -0,0 +1,80 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.12
import QtQuick.Templates 2.12 as T
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
T.Dialog {
id: root
property ColorScheme colorScheme
Component.onCompleted: {
if (!ApplicationWindow.window) {
return
}
if (ApplicationWindow.window.popups === undefined) {
return
}
var obj = this
ApplicationWindow.window.popups.append( { obj } )
}
readonly property int popupType: ApplicationWindow.PopupType.Dialog
property bool shouldShow: false
readonly property var occurred: shouldShow ? new Date() : undefined
function open() {
root.shouldShow = true
}
function close() {
root.shouldShow = false
}
anchors.centerIn: Overlay.overlay
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding,
implicitHeaderWidth,
implicitFooterWidth)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
+ (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
+ (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0))
padding: 24
background: Rectangle {
color: root.colorScheme.background_norm
radius: Style.dialog_radius
}
// TODO: Add DropShadow here
T.Overlay.modal: Rectangle {
color: root.colorScheme.backdrop_norm
}
T.Overlay.modeless: Rectangle {
color: "transparent"
}
}

View File

@ -0,0 +1,142 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
import "." as Proton
T.Label {
id: root
property ColorScheme colorScheme
enum LabelType {
// weight 700, size 28, height 36
Heading,
// weight 700, size 20, height 24
Title,
// weight 400, size 18, height 26
Lead,
// weight 400, size 14, height 20, spacing 0.2
Body,
// weight 600, size 14, height 20, spacing 0.2
Body_semibold,
// weight 700, size 14, height 20, spacing 0.2
Body_bold,
// weight 400, size 12, height 16, spacing 0.4
Caption,
// weight 600, size 12, height 16, spacing 0.4
Caption_semibold,
// weight 700, size 12, height 16, spacing 0.4
Caption_bold
}
property int type: Proton.Label.LabelType.Body
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
linkColor: root.colorScheme.interaction_norm
palette.link: linkColor
font.family: Style.font_family
lineHeightMode: Text.FixedHeight
font.weight: {
switch (root.type) {
case Proton.Label.LabelType.Heading:
return Style.fontWeight_700
case Proton.Label.LabelType.Title:
return Style.fontWeight_700
case Proton.Label.LabelType.Lead:
return Style.fontWeight_400
case Proton.Label.LabelType.Body:
return Style.fontWeight_400
case Proton.Label.LabelType.Body_semibold:
return Style.fontWeight_600
case Proton.Label.LabelType.Body_bold:
return Style.fontWeight_700
case Proton.Label.LabelType.Caption:
return Style.fontWeight_400
case Proton.Label.LabelType.Caption_semibold:
return Style.fontWeight_600
case Proton.Label.LabelType.Caption_bold:
return Style.fontWeight_700
}
}
font.pixelSize: {
switch (root.type) {
case Proton.Label.LabelType.Heading:
return Style.heading_font_size
case Proton.Label.LabelType.Title:
return Style.title_font_size
case Proton.Label.LabelType.Lead:
return Style.lead_font_size
case Proton.Label.LabelType.Body:
case Proton.Label.LabelType.Body_semibold:
case Proton.Label.LabelType.Body_bold:
return Style.body_font_size
case Proton.Label.LabelType.Caption:
case Proton.Label.LabelType.Caption_semibold:
case Proton.Label.LabelType.Caption_bold:
return Style.caption_font_size
}
}
lineHeight: {
switch (root.type) {
case Proton.Label.LabelType.Heading:
return Style.heading_line_height
case Proton.Label.LabelType.Title:
return Style.title_line_height
case Proton.Label.LabelType.Lead:
return Style.lead_line_height
case Proton.Label.LabelType.Body:
case Proton.Label.LabelType.Body_semibold:
case Proton.Label.LabelType.Body_bold:
return Style.body_line_height
case Proton.Label.LabelType.Caption:
case Proton.Label.LabelType.Caption_semibold:
case Proton.Label.LabelType.Caption_bold:
return Style.caption_line_height
}
}
font.letterSpacing: {
switch (root.type) {
case Proton.Label.LabelType.Heading:
case Proton.Label.LabelType.Title:
case Proton.Label.LabelType.Lead:
return 0
case Proton.Label.LabelType.Body:
case Proton.Label.LabelType.Body_semibold:
case Proton.Label.LabelType.Body_bold:
return Style.body_letter_spacing
case Proton.Label.LabelType.Caption:
case Proton.Label.LabelType.Caption_semibold:
case Proton.Label.LabelType.Caption_bold:
return Style.caption_letter_spacing
}
}
verticalAlignment: Text.AlignBottom
function link(url, text) {
return `<a href="${url}">${text}</a>`
}
}

View File

@ -0,0 +1,72 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
import QtQuick.Window 2.12
import "."
T.Menu {
id: control
property ColorScheme colorScheme
implicitWidth: Math.max(
implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding
)
implicitHeight: Math.max(
implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding
)
margins: 0
overlap: 1
delegate: MenuItem {
colorScheme: control.colorScheme
}
contentItem: Item {
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
ListView {
anchors.fill: parent
anchors.margins: 8
implicitHeight: contentHeight
model: control.contentModel
interactive: Window.window ? contentHeight > Window.window.height : false
clip: true
currentIndex: control.currentIndex
ScrollIndicator.vertical: ScrollIndicator {}
}
}
background: Rectangle {
implicitWidth: 200
implicitHeight: 40
color: colorScheme.background_norm
border.width: 1
border.color: colorScheme.border_weak
radius: Style.account_row_radius
}
}

View File

@ -0,0 +1,72 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
import "."
T.MenuItem {
id: control
property ColorScheme colorScheme
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
padding: 6
spacing: 6
icon.width: 24
icon.height: 24
icon.color: control.enabled ? control.colorScheme.text_norm : control.colorScheme.text_disabled
font.family: Style.font_family
font.weight: Style.fontWeight_400
font.pixelSize: Style.body_font_size
font.letterSpacing: Style.body_letter_spacing
contentItem: IconLabel {
id: iconLabel
readonly property real arrowPadding: control.subMenu && control.arrow ? control.arrow.width + control.spacing : 0
readonly property real indicatorPadding: control.checkable && control.indicator ? control.indicator.width + control.spacing : 0
leftPadding: !control.mirrored ? indicatorPadding : arrowPadding
rightPadding: control.mirrored ? indicatorPadding : arrowPadding
spacing: control.spacing
mirrored: control.mirrored
display: control.display
alignment: Qt.AlignLeft
icon: control.icon
text: control.text
font: control.font
color: control.enabled ? control.colorScheme.text_norm : control.colorScheme.text_disabled
}
background: Rectangle {
implicitWidth: 164
implicitHeight: 36
radius: Style.button_radius
color: control.down ? control.colorScheme.interaction_default_active : control.highlighted ? control.colorScheme.interaction_default_hover : control.colorScheme.interaction_default
}
}

View File

@ -0,0 +1,67 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
T.Popup {
id: root
property ColorScheme colorScheme
Component.onCompleted: {
if (!ApplicationWindow.window) {
return
}
if (ApplicationWindow.window.popups === undefined) {
return
}
var obj = this
ApplicationWindow.window.popups.append( { obj } )
}
property int popupType: ApplicationWindow.PopupType.Banner
property bool shouldShow: false
readonly property var occurred: shouldShow ? new Date() : undefined
function open() {
root.shouldShow = true
}
function close() {
root.shouldShow = false
}
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
contentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding)
// TODO: Add DropShadow here
T.Overlay.modal: Rectangle {
color: root.colorScheme.backdrop_norm
}
T.Overlay.modeless: Rectangle {
color: "transparent"
}
}

View File

@ -0,0 +1,115 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
T.RadioButton {
property ColorScheme colorScheme
property bool error: false
id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
padding: 0
spacing: 8
indicator: Rectangle {
implicitWidth: 20
implicitHeight: 20
radius: width / 2
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
y: control.topPadding + (control.availableHeight - height) / 2
color: control.colorScheme.background_norm
border.width: 1
border.color: {
if (!control.enabled) {
return control.colorScheme.field_disabled
}
if (control.error) {
return control.colorScheme.signal_danger
}
if (control.hovered || control.activeFocus) {
return control.colorScheme.interaction_norm_hover
}
return control.colorScheme.field_norm
}
Rectangle {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: 8
height: 8
radius: width / 2
color: {
if (!control.enabled) {
return control.colorScheme.field_disabled
}
if (control.error) {
return control.colorScheme.signal_danger
}
if (control.hovered || control.activeFocus) {
return control.colorScheme.interaction_norm_hover
}
return control.colorScheme.interaction_norm
}
visible: control.checked
}
}
contentItem: CheckLabel {
leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
text: control.text
color: {
if (!enabled) {
return control.colorScheme.text_disabled
}
if (error) {
return control.colorScheme.signal_danger
}
return control.colorScheme.text_norm
}
font.family: Style.font_family
font.weight: Style.fontWeight_400
font.pixelSize: Style.body_font_size
lineHeight: Style.body_line_height
lineHeightMode: Text.FixedHeight
font.letterSpacing: Style.body_letter_spacing
}
}

View File

@ -0,0 +1,394 @@
// Copyright (c) 2022 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/>.
pragma Singleton
import QtQml 2.13
import QtQuick 2.12
import "./"
// https://wiki.qt.io/Qml_Styling
// http://imaginativethinking.ca/make-qml-component-singleton/
QtObject {
id: root
// TODO: Once we will use Qt >=5.15 this should be refactored with inline components as follows:
// https://doc.qt.io/qt-5/qtqml-documents-definetypes.html#inline-components
// component ColorScheme: QtObject {
// property color primay_norm
// ...
// }
property ColorScheme lightStyle: ColorScheme {
id: _lightStyle
prominent: lightProminentStyle
// Primary
primay_norm: "#6D4AFF"
// Interaction-norm
interaction_norm: "#6D4AFF"
interaction_norm_hover: "#4D34B3"
interaction_norm_active: "#372580"
// Text
text_norm: "#0C0C14"
text_weak: "#706D6B"
text_hint: "#8F8D8A"
text_disabled: "#C2BFBC"
text_invert: "#FFFFFF"
// Field
field_norm: "#ADABA8"
field_hover: "#8F8D8A"
field_disabled: "#D1CFCD"
// Border
border_norm: "#D1CFCD"
border_weak: "#EAE7E4"
// Background
background_norm: "#FFFFFF"
background_weak: "#F5F4F2"
background_strong: "#EAE7E4"
background_avatar: "#C2BFBC"
// Interaction-weak
interaction_weak: "#D1CFCD"
interaction_weak_hover: "#C2BFBC"
interaction_weak_active: "#A8A6A3"
// Interaction-default
interaction_default: Qt.rgba(0,0,0,0)
interaction_default_hover: Qt.rgba(194./255., 191./255., 188./255., 0.2)
interaction_default_active: Qt.rgba(194./255., 191./255., 188./255., 0.4)
// Scrollbar
scrollbar_norm: "#D1CFCD"
scrollbar_hover: "#C2BFBC"
// Signal
signal_danger: "#DC3251"
signal_danger_hover: "#F74F6D"
signal_danger_active: "#B72346"
signal_warning: "#FF9900"
signal_warning_hover: "#FFB800"
signal_warning_active: "#FF851A"
signal_success: "#1EA885"
signal_success_hover: "#23C299"
signal_success_active: "#198F71"
signal_info: "#239ECE"
signal_info_hover: "#27B1E8"
signal_info_active: "#1F83B5"
// Shadows
shadow_norm: Qt.rgba(0,0,0, 0.1) // #000000 10% x:0 y:1 blur:4
shadow_lifted: Qt.rgba(0,0,0, 0.16) // #000000 16% x:0 y:8 blur:24
// Backdrop
backdrop_norm: Qt.rgba(12./255., 12./255., 20./255., 0.32)
// Images
welcome_img: "icons/img-welcome.png"
logo_img: "icons/product_logos.svg"
}
property ColorScheme lightProminentStyle: ColorScheme {
id: _lightProminentStyle
prominent: this
// Primary
primay_norm: "#8A6EFF"
// Interaction-norm
interaction_norm: "#6D4AFF"
interaction_norm_hover: "#7C5CFF"
interaction_norm_active: "#8A6EFF"
// Text
text_norm: "#FFFFFF"
text_weak: "#9282D4"
text_hint: "#544399"
text_disabled: "#4A398F"
text_invert: "#1B1340"
// Field
field_norm: "#9282D4"
field_hover: "#7C5CFF"
field_disabled: "#38277A"
// Border
border_norm: "#413085"
border_weak: "#3C2B80"
// Background
background_norm: "#1B1340"
background_weak: "#271C57"
background_strong: "#38277A"
background_avatar: "#6D4AFF"
// Interaction-weak
interaction_weak: "#4A398F"
interaction_weak_hover: "#6D4AFF"
interaction_weak_active: "#8A6EFF"
// Interaction-default
interaction_default: Qt.rgba(0,0,0,0)
interaction_default_hover: Qt.rgba(68./255., 78./255., 114./255., 0.2)
interaction_default_active: Qt.rgba(68./255., 78./255., 114./255., 0.3)
// Scrollbar
scrollbar_norm: "#413085"
scrollbar_hover: "#4A398F"
// Signal
signal_danger: "#F5385A"
signal_danger_hover: "#FF5473"
signal_danger_active: "#DC3251"
signal_warning: "#FF9900"
signal_warning_hover: "#FFB800"
signal_warning_active: "#FF8419"
signal_success: "#1EA885"
signal_success_hover: "#23C299"
signal_success_active: "#198F71"
signal_info: "#2C89DB"
signal_info_hover: "#3491E3"
signal_info_active: "#1F83B5"
// Shadows
shadow_norm: Qt.rgba(0,0,0, 0.32) // #000000 32% x:0 y:1 blur:4
shadow_lifted: Qt.rgba(0,0,0, 0.40) // #000000 40% x:0 y:8 blur:24
// Backdrop
backdrop_norm: Qt.rgba(0,0,0, 0.32)
// Images
welcome_img: "icons/img-welcome-dark.png"
logo_img: "icons/product_logos_dark.svg"
}
property ColorScheme darkStyle: ColorScheme {
id: _darkStyle
prominent: darkProminentStyle
// Primary
primay_norm: "#8A6EFF"
// Interaction-norm
interaction_norm: "#6D4AFF"
interaction_norm_hover: "#7C5CFF"
interaction_norm_active: "#8A6EFF"
// Text
text_norm: "#FFFFFF"
text_weak: "#A7A4B5"
text_hint: "#6D697D"
text_disabled: "#5B576B"
text_invert: "#1C1B24"
// Field
field_norm: "#5B576B"
field_hover: "#6D697D"
field_disabled: "#3F3B4C"
// Border
border_norm: "#4A4658"
border_weak: "#343140"
// Background
background_norm: "#1C1B24"
background_weak: "#292733"
background_strong: "#3F3B4C"
background_avatar: "#6D4AFF"
// Interaction-weak
interaction_weak: "#4A4658"
interaction_weak_hover: "#5B576B"
interaction_weak_active: "#6D697D"
// Interaction-default
interaction_default: "#00000000"
interaction_default_hover: Qt.rgba(91./255.,87./255.,107./255.,0.2)
interaction_default_active: Qt.rgba(91./255.,87./255.,107./255.,0.4)
// Scrollbar
scrollbar_norm: "#4A4658"
scrollbar_hover: "#5B576B"
// Signal
signal_danger: "#F5385A"
signal_danger_hover: "#FF5473"
signal_danger_active: "#DC3251"
signal_warning: "#FF9900"
signal_warning_hover: "#FFB800"
signal_warning_active: "#FF8419"
signal_success: "#1EA885"
signal_success_hover: "#23C299"
signal_success_active: "#198F71"
signal_info: "#239ECE"
signal_info_hover: "#27B1E8"
signal_info_active: "#1F83B5"
// Shadows
shadow_norm: Qt.rgba(0,0,0,0.4) // #000000 40% x+0 y+1 blur:4
shadow_lifted: Qt.rgba(0,0,0,0.48) // #000000 48% x+0 y+8 blur:24
// Backdrop
backdrop_norm: Qt.rgba(0,0,0,0.32)
// Images
welcome_img: "icons/img-welcome-dark.png"
logo_img: "icons/product_logos_dark.svg"
}
property ColorScheme darkProminentStyle: ColorScheme {
id: _darkProminentStyle
prominent: this
// Primary
primay_norm: "#8A6EFF"
// Interaction-norm
interaction_norm: "#6D4AFF"
interaction_norm_hover: "#7C5CFF"
interaction_norm_active: "#8A6EFF"
// Text
text_norm: "#FFFFFF"
text_weak: "#A7A4B5"
text_hint: "#6D697D"
text_disabled: "#5B576B"
text_invert: "#1C1B24"
// Field
field_norm: "#5B576B"
field_hover: "#6D697D"
field_disabled: "#3F3B4C"
// Border
border_norm: "#4A4658"
border_weak: "#343140"
// Background
background_norm: "#16141c"
background_weak: "#292733"
background_strong: "#3F3B4C"
background_avatar: "#6D4AFF"
// Interaction-weak
interaction_weak: "#4A4658"
interaction_weak_hover: "#5B576B"
interaction_weak_active: "#6D697D"
// Interaction-default
interaction_default: "#00000000"
interaction_default_hover: Qt.rgba(91./255.,87./255.,107./255.,0.2)
interaction_default_active: Qt.rgba(91./255.,87./255.,107./255.,0.4)
// Scrollbar
scrollbar_norm: "#4A4658"
scrollbar_hover: "#5B576B"
// Signal
signal_danger: "#F5385A"
signal_danger_hover: "#FF5473"
signal_danger_active: "#DC3251"
signal_warning: "#FF9900"
signal_warning_hover: "#FFB800"
signal_warning_active: "#FF8419"
signal_success: "#1EA885"
signal_success_hover: "#23C299"
signal_success_active: "#198F71"
signal_info: "#239ECE"
signal_info_hover: "#27B1E8"
signal_info_active: "#1F83B5"
// Shadows
shadow_norm: Qt.rgba(0,0,0,0.4) // #000000 40% x+0 y+1 blur:4
shadow_lifted: Qt.rgba(0,0,0,0.48) // #000000 48% x+0 y+8 blur:24
// Backdrop
backdrop_norm: Qt.rgba(0,0,0,0.32)
// Images
welcome_img: "icons/img-welcome-dark.png"
logo_img: "icons/product_logos_dark.svg"
}
property ColorScheme currentStyle: lightStyle
property string font_family: {
switch (Qt.platform.os) {
case "windows":
return "Segoe UI"
case "osx":
return "SF Pro Display"
case "linux":
return "Ubuntu"
default:
console.error("Unknown platform")
}
}
property real px : 1.00 // px
property real input_radius : 8 * root.px // px
property real button_radius : 8 * root.px // px
property real checkbox_radius : 4 * root.px // px
property real avatar_radius : 8 * root.px // px
property real big_avatar_radius : 12 * root.px // px
property real account_hover_radius : 12 * root.px // px
property real account_row_radius : 12 * root.px // px
property real context_item_radius : 8 * root.px // px
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 tooltip_radius : 8 * root.px // px
property int heading_font_size: 28
property int heading_line_height: 36
property int title_font_size: 20
property int title_line_height: 24
property int lead_font_size: 18
property int lead_line_height: 26
property int body_font_size: 14
property int body_line_height: 20
property real body_letter_spacing: 0.2 * root.px
property int caption_font_size: 12
property int caption_line_height: 16
property real caption_letter_spacing: 0.4 * root.px
property int fontWeight_100: Font.Thin
property int fontWeight_200: Font.Light
property int fontWeight_300: Font.ExtraLight
property int fontWeight_400: Font.Normal
property int fontWeight_500: Font.Medium
property int fontWeight_600: Font.DemiBold
property int fontWeight_700: Font.Bold
property int fontWeight_800: Font.ExtraBold
property int fontWeight_900: Font.Black
}

View File

@ -0,0 +1,150 @@
// Copyright (c) 2022 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 2.12
import QtQuick.Templates 2.12 as T
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
T.Switch {
property ColorScheme colorScheme
property bool loading: false
// TODO: store previous enabled state and restore it?
// For now assuming that only enabled buttons could have loading state
onLoadingChanged: {
if (loading) {
enabled = false
} else {
enabled = true
}
}
id: control
implicitWidth: Math.max(implicitBackgroundWidth + leftInset + rightInset,
implicitContentWidth + leftPadding + rightPadding)
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
implicitContentHeight + topPadding + bottomPadding,
implicitIndicatorHeight + topPadding + bottomPadding)
padding: 0
spacing: 7
indicator: Rectangle {
implicitWidth: 40
implicitHeight: 24
x: text ? (control.mirrored ? control.width - width - control.rightPadding : control.leftPadding) : control.leftPadding + (control.availableWidth - width) / 2
y: control.topPadding + (control.availableHeight - height) / 2
radius: height / 2.
color: control.enabled || control.loading ? control.colorScheme.background_norm : control.colorScheme.background_strong
border.width: control.enabled && !loading ? 1 : 0
border.color: control.hovered ? control.colorScheme.field_hover : control.colorScheme.field_norm
Rectangle {
x: Math.max(0, Math.min(parent.width - width, control.visualPosition * parent.width - (width / 2)))
y: (parent.height - height) / 2
width: 24
height: 24
radius: parent.radius
visible: !loading
color: {
if (!control.enabled) {
return control.colorScheme.field_disabled
}
if (control.checked) {
if (control.hovered || control.activeFocus) {
return control.colorScheme.interaction_norm_hover
}
return control.colorScheme.interaction_norm
}
if (control.hovered || control.activeFocus) {
return control.colorScheme.field_hover
}
return control.colorScheme.field_norm
}
ColorImage {
x: (parent.width - width) / 2
y: (parent.height - height) / 2
width: 16
height: 16
sourceSize.width: 16
sourceSize.height: 16
color: "#FFFFFF"
source: "../icons/ic-check.svg"
visible: control.checked
}
Behavior on x {
enabled: !control.down
SmoothedAnimation { velocity: 200 }
}
}
ColorImage {
id: loadingImage
x: parent.width - width
y: (parent.height - height) / 2
width: 18
height: 18
sourceSize.width: 18
sourceSize.height: 18
color: control.colorScheme.interaction_norm_hover
source: "../icons/Loader_16.svg"
visible: control.loading
RotationAnimation {
target: loadingImage
loops: Animation.Infinite
duration: 1000
from: 0
to: 360
direction: RotationAnimation.Clockwise
running: control.loading
}
}
}
contentItem: CheckLabel {
id: label
leftPadding: control.indicator && !control.mirrored ? control.indicator.width + control.spacing : 0
rightPadding: control.indicator && control.mirrored ? control.indicator.width + control.spacing : 0
text: control.text
color: control.enabled || control.loading ? control.colorScheme.text_norm : control.colorScheme.text_disabled
font.family: Style.font_family
font.weight: Style.fontWeight_400
font.pixelSize: Style.body_font_size
lineHeight: Style.body_line_height
lineHeightMode: Text.FixedHeight
font.letterSpacing: Style.body_letter_spacing
}
}

View File

@ -0,0 +1,370 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
import QtQuick.Layouts 1.12
import "." as Proton
FocusScope {
id: root
property ColorScheme colorScheme
property alias background: control.background
property alias bottomInset: control.bottomInset
//property alias flickable: control.flickable
property alias focusReason: control.focusReason
property alias hoverEnabled: control.hoverEnabled
property alias hovered: control.hovered
property alias implicitBackgroundHeight: control.implicitBackgroundHeight
property alias implicitBackgroundWidth: control.implicitBackgroundWidth
property alias leftInset: control.leftInset
property alias palette: control.palette
property alias placeholderText: control.placeholderText
property alias placeholderTextColor: control.placeholderTextColor
property alias rightInset: control.rightInset
property alias topInset: control.topInset
property alias activeFocusOnPress: control.activeFocusOnPress
property alias baseUrl: control.baseUrl
property alias bottomPadding: control.bottomPadding
property alias canPaste: control.canPaste
property alias canRedo: control.canRedo
property alias canUndo: control.canUndo
property alias color: control.color
property alias contentHeight: control.contentHeight
property alias contentWidth: control.contentWidth
property alias cursorDelegate: control.cursorDelegate
property alias cursorPosition: control.cursorPosition
property alias cursorRectangle: control.cursorRectangle
property alias cursorVisible: control.cursorVisible
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
property alias font: control.font
property alias horizontalAlignment: control.horizontalAlignment
property alias hoveredLink: control.hoveredLink
property alias inputMethodComposing: control.inputMethodComposing
property alias inputMethodHints: control.inputMethodHints
property alias leftPadding: control.leftPadding
property alias length: control.length
property alias lineCount: control.lineCount
property alias mouseSelectionMode: control.mouseSelectionMode
property alias overwriteMode: control.overwriteMode
property alias padding: control.padding
property alias persistentSelection: control.persistentSelection
property alias preeditText: control.preeditText
property alias readOnly: control.readOnly
property alias renderType: control.renderType
property alias rightPadding: control.rightPadding
property alias selectByKeyboard: control.selectByKeyboard
property alias selectByMouse: control.selectByMouse
property alias selectedText: control.selectedText
property alias selectedTextColor: control.selectedTextColor
property alias selectionColor: control.selectionColor
property alias selectionEnd: control.selectionEnd
property alias selectionStart: control.selectionStart
property alias tabStopDistance: control.tabStopDistance
property alias text: control.text
property alias textDocument: control.textDocument
property alias textFormat: control.textFormat
property alias textMargin: control.textMargin
property alias topPadding: control.topPadding
// We are using our own type of validators. It should be a function
// returning an error string in case of error and undefined if no error
property var validator
property alias verticalAlignment: control.verticalAlignment
property alias wrapMode: control.wrapMode
implicitWidth: children[0].implicitWidth
implicitHeight: children[0].implicitHeight
property alias label: label.text
property alias hint: hint.text
property string assistiveText
property string errorString
property bool error: false
signal editingFinished()
function append(text) { return control.append(text) }
function clear() { return control.clear() }
function copy() { return control.copy() }
function cut() { return control.cut() }
function deselect() { return control.deselect() }
function getFormattedText(start, end) { return control.getFormattedText(start, end) }
function getText(start, end) { return control.getText(start, end) }
function insert(position, text) { return control.insert(position, text) }
function isRightToLeft(start, end) { return control.isRightToLeft(start, end) }
function linkAt(x, y) { return control.linkAt(x, y) }
function moveCursorSelection(position, mode) { return control.moveCursorSelection(position, mode) }
function paste() { return control.paste() }
function positionAt(x, y) { return control.positionAt(x, y) }
function positionToRectangle(position) { return control.positionToRectangle(position) }
function redo() { return control.redo() }
function remove(start, end) { return control.remove(start, end) }
function select(start, end) { return control.select(start, end) }
function selectAll() { return control.selectAll() }
function selectWord() { return control.selectWord() }
function undo() { return control.undo() }
// Calculates the height of the component to make exactly lineNum visible in edit area
function heightForLinesVisible(lineNum) {
var totalHeight = 0
totalHeight += headerLayout.height
totalHeight += footerLayout.height
totalHeight += control.topPadding + control.bottomPadding
totalHeight += lineNum * fontMetrics.height
return totalHeight
}
FontMetrics {
id: fontMetrics
font: control.font
}
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
id: headerLayout
Layout.fillWidth: true
spacing: 0
Proton.Label {
colorScheme: root.colorScheme
id: label
Layout.fillWidth: true
color: root.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
type: Proton.Label.LabelType.Body_semibold
}
Proton.Label {
colorScheme: root.colorScheme
id: hint
Layout.fillWidth: true
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
horizontalAlignment: Text.AlignRight
type: Proton.Label.LabelType.Caption
}
}
ScrollView {
id: controlView
Layout.fillHeight: true
Layout.fillWidth: true
clip: true
T.TextArea {
id: control
implicitWidth: Math.max(
contentWidth + leftPadding + rightPadding,
implicitBackgroundWidth + leftInset + rightInset,
placeholder.implicitWidth + leftPadding + rightPadding
)
implicitHeight: Math.max(
contentHeight + topPadding + bottomPadding,
implicitBackgroundHeight + topInset + bottomInset,
placeholder.implicitHeight + topPadding + bottomPadding
)
topPadding: 8
bottomPadding: 8
leftPadding: 12
rightPadding: 12
font.family: Style.font_family
font.weight: Style.fontWeight_400
font.pixelSize: Style.body_font_size
font.letterSpacing: Style.body_letter_spacing
color: control.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
selectionColor: control.palette.highlight
selectedTextColor: control.palette.highlightedText
onEditingFinished: root.editingFinished()
wrapMode: TextInput.Wrap
// enforcing default focus here within component
focus: root.focus
KeyNavigation.priority: root.KeyNavigation.priority
KeyNavigation.backtab: root.KeyNavigation.backtab
KeyNavigation.tab: root.KeyNavigation.tab
KeyNavigation.up: root.KeyNavigation.up
KeyNavigation.down: root.KeyNavigation.down
KeyNavigation.left: root.KeyNavigation.left
KeyNavigation.right: root.KeyNavigation.right
selectByMouse: true
cursorDelegate: Rectangle {
id: cursor
width: 1
color: root.colorScheme.interaction_norm
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
Connections {
target: control
onCursorPositionChanged: {
// keep a moving cursor visible
cursor.opacity = 1
timer.restart()
}
}
Timer {
id: timer
running: control.activeFocus && !control.readOnly
repeat: true
interval: Qt.styleHints.cursorFlashTime / 2
onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
// force the cursor visible when gaining focus
onRunningChanged: cursor.opacity = 1
}
}
PlaceholderText {
id: placeholder
x: control.leftPadding
y: control.topPadding
width: control.width - (control.leftPadding + control.rightPadding)
height: control.height - (control.topPadding + control.bottomPadding)
text: control.placeholderText
font: control.font
color: control.placeholderTextColor
verticalAlignment: control.verticalAlignment
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
elide: Text.ElideRight
renderType: control.renderType
}
background: Rectangle {
anchors.fill: parent
radius: Style.input_radius
visible: true
color: root.colorScheme.background_norm
border.color: {
if (!control.enabled) {
return root.colorScheme.field_disabled
}
if (control.activeFocus) {
return root.colorScheme.interaction_norm
}
if (root.error) {
return root.colorScheme.signal_danger
}
if (control.hovered) {
return root.colorScheme.field_hover
}
return root.colorScheme.field_norm
}
border.width: 1
}
}
}
RowLayout {
id: footerLayout
Layout.fillWidth: true
spacing: 0
ColorImage {
id: errorIcon
Layout.rightMargin: 4
visible: root.error && (assistiveText.text.length > 0)
source: "../icons/ic-exclamation-circle-filled.svg"
color: root.colorScheme.signal_danger
height: assistiveText.height
sourceSize.height: assistiveText.height
}
Proton.Label {
colorScheme: root.colorScheme
id: assistiveText
Layout.fillWidth: true
text: root.error ? root.errorString : root.assistiveText
color: {
if (!root.enabled) {
return root.colorScheme.text_disabled
}
if (root.error) {
return root.colorScheme.signal_danger
}
return root.colorScheme.text_weak
}
type: root.error ? Proton.Label.LabelType.Caption_semibold : Proton.Label.LabelType.Caption
}
}
}
property bool validateOnEditingFinished: true
onEditingFinished: {
if (!validateOnEditingFinished) {
return
}
validate()
}
function validate() {
if (validator === undefined) {
return
}
var error = validator(text)
if (error) {
root.error = true
root.errorString = error
} else {
root.error = false
root.errorString = ""
}
}
onTextChanged: {
root.error = false
root.errorString = ""
}
}

View File

@ -0,0 +1,381 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import QtQuick.Templates 2.12 as T
import QtQuick.Layouts 1.12
import "." as Proton
FocusScope {
id: root
property ColorScheme colorScheme
property alias background: control.background
property alias bottomInset: control.bottomInset
property alias focusReason: control.focusReason
property alias hoverEnabled: control.hoverEnabled
property alias hovered: control.hovered
property alias implicitBackgroundHeight: control.implicitBackgroundHeight
property alias implicitBackgroundWidth: control.implicitBackgroundWidth
property alias leftInset: control.leftInset
property alias palette: control.palette
property alias placeholderText: control.placeholderText
property alias placeholderTextColor: control.placeholderTextColor
property alias rightInset: control.rightInset
property alias topInset: control.topInset
property alias acceptableInput: control.acceptableInput
property alias activeFocusOnPress: control.activeFocusOnPress
property alias autoScroll: control.autoScroll
property alias bottomPadding: control.bottomPadding
property alias canPaste: control.canPaste
property alias canRedo: control.canRedo
property alias canUndo: control.canUndo
property alias color: control.color
//property alias contentHeight: control.contentHeight
//property alias contentWidth: control.contentWidth
property alias cursorDelegate: control.cursorDelegate
property alias cursorPosition: control.cursorPosition
property alias cursorRectangle: control.cursorRectangle
property alias cursorVisible: control.cursorVisible
property alias displayText: control.displayText
property alias effectiveHorizontalAlignment: control.effectiveHorizontalAlignment
property alias font: control.font
property alias horizontalAlignment: control.horizontalAlignment
property alias inputMask: control.inputMask
property alias inputMethodComposing: control.inputMethodComposing
property alias inputMethodHints: control.inputMethodHints
property alias leftPadding: control.leftPadding
property alias length: control.length
property alias maximumLength: control.maximumLength
property alias mouseSelectionMode: control.mouseSelectionMode
property alias overwriteMode: control.overwriteMode
property alias padding: control.padding
property alias passwordCharacter: control.passwordCharacter
property alias passwordMaskDelay: control.passwordMaskDelay
property alias persistentSelection: control.persistentSelection
property alias preeditText: control.preeditText
property alias readOnly: control.readOnly
property alias renderType: control.renderType
property alias rightPadding: control.rightPadding
property alias selectByMouse: control.selectByMouse
property alias selectedText: control.selectedText
property alias selectedTextColor: control.selectedTextColor
property alias selectionColor: control.selectionColor
property alias selectionEnd: control.selectionEnd
property alias selectionStart: control.selectionStart
property alias text: control.text
// We are using our own type of validators. It should be a function
// returning an error string in case of error and undefined if no error
property var validator
property alias verticalAlignment: control.verticalAlignment
property alias wrapMode: control.wrapMode
implicitWidth: children[0].implicitWidth
implicitHeight: children[0].implicitHeight
property alias label: label.text
property alias hint: hint.text
property string assistiveText
property string errorString
property int echoMode: TextInput.Normal
property bool error: false
signal accepted()
signal editingFinished()
signal textEdited()
function clear() { control.clear() }
function copy() { control.copy() }
function cut() { control.cut() }
function deselect() { control.deselect() }
function ensureVisible(position) { control.ensureVisible(position) }
function getText(start, end) { control.getText(start, end) }
function insert(position, text) { control.insert(position, text) }
function isRightToLeft(start, end) { control.isRightToLeft(start, end) }
function moveCursorSelection(position, mode) { control.moveCursorSelection(position, mode) }
function paste() { control.paste() }
function positionAt(x, y, position) { control.positionAt(x, y, position) }
function positionToRectangle(pos) { control.positionToRectangle(pos) }
function redo() { control.redo() }
function remove(start, end) { control.remove(start, end) }
function select(start, end) { control.select(start, end) }
function selectAll() { control.selectAll() }
function selectWord() { control.selectWord() }
function undo() { control.undo() }
function forceActiveFocus() { control.forceActiveFocus() }
ColumnLayout {
anchors.fill: parent
spacing: 0
RowLayout {
Layout.fillWidth: true
spacing: 0
Proton.Label {
colorScheme: root.colorScheme
id: label
Layout.fillHeight: true
Layout.fillWidth: true
type: Proton.Label.LabelType.Body_semibold
}
Proton.Label {
colorScheme: root.colorScheme
id: hint
Layout.fillHeight: true
Layout.fillWidth: true
color: root.enabled ? root.colorScheme.text_weak : root.colorScheme.text_disabled
horizontalAlignment: Text.AlignRight
type: Proton.Label.LabelType.Caption
}
}
// Background is moved away from within control to cover eye button as well.
// In case it will remain as control background property - control's width
// will be adjusted to background's width making text field and eye button overlap
Rectangle {
id: background
Layout.fillHeight: true
Layout.fillWidth: true
radius: Style.input_radius
visible: true
color: root.colorScheme.background_norm
border.color: {
if (!control.enabled) {
return root.colorScheme.field_disabled
}
if (control.activeFocus) {
return root.colorScheme.interaction_norm
}
if (root.error) {
return root.colorScheme.signal_danger
}
if (control.hovered) {
return root.colorScheme.field_hover
}
return root.colorScheme.field_norm
}
border.width: 1
implicitWidth: children[0].implicitWidth
implicitHeight: children[0].implicitHeight
RowLayout {
anchors.fill: parent
spacing: 0
T.TextField {
id: control
Layout.fillHeight: true
Layout.fillWidth: true
implicitWidth: implicitBackgroundWidth + leftInset + rightInset
|| Math.max(contentWidth, placeholder.implicitWidth) + leftPadding + rightPadding
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
contentHeight + topPadding + bottomPadding,
placeholder.implicitHeight + topPadding + bottomPadding)
topPadding: 8
bottomPadding: 8
leftPadding: 12
rightPadding: 12
font.family: Style.font_family
font.weight: Style.fontWeight_400
font.pixelSize: Style.body_font_size
font.letterSpacing: Style.body_letter_spacing
color: control.enabled ? root.colorScheme.text_norm : root.colorScheme.text_disabled
placeholderTextColor: control.enabled ? root.colorScheme.text_hint : root.colorScheme.text_disabled
selectionColor: control.palette.highlight
selectedTextColor: control.palette.highlightedText
verticalAlignment: TextInput.AlignVCenter
echoMode: eyeButton.checked ? TextInput.Normal : root.echoMode
// enforcing default focus here within component
focus: true
KeyNavigation.priority: root.KeyNavigation.priority
KeyNavigation.backtab: root.KeyNavigation.backtab
KeyNavigation.tab: root.KeyNavigation.tab
KeyNavigation.up: root.KeyNavigation.up
KeyNavigation.down: root.KeyNavigation.down
KeyNavigation.left: root.KeyNavigation.left
KeyNavigation.right: root.KeyNavigation.right
selectByMouse: true
cursorDelegate: Rectangle {
id: cursor
width: 1
color: root.colorScheme.interaction_norm
visible: control.activeFocus && !control.readOnly && control.selectionStart === control.selectionEnd
Connections {
target: control
onCursorPositionChanged: {
// keep a moving cursor visible
cursor.opacity = 1
timer.restart()
}
}
Timer {
id: timer
running: control.activeFocus && !control.readOnly
repeat: true
interval: Qt.styleHints.cursorFlashTime / 2
onTriggered: cursor.opacity = !cursor.opacity ? 1 : 0
// force the cursor visible when gaining focus
onRunningChanged: cursor.opacity = 1
}
}
PlaceholderText {
id: placeholder
x: control.leftPadding
y: control.topPadding
width: control.width - (control.leftPadding + control.rightPadding)
height: control.height - (control.topPadding + control.bottomPadding)
text: control.placeholderText
font: control.font
color: control.placeholderTextColor
verticalAlignment: control.verticalAlignment
visible: !control.length && !control.preeditText && (!control.activeFocus || control.horizontalAlignment !== Qt.AlignHCenter)
elide: Text.ElideRight
renderType: control.renderType
}
background: Item {
implicitWidth: 80
implicitHeight: 36
visible: false
}
onAccepted: {
root.accepted()
}
onEditingFinished: {
root.editingFinished()
}
onTextEdited: {
root.textEdited()
}
}
Proton.Button {
colorScheme: root.colorScheme
id: eyeButton
Layout.fillHeight: true
visible: root.echoMode === TextInput.Password
icon.color: control.color
checkable: true
icon.source: checked ? "../icons/ic-eye-slash.svg" : "../icons/ic-eye.svg"
}
}
}
RowLayout {
Layout.fillWidth: true
spacing: 0
ColorImage {
id: errorIcon
Layout.rightMargin: 4
visible: root.error && (assistiveText.text.length > 0)
source: "../icons/ic-exclamation-circle-filled.svg"
color: root.colorScheme.signal_danger
height: assistiveText.height
sourceSize.height: assistiveText.height
}
Proton.Label {
colorScheme: root.colorScheme
id: assistiveText
Layout.fillHeight: true
Layout.fillWidth: true
text: root.error ? root.errorString : root.assistiveText
color: {
if (!root.enabled) {
return root.colorScheme.text_disabled
}
if (root.error) {
return root.colorScheme.signal_danger
}
return root.colorScheme.text_weak
}
type: root.error ? Proton.Label.LabelType.Caption_semibold : Proton.Label.LabelType.Caption
}
}
}
property bool validateOnEditingFinished: true
onEditingFinished: {
if (!validateOnEditingFinished) {
return
}
validate()
}
function validate() {
if (validator === undefined) {
return
}
var error = validator(text)
if (error) {
root.error = true
root.errorString = error
} else {
root.error = false
root.errorString = ""
}
}
onTextChanged: {
root.error = false
root.errorString = ""
}
}

View File

@ -0,0 +1,113 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQuick.Controls.impl 2.13
Item {
id: root
property var colorScheme
property bool checked
property bool hovered
property bool loading
signal clicked
property bool _disabled: !enabled
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
Rectangle {
id: indicator
implicitWidth: 40
implicitHeight: 24
radius: width/2
color: {
if (root.loading) return "transparent"
if (root._disabled) return root.colorScheme.background_strong
return root.colorScheme.background_norm
}
border {
width: 1
color: (root._disabled || root.loading) ? "transparent" : colorScheme.field_norm
}
Rectangle {
anchors.verticalCenter: indicator.verticalCenter
anchors.left: indicator.left
anchors.leftMargin: root.checked ? 16 : 0
width: 24
height: 24
radius: width/2
color: {
if (root.loading) return "transparent"
if (root._disabled) return root.colorScheme.field_disabled
if (root.checked) {
if (root.hovered) return root.colorScheme.interaction_norm_hover
return root.colorScheme.interaction_norm
} else {
if (root.hovered) return root.colorScheme.field_hover
return root.colorScheme.field_norm
}
}
ColorImage {
anchors.centerIn: parent
source: "../icons/ic-check.svg"
color: root.colorScheme.background_norm
height: root.colorScheme.body_font_size
sourceSize.height: root.colorScheme.body_font_size
visible: root.checked
}
}
ColorImage {
id: loader
anchors.centerIn: parent
source: "../icons/Loader_16.svg"
color: root.colorScheme.text_norm
height: root.colorScheme.body_font_size
sourceSize.height: root.colorScheme.body_font_size
visible: root.loading
RotationAnimation {
target: loader
loops: Animation.Infinite
duration: 1000
from: 0
to: 360
direction: RotationAnimation.Clockwise
running: root.loading
}
}
MouseArea {
anchors.fill: indicator
hoverEnabled: true
onEntered: {root.hovered = true }
onExited: {root.hovered = false }
onClicked: { if (root.enabled) root.clicked();}
onPressed: {root.hovered = true }
onReleased: { root.hovered = containsMouse }
}
}
}

View File

@ -0,0 +1,37 @@
# Copyright (c) 2021 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/>.
module QQtQuick.Controls.Proton
depends QtQuick.Controls 2.12
singleton ProtonStyle 4.0 Style.qml
ColorScheme 4.0 ColorScheme.qml
ApplicationWindow 4.0 ApplicationWindow.qml
Button 4.0 Button.qml
CheckBox 4.0 CheckBox.qml
ComboBox 4.0 ComboBox.qml
Dialog 4.0 Dialog.qml
Label 4.0 Label.qml
Menu 4.0 Menu.qml
MenuItem 4.0 MenuItem.qml
Popup 4.0 Popup.qml
RadioButton 4.0 RadioButton.qml
Switch 4.0 Switch.qml
TextArea 4.0 TextArea.qml
TextField 4.0 TextField.qml
Toggle 4.0 Toggle.qml

View File

@ -0,0 +1,120 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQuick.Controls.impl 2.13
import Proton 4.0
SettingsView {
id: root
fillHeight: false
Label {
colorScheme: root.colorScheme
text: qsTr("SMTP connection mode")
type: Label.Heading
Layout.fillWidth: true
}
Label {
colorScheme: root.colorScheme
text: qsTr("Changes require reconfiguration of email client. Bridge will automatically restart.")
type: Label.Body
color: root.colorScheme.text_weak
Layout.fillWidth: true
Layout.maximumWidth: this.parent.Layout.maximumWidth
wrapMode: Text.WordWrap
}
ColumnLayout {
spacing: 16
ButtonGroup{ id: protocolSelection }
Label {
colorScheme: root.colorScheme
text: qsTr("SMTP connection security")
}
RadioButton {
id: sslButton
colorScheme: root.colorScheme
ButtonGroup.group: protocolSelection
text: qsTr("SSL")
}
RadioButton {
id: starttlsButton
colorScheme: root.colorScheme
ButtonGroup.group: protocolSelection
text: qsTr("STARTTLS")
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: root.colorScheme.border_weak
}
RowLayout {
spacing: 12
Button {
id: submitButton
colorScheme: root.colorScheme
text: qsTr("Save and restart")
onClicked: {
submitButton.loading = true
root.submit()
}
enabled: sslButton.checked !== root.backend.useSSLforSMTP
}
Button {
colorScheme: root.colorScheme
text: qsTr("Cancel")
onClicked: root.back()
secondary: true
}
Connections {
target: root.backend
onToggleUseSSLFinished: submitButton.loading = false
}
}
function submit(){
submitButton.loading = true
root.backend.toggleUseSSLforSMTP(sslButton.checked)
}
function setDefaultValues(){
sslButton.checked = root.backend.useSSLforSMTP
starttlsButton.checked = !root.backend.useSSLforSMTP
}
onVisibleChanged: {
root.setDefaultValues()
}
}

View File

@ -0,0 +1,118 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
Item {
id: root
property var colorScheme
property string text: "Text"
property string actionText: "Action"
property string actionIcon: ""
property string description: "Lorem ipsum dolor sit amet"
property alias descriptionWrap: descriptionLabel.wrapMode
property var type: SettingsItem.ActionType.Toggle
property bool checked: true
property bool loading: false
property bool showSeparator: true
property var _bottomMargin: 20
property var _lineWidth: 1
property var _toggleTopMargin: 6
signal clicked
enum ActionType {
Toggle = 1, Button = 2, PrimaryButton = 3
}
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
RowLayout {
anchors.fill: parent
spacing: 16
ColumnLayout {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.bottomMargin: root._bottomMargin
spacing: 4
Label {
id: mainLabel
colorScheme: root.colorScheme
text: root.text
type: Label.Body_semibold
}
Label {
id: descriptionLabel
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: parent.width
wrapMode: Text.WordWrap
colorScheme: root.colorScheme
text: root.description
color: root.colorScheme.text_weak
}
}
Toggle {
Layout.alignment: Qt.AlignTop
Layout.topMargin: root._toggleTopMargin
id: toggle
colorScheme: root.colorScheme
visible: root.type === SettingsItem.ActionType.Toggle
checked: root.checked
loading: root.loading
onClicked: { if (!root.loading) root.clicked() }
}
Button {
Layout.alignment: Qt.AlignTop
id: button
colorScheme: root.colorScheme
visible: root.type === SettingsItem.Button || root.type === SettingsItem.PrimaryButton
text: root.actionText + (root.actionIcon != "" ? " " : "")
loading: root.loading
icon.source: root.actionIcon
onClicked: { if (!root.loading) root.clicked() }
secondary: root.type !== SettingsItem.PrimaryButton
}
}
Rectangle {
anchors.left: root.left
anchors.right: root.right
anchors.bottom: root.bottom
color: colorScheme.border_weak
height: root._lineWidth
visible: root.showSeparator
}
}

View File

@ -0,0 +1,99 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import QtQuick.Controls.impl 2.13
import Proton 4.0
Item {
id: root
property var colorScheme
property var backend
default property alias items: content.children
signal back()
property int _leftMargin: 64
property int _rightMargin: 64
property int _topMargin: 32
property int _bottomMargin: 32
property int _spacing: 20
// fillHeight indicates whether the SettingsView should fill all available explicit height set
property bool fillHeight: false
ScrollView {
id: scrollView
clip: true
anchors.fill: parent
Item {
// can't use parent here because parent is not ScrollView (Flickable inside contentItem inside ScrollView)
width: scrollView.availableWidth
height: scrollView.availableHeight
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
// do not set implicitWidth because implicit width of ColumnLayout will be equal to maximum implicit width of
// internal items. And if one of internal items would be a Text or Label - implicit width of those is always
// equal to non-wrapped text (i.e. one line only). That will lead to enabling horizontal scroll when not needed
implicitWidth: width
ColumnLayout {
anchors.fill: parent
spacing: 0
ColumnLayout {
id: content
spacing: root._spacing
Layout.fillWidth: true
Layout.topMargin: root._topMargin
Layout.bottomMargin: root._bottomMargin
Layout.leftMargin: root._leftMargin
Layout.rightMargin: root._rightMargin
}
Item {
id: filler
Layout.fillHeight: true
visible: !root.fillHeight
}
}
}
}
Button {
id: backButton
anchors {
top: parent.top
left: parent.left
topMargin: root._topMargin
leftMargin: (root._leftMargin-backButton.width) / 2
}
colorScheme: root.colorScheme
onClicked: root.back()
icon.source: "icons/ic-arrow-left.svg"
secondary: true
horizontalPadding: 8
}
}

View File

@ -0,0 +1,317 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import Proton 4.0
Item {
id:root
property ColorScheme colorScheme
property var backend
property var user
property string address
signal dismissed()
signal finished()
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
ListModel {
id: clients
property string name : "Apple Mail"
property string iconSource : "./icons/ic-apple-mail.svg"
property bool haveAutoSetup: true
property string link: "https://protonmail.com/bridge/applemail"
Component.onCompleted : {
if (root.backend.goos == "darwin") {
append({
"name" : "Apple Mail",
"iconSource" : "./icons/ic-apple-mail.svg",
"haveAutoSetup" : true,
"link" : "https://protonmail.com/bridge/applemail"
})
append({
"name" : "Microsoft Outlook",
"iconSource" : "./icons/ic-microsoft-outlook.svg",
"haveAutoSetup" : false,
"link" : "https://protonmail.com/bridge/outlook2019-mac"
})
}
if (root.backend.goos == "windows") {
append({
"name" : "Microsoft Outlook",
"iconSource" : "./icons/ic-microsoft-outlook.svg",
"haveAutoSetup" : false,
"link" : "https://protonmail.com/bridge/outlook2019"
})
}
append({
"name" : "Mozilla Thunderbird",
"iconSource" : "./icons/ic-mozilla-thunderbird.svg",
"haveAutoSetup" : false,
"link" : "https://protonmail.com/bridge/thunderbird"
})
append({
"name" : "Other",
"iconSource" : "./icons/ic-other-mail-clients.svg",
"haveAutoSetup" : false,
"link" : "https://protonmail.com/bridge/clients"
})
}
}
Rectangle {
anchors.fill: root
color: root.colorScheme.background_norm
}
StackLayout {
id: guidePages
anchors.fill: parent
anchors.leftMargin: 80
anchors.rightMargin: 80
anchors.topMargin: 30
anchors.bottomMargin: 70
ColumnLayout { // 0: Client selection
id: clientView
Layout.fillHeight: true
property int columnWidth: 268
spacing: 8
Label {
colorScheme: root.colorScheme
text: qsTr("Setting up email client")
type: Label.LabelType.Heading
}
Label {
colorScheme: root.colorScheme
text: address
color: root.colorScheme.text_weak
type: Label.LabelType.Lead
}
RowLayout {
Layout.topMargin: 32-clientView.spacing
spacing: 24
ColumnLayout {
id: clientColumn
Layout.alignment: Qt.AlignTop
Label {
id: labelA
colorScheme: root.colorScheme
text: qsTr("Choose an email client")
type: Label.LabelType.Body_semibold
}
ListView {
id: clientList
Layout.fillHeight: true
width: clientView.columnWidth
model: clients
highlight: Rectangle {
color: root.colorScheme.interaction_default_active
radius: ProtonStyle.context_item_radius
}
delegate: Item {
implicitWidth: clientRow.width
implicitHeight: clientRow.height
ColumnLayout {
id: clientRow
width: clientList.width
RowLayout {
Layout.topMargin: 12
Layout.bottomMargin: 12
Layout.leftMargin: 16
Layout.rightMargin: 16
ColorImage {
source: model.iconSource
height: 36
sourceSize.height: 36
}
Label {
colorScheme: root.colorScheme
Layout.leftMargin: 12
text: model.name
type: Label.LabelType.Body
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: root.colorScheme.border_weak
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
clientList.currentIndex = index
if (!model.haveAutoSetup) {
root.setupAction(1,index)
}
}
}
}
}
}
ColumnLayout {
id: actionColumn
visible: clientList.currentIndex >= 0 && clients.get(clientList.currentIndex).haveAutoSetup
Layout.alignment: Qt.AlignTop
Label {
colorScheme: root.colorScheme
text: qsTr("Choose configuration mode")
type: Label.LabelType.Body_semibold
}
ListView {
id: actionList
Layout.fillHeight: true
width: clientView.columnWidth
model: [
qsTr("Configure automatically"),
qsTr("Configure manually"),
]
highlight: Rectangle {
color: root.colorScheme.interaction_default_active
radius: ProtonStyle.context_item_radius
}
delegate: Item {
implicitWidth: children[0].width
implicitHeight: children[0].height
ColumnLayout {
width: actionList.width
Label {
Layout.topMargin: 20
Layout.bottomMargin: 20
Layout.leftMargin: 16
Layout.rightMargin: 16
colorScheme: root.colorScheme
text: modelData
type: Label.LabelType.Body
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: root.colorScheme.border_weak
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
actionList.currentIndex = index
root.setupAction(index,clientList.currentIndex)
}
}
}
}
}
}
Item { Layout.fillHeight: true }
Button {
colorScheme: root.colorScheme
text: qsTr("Set up later")
flat: true
onClicked: {
root.setupAction(-1,-1)
if (user) {
user.setupGuideSeen = true
}
root.dismissed()
}
}
}
}
function setupAction(actionID,clientID){
if (user) {
user.setupGuideSeen = true
}
switch (actionID) {
case -1: root.dismissed(); break; // dismiss
case 0: // automatic
if (user) {
switch (clientID) {
case 0:
root.user.configureAppleMail(root.address)
break;
}
}
root.finished()
break;
case 1: // manual
var clientObj = clients.get(clientID)
if (clientObj != undefined && clientObj.link != "" ) {
Qt.openUrlExternally(clientObj.link)
} else {
console.log("unexpected client index", actionID, clientID)
}
root.finished();
break;
default:
console.log("unexpected client setup action", actionID, clientID)
}
}
function reset(){
guidePages.currentIndex = 0
clientList.currentIndex = -1
actionList.currentIndex = -1
}
}

View File

@ -0,0 +1,457 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import Proton 4.0
Item {
id: root
property ColorScheme colorScheme
function reset() {
stackLayout.currentIndex = 0
loginNormalLayout.reset()
login2FALayout.reset()
login2PasswordLayout.reset()
}
function abort() {
root.reset()
root.backend.loginAbort(usernameTextField.text)
}
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
property var backend
property alias username: usernameTextField.text
state: "Page 1"
property alias currentIndex: stackLayout.currentIndex
StackLayout {
id: stackLayout
anchors.fill: parent
function loginFailed() {
signInButton.loading = false
usernameTextField.enabled = true
usernameTextField.error = true
passwordTextField.enabled = true
passwordTextField.error = true
}
Connections {
target: root.backend
onLoginUsernamePasswordError: {
console.assert(stackLayout.currentIndex == 0, "Unexpected loginUsernamePasswordError")
console.assert(signInButton.loading == true, "Unexpected loginUsernamePasswordError")
stackLayout.loginFailed()
if (errorMsg!="") errorLabel.text = errorMsg
else errorLabel.text = qsTr("Incorrect login credentials")
}
onLoginFreeUserError: {
console.assert(stackLayout.currentIndex == 0, "Unexpected loginFreeUserError")
stackLayout.loginFailed()
}
onLoginConnectionError: {
if (stackLayout.currentIndex == 0 ) {
stackLayout.loginFailed()
}
}
onLogin2FARequested: {
console.assert(stackLayout.currentIndex == 0, "Unexpected login2FARequested")
twoFactorUsernameLabel.text = username
stackLayout.currentIndex = 1
}
onLogin2FAError: {
console.assert(stackLayout.currentIndex == 1, "Unexpected login2FAError")
twoFAButton.loading = false
twoFactorPasswordTextField.enabled = true
twoFactorPasswordTextField.error = true
twoFactorPasswordTextField.errorString = qsTr("Your code is incorrect")
}
onLogin2FAErrorAbort: {
console.assert(stackLayout.currentIndex == 1, "Unexpected login2FAErrorAbort")
root.reset()
errorLabel.text = qsTr("Incorrect login credentials. Please try again.")
}
onLogin2PasswordRequested: {
console.assert(stackLayout.currentIndex == 0 || stackLayout.currentIndex == 1, "Unexpected login2PasswordRequested")
stackLayout.currentIndex = 2
}
onLogin2PasswordError: {
console.assert(stackLayout.currentIndex == 2, "Unexpected login2PasswordError")
secondPasswordButton.loading = false
secondPasswordTextField.enabled = true
secondPasswordTextField.error = true
secondPasswordTextField.errorString = qsTr("Your mailbox password is incorrect")
}
onLogin2PasswordErrorAbort: {
console.assert(stackLayout.currentIndex == 2, "Unexpected login2PasswordErrorAbort")
root.reset()
errorLabel.text = qsTr("Incorrect login credentials. Please try again.")
}
onLoginFinished: {
stackLayout.currentIndex = 0
root.reset()
}
}
ColumnLayout {
id: loginNormalLayout
function reset() {
signInButton.loading = false
errorLabel.text = ""
usernameTextField.enabled = true
usernameTextField.error = false
usernameTextField.errorString = ""
passwordTextField.enabled = true
passwordTextField.error = false
passwordTextField.errorString = ""
passwordTextField.text = ""
}
spacing: 0
Label {
colorScheme: root.colorScheme
text: qsTr("Sign in")
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 16
type: Label.LabelType.Title
}
Label {
colorScheme: root.colorScheme
id: subTitle
text: qsTr("Enter your Proton Account details.")
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 8
color: root.colorScheme.text_weak
type: Label.LabelType.Body
}
RowLayout {
Layout.fillWidth: true
Layout.topMargin: 36
spacing: 0
visible: errorLabel.text.length > 0
ColorImage {
color: root.colorScheme.signal_danger
source: "./icons/ic-exclamation-circle-filled.svg"
height: errorLabel.height
sourceSize.height: errorLabel.height
}
Label {
colorScheme: root.colorScheme
id: errorLabel
Layout.leftMargin: 4
color: root.colorScheme.signal_danger
type: root.error ? Label.LabelType.Caption_semibold : Label.LabelType.Caption
}
}
TextField {
colorScheme: root.colorScheme
id: usernameTextField
label: qsTr("Username or email")
Layout.fillWidth: true
Layout.topMargin: 24
onTextChanged: {
// remove "invalid username / password error"
if (error || errorLabel.text.length > 0) {
errorLabel.text = ""
usernameTextField.error = false
passwordTextField.error = false
}
}
validator: function(str) {
if (str.length === 0) {
return qsTr("Enter username or email")
}
return
}
onAccepted: passwordTextField.forceActiveFocus()
}
TextField {
colorScheme: root.colorScheme
id: passwordTextField
label: qsTr("Password")
Layout.fillWidth: true
Layout.topMargin: 8
echoMode: TextInput.Password
onTextChanged: {
// remove "invalid username / password error"
if (error || errorLabel.text.length > 0) {
errorLabel.text = ""
usernameTextField.error = false
passwordTextField.error = false
}
}
validator: function(str) {
if (str.length === 0) {
return qsTr("Enter password")
}
return
}
onAccepted: signInButton.checkAndSignIn()
}
Button {
colorScheme: root.colorScheme
id: signInButton
text: qsTr("Sign in")
Layout.fillWidth: true
Layout.topMargin: 24
onClicked: checkAndSignIn()
function checkAndSignIn() {
usernameTextField.validate()
passwordTextField.validate()
if (usernameTextField.error || passwordTextField.error) {
return
}
usernameTextField.enabled = false
passwordTextField.enabled = false
enabled = false
loading = true
root.backend.login(usernameTextField.text, Qt.btoa(passwordTextField.text))
}
}
Label {
colorScheme: root.colorScheme
textFormat: Text.StyledText
text: link("https://protonmail.com/signup", qsTr("Create or upgrade your account"))
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 24
type: Label.LabelType.Body
onLinkActivated: {
Qt.openUrlExternally(link)
}
}
}
ColumnLayout {
id: login2FALayout
function reset() {
twoFAButton.loading = false
twoFactorPasswordTextField.enabled = true
twoFactorPasswordTextField.error = false
twoFactorPasswordTextField.errorString = ""
twoFactorPasswordTextField.text = ""
}
spacing: 0
Label {
colorScheme: root.colorScheme
text: qsTr("Two-factor authentication")
Layout.topMargin: 16
Layout.alignment: Qt.AlignCenter
type: Label.LabelType.Heading
}
Label {
colorScheme: root.colorScheme
id: twoFactorUsernameLabel
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 8
type: Label.LabelType.Lead
color: root.colorScheme.text_weak
}
TextField {
colorScheme: root.colorScheme
id: twoFactorPasswordTextField
label: qsTr("Two-factor code")
assistiveText: qsTr("Enter the 6-digit code")
Layout.fillWidth: true
Layout.topMargin: 32
validator: function(str) {
if (str.length === 0) {
return qsTr("Enter username or email")
}
return
}
}
Button {
colorScheme: root.colorScheme
id: twoFAButton
text: loading ? qsTr("Authenticating") : qsTr("Authenticate")
Layout.fillWidth: true
Layout.topMargin: 24
onClicked: {
twoFactorPasswordTextField.validate()
if (twoFactorPasswordTextField.error) {
return
}
twoFactorPasswordTextField.enabled = false
enabled = false
loading = true
root.backend.login2FA(usernameTextField.text, Qt.btoa(twoFactorPasswordTextField.text))
}
}
}
ColumnLayout {
id: login2PasswordLayout
function reset() {
secondPasswordButton.loading = false
secondPasswordTextField.enabled = true
secondPasswordTextField.error = false
secondPasswordTextField.errorString = ""
secondPasswordTextField.text = ""
}
spacing: 0
Label {
colorScheme: root.colorScheme
text: qsTr("Unlock your mailbox")
Layout.topMargin: 16
Layout.alignment: Qt.AlignCenter
type: Label.LabelType.Heading
}
TextField {
colorScheme: root.colorScheme
id: secondPasswordTextField
label: qsTr("Mailbox password")
Layout.fillWidth: true
Layout.topMargin: 8 + implicitHeight + 24 + subTitle.implicitHeight
echoMode: TextInput.Password
validator: function(str) {
if (str.length === 0) {
return qsTr("Enter username or email")
}
return
}
}
Button {
colorScheme: root.colorScheme
id: secondPasswordButton
text: loading ? qsTr("Unlocking") : qsTr("Unlock")
Layout.fillWidth: true
Layout.topMargin: 24
onClicked: {
secondPasswordTextField.validate()
if (secondPasswordTextField.error) {
return
}
secondPasswordTextField.enabled = false
enabled = false
loading = true
root.backend.login2Password(usernameTextField.text, Qt.btoa(secondPasswordTextField.text))
}
}
}
}
states: [
State {
name: "Page 1"
PropertyChanges {
target: stackLayout
currentIndex: 0
}
},
State {
name: "Page 2"
PropertyChanges {
target: stackLayout
currentIndex: 1
}
},
State {
name: "Page 3"
PropertyChanges {
target: stackLayout
currentIndex: 2
}
}
]
}

View File

@ -0,0 +1,108 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import Proton 4.0
Dialog {
id: root
property var backend
shouldShow: root.backend.showSplashScreen
modal: true
topPadding : 0
leftPadding : 0
rightPadding : 0
ColumnLayout {
spacing: 20
Image {
Layout.alignment: Qt.AlignHCenter
sourceSize.width: 400
sourceSize.height: 225
Layout.preferredWidth: 400
Layout.preferredHeight: 225
source: "./icons/img-splash.png"
}
Label {
colorScheme: root.colorScheme;
Layout.alignment: Qt.AlignHCenter;
Layout.leftMargin: 24
Layout.rightMargin: 24
Layout.preferredWidth: 336
type: Label.Title
horizontalAlignment: Text.AlignHCenter
text: qsTr("Updated Proton, unified protection")
}
Label {
colorScheme: root.colorScheme
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter;
Layout.preferredWidth: 336
Layout.leftMargin: 24
Layout.rightMargin: 24
wrapMode: Text.WordWrap
type: Label.Body
horizontalAlignment: Text.AlignHCenter
textFormat: Text.StyledText
text: qsTr("Introducing Protons refreshed look.<br/>") +
qsTr("Many services, one mission. Welcome to an Internet where privacy is the default. ") +
link("https://proton.me/news/updated-proton",qsTr("Learn More"))
onLinkActivated: Qt.openUrlExternally(link)
}
Button {
Layout.fillWidth: true
Layout.leftMargin: 24
Layout.rightMargin: 24
colorScheme: root.colorScheme
text: "Got it"
onClicked: root.backend.showSplashScreen = false
}
Image {
Layout.alignment: Qt.AlignHCenter
sourceSize.width: 164
sourceSize.height: 32
Layout.preferredWidth: 164
Layout.preferredHeight: 32
source: "./icons/img-proton-logos.svg"
}
}
}

View File

@ -0,0 +1,112 @@
// Copyright (c) 2022 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 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import QtQuick.Controls.impl 2.12
import Proton 4.0
import Notifications 1.0
Item {
id: root
property var backend
property var notifications
property ColorScheme colorScheme
property int notificationWhitelist: NotificationFilter.FilterConsts.All
property int notificationBlacklist: NotificationFilter.FilterConsts.None
readonly property Notification activeNotification: notificationFilter.topmost
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
NotificationFilter {
id: notificationFilter
source: root.notifications ? root.notifications.all : undefined
whitelist: root.notificationWhitelist
blacklist: root.notificationBlacklist
onTopmostChanged: {
if (!topmost) {
image.source = "./icons/ic-connected.svg"
image.color = root.colorScheme.signal_success
label.text = qsTr("Connected")
label.color = root.colorScheme.signal_success
return;
}
image.source = topmost.icon
label.text = topmost.brief
switch (topmost.type) {
case Notification.NotificationType.Danger:
image.color = root.colorScheme.signal_danger
label.color = root.colorScheme.signal_danger
break;
case Notification.NotificationType.Warning:
image.color = root.colorScheme.signal_warning
label.color = root.colorScheme.signal_warning
break;
case Notification.NotificationType.Success:
image.color = root.colorScheme.signal_success
label.color = root.colorScheme.signal_success
break;
case Notification.NotificationType.Info:
image.color = root.colorScheme.signal_info
label.color = root.colorScheme.signal_info
break;
}
}
}
RowLayout {
anchors.fill: parent
spacing: 8
ColorImage {
id: image
width: 16
height: 16
sourceSize.width: width
sourceSize.height: height
source: "./icons/ic-connected.svg"
color: root.colorScheme.signal_success
}
Label {
colorScheme: root.colorScheme
id: label
Layout.fillHeight: true
Layout.fillWidth: true
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
verticalAlignment: Text.AlignVCenter
text: qsTr("Connected")
color: root.colorScheme.signal_success
}
}
}

View File

@ -0,0 +1,328 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Window 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.13
import Proton 4.0
import Notifications 1.0
Window {
id: root
height: contentLayout.implicitHeight
width: contentLayout.implicitWidth
flags: (Qt.platform.os === "linux" ? Qt.Tool : 0) | Qt.FramelessWindowHint | Qt.NoDropShadowWindowHint | Qt.WindowStaysOnTopHint | Qt.WA_TranslucentBackground
color: "transparent"
property ColorScheme colorScheme: ProtonStyle.currentStyle
property var backend
property var notifications
signal showMainWindow()
signal showHelp()
signal showSettings()
signal showSignIn(string username)
signal quit()
ColumnLayout {
id: contentLayout
Layout.minimumHeight: 201
anchors.fill: parent
spacing: 0
ColumnLayout {
Layout.minimumWidth: 448
Layout.fillWidth: true
spacing: 0
Item {
implicitHeight: 12
Layout.fillWidth: true
clip: true
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: parent.height * 2
radius: ProtonStyle.dialog_radius
color: {
if (!statusItem.activeNotification) {
return root.colorScheme.signal_success
}
switch (statusItem.activeNotification.type) {
case Notification.NotificationType.Danger:
return root.colorScheme.signal_danger
case Notification.NotificationType.Warning:
return root.colorScheme.signal_warning
case Notification.NotificationType.Success:
return root.colorScheme.signal_success
case Notification.NotificationType.Info:
return root.colorScheme.signal_info
}
}
}
}
Rectangle {
Layout.fillWidth: true
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
color: colorScheme.background_norm
RowLayout {
anchors.fill: parent
anchors.topMargin: 8
anchors.bottomMargin: 8
anchors.leftMargin: 24
anchors.rightMargin: 24
spacing: 8
Status {
id: statusItem
Layout.fillWidth: true
Layout.topMargin: 12
Layout.bottomMargin: 12
colorScheme: root.colorScheme
backend: root.backend
notifications: root.notifications
notificationWhitelist: Notifications.Group.Connection | Notifications.Group.Update | Notifications.Group.Configuration
}
Button {
colorScheme: root.colorScheme
secondary: true
Layout.topMargin: 12
Layout.bottomMargin: 12
visible: statusItem.activeNotification && statusItem.activeNotification.action.length > 0
action: statusItem.activeNotification && statusItem.activeNotification.action.length > 0 ? statusItem.activeNotification.action[0] : null
}
}
}
Rectangle {
Layout.fillWidth: true
height: 1
color: root.colorScheme.background_norm
Rectangle {
anchors.fill: parent
anchors.leftMargin: 24
anchors.rightMargin: 24
color: root.colorScheme.border_norm
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumHeight: accountListView.count ?
accountListView.contentHeight / accountListView.count * 3 + accountListView.anchors.topMargin + accountListView.anchors.bottomMargin :
Number.POSITIVE_INFINITY
color: root.colorScheme.background_norm
clip: true
implicitHeight: children[0].contentHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].contentWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
ListView {
id: accountListView
model: root.backend.users
anchors.fill: parent
anchors.topMargin: 8
anchors.bottomMargin: 8
anchors.leftMargin: 24
anchors.rightMargin: 24
interactive: contentHeight > parent.height
snapMode: ListView.SnapToItem
boundsBehavior: Flickable.StopAtBounds
spacing: 4
delegate: Item {
id: viewItem
width: ListView.view.width
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
property var user: root.backend.users.get(index)
RowLayout {
spacing: 0
anchors.fill: parent
AccountDelegate {
Layout.fillWidth: true
Layout.topMargin: 12
Layout.bottomMargin: 12
user: viewItem.user
colorScheme: root.colorScheme
}
Button {
Layout.topMargin: 12
Layout.bottomMargin: 12
colorScheme: root.colorScheme
visible: viewItem.user ? !viewItem.user.loggedIn : false
text: qsTr("Sign in")
onClicked: {
root.showSignIn(viewItem.username)
root.close()
}
}
}
}
}
}
Item {
Layout.fillWidth: true
implicitHeight: children[1].implicitHeight + children[1].anchors.topMargin + children[1].anchors.bottomMargin
implicitWidth: children[1].implicitWidth + children[1].anchors.leftMargin + children[1].anchors.rightMargin
// background:
clip: true
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
height: parent.height * 2
radius: ProtonStyle.dialog_radius
color: root.colorScheme.background_weak
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 0
Button {
colorScheme: root.colorScheme
secondary: true
text: qsTr("Open Bridge")
borderless: true
labelType: Label.LabelType.Caption_semibold
onClicked: {
root.showMainWindow()
root.close()
}
}
Item {
Layout.fillWidth: true
}
Button {
colorScheme: root.colorScheme
secondary: true
icon.source: "./icons/ic-three-dots-vertical.svg"
borderless: true
checkable: true
onClicked: {
menu.open()
}
Menu {
id: menu
colorScheme: root.colorScheme
modal: true
y: 0 - height
MenuItem {
colorScheme: root.colorScheme
text: qsTr("Help")
onClicked: {
root.showHelp()
root.close()
}
}
MenuItem {
colorScheme: root.colorScheme
text: qsTr("Settings")
onClicked: {
root.showSettings()
root.close()
}
}
MenuItem {
colorScheme: root.colorScheme
text: qsTr("Quit Bridge")
onClicked: {
root.quit()
root.close()
}
}
onClosed: {
parent.checked = false
}
onOpened: {
parent.checked = true
}
}
}
}
}
}
onActiveChanged: {
if (!active) root.close()
}
function showAndRise() {
root.show()
root.raise()
if (!root.active) {
root.requestActivate()
}
}
}

View File

@ -0,0 +1,278 @@
// Copyright (c) 2022 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 QtQml 2.12
import QtQuick 2.13
import QtQuick.Layouts 1.12
import QtQuick.Controls 2.12
import Proton 4.0
Item {
id: root
property ColorScheme colorScheme
property var backend
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
RowLayout {
anchors.fill: parent
spacing: 0
Rectangle {
color: root.colorScheme.background_norm
Layout.fillHeight: true
Layout.fillWidth: true
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
visible: signInItem.currentIndex == 0
GridLayout {
anchors.fill: parent
columnSpacing: 0
rowSpacing: 0
columns: 3
// top margin
Item {
Layout.columnSpan: 3
Layout.fillWidth: true
// Using binding component here instead of direct binding to avoid binding loop during construction of element
Binding on Layout.preferredHeight {
value: (parent.height - welcomeContentItem.height) / 4
}
}
// left margin
Item {
Layout.minimumWidth: 48
Layout.maximumWidth: 80
Layout.fillWidth: true
Layout.preferredHeight: welcomeContentItem.height
}
ColumnLayout {
id: welcomeContentItem
Layout.fillWidth: true
spacing: 0
Image {
source: colorScheme.welcome_img
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 16
sourceSize.height: 148
sourceSize.width: 264
}
Label {
colorScheme: root.colorScheme
text: qsTr("Welcome to\nProton Mail Bridge")
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: 16
horizontalAlignment: Text.AlignHCenter
type: Label.LabelType.Heading
}
Label {
colorScheme: root.colorScheme
id: longTextLabel
text: qsTr("Add your Proton Mail account to securely access and manage your messages in your favorite email client. Bridge runs in the background and encrypts and decrypts your messages seamlessly.")
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: 16
Layout.preferredWidth: 320
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
type: Label.LabelType.Body
}
}
// Right margin
Item {
Layout.minimumWidth: 48
Layout.maximumWidth: 80
Layout.fillWidth: true
Layout.preferredHeight: welcomeContentItem.height
}
// bottom margin
Item {
Layout.columnSpan: 3
Layout.fillWidth: true
Layout.fillHeight: true
implicitHeight: children[0].implicitHeight + children[0].anchors.bottomMargin + children[0].anchors.topMargin
implicitWidth: children[0].implicitWidth
Image {
id: logoImage
source: colorScheme.logo_img
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.topMargin: 48
anchors.bottomMargin: 48
sourceSize.height: 25
sourceSize.width: 200
}
}
}
}
Rectangle {
color: (signInItem.currentIndex == 0) ? root.colorScheme.background_weak : root.colorScheme.background_norm
Layout.fillHeight: true
Layout.fillWidth: true
implicitHeight: children[0].implicitHeight
implicitWidth: children[0].implicitWidth
RowLayout {
anchors.fill: parent
spacing: 0
Item {
Layout.fillHeight: true
Layout.fillWidth: true
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
implicitHeight: children[0].implicitHeight + children[0].anchors.topMargin + children[0].anchors.bottomMargin
implicitWidth: children[0].implicitWidth + children[0].anchors.leftMargin + children[0].anchors.rightMargin
Button {
colorScheme: root.colorScheme
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 80
anchors.rightMargin: 80
anchors.topMargin: 80
anchors.bottomMargin: 80
visible: signInItem.currentIndex != 0
secondary: true
text: qsTr("Back")
onClicked: {
signInItem.abort()
}
}
}
GridLayout {
Layout.fillHeight: true
Layout.fillWidth: true
columnSpacing: 0
rowSpacing: 0
columns: 3
// top margin
Item {
Layout.columnSpan: 3
Layout.fillWidth: true
// Using binding component here instead of direct binding to avoid binding loop during construction of element
Binding on Layout.preferredHeight {
value: (parent.height - signInItem.height) / 4
}
}
// left margin
Item {
Layout.minimumWidth: 48
Layout.maximumWidth: 80
Layout.fillWidth: true
Layout.preferredHeight: signInItem.height
}
SignIn {
id: signInItem
colorScheme: root.colorScheme
Layout.preferredWidth: 320
Layout.fillWidth: true
username: root.backend.users.count === 1 && root.backend.users.get(0) && root.backend.users.get(0).loggedIn === false ? root.backend.users.get(0).username : ""
backend: root.backend
}
// Right margin
Item {
Layout.minimumWidth: 48
Layout.maximumWidth: 80
Layout.fillWidth: true
Layout.preferredHeight: signInItem.height
}
// bottom margin
Item {
Layout.columnSpan: 3
Layout.fillWidth: true
Layout.fillHeight: true
}
}
Item {
Layout.fillHeight: true
Layout.preferredWidth: signInItem.currentIndex == 0 ? 0 : parent.width / 4
}
}
}
states: [
State {
name: "Page 1"
PropertyChanges {
target: signInItem
currentIndex: 0
}
},
State {
name: "Page 2"
PropertyChanges {
target: signInItem
currentIndex: 1
}
},
State {
name: "Page 3"
PropertyChanges {
target: signInItem
currentIndex: 2
}
}
]
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) 2022 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 QmlProject 1.1
Project {
mainFile: "./MainWindow.qml"
/* Include .qml, .js, and image files from current directory and subdirectories */
QmlFiles {
directory: "./"
}
JavaScriptFiles {
directory: "./"
}
ImageFiles {
directory: "./"
}
/* List of plugin directories passed to QML runtime */
importPaths: [
"./"
]
}

View File

@ -0,0 +1,4 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.2" d="M8 2.75C8.68944 2.75 9.37213 2.8858 10.0091 3.14963C10.646 3.41347 11.2248 3.80018 11.7123 4.28769C12.1998 4.7752 12.5865 5.35395 12.8504 5.99091C13.1142 6.62787 13.25 7.31056 13.25 8C13.25 8.68944 13.1142 9.37213 12.8504 10.0091C12.5865 10.646 12.1998 11.2248 11.7123 11.7123C11.2248 12.1998 10.646 12.5865 10.0091 12.8504C9.37213 13.1142 8.68944 13.25 8 13.25C7.31056 13.25 6.62787 13.1142 5.99091 12.8504C5.35395 12.5865 4.7752 12.1998 4.28769 11.7123C3.80018 11.2248 3.41347 10.646 3.14963 10.0091C2.88579 9.37213 2.75 8.68944 2.75 8C2.75 7.31056 2.8858 6.62787 3.14963 5.99091C3.41347 5.35395 3.80018 4.77519 4.28769 4.28769C4.7752 3.80018 5.35396 3.41347 5.99092 3.14963C6.62787 2.88579 7.31056 2.75 8 2.75L8 2.75Z" stroke="#5064B6" stroke-width="1.5"/>
<path d="M11.7123 11.7123C11.1018 12.3228 10.3502 12.7733 9.52399 13.0239C8.6978 13.2746 7.82255 13.3176 6.97578 13.1491C6.129 12.9807 5.33683 12.606 4.66944 12.0583C4.00204 11.5106 3.48003 10.8067 3.14963 10.0091C2.81924 9.21144 2.69066 8.34462 2.77528 7.48541C2.85991 6.6262 3.15512 5.80112 3.63478 5.08326C4.11445 4.36539 4.76374 3.7769 5.52517 3.36991C6.28659 2.96292 7.13663 2.75 8 2.75" stroke="#5064B6" stroke-width="1.5"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,4 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.2" d="M24 1C27.0204 1 30.0112 1.59491 32.8017 2.75077C35.5922 3.90663 38.1277 5.6008 40.2635 7.73655C42.3992 9.87229 44.0934 12.4078 45.2492 15.1983C46.4051 17.9888 47 20.9796 47 24C47 27.0204 46.4051 30.0112 45.2492 32.8017C44.0934 35.5922 42.3992 38.1277 40.2635 40.2635C38.1277 42.3992 35.5922 44.0934 32.8017 45.2492C30.0112 46.4051 27.0204 47 24 47C20.9796 47 17.9888 46.4051 15.1983 45.2492C12.4078 44.0934 9.87228 42.3992 7.73654 40.2634C5.60079 38.1277 3.90662 35.5922 2.75077 32.8017C1.59491 30.0112 0.999998 27.0204 1 24C1 20.9796 1.59492 17.9888 2.75078 15.1983C3.90664 12.4078 5.6008 9.87228 7.73655 7.73653C9.8723 5.60079 12.4078 3.90662 15.1983 2.75077C17.9888 1.59491 20.9796 0.999998 24 1L24 1Z" stroke="#5064B6" stroke-width="2"/>
<path d="M40.2635 40.2635C37.5889 42.938 34.2961 44.9117 30.6765 46.0096C27.057 47.1076 23.2226 47.296 19.5129 46.5581C15.8032 45.8202 12.3328 44.1788 9.40895 41.7792C6.48514 39.3797 4.19822 36.2962 2.75077 32.8017C1.30332 29.3073 0.740014 25.5098 1.11075 21.7456C1.48149 17.9814 2.77483 14.3668 4.8762 11.2219C6.97757 8.07696 9.82212 5.49881 13.1579 3.71581C16.4936 1.93281 20.2176 1 24 1" stroke="#5064B6" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -0,0 +1,11 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M25.9729 8.06214C28.6889 3.51461 35.2762 3.51461 37.9923 8.06214L62.464 49.0356C65.2508 53.7015 61.8891 59.625 56.4543 59.625H7.51078C2.07602 59.625 -1.28569 53.7015 1.50106 49.0356L25.9729 8.06214Z" fill="url(#paint0_linear)"/>
<path d="M29.0775 24.3888C29.0864 22.7908 30.3843 21.5 31.9824 21.5C33.5805 21.5 34.8785 22.7908 34.8874 24.3888L34.9658 38.5C34.9749 40.1542 33.6366 41.5 31.9824 41.5C30.3283 41.5 28.9899 40.1542 28.9991 38.5L29.0775 24.3888Z" fill="white"/>
<path d="M34.9824 47.5C34.9824 49.1569 33.6393 50.5 31.9824 50.5C30.3256 50.5 28.9824 49.1569 28.9824 47.5C28.9824 45.8431 30.3256 44.5 31.9824 44.5C33.6393 44.5 34.9824 45.8431 34.9824 47.5Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear" x1="31.9827" y1="4.18421" x2="31.9827" y2="84.2072" gradientUnits="userSpaceOnUse">
<stop stop-color="#FF7171"/>
<stop offset="1" stop-color="#E63757"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1013 B

View File

@ -0,0 +1,10 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.29416 0.0598755H27.7073C32.2688 0.0598755 35.9409 3.73205 35.9409 8.29347V27.7067C35.9409 32.2681 32.2688 35.9403 27.7073 35.9403H8.29416C3.73273 35.9403 0.0605469 32.2681 0.0605469 27.7067V8.29347C0.0605469 3.73205 3.73273 0.0598755 8.29416 0.0598755Z" fill="url(#paint0_linear)"/>
<path d="M7.92234 10.2633C7.73329 10.2633 7.55519 10.2959 7.38662 10.363L10.7629 13.8389L14.1765 17.3771L14.2388 17.4518L14.3385 17.5515L14.4381 17.6512L14.6375 17.863L17.5652 20.8655C17.6139 20.8958 17.7552 21.0266 17.8656 21.0818C18.0077 21.1529 18.1618 21.2183 18.3207 21.224C18.492 21.2302 18.6672 21.1811 18.8215 21.106C18.937 21.0498 18.9883 20.9693 19.1225 20.8655L22.5112 17.3646L25.9373 13.8389L29.2388 10.4377C29.0268 10.3229 28.7921 10.2633 28.5411 10.2633H7.92234ZM6.88828 10.6869C6.52824 11.028 6.30273 11.5409 6.30273 12.1196V23.5316C6.30273 24.0002 6.45323 24.4258 6.70141 24.7525L7.17483 24.304L10.7006 20.8779L13.8277 17.8505L13.7654 17.7758L10.3393 14.25L6.9132 10.7118L6.88828 10.6869ZM29.6873 10.799L26.3484 14.25L22.9348 17.7758L22.8725 17.8381L26.1242 20.9901L29.6499 24.4161L29.8617 24.6155C30.0516 24.3109 30.1607 23.9357 30.1607 23.5316V12.1196C30.1607 11.6036 29.9819 11.1357 29.6873 10.799ZM14.2263 18.2617L11.1117 21.2891L7.5735 24.7151L7.12499 25.1512C7.36144 25.3035 7.63053 25.4004 7.92234 25.4004H28.5411C28.8919 25.4004 29.2101 25.2654 29.4755 25.0515L29.2513 24.8273L25.713 21.4012L22.4614 18.2617L19.5336 21.2766C19.3752 21.3817 19.2693 21.4982 19.1146 21.5697C18.8656 21.6849 18.5926 21.7823 18.3183 21.7781C18.0433 21.7739 17.7736 21.6662 17.5267 21.5448C17.4028 21.4839 17.3368 21.4233 17.1914 21.3015L14.2263 18.2617Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear" x1="18.252" y1="35.7962" x2="18.2869" y2="0.493036" gradientUnits="userSpaceOnUse">
<stop stop-color="#70EFFF"/>
<stop offset="1" stop-color="#5770FF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-arrow-left">
<path id="icon" fill-rule="evenodd" clip-rule="evenodd" d="M14 8H3.20998L7.68998 3.52L6.97998 2.81L1.28998 8.5L6.97998 14.19L7.68998 13.48L3.20998 9H14V8Z" fill="#17181C"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 304 B

View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
version="1.1"
id="svg12"
sodipodi:docname="ic-card-identity.svg"
inkscape:version="1.1.1 (3bf5ae0d25, 2021-09-20, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs16" />
<sodipodi:namedview
id="namedview14"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
inkscape:zoom="40.25"
inkscape:cx="7.9875776"
inkscape:cy="8"
inkscape:window-width="2556"
inkscape:window-height="1401"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="svg12" />
<g
id="ic-card-identity"
transform="translate(0,-3.9095641)">
<g
id="icon">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 5.32,9.64 C 5.96894,9.64404 6.55605,9.25571 6.80626,8.65693 7.05647,8.05815 6.92022,7.36754 6.46134,6.90866 6.00246,6.44978 5.31185,6.31353 4.71307,6.56374 4.11429,6.81395 3.72596,7.40106 3.73,8.05 c 0,0.87813 0.71187,1.59 1.59,1.59 z m 0,-2.19 C 5.61898,7.48884 5.8427,7.74351 5.8427,8.045 5.8427,8.34649 5.61898,8.60116 5.32,8.64 5.02102,8.60116 4.7973,8.34649 4.7973,8.045 4.7973,7.74351 5.02102,7.48884 5.32,7.45 Z"
fill="#0c0c14"
id="path2" />
<path
d="m 9,8 h 4 V 9 H 9 Z"
fill="#0c0c14"
id="path4" />
<path
d="M 13,10 H 9 v 1 h 4 z"
fill="#0c0c14"
id="path6" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 1,14 V 4 H 15 V 14 Z M 3.64,12.49 V 13 H 7 V 12.49 L 6.1,11.57 H 4.54 Z M 14,13 H 8 V 12.08 L 6.52,10.57 H 4.12 L 2.64,12.08 V 13 H 2 V 5 h 12 z"
fill="#0c0c14"
id="path8" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 8.5L2.7 7.8L6.05 11.14L13.2 4L13.9 4.7L6.05 12.56L2 8.5Z" fill="#17181C"/>
</svg>

After

Width:  |  Height:  |  Size: 230 B

View File

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-chevron-down">
<path id="icon" fill-rule="evenodd" clip-rule="evenodd" d="M2.3 6.30001L8 12L13.7 6.30001L13 5.60001L8 10.58L3 5.60001L2.3 6.30001Z" fill="#17181C"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 283 B

View File

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-chevron-up">
<path id="icon" fill-rule="evenodd" clip-rule="evenodd" d="M13.7 9.7L7.99999 4L2.29999 9.7L2.99999 10.4L7.99999 5.42L13 10.4L13.7 9.7Z" fill="#17181C"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 284 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.7166 1H9.29278L9.83484 2.72079C10.0427 2.79404 10.2448 2.87886 10.4428 2.97503L12.04 2.14318L13.8662 3.97057L13.035 5.56869C13.1312 5.7671 13.2161 5.96972 13.2894 6.178L15 6.72052V9.29956L13.2799 9.83276C13.2068 10.0403 13.1222 10.2423 13.0262 10.4401L13.8574 12.0382L12.0312 13.8656L10.434 13.0338C10.2359 13.13 10.0336 13.2149 9.82558 13.2882L9.2834 15H6.70782L6.16564 13.2882C5.95765 13.2149 5.75532 13.13 5.55718 13.0338L3.95998 13.8656L2.13382 12.0382L2.96503 10.4401C2.86877 10.2417 2.78388 10.0391 2.71059 9.83078L1 9.28826V6.71174L2.71059 6.16922C2.78388 5.96094 2.86877 5.75832 2.96503 5.55991L2.13323 3.96064L3.96989 2.13499L5.56596 2.96625C5.76409 2.87 5.96643 2.78513 6.17441 2.71184L6.7166 1ZM7.44883 2L6.97047 3.51034L6.71736 3.58632C6.40057 3.68142 6.09845 3.8085 5.80203 3.96884L5.56858 4.09511L4.15637 3.35961L3.35968 4.15152L4.09358 5.56254L3.9676 5.79574C3.80736 6.0924 3.68034 6.39477 3.58529 6.71183L3.5094 6.96496L2 7.44367V8.55633L3.5094 9.03504L3.58529 9.28817C3.68034 9.60523 3.80736 9.9076 3.9676 10.2043L4.09358 10.4375L3.35909 11.8496L4.14873 12.6398L5.5598 11.9049L5.79325 12.0312C6.08967 12.1915 6.39179 12.3186 6.70858 12.4137L6.96169 12.4897L7.44005 14H8.55117L9.02953 12.4897L9.28264 12.4137C9.59943 12.3186 9.90155 12.1915 10.198 12.0312L10.4314 11.9049L11.8425 12.6398L12.6321 11.8496L11.8976 10.4375L12.0236 10.2043C12.1839 9.9076 12.3109 9.60523 12.4059 9.28817L12.4824 9.03304L14 8.5626V7.45245L12.4906 6.97374L12.4147 6.72061C12.3197 6.40355 12.1926 6.10118 12.0324 5.80453L11.9064 5.57132L12.6409 4.15916L11.8513 3.36898L10.4402 4.1039L10.2067 3.97762C9.91033 3.81729 9.60821 3.6902 9.29142 3.5951L9.03783 3.51898L8.55935 2H7.44883ZM7.99561 5.81216C6.78861 5.81216 5.80959 6.79136 5.80959 8C5.80959 9.20864 6.78861 10.1878 7.99561 10.1878C9.20261 10.1878 10.1816 9.20864 10.1816 8C10.1816 6.79136 9.20261 5.81216 7.99561 5.81216ZM4.80959 8C4.80959 6.23972 6.23569 4.81216 7.99561 4.81216C9.75553 4.81216 11.1816 6.23972 11.1816 8C11.1816 9.76028 9.75553 11.1878 7.99561 11.1878C6.23569 11.1878 4.80959 9.76028 4.80959 8Z" fill="#17181C"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M15.9257 5.48656C13.8921 3.271 11.0022 2 8 2C4.99776 2 2.10791 3.271 0.0743047 5.48656C-0.0273754 5.59626 -0.0246996 5.76751 0.0823321 5.87455L1.67175 7.4637C1.78146 7.57341 1.95806 7.57341 2.06777 7.4637C2.0865 7.44524 2.09988 7.42383 2.11058 7.39975C3.61705 5.74584 5.76036 4.7962 8 4.7962C10.2396 4.7962 12.3829 5.74611 13.8894 7.39975C13.9001 7.42383 13.9135 7.44524 13.9322 7.4637C13.9857 7.51989 14.058 7.54692 14.1302 7.54692C14.2025 7.54692 14.2747 7.51989 14.3282 7.4637L15.9177 5.87455C16.0247 5.76751 16.0274 5.59653 15.9257 5.48656Z" fill="#349172"/>
<path d="M12.7547 8.56633C11.5533 7.20168 9.82473 6.41794 7.99984 6.41794C6.17495 6.41794 4.44639 7.20195 3.24228 8.56633C3.2289 8.57436 3.21285 8.58265 3.19947 8.59577C3.08976 8.70547 3.08976 8.88208 3.19947 8.99151L4.72735 10.5194C4.7541 10.5488 4.78621 10.5678 4.821 10.5809C4.83705 10.589 4.85311 10.589 4.86649 10.5916C4.88789 10.5943 4.90662 10.6023 4.92535 10.6023C4.92803 10.6023 4.93071 10.5997 4.93338 10.5997C4.93606 10.5997 4.93873 10.6023 4.94141 10.6023C4.95211 10.6023 4.96281 10.5943 4.97352 10.5916C4.9976 10.589 5.02168 10.5836 5.04309 10.5729C5.05914 10.5676 5.06985 10.5569 5.08323 10.5462C5.0966 10.5381 5.11266 10.5328 5.12336 10.5191C5.12871 10.5164 5.12871 10.5087 5.13407 10.5057C5.13674 10.5004 5.14209 10.5004 5.14477 10.495C5.83512 9.62006 6.87601 9.11969 7.99984 9.11969C9.12367 9.11969 10.1646 9.62006 10.8549 10.495C10.8576 10.5007 10.8629 10.5007 10.8656 10.5057C10.871 10.5114 10.871 10.5164 10.8763 10.5191C10.887 10.5328 10.9031 10.5379 10.9165 10.5462C10.9298 10.5569 10.9405 10.5676 10.9566 10.5729C10.978 10.5836 11.0021 10.5892 11.0262 10.5916C11.0369 10.5943 11.0476 10.6023 11.0583 10.6023H11.0743C11.1118 10.6023 11.1466 10.5943 11.1814 10.5809C11.2161 10.5676 11.2456 10.5461 11.2723 10.5194L12.8002 8.99151C12.9099 8.88208 12.9099 8.70547 12.8002 8.59577C12.7868 8.58265 12.7708 8.57436 12.7547 8.56633Z" fill="#349172"/>
<path d="M7.99991 10.2626C7.17309 10.2626 6.50146 10.9371 6.50146 11.7637C6.50146 12.5902 7.17309 13.2621 7.99991 13.2621C8.82673 13.2621 9.49835 12.5905 9.49835 11.7637C9.49835 10.9369 8.82673 10.2626 7.99991 10.2626Z" fill="#349172"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,4 @@
<svg width="12" height="13" viewBox="0 0 12 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M4 2V0H12V10H9V9H11V1H5V2H4Z" fill="#262A33"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 3V13H8V3H0ZM7 4V12H1V4H7Z" fill="#262A33"/>
</svg>

After

Width:  |  Height:  |  Size: 255 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.85 2.8499L13.15 2.1499L8 7.2899L2.85 2.1499L2.15 2.8499L7.29 7.9999L2.15 13.1499L2.85 13.8499L8 8.7099L13.15 13.8499L13.85 13.1499L8.71 7.9999L13.85 2.8499Z" fill="#17181C"/>
</svg>

After

Width:  |  Height:  |  Size: 331 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="17" viewBox="0 0 16 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.8561 3.06675L13.9015 7.66688H2.09846L4.14391 3.06675H11.8561ZM14 8.68913H2V12.267H14V8.68913ZM12.5 2.04449H3.5L1 7.66688V13.2893H15V7.66688L12.5 2.04449ZM12 10.9381V9.91584H13V10.9381H12ZM10 10.9381H11V9.91584H10V10.9381Z" fill="#0C0C14"/>
</svg>

After

Width:  |  Height:  |  Size: 396 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM7.5 5V9H8.5V5H7.5ZM7.5 10V11H8.5V10H7.5Z" fill="#17181C"/>
</svg>

After

Width:  |  Height:  |  Size: 316 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M14 2L13.2427 2L13.2427 1.99998L13.2426 2L13 2L9 2V3H12.2426L5.76613 9.47651L6.47324 10.1836L13 3.65686V7H14V3V2ZM2 2H5V3H3L3 13L13 13V11H14V14H13H3H2V13V3V2Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 327 B

View File

@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-eye-slash">
<g id="icon">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11 4.33002L13.15 2.15002L13.86 2.84002L2.85999 13.84L2.15999 13.14L4.15999 11.14C3.12487 10.3566 2.24914 9.38244 1.57999 8.27002L1.39999 8.00002L1.57999 7.70002C3.14999 5.31002 4.83999 3.70002 7.99999 3.70002C9.03578 3.66939 10.064 3.88532 11 4.33002ZM6.92814 6.91817C6.47409 7.37223 6.362 8.0661 6.64999 8.64003L8.64999 6.64003C8.07607 6.35203 7.3822 6.46412 6.92814 6.91817ZM9.36999 5.93002C8.96735 5.65139 8.48965 5.50146 7.99999 5.50002C6.61928 5.50002 5.49999 6.61931 5.49999 8.00002C5.50143 8.48968 5.65136 8.96738 5.92999 9.37002L4.86999 10.42C3.96048 9.77171 3.18887 8.94911 2.59999 8.00002C3.99999 6.00002 5.39999 4.70002 7.99999 4.70002C8.75151 4.68822 9.49833 4.82061 10.2 5.09002L9.36999 5.93002Z" fill="#17181C"/>
<path d="M14.42 7.73002C13.8714 6.82671 13.1981 6.00529 12.42 5.29002L11.7 6.00002C12.3591 6.5822 12.9316 7.25569 13.4 8.00002C12 10 10.6 11.3 7.99999 11.3C7.51257 11.3044 7.0262 11.2541 6.54999 11.15L5.73999 12C6.47229 12.2209 7.23543 12.3222 7.99999 12.3C11.16 12.3 12.85 10.69 14.42 8.30002L14.6 8.00002L14.42 7.73002Z" fill="#17181C"/>
<path d="M8.21999 9.48002L7.29999 10.4C7.52685 10.4689 7.76292 10.5027 7.99999 10.5C8.66304 10.5 9.29892 10.2366 9.76776 9.76779C10.2366 9.29895 10.5 8.66307 10.5 8.00002C10.5026 7.76295 10.4689 7.52688 10.4 7.30002L9.47999 8.22003C9.38822 8.87414 8.8741 9.38825 8.21999 9.48002Z" fill="#17181C"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-eye">
<g id="icon">
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99999 5.49998C6.61928 5.49998 5.49999 6.61927 5.49999 7.99998C5.49999 9.38069 6.61928 10.5 7.99999 10.5C9.38071 10.5 10.5 9.38069 10.5 7.99998C10.5 7.33694 10.2366 6.70106 9.76776 6.23221C9.29892 5.76337 8.66304 5.49998 7.99999 5.49998ZM7.99999 9.49998C7.17157 9.49998 6.49999 8.82841 6.49999 7.99998C6.49999 7.17155 7.17157 6.49998 7.99999 6.49998C8.82842 6.49998 9.49999 7.17155 9.49999 7.99998C9.49999 8.82841 8.82842 9.49998 7.99999 9.49998Z" fill="#17181C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.42 7.72998C12.85 5.30998 11.16 3.72998 7.99999 3.72998C4.83999 3.72998 3.14999 5.33998 1.57999 7.72998L1.39999 7.99998L1.57999 8.26998C3.14999 10.69 4.83999 12.27 7.99999 12.27C11.16 12.27 12.85 10.66 14.42 8.26998L14.6 7.99998L14.42 7.72998ZM7.99999 11.3C5.39999 11.3 3.99999 9.99998 2.59999 7.99998C3.99999 5.99998 5.39999 4.69998 7.99999 4.69998C10.6 4.69998 12 5.99998 13.4 7.99998C12 9.99998 10.6 11.3 7.99999 11.3Z" fill="#17181C"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,8 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-illustrative-view-html-code">
<g id="icon">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.8 9.83C5.017 9.94015 5.25665 9.99835 5.5 10C5.66952 10.0069 5.8389 9.98323 6 9.93C6.67538 9.71289 7.10403 9.04971 7.02464 8.34474C6.94525 7.63978 6.37983 7.08855 5.67308 7.02709C4.96633 6.96564 4.31425 7.411 4.11437 8.09168C3.91448 8.77236 4.22223 9.49957 4.85 9.83H4.8ZM5.06 8.27C5.11328 8.14244 5.21896 8.04404 5.35 8H5.5C5.5806 7.9995 5.65991 8.0202 5.73 8.06C5.85756 8.11328 5.95596 8.21896 6 8.35C6.05001 8.47173 6.05001 8.60827 6 8.73C5.93418 8.87156 5.80363 8.97227 5.65 9C5.52827 9.05001 5.39173 9.05001 5.27 9C5.12844 8.93418 5.02773 8.80363 5 8.65C4.96852 8.5204 4.99012 8.3836 5.06 8.27Z" fill="#0C0C14"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 2V15H15V2H1ZM14 14H2V12.79L4.2 11.49L6.56 12L10.45 10.05L14 12.75V14ZM10.55 8.92L14 11.5V6H2V11.63L4 10.43L6.44 11L10.55 8.92ZM14 5H2V3H10V4H11V3H12V4H13V3H14V5Z" fill="#0C0C14"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15C11.866 15 15 11.866 15 8C15 6.14348 14.2625 4.36301 12.9497 3.05025C11.637 1.7375 9.85652 1 8 1ZM8.5 4V5H7.5V4H8.5ZM8.5 6V11H9.5V12H6.5V11H7.5V7H6.5V6H8.5Z" fill="#17181C"/>
</svg>

After

Width:  |  Height:  |  Size: 380 B

View File

@ -0,0 +1,12 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="32" cy="32" r="30" fill="url(#paint0_linear)"/>
<path d="M32 18C30.3431 18 29 19.3431 29 21C29 22.6569 30.3431 24 32 24C33.6569 24 35 22.6569 35 21C35 19.3431 33.6569 18 32 18Z" fill="white"/>
<path d="M30 28C28.8954 28 28 28.8954 28 30C28 31.1046 28.8954 32 30 32V42C28.8954 42 28 42.8954 28 44C28 45.1046 28.8954 46 30 46H34C35.1046 46 36 45.1046 36 44C36 42.8954 35.1046 42 34 42V30C34 28.8954 33.1046 28 32 28H30Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear" x1="32" y1="62" x2="14.4192" y2="7.69125" gradientUnits="userSpaceOnUse">
<stop stop-color="#4F6DE6"/>
<stop offset="0.483234" stop-color="#63A1FE"/>
<stop offset="1" stop-color="#82D2FF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 807 B

View File

@ -0,0 +1,20 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M32.1672 2.25H12.8275C11.961 2.25 11.248 2.96301 11.248 3.82952V5.62489L22.1485 9.0001L33.7466 5.62489V3.82952C33.7466 2.96301 33.0336 2.25 32.1672 2.25Z" fill="#0364B8"/>
<path d="M35.6033 19.5809C35.7681 19.0629 35.8997 18.535 35.9971 18.0002C35.9972 17.7327 35.8542 17.4847 35.6225 17.351L35.6079 17.3432L35.6033 17.3411L23.4113 10.3952C23.3588 10.3612 23.3043 10.3299 23.2483 10.3018C22.7762 10.0678 22.2208 10.0678 21.7487 10.3018C21.6928 10.3299 21.6383 10.361 21.5856 10.3952L9.39371 17.3411L9.38913 17.3432L9.3744 17.351C9.14286 17.4847 8.99984 17.7327 9 18.0002C9.09736 18.535 9.22893 19.0629 9.39371 19.5809L22.3213 29.0364L35.6033 19.5809Z" fill="#0A2767"/>
<path d="M26.9977 5.625H19.1232L16.8496 9.00021L19.1232 12.3751L26.9977 19.125H33.7472V12.3751L26.9977 5.625Z" fill="#28A8EA"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 5.625H19.1245V12.3749H11.25V5.625Z" fill="#0078D4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.998 5.625H33.7476V12.3749H26.998V5.625Z" fill="#50D9FF"/>
<path d="M26.999 19.1251L19.1245 12.3752H11.25V19.1251L19.1245 25.8752L31.3097 27.8642L26.999 19.1251Z" fill="#0364B8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.123 12.3752H26.9976V19.1251H19.123V12.3752Z" fill="#0078D4"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.25 19.125H19.1245V25.8749H11.25V19.125Z" fill="#064A8C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M26.998 19.125H33.7476V25.8749H26.998V19.125Z" fill="#0078D4"/>
<path d="M22.7128 28.3704L9.44531 18.6955L10.001 17.7179C10.001 17.7179 22.0883 24.6028 22.2728 24.7064C22.4256 24.7678 22.5974 24.7628 22.7465 24.6928C22.9185 24.5961 35.0441 17.6841 35.0441 17.6841L35.6021 18.6618L22.7128 28.3704Z" fill="#0A2767" fill-opacity="0.498039"/>
<path d="M35.6238 18.6492L35.6092 18.6582L35.6058 18.6605L23.4139 25.6063C22.922 25.9235 22.2989 25.9623 21.7715 25.7086L26.018 31.4022L35.3032 33.4239V33.4285C35.741 33.1118 36.0008 32.603 36.0006 32.0627V18.0002C36.0008 18.2676 35.8576 18.5157 35.6261 18.6492H35.6238Z" fill="#1490DF"/>
<path d="M35.9976 32.0629V31.2328L24.7674 24.8337L23.4118 25.6054C22.9201 25.9226 22.2969 25.9615 21.7695 25.7077L26.0161 31.4015L35.3013 33.423V33.4276C35.739 33.1108 35.9987 32.6022 35.9987 32.0618L35.9976 32.0629Z" fill="black" fill-opacity="0.047059"/>
<path d="M35.9414 32.4937L23.6312 25.4804L23.4118 25.6053C22.9201 25.9224 22.2969 25.9614 21.7695 25.7075L26.0161 31.4013L35.3013 33.4228V33.4274C35.6163 33.199 35.8429 32.8686 35.9424 32.4925L35.9414 32.4937Z" fill="black" fill-opacity="0.098039"/>
<path d="M9.39373 18.6639V18.6526H9.38261L9.34873 18.6302C9.13044 18.4959 8.9979 18.2565 9.00003 18.0002V32.065C9.00003 32.9894 9.76077 33.7502 10.6853 33.7502H34.3111C34.4515 33.7487 34.5914 33.7299 34.7273 33.6939C34.7978 33.6816 34.866 33.6589 34.9298 33.6265C34.9537 33.624 34.9766 33.6163 34.9973 33.6039C35.0892 33.5663 35.1763 33.5172 35.256 33.4576L35.301 33.4239L9.39373 18.6639Z" fill="#28A8EA"/>
<path d="M20.2493 27.7507V9.37612C20.2469 8.55429 19.5716 7.87892 18.7498 7.87646H11.2849V16.2643L9.39371 17.3421L9.38929 17.3444L9.37473 17.3523C9.14302 17.4861 9 17.7341 9 18.0013V29.2548V29.2502H18.7498C19.5716 29.2479 20.2469 28.5724 20.2493 27.7507Z" fill="black" fill-opacity="0.098039"/>
<path d="M19.1245 28.8755V10.5009C19.1221 9.67927 18.4466 9.0039 17.6249 9.00128H11.2849V16.2643L9.39371 17.342L9.38929 17.3443L9.37473 17.3522C9.14302 17.486 9 17.734 9 18.0012V30.3798V30.3752H17.6249C18.4466 30.3727 19.1221 29.6974 19.1245 28.8755ZM19.1245 26.6255V10.5009C19.1221 9.67927 18.4466 9.0039 17.6249 9.00128H11.2849V16.2643L9.39371 17.342L9.38929 17.3443L9.37473 17.3522C9.14302 17.486 9 17.734 9 18.0012V28.1296V28.1252H17.6249C18.4466 28.1228 19.1221 27.4474 19.1245 26.6255ZM17.9995 26.6255V10.5009C17.9971 9.67927 17.3217 9.0039 16.5001 9.00128H11.2849V16.2643L9.39371 17.342L9.38929 17.3443L9.37473 17.3522C9.14302 17.486 9 17.734 9 18.0012V28.1296V28.1252H16.5001C17.3217 28.1228 17.9971 27.4474 17.9995 26.6255Z" fill="black" fill-opacity="0.2"/>
<path d="M1.49956 9.00037H16.4981C17.3207 9.00037 17.9977 9.67721 17.9977 10.4999V25.4996C17.9977 26.3222 17.3207 26.9992 16.4981 26.9992H1.49956C0.676964 26.9992 0 26.3222 0 25.4996V10.4999C0 9.67721 0.676964 9.00037 1.49956 9.00037Z" fill="#0078D4"/>
<path d="M4.35008 15.1517C4.74854 14.3026 5.39195 13.5919 6.19721 13.1111C7.08952 12.6002 8.10554 12.3457 9.13334 12.3753C10.0852 12.3544 11.0248 12.5957 11.8489 13.0728C12.6244 13.5346 13.249 14.2121 13.6464 15.0225C14.0794 15.9153 14.2954 16.8978 14.2764 17.89C14.2975 18.9271 14.0752 19.9548 13.6274 20.8905C13.2211 21.7295 12.5777 22.4314 11.7769 22.9088C10.9207 23.4008 9.94612 23.6488 8.9589 23.6254C7.98641 23.6484 7.02603 23.4044 6.18265 22.9199C5.40112 22.4573 4.76899 21.7791 4.36252 20.9669C3.92676 20.0868 3.7083 19.1149 3.72581 18.1331C3.7065 17.1049 3.92037 16.0857 4.35123 15.1517H4.35008ZM6.31863 19.942C6.53119 20.4789 6.89168 20.9448 7.35821 21.2852C7.83292 21.6177 8.40205 21.7892 8.98132 21.7747C9.59856 21.7991 10.2071 21.6222 10.7149 21.2707C11.1757 20.9303 11.5267 20.4619 11.724 19.924C11.9459 19.3244 12.0552 18.689 12.0469 18.0498C12.0538 17.405 11.951 16.7635 11.7432 16.1531C11.5599 15.6036 11.2215 15.1185 10.7689 14.757C10.2745 14.3878 9.66761 14.2003 9.05119 14.226C8.45932 14.2106 7.87743 14.3831 7.38963 14.7187C6.91443 15.0603 6.54592 15.5301 6.32763 16.0732C5.8449 17.3168 5.84212 18.6967 6.31977 19.942H6.31863Z" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -0,0 +1,112 @@
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.429 5.15378C25.3233 3.77844 22.6304 3.33309 21.1443 3.17756C19.5536 3.0113 18.1721 3.04053 16.9571 3.2189C16.8541 3.21753 16.7518 3.21365 16.6484 3.21365C16.5693 3.21365 16.4915 3.21707 16.412 3.21799C16.4446 3.17802 16.4673 3.15335 16.4673 3.15335C16.4673 3.15335 16.4033 3.1634 16.2779 3.22027C15.8835 3.22666 15.4907 3.24037 15.104 3.26686C15.6428 2.67626 16.0843 2.36657 16.0843 2.36657C16.0843 2.36657 15.5603 2.4433 14.6043 3.3098C14.1496 3.35159 13.7008 3.40709 13.26 3.47606C14.2647 2.11671 15.3155 1.54415 15.3155 1.54415C15.3155 1.54415 14.0418 1.28494 12.0473 3.28901C11.8804 3.45688 11.7258 3.63113 11.5759 3.80768C5.76262 5.21407 1.60236 8.96803 1.60236 13.3795C1.60236 14.2446 1.33675 15.1961 1.62314 16.1464C1.50872 17.6642 1.75241 21.806 1.75241 21.806C1.75241 21.806 3.32598 30.3225 9.37727 31.8031C9.47342 31.8262 8.11293 29.6803 7.51114 27.0922C8.68275 28.2839 10.0791 29.2267 11.6465 29.39C11.8388 29.4099 10.7512 28.1352 9.79864 26.5971L23.5497 31.2301C30.7418 28.141 29.9045 28.4194 31.4486 26.7467C34.8506 23.0642 35.3268 20.9747 34.4697 14.8053C33.8903 10.641 30.8165 6.51632 27.429 5.15378Z" fill="url(#paint0_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2.37873 24.2463L25.1074 31.8031L29.3227 13.8808L3.29364 9.04291L1.24023 20.7184L2.37873 24.2463Z" fill="#FBFBFB"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.35905 9.44019C3.47302 9.68547 3.43945 9.74348 3.36842 9.74348C3.33644 9.74348 3.29693 9.73183 3.25993 9.71996C3.22294 9.70831 3.18822 9.69643 3.16584 9.69643C3.10303 9.69643 3.13843 9.78939 3.49449 10.2366C3.99808 10.8763 11.3936 22.0411 11.7979 22.0411C11.7999 22.0411 11.802 22.0409 11.8036 22.0402C15.2465 20.8133 29.8885 15.7877 29.8885 15.7877L28.3903 13.6055L3.35905 9.44019Z" fill="#999999"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.57227 9.16595C3.57227 9.16595 3.68258 9.73692 4.19393 10.3689C4.70209 11.0065 11.5285 21.3425 11.8711 21.2514C16.3429 20.0643 32.5671 13.7478 32.5671 13.7478L3.57227 9.16595Z" fill="#F8F8F8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.2955 31.534L2.69531 24.3522L2.77159 24.857L24.536 32.0933L24.2955 31.534Z" fill="#999999"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.37825 8.6828C8.37825 8.6828 0.519063 10.9845 4.12526 18.9973C4.12526 18.9973 2.47175 17.4834 1.48719 15.5476C1.44288 15.461 3.12836 9.4737 3.12836 9.4737L8.37825 8.6828Z" fill="url(#paint1_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.12623 18.9976C3.4429 18.9857 2.3446 18.4218 1.98855 17.7392C1.54343 25.1592 4.57067 29.4924 9.3768 31.8032C7.8062 31.6232 0 27.5038 0 17.5908C0 9.16175 6.68848 1.62756 17.1554 1.28247C17.2195 1.755 11.9662 2.93986 11.7981 3.49712C11.5373 4.3643 10.9378 5.30821 10.4324 6.06508C9.74724 7.0903 11.3845 7.96707 10.1814 8.24342C8.37099 8.65885 5.95605 8.42886 4.17533 10.7438C1.49843 14.2234 3.28463 18.2042 4.12623 18.9976Z" fill="url(#paint2_radial)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9001 1.55371C13.1217 1.03345 9.63287 3.74598 7.39333 6.89975C6.57777 8.04784 6.66684 9.10206 6.94775 10.3755C7.06834 10.9191 7.19029 11.1797 7.04344 11.734C6.96237 12.0398 6.97584 12.3253 6.87512 12.5057C6.73604 12.7551 6.55858 13.0275 6.4992 13.4752C6.35075 14.5931 6.74654 14.9492 6.99388 15.3253C7.34628 14.9754 8.03441 14.1521 9.27499 13.6604C10.5158 13.1687 11.3323 12.4294 12.8874 11.636C15.012 10.5523 17.5026 12.1965 22.2244 10.6491C23.6774 10.173 27.0166 5.22637 27.8093 5.03635C25.6018 2.59538 21.8462 1.70285 19.9001 1.55371Z" fill="url(#paint3_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.4217 0.00476074C15.4217 0.00476074 14.2113 0.589426 13.6844 1.64571C14.9015 1.03226 15.6079 0.696996 15.9418 0.657028C15.9418 0.657028 15.6218 0.811873 15.2251 1.65461C15.9265 1.43011 16.1679 1.28828 16.3604 1.28623C16.3604 1.28623 16.3044 1.37484 16.2533 2.21576C15.1762 1.86678 13.1034 2.27331 12.0005 2.96394C11.6267 0.969916 15.4217 0.00476074 15.4217 0.00476074Z" fill="#3F6499"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.24 1.25326C18.1622 0.983992 16.4253 1.03698 14.9486 1.32155C15.4129 0.876423 15.9503 0.657173 15.9503 0.657173C15.2539 0.718609 14.3849 1.17332 13.639 1.65453C13.2631 1.77443 12.905 1.90986 12.5658 2.06083C12.7734 1.78722 13.0151 1.49466 13.2512 1.27016C14.128 0.436325 15.4302 0.00467751 15.4302 0.00467751C14.3189 -0.0768559 10.5994 0.875281 8.36126 5.40827C7.99448 5.85522 7.65213 6.31358 7.32599 6.77309C6.48417 7.95864 6.57643 9.0469 6.86671 10.3619C6.9905 10.9235 7.01333 11.8199 6.89115 12.3991C6.86671 12.5152 6.54811 12.8148 6.46635 13.6228C6.37888 14.4802 6.68492 14.9013 6.98661 15.2496C8.05842 13.6824 9.11562 13.3718 9.11562 13.3718C10.6157 12.7572 11.2401 12.1009 12.8458 11.2816C15.0397 10.1623 22.7114 13.8156 27.5865 12.218C29.0867 11.7265 27.0089 5.34089 27.8279 5.14494C25.5481 2.62448 22.2379 1.51225 20.24 1.25326Z" fill="url(#paint4_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.4576 14.9986C34.2558 8.72034 28.4736 3.00843 23.8239 2.69189C21.7602 2.55143 22.394 3.60931 21.1027 4.01241C15.5381 5.74882 15.4531 6.76262 15.4531 6.76262C27.2517 7.31714 27.4305 18.9696 24.2708 21.9176C25.1599 21.7102 26.124 20.6231 27.0094 18.9166C26.9032 19.6326 26.8118 20.5109 26.6792 21.4909C26.2745 24.4814 26.7986 30.7284 18.2054 34.909C18.2054 34.909 22.9062 34.5249 25.2371 31.9007C24.2891 34.0807 21.688 35.2582 21.688 35.2582C23.6024 34.9782 28.6305 33.4944 31.8336 29.4888C35.4587 24.9546 36.549 20.701 35.4576 14.9986Z" fill="url(#paint5_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.9593 26.4602C23.9593 26.4602 27.7923 25.3886 29.0274 23.3188C28.8776 25.7321 26.6885 28.2069 26.6885 28.2069C26.6885 28.2069 29.8887 27.5592 31.2533 25.3073C31.0756 27.3395 28.6118 30.0858 28.6118 30.0858C31.1306 29.6339 37.4594 25.636 35.4574 14.9983C34.275 8.71609 28.4734 3.00806 23.8234 2.69152C21.7597 2.55106 22.3935 3.60894 21.1022 4.01204C15.5381 5.74868 15.4531 6.76248 15.4531 6.76248C27.2517 7.317 30.387 13.4921 24.2708 21.7525C25.1599 21.5452 25.5165 21.0564 26.1845 20.2568C26.1842 20.2568 26.4871 23.4195 23.9593 26.4602Z" fill="url(#paint6_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.80049 12.4251C8.80049 12.4251 8.11534 12.9045 7.61906 12.7823C7.03165 12.6382 6.9323 12.0691 6.9323 12.0691C6.91928 12.1856 6.90444 12.2982 6.88274 12.3991C6.85831 12.5151 6.53971 12.8148 6.45795 13.6228C6.37047 14.4802 6.6925 14.9769 6.99419 15.3252C8.066 13.758 9.10721 13.3718 9.10721 13.3718C8.69909 13.2293 8.80049 12.4251 8.80049 12.4251Z" fill="url(#paint7_linear)"/>
<g opacity="0.6">
<g opacity="0.6">
<g opacity="0.2">
<path opacity="0.2" fill-rule="evenodd" clip-rule="evenodd" d="M23.7441 2.67671C23.9046 2.63515 24.5603 2.7064 24.9906 2.89939C25.4209 3.09237 26.1629 3.43358 25.5693 3.58203C24.9757 3.73048 24.6343 3.49296 24.3376 3.24082C24.041 2.98869 23.3435 2.78063 23.7441 2.67671Z" fill="url(#paint8_linear)"/>
</g>
</g>
</g>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.86562 12.3314C8.86562 12.3314 12.6248 6.1365 16.0234 4.46473C16.3391 4.306 11.318 5.79941 9.29726 8.02959C8.09619 9.35513 8.73201 11.9505 8.86562 12.3314Z" fill="url(#paint9_linear)"/>
<path opacity="0.1" fill-rule="evenodd" clip-rule="evenodd" d="M8.86523 12.3314C8.86866 12.3292 8.87254 12.3271 8.8762 12.3248C9.8482 11.7242 11.514 11.3784 12.051 10.2897C14.1183 6.09767 16.023 4.46472 16.023 4.46472C12.6245 6.13627 8.86523 12.3314 8.86523 12.3314Z" fill="#F2F2F2"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.22311 11.1985C9.22311 11.1985 8.54572 10.1591 9.52116 9.21108C10.0359 8.7116 10.8163 8.98909 10.9065 9.07884C11.2905 9.45956 11.0491 10.2632 10.7035 10.718C10.5082 10.974 9.93979 11.3727 9.22311 11.1985Z" fill="#2F4282"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.61129 11.1338C9.61129 11.1338 9.1431 10.4151 9.81775 9.76032C10.1729 9.41523 10.7123 9.60707 10.7749 9.66874C11.0401 9.93184 10.8731 10.4873 10.6345 10.8013C10.4997 10.979 10.1067 11.2544 9.61129 11.1338Z" fill="url(#paint10_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5456 10.3672C10.5351 10.6543 10.2921 10.8786 10.0028 10.8688C9.71295 10.8587 9.48594 10.6182 9.49644 10.3309C9.50672 10.0438 9.74972 9.81956 10.0398 9.82961C10.3289 9.83943 10.5555 10.0801 10.5456 10.3672Z" fill="black"/>
<path d="M9.68961 10.5015C9.85258 10.5015 9.98469 10.3694 9.98469 10.2064C9.98469 10.0434 9.85258 9.91132 9.68961 9.91132C9.52664 9.91132 9.39453 10.0434 9.39453 10.2064C9.39453 10.3694 9.52664 10.5015 9.68961 10.5015Z" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M33.1814 9.44009C30.6082 5.52283 28.5694 4.52753 28.5694 4.52753C28.5694 4.52753 28.663 7.38097 30.0918 8.75722C30.2551 8.91435 28.3821 7.53284 28.3821 7.53284C28.3821 7.53284 27.9815 8.85085 28.8994 10.366C28.6329 10.0117 28.4564 9.87767 28.4564 9.87767C28.4564 9.87767 26.7273 10.675 26.2168 11.7813C25.9533 11.2838 25.7555 10.9906 25.7555 10.9906C25.7555 10.9906 24.5594 13.3774 24.7099 16.1448C24.9545 20.6444 23.8672 22.3023 23.8672 22.3023C23.8672 22.3023 26.7798 21.1848 28.1764 17.5875C28.7533 19.117 28.1588 20.7269 28.1588 20.7269C28.1588 20.7269 30.512 18.9685 31.0181 15.7636C31.5791 16.6682 31.4142 18.4739 31.4142 18.4739C31.4142 18.4739 32.9163 16.3825 32.9528 14.1051C33.6489 14.5904 33.7131 16.4864 33.7131 16.4864C33.7131 16.4864 35.9401 13.6392 33.1814 9.44009Z" fill="url(#paint11_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M30.6808 6.70268C30.0082 5.44954 28.8217 4.65087 27.7787 4.00454C25.1781 2.39192 23.5056 2.16559 23.5056 2.16559C23.5056 2.16559 21.1057 2.61185 21.5177 3.27965C21.539 3.31391 21.5883 3.3573 21.6584 3.40709C19.5532 2.43737 18.359 4.66709 18.359 4.66709C17.156 4.59401 15.6912 5.18621 15.2641 6.77257C15.2198 6.93723 15.8029 6.84451 16.103 6.90983C17.857 7.29123 19.5082 8.11799 20.2669 8.52657C22.0318 9.47733 23.0788 11.0605 23.6835 12.2481C24.4148 13.6849 24.7135 16.2129 24.7135 16.2129C24.7135 16.2129 26.6379 13.5693 26.1782 12.212C26.9403 12.6855 27.0908 14.3744 27.0908 14.3744C27.0908 14.3744 28.3736 12.365 27.9945 10.6147C28.9841 11.2179 29.0172 12.4445 29.0172 12.4445C29.0172 12.4445 30.0249 11.0461 29.5695 8.75221C30.5652 9.54402 30.7737 10.5697 30.7737 10.5697C30.7737 10.5697 31.5875 8.39228 30.6808 6.70268Z" fill="url(#paint12_linear)"/>
<defs>
<linearGradient id="paint0_linear" x1="28.9748" y1="29.3189" x2="8.19927" y2="4.09908" gradientUnits="userSpaceOnUse">
<stop stop-color="#130036"/>
<stop offset="0.2297" stop-color="#18023B"/>
<stop offset="0.5122" stop-color="#26094A"/>
<stop offset="0.8211" stop-color="#3D1563"/>
<stop offset="1" stop-color="#4E1D75"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="1.48639" y1="13.8402" x2="8.37818" y2="13.8402" gradientUnits="userSpaceOnUse">
<stop stop-color="#3156A8"/>
<stop offset="0.2474" stop-color="#3351A4"/>
<stop offset="0.5365" stop-color="#3B4397"/>
<stop offset="0.8453" stop-color="#472C82"/>
<stop offset="1" stop-color="#4E1D75"/>
</linearGradient>
<radialGradient id="paint2_radial" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(8.578 9.36353) scale(12.3786)">
<stop offset="0.1654" stop-color="#14CDDA"/>
<stop offset="0.5478" stop-color="#2061BD"/>
<stop offset="0.6546" stop-color="#2658AC"/>
<stop offset="0.864" stop-color="#373F81"/>
<stop offset="1" stop-color="#432D62"/>
</radialGradient>
<linearGradient id="paint3_linear" x1="6.46705" y1="8.40727" x2="27.8094" y2="8.40727" gradientUnits="userSpaceOnUse">
<stop stop-color="#2061BD"/>
<stop offset="0.1846" stop-color="#2B51AC"/>
<stop offset="0.6826" stop-color="#442C84"/>
<stop offset="0.9409" stop-color="#4E1D75"/>
</linearGradient>
<linearGradient id="paint4_linear" x1="9.60982" y1="3.36979" x2="18.6751" y2="19.3209" gradientUnits="userSpaceOnUse">
<stop offset="0.0202" stop-color="#48A8E0"/>
<stop offset="0.3883" stop-color="#2061BD"/>
<stop offset="0.4968" stop-color="#2B51AC"/>
<stop offset="0.7892" stop-color="#442C84"/>
<stop offset="0.9409" stop-color="#4E1D75"/>
</linearGradient>
<linearGradient id="paint5_linear" x1="2.85799" y1="48.9959" x2="42.8988" y2="25.7007" gradientUnits="userSpaceOnUse">
<stop offset="0.3787" stop-color="#3156A8"/>
<stop offset="1" stop-color="#4E1D75"/>
</linearGradient>
<linearGradient id="paint6_linear" x1="14.0492" y1="40.462" x2="37.7537" y2="10.9949" gradientUnits="userSpaceOnUse">
<stop stop-color="#29ABE2"/>
<stop offset="0.7733" stop-color="#385AA6"/>
<stop offset="0.8575" stop-color="#414293"/>
<stop offset="1" stop-color="#4E1D75"/>
</linearGradient>
<linearGradient id="paint7_linear" x1="7.00851" y1="13.3643" x2="7.93109" y2="13.7193" gradientUnits="userSpaceOnUse">
<stop stop-color="#B0DCD6"/>
<stop offset="1" stop-color="#53ACE0"/>
</linearGradient>
<linearGradient id="paint8_linear" x1="25.5884" y1="3.35221" x2="24.5953" y2="2.28868" gradientUnits="userSpaceOnUse">
<stop stop-color="#3092B9"/>
<stop offset="0.2199" stop-color="#258DB6"/>
<stop offset="0.6564" stop-color="#1685B1"/>
<stop offset="1" stop-color="#1082AF"/>
</linearGradient>
<linearGradient id="paint9_linear" x1="11.731" y1="5.52165" x2="13.3151" y2="11.2176" gradientUnits="userSpaceOnUse">
<stop offset="0.0074" stop-color="#1398D1" stop-opacity="0"/>
<stop offset="0.2482" stop-color="#1187C2" stop-opacity="0.6197"/>
<stop offset="0.6422" stop-color="#3F6499" stop-opacity="0.71"/>
<stop offset="1" stop-color="#2F4282" stop-opacity="0.5"/>
</linearGradient>
<linearGradient id="paint10_linear" x1="10.5714" y1="10.6273" x2="9.7502" y2="10.22" gradientUnits="userSpaceOnUse">
<stop stop-color="#F9C21B"/>
<stop offset="0.1479" stop-color="#F3BA1B"/>
<stop offset="0.3787" stop-color="#E3A41B"/>
<stop offset="0.6634" stop-color="#C9801C"/>
<stop offset="0.9884" stop-color="#A44E1C"/>
<stop offset="1" stop-color="#A34C1C"/>
</linearGradient>
<linearGradient id="paint11_linear" x1="29.9441" y1="18.8975" x2="20.857" y2="11.5081" gradientUnits="userSpaceOnUse">
<stop stop-color="#409EC3"/>
<stop offset="0.62" stop-color="#2061BD"/>
</linearGradient>
<linearGradient id="paint12_linear" x1="25.668" y1="14.9655" x2="22.6111" y2="2.99136" gradientUnits="userSpaceOnUse">
<stop stop-color="#14B2DA"/>
<stop offset="0.4028" stop-color="#297CCC"/>
<stop offset="0.5077" stop-color="#256FC5"/>
<stop offset="0.6492" stop-color="#2164BF"/>
<stop offset="0.8162" stop-color="#2061BD"/>
<stop offset="0.9835" stop-color="#2061BD"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M1 13L2 14L6.60951 9.39049L9.42104 6.57896L10.7224 5.27756L12.8435 3.15654L15 1L14 0L11.4338 2.56619C10.3363 2.19528 9.1768 2 8 2C4.99776 2 2.10791 3.271 0.0743047 5.48656C-0.0273754 5.59626 -0.0246996 5.76752 0.0823321 5.87455L1.67175 7.4637C1.78146 7.57341 1.95806 7.57341 2.06777 7.4637C2.0865 7.44524 2.09988 7.42383 2.11058 7.39975C3.61705 5.74584 5.76036 4.7962 8 4.7962C8.37819 4.7962 8.75363 4.82329 9.12372 4.87628L7.56734 6.43266C5.90389 6.54608 4.35089 7.31026 3.24238 8.56633L3.2354 8.57044C3.22366 8.5773 3.21069 8.58487 3.19957 8.59577C3.08986 8.70547 3.08986 8.88208 3.19957 8.99152L4.10403 9.89597L1 13ZM7.99994 9.11969C7.9593 9.11969 7.91877 9.12034 7.87836 9.12164L10.1916 6.80845C11.1689 7.16857 12.052 7.76806 12.7548 8.56633C12.7709 8.57436 12.7869 8.58265 12.8003 8.59577C12.91 8.70547 12.91 8.88208 12.8003 8.99152L11.2724 10.5194C11.2457 10.5462 11.2162 10.5676 11.1815 10.5809C11.1467 10.5943 11.1119 10.6023 11.0744 10.6023H11.0584C11.0519 10.6023 11.0455 10.5995 11.0391 10.5966C11.0366 10.5954 11.0341 10.5943 11.0317 10.5934C11.0299 10.5927 11.0281 10.5921 11.0263 10.5916C11.0022 10.5892 10.9781 10.5836 10.9567 10.5729C10.9436 10.5685 10.9341 10.5606 10.9238 10.5521L10.9238 10.5521L10.9166 10.5462L10.9095 10.5421L10.9045 10.5395C10.8944 10.5342 10.884 10.5288 10.8764 10.5191C10.8734 10.5176 10.872 10.5153 10.8705 10.5125C10.8693 10.5105 10.868 10.5082 10.8657 10.5057C10.8644 10.5033 10.8625 10.502 10.8606 10.5007C10.8585 10.4994 10.8564 10.498 10.855 10.495C10.1647 9.62006 9.12377 9.11969 7.99994 9.11969ZM13.8894 7.39975C13.1883 6.63018 12.3493 6.01303 11.4269 5.5731L13.4895 3.51054C14.3862 4.0424 15.2091 4.70584 15.9257 5.48656C16.0274 5.59653 16.0247 5.76752 15.9177 5.87455L14.3282 7.4637C14.2747 7.51989 14.2025 7.54692 14.1302 7.54692C14.058 7.54692 13.9857 7.51989 13.9322 7.4637C13.9135 7.44524 13.9001 7.42383 13.8894 7.39975ZM7.99998 10.2626C7.17317 10.2626 6.50154 10.9371 6.50154 11.7637C6.50154 12.5903 7.17317 13.2621 7.99998 13.2621C8.8268 13.2621 9.49843 12.5905 9.49843 11.7637C9.49843 10.9369 8.8268 10.2626 7.99998 10.2626Z" fill="#D42F34"/>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,29 @@
<svg width="37" height="36" viewBox="0 0 37 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<g opacity="0.7">
<path d="M15 14H20V36H15V14Z" fill="#626262"/>
<path d="M3 18H32.1444C32.6127 18 33 17.6217 33 17.1444V8.88969C33 5.0801 29.9109 2 26.1103 2H9.53002C5.72043 2 3 5.0891 3 8.88969V18Z" fill="url(#paint0_linear)"/>
<path d="M20 18H32V9C32 5.69475 29.1238 2.82001 25.8185 2.82001C22.5133 2.82001 20 5.69475 20 9V18Z" fill="url(#paint1_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.78225 11.2845L4.78225 16.4365C4.78225 16.7247 4.54809 16.9589 4.2599 16.9589L0.522355 16.9589C0.234159 16.9589 -3.19682e-07 16.7247 -3.07084e-07 16.4365L-2.28536e-08 9.93408C-1.02561e-08 9.64589 0.234159 9.41173 0.522355 9.41173L2.19946 9.41173L2.19946 9.41125L15.0401 9.41126C15.5534 9.41126 15.9767 9.83454 15.9767 10.3479C15.9767 10.8702 15.5534 11.2845 15.0401 11.2845L4.78225 11.2845Z" fill="url(#paint2_linear)"/>
<path d="M20 17.9866H21.1618V18.509L31.9653 18.5823L34.9463 20.7258C34.9463 20.7258 35.7748 21.2165 36 22V23C36 23.3873 35.8379 23.8196 35.3966 24.2428C33.9916 25.4317 28.6562 25.8637 24.8646 23.0087C21.0821 20.1538 20 19 20 19V17.9866Z" fill="url(#paint3_linear)"/>
<path d="M31.9997 18L34.9808 20.1435C34.9808 20.1435 37.0161 21.6385 35.4311 23.1695C34.0261 24.3583 28.6556 24.8503 24.864 21.9954C21.0724 19.1405 20.0001 18 20.0001 18H31.9997Z" fill="#E1E1E1"/>
<path d="M14.8849 10.8636C14.6411 10.8636 14.4436 10.666 14.4436 10.4223C14.4436 10.1785 14.6411 9.98096 14.8849 9.98096C15.1286 9.98096 15.3262 10.1785 15.3262 10.4223C15.3262 10.666 15.1286 10.8636 14.8849 10.8636Z" fill="white"/>
</g>
<defs>
<linearGradient id="paint0_linear" x1="17.3522" y1="5.73755" x2="17.3522" y2="18.094" gradientUnits="userSpaceOnUse">
<stop stop-color="#E1E1E1"/>
<stop offset="1" stop-color="#BEBEBE"/>
</linearGradient>
<linearGradient id="paint1_linear" x1="23.5" y1="5.5" x2="32" y2="25.5" gradientUnits="userSpaceOnUse">
<stop stop-color="#1F1F1F"/>
<stop offset="1" stop-color="#6D6D6D" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear" x1="0.80402" y1="6.9834" x2="6.99556" y2="16.0441" gradientUnits="userSpaceOnUse">
<stop stop-color="#DFDFDF"/>
<stop offset="0.96875" stop-color="#9E9E9E"/>
</linearGradient>
<linearGradient id="paint3_linear" x1="32.1687" y1="18.4701" x2="24.2916" y2="24.1519" gradientUnits="userSpaceOnUse">
<stop stop-color="#E9E9E9"/>
<stop offset="1" stop-color="#A5A5A5"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M15 8H9V2H8V8H2V9H8V15H9V9H15V8Z" fill="#17181C"/>
</svg>

After

Width:  |  Height:  |  Size: 203 B

View File

@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-question-circle">
<g id="icon">
<path d="M8.69 3.92C7.89347 3.70879 7.04403 3.87868 6.39 4.38C5.7528 4.88127 5.38356 5.64929 5.39 6.46H6.39C6.3828 5.95878 6.60804 5.48247 7 5.17C7.40675 4.8584 7.93577 4.75482 8.43 4.89C8.97906 5.03325 9.41216 5.45496 9.57 6C9.71599 6.49287 9.6194 7.02578 9.30972 7.43606C9.00003 7.84634 8.51401 8.0853 8 8.08H7.5V10H8.5V9C9.70091 8.76754 10.583 7.73782 10.6284 6.51546C10.6738 5.29309 9.87038 4.20082 8.69 3.88V3.92Z" fill="#17181C"/>
<path d="M8.5 11V12H7.5V11H8.5Z" fill="#17181C"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15C11.866 15 15 11.866 15 8C15 6.14348 14.2625 4.36301 12.9497 3.05025C11.637 1.7375 9.85652 1 8 1ZM8 14C4.68629 14 2 11.3137 2 8C2 4.68629 4.68629 2 8 2C11.3137 2 14 4.68629 14 8C14 9.5913 13.3679 11.1174 12.2426 12.2426C11.1174 13.3679 9.5913 14 8 14Z" fill="#17181C"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1014 B

View File

@ -0,0 +1,10 @@
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="32" cy="32" r="30" fill="url(#paint0_linear)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M45.6652 22.2252C46.6453 23.1977 46.6514 24.7806 45.6789 25.7607L29.8028 41.7606C29.3334 42.2337 28.6946 42.4997 28.0282 42.4997C27.3618 42.4998 26.723 42.2337 26.2536 41.7606L19.1297 34.5735C18.1572 33.5935 18.1633 32.0106 19.1434 31.038C20.1235 30.0655 21.7064 30.0717 22.6789 31.0517L28.0282 36.4504L42.1297 22.2389C43.1022 21.2588 44.6851 21.2527 45.6652 22.2252Z" fill="white"/>
<defs>
<linearGradient id="paint0_linear" x1="32" y1="2" x2="32" y2="62" gradientUnits="userSpaceOnUse">
<stop stop-color="#5BCB6A"/>
<stop offset="1" stop-color="#1CB78F"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 796 B

View File

@ -0,0 +1,9 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="ic-three-dots-vertical">
<g id="icon">
<path d="M7 2C7 1.44772 7.44772 1 8 1C8.55228 1 9 1.44772 9 2C9 2.55228 8.55228 3 8 3C7.44772 3 7 2.55228 7 2Z" fill="#17181C"/>
<path d="M7 14C7 13.4477 7.44772 13 8 13C8.55228 13 9 13.4477 9 14C9 14.5523 8.55228 15 8 15C7.44772 15 7 14.5523 7 14Z" fill="#17181C"/>
<path d="M8 7C7.44772 7 7 7.44772 7 8C7 8.55228 7.44772 9 8 9C8.55228 9 9 8.55228 9 8C9 7.44772 8.55228 7 8 7Z" fill="#17181C"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 555 B

View File

@ -0,0 +1,5 @@
<svg width="14" height="13" viewBox="0 0 14 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5 5H6V10H5V5Z" fill="#262A33"/>
<path d="M8 5H9V10H8V5Z" fill="#262A33"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 2H14V3H12V13H2V3H0V2H4V0H10V2ZM9 1H5V2H9V1ZM11 12H3V3H11V12Z" fill="#262A33"/>
</svg>

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -0,0 +1,63 @@
<svg width="164" height="32" viewBox="0 0 164 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.91309 8.64043C5.91309 8.2476 6.37094 8.03268 6.67315 8.28365L14.6667 14.922C15.4397 15.5639 16.5604 15.5639 17.3334 14.922L25.3269 8.28366C25.6291 8.03268 26.087 8.2476 26.087 8.64043V22.2612C26.087 23.4137 25.1526 24.3481 24 24.3481H8.00004C6.84745 24.3481 5.91309 23.4137 5.91309 22.2612V8.64043Z" fill="#6D4AFF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.7236 13.7655L18.7247 13.7664L14.699 17.3224C14.0133 17.9282 12.9882 17.9435 12.2847 17.3584L5.91309 12.0594V8.63848C5.91309 8.24565 6.37094 8.03073 6.67315 8.2817L14.6667 14.9201C15.4397 15.562 16.5604 15.562 17.3334 14.9201L18.7236 13.7655Z" fill="url(#paint0_linear_2302_47705)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.7971 11.2151V24.3481H24C25.1526 24.3481 26.087 23.4137 26.087 22.2611V8.64045C26.087 8.24762 25.6291 8.03266 25.3269 8.28368L21.7971 11.2151Z" fill="url(#paint1_linear_2302_47705)"/>
<path d="M52.4445 8.11523C51.356 8.11523 50.4735 8.99769 50.4735 10.0862V21.9123C50.4735 23.0009 51.356 23.8834 52.4445 23.8834H62.5186V19.0653C62.5186 17.8558 63.4991 16.8753 64.7086 16.8753H69.5266V10.0862C69.5266 8.99769 68.6442 8.11523 67.5556 8.11523H52.4445Z" fill="url(#paint2_linear_2302_47705)"/>
<path d="M52.4445 8.11523C51.356 8.11523 50.4735 8.99769 50.4735 10.0862V21.9123C50.4735 23.0009 51.356 23.8834 52.4445 23.8834H62.5186V19.0653C62.5186 17.8558 63.4991 16.8753 64.7086 16.8753H69.5266V10.0862C69.5266 8.99769 68.6442 8.11523 67.5556 8.11523H52.4445Z" fill="url(#paint3_radial_2302_47705)"/>
<path d="M63.0661 17.6175C62.7253 18.0035 62.5185 18.5107 62.5185 19.0661V23.8842H59.124V22.9671C59.124 22.4986 59.2909 22.0454 59.5948 21.6887L63.0661 17.6152L63.0661 17.6175Z" fill="#BFD8FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M65.4751 8.11523H52.4445C51.356 8.11523 50.4735 8.99769 50.4735 10.0862V10.4695H63.9421C64.7888 10.4695 65.4751 11.1559 65.4751 12.0025V16.8753H69.5266V10.0862C69.5266 8.99769 68.6442 8.11523 67.5556 8.11523H65.6941H65.4751Z" fill="url(#paint4_linear_2302_47705)"/>
<path d="M64.4348 21.3822H65.1279C65.1566 21.5028 65.2266 21.6096 65.3258 21.6839C65.4249 21.7583 65.547 21.7956 65.6708 21.7894C65.984 21.7894 66.1902 21.6165 66.1902 21.364C66.1902 21.1115 65.9749 20.9705 65.5487 20.9705H65.2727V20.4002H65.5116C65.9233 20.4002 66.0917 20.2486 66.0917 20.0211C66.0917 19.7936 65.9089 19.6374 65.6564 19.6374C65.5432 19.6307 65.4319 19.6684 65.3461 19.7427C65.2604 19.8169 65.2071 19.9218 65.1976 20.0348H64.5334C64.5569 19.6374 64.8701 19.0664 65.6564 19.0664C66.2881 19.0664 66.7233 19.4266 66.7233 19.9316C66.7241 20.094 66.673 20.2524 66.5775 20.3837C66.4821 20.515 66.3472 20.6125 66.1925 20.6619V20.6709C66.3729 20.7038 66.5354 20.8003 66.6505 20.943C66.7657 21.0857 66.8257 21.265 66.8196 21.4482C66.8196 21.9957 66.3184 22.3581 65.6685 22.3581C64.9777 22.3604 64.4864 21.9676 64.4348 21.3822Z" fill="#6D4AFF"/>
<path d="M68.0302 19.1191H68.5352V22.31H67.8845V19.863L67.2574 20.2885V19.6431L68.0302 19.1191Z" fill="#6D4AFF"/>
<path d="M93.9131 22.261V9.7393C93.9131 8.58671 94.8474 7.65234 96 7.65234H99.9139C100.309 7.65234 100.693 7.77837 101.012 8.01208L102.322 8.97377C102.64 9.20748 103.025 9.3335 103.419 9.3335H112C113.153 9.3335 114.087 10.2679 114.087 11.4205V22.261C114.087 23.4136 113.153 24.348 112 24.348H96C94.8474 24.348 93.9131 23.4136 93.9131 22.261Z" fill="url(#paint5_linear_2302_47705)"/>
<path d="M93.9131 22.261V9.7393C93.9131 8.58671 94.8474 7.65234 96 7.65234H99.9139C100.309 7.65234 100.693 7.77837 101.012 8.01208L102.322 8.97377C102.64 9.20748 103.025 9.3335 103.419 9.3335H112C113.153 9.3335 114.087 10.2679 114.087 11.4205V22.261C114.087 23.4136 113.153 24.348 112 24.348H96C94.8474 24.348 93.9131 23.4136 93.9131 22.261Z" fill="url(#paint6_radial_2302_47705)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M103.419 9.3335H104.754H109.913H112C113.153 9.3335 114.087 10.2678 114.087 11.4205V22.261C114.087 23.4137 113.153 24.348 112 24.348H109.797V13.4588C109.797 12.5587 109.065 11.8305 108.165 11.8357L100.263 11.8812C99.9223 11.8832 99.5899 11.7781 99.3126 11.5809L97.5529 10.3295C97.2781 10.1341 96.9493 10.0292 96.6122 10.0292H93.9131V9.7393C93.9131 8.58667 94.8474 7.65234 96 7.65234H99.9139C100.309 7.65234 100.693 7.77836 101.012 8.01206L102.322 8.97379C102.64 9.20748 103.025 9.3335 103.419 9.3335Z" fill="url(#paint7_linear_2302_47705)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M146.124 24.3322C146.883 25.7082 148.834 25.7827 149.696 24.4687L157.736 12.2167C158.588 10.9174 157.773 9.17537 156.229 8.99833L140.426 7.18626C138.741 6.99307 137.542 8.78343 138.361 10.2681L146.124 24.3322Z" fill="url(#paint8_linear_2302_47705)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M146.124 24.3322C146.883 25.7082 148.834 25.7827 149.696 24.4687L157.736 12.2167C158.588 10.9174 157.773 9.17537 156.229 8.99833L140.426 7.18626C138.741 6.99307 137.542 8.78343 138.361 10.2681L146.124 24.3322Z" fill="url(#paint9_linear_2302_47705)"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M147.29 21.9765L146.577 23.049C146.287 23.4836 145.641 23.4568 145.388 22.9999L146.124 24.3324C146.255 24.57 146.422 24.7688 146.612 24.9283L146.612 24.9282C147.524 25.6925 148.982 25.5558 149.696 24.4687L157.736 12.2167C158.588 10.9174 157.772 9.17539 156.228 8.99836L140.426 7.18626C138.741 6.9931 137.542 8.78346 138.361 10.2681L138.424 10.3811L151.874 11.9374C152.731 12.0366 153.184 13.0043 152.71 13.7257L147.29 21.9765Z" fill="url(#paint10_linear_2302_47705)"/>
<defs>
<linearGradient id="paint0_linear_2302_47705" x1="21.1305" y1="16.0853" x2="16.5321" y2="-3.54607" gradientUnits="userSpaceOnUse">
<stop stop-color="#E2DBFF"/>
<stop offset="1" stop-color="#6D4AFF"/>
</linearGradient>
<linearGradient id="paint1_linear_2302_47705" x1="32.2319" y1="34.9858" x2="16.5393" y2="1.46296" gradientUnits="userSpaceOnUse">
<stop offset="0.271019" stop-color="#E2DBFF"/>
<stop offset="1" stop-color="#6D4AFF"/>
</linearGradient>
<linearGradient id="paint2_linear_2302_47705" x1="60.0001" y1="1.81548" x2="61.298" y2="20.2516" gradientUnits="userSpaceOnUse">
<stop offset="0.988738" stop-color="#6D4AFF"/>
</linearGradient>
<radialGradient id="paint3_radial_2302_47705" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(63.4494 0.0669251) rotate(110.421) scale(27.2247 36.2787)">
<stop offset="0.556057" stop-color="#54B7FF" stop-opacity="0"/>
<stop offset="0.994421" stop-color="#54B7FF"/>
</radialGradient>
<linearGradient id="paint4_linear_2302_47705" x1="69.5266" y1="16.6837" x2="57.6142" y2="0.25498" gradientUnits="userSpaceOnUse">
<stop stop-color="#C8E8FF"/>
<stop offset="0.307532" stop-color="#BDAEFF"/>
<stop offset="1" stop-color="#6D4AFF"/>
</linearGradient>
<linearGradient id="paint5_linear_2302_47705" x1="104.015" y1="-0.798116" x2="105.578" y2="21.2045" gradientUnits="userSpaceOnUse">
<stop offset="0.988738" stop-color="#6D4AFF"/>
</linearGradient>
<radialGradient id="paint6_radial_2302_47705" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(110.469 0.333684) rotate(116.076) scale(29.0915 45.26)">
<stop offset="0.556057" stop-color="#FF62C0" stop-opacity="0"/>
<stop offset="0.994421" stop-color="#FF62C0"/>
</radialGradient>
<linearGradient id="paint7_linear_2302_47705" x1="87.1305" y1="4.86974" x2="112.348" y2="32.5799" gradientUnits="userSpaceOnUse">
<stop stop-color="#6D4AFF"/>
<stop offset="0.35934" stop-color="#AE8CFF"/>
<stop offset="1" stop-color="#F8CCFF"/>
</linearGradient>
<linearGradient id="paint8_linear_2302_47705" x1="149.077" y1="-3.88232" x2="149.757" y2="19.2346" gradientUnits="userSpaceOnUse">
<stop offset="0.988738" stop-color="#6D4AFF"/>
</linearGradient>
<linearGradient id="paint9_linear_2302_47705" x1="149.21" y1="-0.682113" x2="143.559" y2="23.6447" gradientUnits="userSpaceOnUse">
<stop offset="0.47989" stop-color="#24ECC6" stop-opacity="0"/>
<stop offset="0.994421" stop-color="#24ECC6"/>
</linearGradient>
<linearGradient id="paint10_linear_2302_47705" x1="151.078" y1="26.3101" x2="138.551" y2="4.88666" gradientUnits="userSpaceOnUse">
<stop offset="0.0660125" stop-color="#ABFFEF"/>
<stop offset="0.44988" stop-color="#CAC9FF"/>
<stop offset="1" stop-color="#6D4AFF"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 937 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@ -0,0 +1,331 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="265"
height="148"
viewBox="0 0 265 148"
fill="none"
version="1.1"
id="svg110"
sodipodi:docname="img-welcome-dark.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
inkscape:export-xdpi="400"
inkscape:export-ydpi="400"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview112"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
inkscape:snap-global="true"
inkscape:snap-page="true"
inkscape:zoom="0.69351284"
inkscape:cx="93.004767"
inkscape:cy="115.35475"
inkscape:window-width="1916"
inkscape:window-height="1041"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="svg110" />
<rect
style="fill:#1c1b24;fill-opacity:1;stroke:none;stroke-width:27.9987;stroke-linecap:round;stroke-linejoin:round"
id="rect951"
width="265"
height="148"
x="0"
y="0"
ry="0"
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
inkscape:export-xdpi="400"
inkscape:export-ydpi="400" />
<path
d="M221.171 147.001H44.8555C43.1441 147.001 41.8047 145.661 41.8047 143.95V142.238C41.8047 140.527 43.1441 139.188 44.8555 139.188H221.171C222.882 139.188 224.221 140.527 224.221 142.238V143.95C224.221 145.661 222.808 147.001 221.171 147.001Z"
fill="#B0D4E5"
id="path2" />
<path
d="M141.83 143.503H123.376C120.995 143.503 119.135 141.568 119.135 139.262H146.071C146.071 141.568 144.211 143.503 141.83 143.503Z"
fill="#DAF3FF"
id="path4" />
<path
d="M206.034 139.187H58.501V53.9292C58.501 49.0182 62.5191 45 67.4302 45H197.104C202.016 45 206.034 49.0182 206.034 53.9292V139.187Z"
fill="url(#paint0_radial_8674_44242)"
id="path6" />
<path
d="M199.115 139.187H66.167V55.3434C66.167 54.1529 67.1343 53.1855 68.3249 53.1855H196.883C198.074 53.1855 199.041 54.1529 199.041 55.3434V139.187H199.115Z"
fill="url(#paint1_linear_8674_44242)"
id="path8" />
<path
d="M190.805 131.286H76.1797V62.5185C76.1797 61.5234 77.028 60.7148 78.0721 60.7148H188.847C189.891 60.7148 190.739 61.5234 190.739 62.5185V131.286H190.805Z"
fill="url(#paint2_radial_8674_44242)"
id="path10" />
<path
d="M84.1558 70.7744C85.3064 70.7744 86.2392 69.8416 86.2392 68.6909C86.2392 67.5402 85.3064 66.6074 84.1558 66.6074C83.0051 66.6074 82.0723 67.5402 82.0723 68.6909C82.0723 69.8416 83.0051 70.7744 84.1558 70.7744Z"
fill="#B0D4E5"
id="path12" />
<path
d="M90.6304 70.7744C91.781 70.7744 92.7139 69.8416 92.7139 68.6909C92.7139 67.5402 91.781 66.6074 90.6304 66.6074C89.4797 66.6074 88.5469 67.5402 88.5469 68.6909C88.5469 69.8416 89.4797 70.7744 90.6304 70.7744Z"
fill="#B0D4E5"
id="path14" />
<path
d="M97.105 70.7744C98.2557 70.7744 99.1885 69.8416 99.1885 68.6909C99.1885 67.5402 98.2557 66.6074 97.105 66.6074C95.9543 66.6074 95.0215 67.5402 95.0215 68.6909C95.0215 69.8416 95.9543 70.7744 97.105 70.7744Z"
fill="#B0D4E5"
id="path16" />
<path
d="M242.633 76.3538H180.924C178.747 76.3538 177 74.5616 177 72.3891V33.9646C177 31.7922 178.774 30 180.924 30H242.606C244.783 30 246.53 31.7922 246.53 33.9646V72.3891C246.557 74.5887 244.783 76.3538 242.633 76.3538Z"
fill="url(#paint3_linear_8674_44242)"
id="path18" />
<path
d="M209.232 56.6899C210.689 57.8921 212.822 57.8921 214.28 56.6899L245.431 31.0419C244.729 30.4007 243.784 30 242.758 30H180.78C179.754 30 178.809 30.4007 178.107 31.0419L209.232 56.6899Z"
fill="url(#paint4_linear_8674_44242)"
id="path20" />
<path
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
fill="#6D4AFF"
id="path22" />
<path
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
fill="url(#paint5_linear_8674_44242)"
id="path24" />
<g
filter="url(#filter0_i_8674_44242)"
id="g28">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M137.057 101.095C136.188 101.799 133.926 102.785 131.838 101.095C129.751 99.4048 125.418 95.6687 123.513 94.0119H123.524L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814L145.351 94.0119H145.383V117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V95.2946L127.117 105.444C127.986 106.272 130.273 107.432 132.46 105.444C134.647 103.456 136.436 101.716 137.057 101.095Z"
fill="url(#paint6_radial_8674_44242)"
id="path26" />
</g>
<circle
cx="239.278"
cy="30.2778"
r="15.2778"
fill="url(#paint7_linear_8674_44242)"
id="circle30" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M245.702 26.668C246.113 27.0766 246.116 27.7417 245.707 28.1534L238.128 35.7923C237.93 35.9911 237.662 36.1028 237.382 36.1028C237.102 36.1028 236.834 35.9911 236.636 35.7923L232.758 31.8835C232.349 31.4718 232.352 30.8067 232.764 30.3981C233.175 29.9895 233.84 29.9921 234.249 30.4039L237.382 33.5613L244.216 26.6738C244.625 26.262 245.29 26.2595 245.702 26.668Z"
fill="white"
id="path32" />
<path
d="M0.878906 69.6212C0.878906 56.0233 11.9022 45 25.5001 45V45C39.0981 45 50.1214 56.0233 50.1214 69.6212V94.2425H25.5002C11.9022 94.2425 0.878906 83.2192 0.878906 69.6212V69.6212Z"
fill="url(#paint8_linear_8674_44242)"
id="path34" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M31.4987 62.28V64.8004H33.9547C34.7948 64.8004 35.5056 65.4789 35.5056 66.3513V79.5019C35.5056 80.342 34.8271 81.0529 33.9547 81.0529H17.3791C16.539 81.0529 15.8281 80.3743 15.8281 79.5019V66.3513C15.8281 65.5112 16.5067 64.8004 17.3791 64.8004H19.802V62.28C19.802 59.0488 22.4192 56.4316 25.6504 56.4316C28.8815 56.4316 31.4987 59.0488 31.4987 62.28ZM29.0361 62.28V64.8004H22.3292V62.28C22.3292 59.9536 24.1059 58.9265 25.6827 58.9265C27.2594 58.9265 29.0361 59.9536 29.0361 62.28ZM25.9832 69.195C27.1141 69.195 28.0188 70.0997 28.0188 71.2306C28.0188 72.006 27.5988 72.6846 26.9526 73.0077L27.7927 76.6265H24.1738L25.0139 73.0077C24.3677 72.6523 23.9476 72.006 23.9476 71.2306C23.9476 70.0997 24.8523 69.195 25.9832 69.195Z"
fill="white"
id="path36" />
<defs
id="defs108">
<filter
id="filter0_i_8674_44242"
x="114.72"
y="75"
width="38.7675"
height="43.6537"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB">
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood38" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
id="feBlend40" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
id="feColorMatrix42" />
<feOffset
dx="-0.592742"
dy="1.65375"
id="feOffset44" />
<feGaussianBlur
stdDeviation="4.44556"
id="feGaussianBlur46" />
<feComposite
in2="hardAlpha"
operator="arithmetic"
k2="-1"
k3="1"
id="feComposite48" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"
id="feColorMatrix50" />
<feBlend
mode="normal"
in2="shape"
result="effect1_innerShadow_8674_44242"
id="feBlend52" />
</filter>
<radialGradient
id="paint0_radial_8674_44242"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(202 51.5) rotate(145.641) scale(59.357 92.9759)">
<stop
stop-color="#292842"
id="stop55" />
<stop
offset="1"
stop-color="#38385F"
id="stop57" />
</radialGradient>
<linearGradient
id="paint1_linear_8674_44242"
x1="63.7079"
y1="144.988"
x2="207.623"
y2="58.7515"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#35168C"
id="stop60" />
<stop
offset="0.317708"
stop-color="#FF5454"
id="stop62" />
<stop
offset="0.46875"
stop-color="#FFDD64"
id="stop64" />
<stop
offset="0.677083"
stop-color="#BCE6FF"
id="stop66" />
<stop
offset="0.911458"
stop-color="#6983EF"
id="stop68" />
<stop
offset="1"
stop-color="#2395FF"
id="stop70" />
</linearGradient>
<radialGradient
id="paint2_radial_8674_44242"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(188.5 63.5) rotate(135) scale(33.9411 55.1286)">
<stop
stop-color="#DDDBE3"
id="stop73" />
<stop
offset="1"
stop-color="white"
id="stop75" />
</radialGradient>
<linearGradient
id="paint3_linear_8674_44242"
x1="211.765"
y1="30"
x2="211.765"
y2="66.4208"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#C1DEF8"
id="stop78" />
<stop
offset="1"
stop-color="#ECFAFF"
id="stop80" />
</linearGradient>
<linearGradient
id="paint4_linear_8674_44242"
x1="211.769"
y1="18.4116"
x2="211.769"
y2="50.4177"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#DEEBF7"
id="stop83" />
<stop
offset="1"
stop-color="white"
id="stop85" />
</linearGradient>
<linearGradient
id="paint5_linear_8674_44242"
x1="116.679"
y1="121.846"
x2="122.395"
y2="105.692"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#28B0E8"
id="stop88" />
<stop
offset="1"
stop-color="#C5B7FF"
stop-opacity="0"
id="stop90" />
</linearGradient>
<radialGradient
id="paint6_radial_8674_44242"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(151.237 120.479) rotate(-138.034) scale(48.3148 39.5031)">
<stop
stop-color="#E2DBFF"
id="stop93" />
<stop
offset="1"
stop-color="#6D4AFF"
id="stop95" />
</radialGradient>
<linearGradient
id="paint7_linear_8674_44242"
x1="240.861"
y1="12.772"
x2="241.004"
y2="45.5557"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#2AF091"
id="stop98" />
<stop
offset="1"
stop-color="#00C5A1"
id="stop100" />
</linearGradient>
<linearGradient
id="paint8_linear_8674_44242"
x1="41.1252"
y1="56.3637"
x2="5.14027"
y2="101.345"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#FFD66C"
id="stop103" />
<stop
offset="1"
stop-color="#FF8E4F"
id="stop105" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View File

@ -0,0 +1,331 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="265"
height="148"
viewBox="0 0 265 148"
fill="none"
version="1.1"
id="svg110"
sodipodi:docname="img-welcome.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
inkscape:export-xdpi="400"
inkscape:export-ydpi="400"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview112"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
inkscape:snap-global="true"
inkscape:snap-page="true"
inkscape:zoom="0.69351284"
inkscape:cx="93.004767"
inkscape:cy="115.35475"
inkscape:window-width="1916"
inkscape:window-height="1041"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="svg110" />
<rect
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:27.9987;stroke-linecap:round;stroke-linejoin:round"
id="rect951"
width="265"
height="148"
x="0"
y="0"
ry="0"
inkscape:export-filename="/home/dev/gopath/src/github.com/ProtonMail/proton-bridge/internal/frontend/qml/icons/img-welcome.png"
inkscape:export-xdpi="400"
inkscape:export-ydpi="400" />
<path
d="M221.171 147.001H44.8555C43.1441 147.001 41.8047 145.661 41.8047 143.95V142.238C41.8047 140.527 43.1441 139.188 44.8555 139.188H221.171C222.882 139.188 224.221 140.527 224.221 142.238V143.95C224.221 145.661 222.808 147.001 221.171 147.001Z"
fill="#B0D4E5"
id="path2" />
<path
d="M141.83 143.503H123.376C120.995 143.503 119.135 141.568 119.135 139.262H146.071C146.071 141.568 144.211 143.503 141.83 143.503Z"
fill="#DAF3FF"
id="path4" />
<path
d="M206.034 139.187H58.501V53.9292C58.501 49.0182 62.5191 45 67.4302 45H197.104C202.016 45 206.034 49.0182 206.034 53.9292V139.187Z"
fill="url(#paint0_radial_8674_44242)"
id="path6" />
<path
d="M199.115 139.187H66.167V55.3434C66.167 54.1529 67.1343 53.1855 68.3249 53.1855H196.883C198.074 53.1855 199.041 54.1529 199.041 55.3434V139.187H199.115Z"
fill="url(#paint1_linear_8674_44242)"
id="path8" />
<path
d="M190.805 131.286H76.1797V62.5185C76.1797 61.5234 77.028 60.7148 78.0721 60.7148H188.847C189.891 60.7148 190.739 61.5234 190.739 62.5185V131.286H190.805Z"
fill="url(#paint2_radial_8674_44242)"
id="path10" />
<path
d="M84.1558 70.7744C85.3064 70.7744 86.2392 69.8416 86.2392 68.6909C86.2392 67.5402 85.3064 66.6074 84.1558 66.6074C83.0051 66.6074 82.0723 67.5402 82.0723 68.6909C82.0723 69.8416 83.0051 70.7744 84.1558 70.7744Z"
fill="#B0D4E5"
id="path12" />
<path
d="M90.6304 70.7744C91.781 70.7744 92.7139 69.8416 92.7139 68.6909C92.7139 67.5402 91.781 66.6074 90.6304 66.6074C89.4797 66.6074 88.5469 67.5402 88.5469 68.6909C88.5469 69.8416 89.4797 70.7744 90.6304 70.7744Z"
fill="#B0D4E5"
id="path14" />
<path
d="M97.105 70.7744C98.2557 70.7744 99.1885 69.8416 99.1885 68.6909C99.1885 67.5402 98.2557 66.6074 97.105 66.6074C95.9543 66.6074 95.0215 67.5402 95.0215 68.6909C95.0215 69.8416 95.9543 70.7744 97.105 70.7744Z"
fill="#B0D4E5"
id="path16" />
<path
d="M242.633 76.3538H180.924C178.747 76.3538 177 74.5616 177 72.3891V33.9646C177 31.7922 178.774 30 180.924 30H242.606C244.783 30 246.53 31.7922 246.53 33.9646V72.3891C246.557 74.5887 244.783 76.3538 242.633 76.3538Z"
fill="url(#paint3_linear_8674_44242)"
id="path18" />
<path
d="M209.232 56.6899C210.689 57.8921 212.822 57.8921 214.28 56.6899L245.431 31.0419C244.729 30.4007 243.784 30 242.758 30H180.78C179.754 30 178.809 30.4007 178.107 31.0419L209.232 56.6899Z"
fill="url(#paint4_linear_8674_44242)"
id="path20" />
<path
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
fill="#6D4AFF"
id="path22" />
<path
d="M134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V113.301C115.312 115.344 116.968 117 119.011 117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75ZM145.387 93.9814L137.044 101.01C135.538 102.279 133.336 102.279 131.83 101.01L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814Z"
fill="url(#paint5_linear_8674_44242)"
id="path24" />
<g
filter="url(#filter0_i_8674_44242)"
id="g28">
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M137.057 101.095C136.188 101.799 133.926 102.785 131.838 101.095C129.751 99.4048 125.418 95.6687 123.513 94.0119H123.524L123.487 93.9814C123.487 88.0172 128.323 83.1817 134.287 83.1817H134.587C140.551 83.1817 145.387 88.0172 145.387 93.9814L145.351 94.0119H145.383V117H150.184C152.008 117 153.487 115.52 153.487 113.696V94.0874C153.487 83.5465 144.942 75 134.4 75C123.858 75 115.312 83.5451 115.312 94.0874V95.2946L127.117 105.444C127.986 106.272 130.273 107.432 132.46 105.444C134.647 103.456 136.436 101.716 137.057 101.095Z"
fill="url(#paint6_radial_8674_44242)"
id="path26" />
</g>
<circle
cx="239.278"
cy="30.2778"
r="15.2778"
fill="url(#paint7_linear_8674_44242)"
id="circle30" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M245.702 26.668C246.113 27.0766 246.116 27.7417 245.707 28.1534L238.128 35.7923C237.93 35.9911 237.662 36.1028 237.382 36.1028C237.102 36.1028 236.834 35.9911 236.636 35.7923L232.758 31.8835C232.349 31.4718 232.352 30.8067 232.764 30.3981C233.175 29.9895 233.84 29.9921 234.249 30.4039L237.382 33.5613L244.216 26.6738C244.625 26.262 245.29 26.2595 245.702 26.668Z"
fill="white"
id="path32" />
<path
d="M0.878906 69.6212C0.878906 56.0233 11.9022 45 25.5001 45V45C39.0981 45 50.1214 56.0233 50.1214 69.6212V94.2425H25.5002C11.9022 94.2425 0.878906 83.2192 0.878906 69.6212V69.6212Z"
fill="url(#paint8_linear_8674_44242)"
id="path34" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M31.4987 62.28V64.8004H33.9547C34.7948 64.8004 35.5056 65.4789 35.5056 66.3513V79.5019C35.5056 80.342 34.8271 81.0529 33.9547 81.0529H17.3791C16.539 81.0529 15.8281 80.3743 15.8281 79.5019V66.3513C15.8281 65.5112 16.5067 64.8004 17.3791 64.8004H19.802V62.28C19.802 59.0488 22.4192 56.4316 25.6504 56.4316C28.8815 56.4316 31.4987 59.0488 31.4987 62.28ZM29.0361 62.28V64.8004H22.3292V62.28C22.3292 59.9536 24.1059 58.9265 25.6827 58.9265C27.2594 58.9265 29.0361 59.9536 29.0361 62.28ZM25.9832 69.195C27.1141 69.195 28.0188 70.0997 28.0188 71.2306C28.0188 72.006 27.5988 72.6846 26.9526 73.0077L27.7927 76.6265H24.1738L25.0139 73.0077C24.3677 72.6523 23.9476 72.006 23.9476 71.2306C23.9476 70.0997 24.8523 69.195 25.9832 69.195Z"
fill="white"
id="path36" />
<defs
id="defs108">
<filter
id="filter0_i_8674_44242"
x="114.72"
y="75"
width="38.7675"
height="43.6537"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB">
<feFlood
flood-opacity="0"
result="BackgroundImageFix"
id="feFlood38" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
id="feBlend40" />
<feColorMatrix
in="SourceAlpha"
type="matrix"
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha"
id="feColorMatrix42" />
<feOffset
dx="-0.592742"
dy="1.65375"
id="feOffset44" />
<feGaussianBlur
stdDeviation="4.44556"
id="feGaussianBlur46" />
<feComposite
in2="hardAlpha"
operator="arithmetic"
k2="-1"
k3="1"
id="feComposite48" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0.462745 0 0 0 0 0.337255 0 0 0 0 1 0 0 0 0.24 0"
id="feColorMatrix50" />
<feBlend
mode="normal"
in2="shape"
result="effect1_innerShadow_8674_44242"
id="feBlend52" />
</filter>
<radialGradient
id="paint0_radial_8674_44242"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(202 51.5) rotate(145.641) scale(59.357 92.9759)">
<stop
stop-color="#292842"
id="stop55" />
<stop
offset="1"
stop-color="#38385F"
id="stop57" />
</radialGradient>
<linearGradient
id="paint1_linear_8674_44242"
x1="63.7079"
y1="144.988"
x2="207.623"
y2="58.7515"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#35168C"
id="stop60" />
<stop
offset="0.317708"
stop-color="#FF5454"
id="stop62" />
<stop
offset="0.46875"
stop-color="#FFDD64"
id="stop64" />
<stop
offset="0.677083"
stop-color="#BCE6FF"
id="stop66" />
<stop
offset="0.911458"
stop-color="#6983EF"
id="stop68" />
<stop
offset="1"
stop-color="#2395FF"
id="stop70" />
</linearGradient>
<radialGradient
id="paint2_radial_8674_44242"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(188.5 63.5) rotate(135) scale(33.9411 55.1286)">
<stop
stop-color="#DDDBE3"
id="stop73" />
<stop
offset="1"
stop-color="white"
id="stop75" />
</radialGradient>
<linearGradient
id="paint3_linear_8674_44242"
x1="211.765"
y1="30"
x2="211.765"
y2="66.4208"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#C1DEF8"
id="stop78" />
<stop
offset="1"
stop-color="#ECFAFF"
id="stop80" />
</linearGradient>
<linearGradient
id="paint4_linear_8674_44242"
x1="211.769"
y1="18.4116"
x2="211.769"
y2="50.4177"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#DEEBF7"
id="stop83" />
<stop
offset="1"
stop-color="white"
id="stop85" />
</linearGradient>
<linearGradient
id="paint5_linear_8674_44242"
x1="116.679"
y1="121.846"
x2="122.395"
y2="105.692"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#28B0E8"
id="stop88" />
<stop
offset="1"
stop-color="#C5B7FF"
stop-opacity="0"
id="stop90" />
</linearGradient>
<radialGradient
id="paint6_radial_8674_44242"
cx="0"
cy="0"
r="1"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(151.237 120.479) rotate(-138.034) scale(48.3148 39.5031)">
<stop
stop-color="#E2DBFF"
id="stop93" />
<stop
offset="1"
stop-color="#6D4AFF"
id="stop95" />
</radialGradient>
<linearGradient
id="paint7_linear_8674_44242"
x1="240.861"
y1="12.772"
x2="241.004"
y2="45.5557"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#2AF091"
id="stop98" />
<stop
offset="1"
stop-color="#00C5A1"
id="stop100" />
</linearGradient>
<linearGradient
id="paint8_linear_8674_44242"
x1="41.1252"
y1="56.3637"
x2="5.14027"
y2="101.345"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#FFD66C"
id="stop103" />
<stop
offset="1"
stop-color="#FF8E4F"
id="stop105" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 12 KiB

View File

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="200"
height="25"
viewBox="0 0 200 25"
fill="none"
version="1.1"
id="svg40"
sodipodi:docname="product_logo.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview42"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
inkscape:zoom="6.36"
inkscape:cx="112.5"
inkscape:cy="22.877358"
inkscape:window-width="2556"
inkscape:window-height="690"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="svg40" />
<path
d="m 29.2,1.42023 c 0,-0.564698 0.6795,-0.873645 1.128,-0.512874 L 42.1919,10.45 c 1.1472,0.9228 2.8105,0.9228 3.9577,0 L 58.0135,0.907358 C 58.462,0.546586 59.1416,0.855533 59.1416,1.42023 V 21 c 0,1.6569 -1.3868,3 -3.0975,3 H 32.2974 C 30.5867,24 29.2,22.6569 29.2,21 Z"
fill="#6d4aff"
id="path2" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 48.2129,8.78808 0.0017,0.00131 -5.9748,5.11171 c -1.0178,0.8708 -2.5391,0.8928 -3.5832,0.0517 L 29.2,6.33549 V 1.41794 c 0,-0.564697 0.6795,-0.873644 1.128,-0.512872 L 42.1919,10.4477 c 1.1472,0.9228 2.8105,0.9228 3.9577,0 z"
fill="url(#paint0_linear_499_22895)"
id="path4" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 52.7747,5.11897 V 23.9977 h 3.2694 c 1.7107,0 3.0975,-1.3432 3.0975,-3 V 1.41796 c 0,-0.564699 -0.6796,-0.873699 -1.1281,-0.512859 z"
fill="url(#paint1_linear_499_22895)"
id="path6" />
<path
d="M 133.107,19.2709 V 4.88245 h 3.856 l 3.615,8.62775 c 0.321,0.7221 0.598,1.4619 0.829,2.2154 h 0.036 c 0.233,-0.7531 0.51,-1.4928 0.829,-2.2154 l 3.615,-8.62775 h 3.856 V 19.2709 h -2.795 V 9.63023 c -0.003,-0.31749 0.012,-0.63491 0.046,-0.95074 h -0.046 c -0.086,0.34013 -0.202,0.67219 -0.349,0.99221 l -4.006,9.4304 h -2.309 l -4.018,-9.4304 c -0.144,-0.32293 -0.268,-0.65432 -0.37,-0.99221 h -0.043 c 0.031,0.316 0.046,0.63333 0.043,0.95074 v 9.64067 z"
fill="#6d4aff"
id="path8" />
<path
d="m 159.302,9.77835 c 0.81,0.41125 1.479,1.04085 1.927,1.81265 0.48,0.8381 0.722,1.7843 0.703,2.7426 v 4.9374 h -2.447 l -0.174,-1.4809 c -0.319,0.5368 -0.787,0.9761 -1.351,1.2676 -0.605,0.3044 -1.279,0.4562 -1.961,0.4413 -0.87,0.0085 -1.725,-0.2171 -2.47,-0.6516 -0.755,-0.4435 -1.369,-1.0786 -1.777,-1.8363 -0.446,-0.834 -0.67,-1.7623 -0.651,-2.7012 -0.013,-0.924 0.231,-1.8343 0.706,-2.636 0.464,-0.7792 1.138,-1.4213 1.951,-1.85704 0.841,-0.45278 1.79,-0.68552 2.752,-0.67529 0.971,-0.01631 1.931,0.20258 2.792,0.63678 z m -0.896,6.64335 c 0.532,-0.4917 0.795,-1.1847 0.795,-2.1088 0.034,-0.7552 -0.24,-1.4931 -0.761,-2.0555 -0.249,-0.2513 -0.547,-0.4513 -0.877,-0.588 -0.331,-0.1366 -0.686,-0.207 -1.045,-0.207 -0.359,0 -0.715,0.0704 -1.045,0.207 -0.331,0.1367 -0.629,0.3367 -0.877,0.588 -0.496,0.5761 -0.768,1.3024 -0.768,2.0525 0,0.7501 0.272,1.4765 0.768,2.0526 0.245,0.2567 0.543,0.46 0.875,0.5963 0.332,0.1364 0.691,0.2027 1.051,0.1945 0.349,0.0056 0.695,-0.0562 1.018,-0.1818 0.324,-0.1256 0.618,-0.3125 0.866,-0.5498 z"
fill="#6d4aff"
id="path10" />
<path
d="m 163.439,7.29934 c -0.164,-0.14555 -0.295,-0.32321 -0.384,-0.52129 -0.088,-0.19807 -0.132,-0.41207 -0.129,-0.6279 -0.003,-0.21748 0.042,-0.43307 0.13,-0.63299 0.088,-0.19991 0.219,-0.37975 0.383,-0.52804 C 163.764,4.67587 164.204,4.5 164.663,4.5 c 0.458,0 0.898,0.17587 1.223,0.48912 0.163,0.14888 0.293,0.32891 0.381,0.52873 0.087,0.19982 0.132,0.41511 0.129,0.6323 0.003,0.21553 -0.041,0.42921 -0.129,0.62719 -0.088,0.19798 -0.218,0.37583 -0.381,0.522 -0.329,0.30624 -0.767,0.4773 -1.223,0.4773 -0.457,0 -0.895,-0.17106 -1.224,-0.4773 z m 2.603,11.97166 h -2.753 V 9.35483 h 2.753 z"
fill="#6d4aff"
id="path12" />
<path
d="m 170.8,19.2709 h -2.752 V 4.88245 h 2.752 z"
fill="#6d4aff"
id="path14" />
<path
d="M 75.9842,4.84671 H 69.1221 V 19.2441 h 2.7185 v -3.572 c 0,-0.3487 0.1431,-0.6832 0.3977,-0.9298 0.2546,-0.2467 0.6,-0.3852 0.9601,-0.3852 h 2.7858 c 1.2976,0 2.5421,-0.4993 3.4597,-1.388 0.9176,-0.8887 1.433,-2.0941 1.433,-3.35091 C 80.8814,8.99313 80.7581,8.3734 80.5142,7.79469 80.2703,7.21597 79.9106,6.6897 79.4558,6.2462 79.0011,5.8027 78.4602,5.45072 77.8644,5.21053 77.2685,4.97034 76.6295,4.8467 75.9842,4.84671 Z m 2.1406,4.7389 c -0.0025,0.59489 -0.2486,1.16459 -0.6843,1.58389 -0.4358,0.4192 -1.0256,0.6539 -1.6398,0.6523 H 71.8253 V 7.32279 h 3.9754 c 0.3048,0 0.6066,0.05814 0.8882,0.17112 0.2816,0.11297 0.5375,0.27856 0.753,0.4873 0.2155,0.20875 0.3865,0.45657 0.5031,0.72931 0.1167,0.27274 0.1767,0.56507 0.1767,0.86028 z"
fill="#1b1340"
id="path16" />
<path
d="m 81.3051,19.2443 v -5.7104 c 0,-2.331 1.4066,-4.18802 4.2169,-4.18802 0.452,-0.00632 0.9031,0.04243 1.3425,0.14513 V 11.8427 C 86.5434,11.822 86.2529,11.822 86.1397,11.822 c -1.4892,0 -2.1406,0.6575 -2.1406,1.9992 v 5.4231 z"
fill="#1b1340"
id="path18" />
<path
d="m 87.6719,14.3988 c 0,-2.867 2.2354,-5.05285 5.3453,-5.05285 3.11,0 5.3423,2.17985 5.3423,5.05285 0,2.873 -2.2354,5.0736 -5.3423,5.0736 -3.1069,0 -5.3453,-2.2066 -5.3453,-5.0736 z m 8.0272,0 c 0,-1.629 -1.1284,-2.7841 -2.6819,-2.7841 -1.5534,0 -2.6849,1.1551 -2.6849,2.7841 0,1.629 1.1284,2.7841 2.6849,2.7841 1.5565,0 2.6819,-1.1344 2.6819,-2.7841 z"
fill="#1b1340"
id="path20" />
<path
d="m 106.194,11.6353 h -2.914 v 3.6075 c 0,1.2588 0.468,1.8363 1.807,1.8363 0.129,0 0.45,0 0.853,-0.0207 v 2.1236 c -0.512,0.1452 -1.041,0.2219 -1.575,0.2281 -2.256,0 -3.791,-1.321 -3.791,-3.8149 V 11.6353 H 98.7662 V 9.56203 h 0.4496 c 0.3601,0 0.7054,-0.13855 0.9602,-0.38517 0.254,-0.24662 0.398,-0.5811 0.398,-0.92988 V 6.27441 h 2.706 v 3.27873 h 2.914 z"
fill="#1b1340"
id="path22" />
<path
d="m 107.004,14.3988 c 0,-2.867 2.236,-5.05285 5.343,-5.05285 3.107,0 5.345,2.17985 5.345,5.05285 0,2.873 -2.235,5.0736 -5.345,5.0736 -3.11,0 -5.343,-2.2066 -5.343,-5.0736 z m 8.028,0 c 0,-1.629 -1.129,-2.7841 -2.685,-2.7841 -1.557,0 -2.682,1.1551 -2.682,2.7841 0,1.629 1.128,2.7841 2.682,2.7841 1.553,0 2.685,-1.1344 2.685,-2.7841 z"
fill="#1b1340"
id="path24" />
<path
d="m 119.096,19.2443 v -5.506 c 0,-2.556 1.682,-4.39235 4.685,-4.39235 3.003,0 4.663,1.83635 4.663,4.39235 v 5.506 h -2.682 v -5.2987 c 0,-1.4246 -0.66,-2.3102 -1.981,-2.3102 -1.321,0 -1.979,0.8886 -1.979,2.3102 v 5.2987 z"
fill="#1b1340"
id="path26" />
<defs
id="defs38">
<linearGradient
id="paint0_linear_499_22895"
x1="51.785198"
y1="12.1227"
x2="45.362202"
y2="-16.188499"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#E2DBFF"
id="stop28" />
<stop
offset="1"
stop-color="#6D4AFF"
id="stop30" />
</linearGradient>
<linearGradient
id="paint1_linear_499_22895"
x1="68.261703"
y1="39.289398"
x2="46.167099"
y2="-9.4419403"
gradientUnits="userSpaceOnUse">
<stop
offset="0.271019"
stop-color="#E2DBFF"
id="stop33" />
<stop
offset="1"
stop-color="#6D4AFF"
id="stop35" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
width="200"
height="25"
viewBox="0 0 200 25"
fill="none"
version="1.1"
id="svg40"
sodipodi:docname="product_logos_dark.svg"
inkscape:version="1.1.2 (0a00cf5339, 2022-02-04, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview42"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
showgrid="false"
inkscape:zoom="6.36"
inkscape:cx="112.5"
inkscape:cy="22.877358"
inkscape:window-width="2556"
inkscape:window-height="690"
inkscape:window-x="0"
inkscape:window-y="18"
inkscape:window-maximized="1"
inkscape:current-layer="svg40" />
<path
d="m 29.2,1.42023 c 0,-0.564698 0.6795,-0.873645 1.128,-0.512874 L 42.1919,10.45 c 1.1472,0.9228 2.8105,0.9228 3.9577,0 L 58.0135,0.907358 C 58.462,0.546586 59.1416,0.855533 59.1416,1.42023 V 21 c 0,1.6569 -1.3868,3 -3.0975,3 H 32.2974 C 30.5867,24 29.2,22.6569 29.2,21 Z"
fill="#6d4aff"
id="path2" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="m 48.2129,8.78808 0.0017,0.00131 -5.9748,5.11171 c -1.0178,0.8708 -2.5391,0.8928 -3.5832,0.0517 L 29.2,6.33549 V 1.41794 c 0,-0.564697 0.6795,-0.873644 1.128,-0.512872 L 42.1919,10.4477 c 1.1472,0.9228 2.8105,0.9228 3.9577,0 z"
fill="url(#paint0_linear_499_22895)"
id="path4" />
<path
fill-rule="evenodd"
clip-rule="evenodd"
d="M 52.7747,5.11897 V 23.9977 h 3.2694 c 1.7107,0 3.0975,-1.3432 3.0975,-3 V 1.41796 c 0,-0.564699 -0.6796,-0.873699 -1.1281,-0.512859 z"
fill="url(#paint1_linear_499_22895)"
id="path6" />
<path
d="M 133.107,19.2709 V 4.88245 h 3.856 l 3.615,8.62775 c 0.321,0.7221 0.598,1.4619 0.829,2.2154 h 0.036 c 0.233,-0.7531 0.51,-1.4928 0.829,-2.2154 l 3.615,-8.62775 h 3.856 V 19.2709 h -2.795 V 9.63023 c -0.003,-0.31749 0.012,-0.63491 0.046,-0.95074 h -0.046 c -0.086,0.34013 -0.202,0.67219 -0.349,0.99221 l -4.006,9.4304 h -2.309 l -4.018,-9.4304 c -0.144,-0.32293 -0.268,-0.65432 -0.37,-0.99221 h -0.043 c 0.031,0.316 0.046,0.63333 0.043,0.95074 v 9.64067 z"
fill="#6d4aff"
id="path8" />
<path
d="m 159.302,9.77835 c 0.81,0.41125 1.479,1.04085 1.927,1.81265 0.48,0.8381 0.722,1.7843 0.703,2.7426 v 4.9374 h -2.447 l -0.174,-1.4809 c -0.319,0.5368 -0.787,0.9761 -1.351,1.2676 -0.605,0.3044 -1.279,0.4562 -1.961,0.4413 -0.87,0.0085 -1.725,-0.2171 -2.47,-0.6516 -0.755,-0.4435 -1.369,-1.0786 -1.777,-1.8363 -0.446,-0.834 -0.67,-1.7623 -0.651,-2.7012 -0.013,-0.924 0.231,-1.8343 0.706,-2.636 0.464,-0.7792 1.138,-1.4213 1.951,-1.85704 0.841,-0.45278 1.79,-0.68552 2.752,-0.67529 0.971,-0.01631 1.931,0.20258 2.792,0.63678 z m -0.896,6.64335 c 0.532,-0.4917 0.795,-1.1847 0.795,-2.1088 0.034,-0.7552 -0.24,-1.4931 -0.761,-2.0555 -0.249,-0.2513 -0.547,-0.4513 -0.877,-0.588 -0.331,-0.1366 -0.686,-0.207 -1.045,-0.207 -0.359,0 -0.715,0.0704 -1.045,0.207 -0.331,0.1367 -0.629,0.3367 -0.877,0.588 -0.496,0.5761 -0.768,1.3024 -0.768,2.0525 0,0.7501 0.272,1.4765 0.768,2.0526 0.245,0.2567 0.543,0.46 0.875,0.5963 0.332,0.1364 0.691,0.2027 1.051,0.1945 0.349,0.0056 0.695,-0.0562 1.018,-0.1818 0.324,-0.1256 0.618,-0.3125 0.866,-0.5498 z"
fill="#6d4aff"
id="path10" />
<path
d="m 163.439,7.29934 c -0.164,-0.14555 -0.295,-0.32321 -0.384,-0.52129 -0.088,-0.19807 -0.132,-0.41207 -0.129,-0.6279 -0.003,-0.21748 0.042,-0.43307 0.13,-0.63299 0.088,-0.19991 0.219,-0.37975 0.383,-0.52804 C 163.764,4.67587 164.204,4.5 164.663,4.5 c 0.458,0 0.898,0.17587 1.223,0.48912 0.163,0.14888 0.293,0.32891 0.381,0.52873 0.087,0.19982 0.132,0.41511 0.129,0.6323 0.003,0.21553 -0.041,0.42921 -0.129,0.62719 -0.088,0.19798 -0.218,0.37583 -0.381,0.522 -0.329,0.30624 -0.767,0.4773 -1.223,0.4773 -0.457,0 -0.895,-0.17106 -1.224,-0.4773 z m 2.603,11.97166 h -2.753 V 9.35483 h 2.753 z"
fill="#6d4aff"
id="path12" />
<path
d="m 170.8,19.2709 h -2.752 V 4.88245 h 2.752 z"
fill="#6d4aff"
id="path14" />
<path
d="M 75.9842,4.84671 H 69.1221 V 19.2441 h 2.7185 v -3.572 c 0,-0.3487 0.1431,-0.6832 0.3977,-0.9298 0.2546,-0.2467 0.6,-0.3852 0.9601,-0.3852 h 2.7858 c 1.2976,0 2.5421,-0.4993 3.4597,-1.388 0.9176,-0.8887 1.433,-2.0941 1.433,-3.35091 C 80.8814,8.99313 80.7581,8.3734 80.5142,7.79469 80.2703,7.21597 79.9106,6.6897 79.4558,6.2462 79.0011,5.8027 78.4602,5.45072 77.8644,5.21053 77.2685,4.97034 76.6295,4.8467 75.9842,4.84671 Z m 2.1406,4.7389 c -0.0025,0.59489 -0.2486,1.16459 -0.6843,1.58389 -0.4358,0.4192 -1.0256,0.6539 -1.6398,0.6523 H 71.8253 V 7.32279 h 3.9754 c 0.3048,0 0.6066,0.05814 0.8882,0.17112 0.2816,0.11297 0.5375,0.27856 0.753,0.4873 0.2155,0.20875 0.3865,0.45657 0.5031,0.72931 0.1167,0.27274 0.1767,0.56507 0.1767,0.86028 z"
fill="#ffffff"
id="path16"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 81.3051,19.2443 v -5.7104 c 0,-2.331 1.4066,-4.18802 4.2169,-4.18802 0.452,-0.00632 0.9031,0.04243 1.3425,0.14513 V 11.8427 C 86.5434,11.822 86.2529,11.822 86.1397,11.822 c -1.4892,0 -2.1406,0.6575 -2.1406,1.9992 v 5.4231 z"
fill="#ffffff"
id="path18"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 87.6719,14.3988 c 0,-2.867 2.2354,-5.05285 5.3453,-5.05285 3.11,0 5.3423,2.17985 5.3423,5.05285 0,2.873 -2.2354,5.0736 -5.3423,5.0736 -3.1069,0 -5.3453,-2.2066 -5.3453,-5.0736 z m 8.0272,0 c 0,-1.629 -1.1284,-2.7841 -2.6819,-2.7841 -1.5534,0 -2.6849,1.1551 -2.6849,2.7841 0,1.629 1.1284,2.7841 2.6849,2.7841 1.5565,0 2.6819,-1.1344 2.6819,-2.7841 z"
fill="#ffffff"
id="path20"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 106.194,11.6353 h -2.914 v 3.6075 c 0,1.2588 0.468,1.8363 1.807,1.8363 0.129,0 0.45,0 0.853,-0.0207 v 2.1236 c -0.512,0.1452 -1.041,0.2219 -1.575,0.2281 -2.256,0 -3.791,-1.321 -3.791,-3.8149 V 11.6353 H 98.7662 V 9.56203 h 0.4496 c 0.3601,0 0.7054,-0.13855 0.9602,-0.38517 0.254,-0.24662 0.398,-0.5811 0.398,-0.92988 V 6.27441 h 2.706 v 3.27873 h 2.914 z"
fill="#ffffff"
id="path22"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 107.004,14.3988 c 0,-2.867 2.236,-5.05285 5.343,-5.05285 3.107,0 5.345,2.17985 5.345,5.05285 0,2.873 -2.235,5.0736 -5.345,5.0736 -3.11,0 -5.343,-2.2066 -5.343,-5.0736 z m 8.028,0 c 0,-1.629 -1.129,-2.7841 -2.685,-2.7841 -1.557,0 -2.682,1.1551 -2.682,2.7841 0,1.629 1.128,2.7841 2.682,2.7841 1.553,0 2.685,-1.1344 2.685,-2.7841 z"
fill="#ffffff"
id="path24"
style="fill:#ffffff;fill-opacity:1" />
<path
d="m 119.096,19.2443 v -5.506 c 0,-2.556 1.682,-4.39235 4.685,-4.39235 3.003,0 4.663,1.83635 4.663,4.39235 v 5.506 h -2.682 v -5.2987 c 0,-1.4246 -0.66,-2.3102 -1.981,-2.3102 -1.321,0 -1.979,0.8886 -1.979,2.3102 v 5.2987 z"
fill="#ffffff"
id="path26"
style="fill:#ffffff;fill-opacity:1" />
<defs
id="defs38">
<linearGradient
id="paint0_linear_499_22895"
x1="51.785198"
y1="12.1227"
x2="45.362202"
y2="-16.188499"
gradientUnits="userSpaceOnUse">
<stop
stop-color="#E2DBFF"
id="stop28" />
<stop
offset="1"
stop-color="#6D4AFF"
id="stop30" />
</linearGradient>
<linearGradient
id="paint1_linear_499_22895"
x1="68.261703"
y1="39.289398"
x2="46.167099"
y2="-9.4419403"
gradientUnits="userSpaceOnUse">
<stop
offset="0.271019"
stop-color="#E2DBFF"
id="stop33" />
<stop
offset="1"
stop-color="#6D4AFF"
id="stop35" />
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Some files were not shown because too many files have changed in this diff Show More