status-desktop/ui/main.qml

612 lines
18 KiB
QML
Raw Normal View History

2020-06-17 19:18:31 +00:00
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import Qt.labs.platform 1.1
import QtQml.StateMachine 1.14 as DSM
import Qt.labs.settings 1.0
import QtQuick.Window 2.12
import QtQml 2.13
2020-07-12 16:47:46 +00:00
import QtQuick.Window 2.0
import QtQuick.Controls.Universal 2.12
2020-07-12 16:47:46 +00:00
import DotherSide 0.1
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import "./app/AppLayouts/Onboarding/views"
import "./app"
StatusWindow {
property bool hasAccounts: startupModule.appState !== Constants.appState.onboarding
property bool removeMnemonicAfterLogin: false
property alias dragAndDrop: dragTarget
property bool popupOpened: false
property bool displayBeforeGetStartedModal: !hasAccounts
Universal.theme: Universal.System
id: applicationWindow
objectName: "mainWindow"
minimumWidth: 900
2020-11-18 14:01:09 +00:00
minimumHeight: 600
width: localAppSettings.appWidth
height: localAppSettings.appHeight
color: Style.current.background
title: {
// Set application settings
//% "Status Desktop"
Qt.application.name = qsTrId("status-desktop")
Qt.application.organization = "Status"
Qt.application.domain = "status.im"
return Qt.application.name
}
visible: true
2020-05-18 15:07:30 +00:00
function storeWidth() {
localAppSettings.appWidth = width
}
function storeHeight() {
localAppSettings.appHeight = height
}
onWidthChanged: Qt.callLater(storeWidth)
onHeightChanged: Qt.callLater(storeHeight)
Action {
shortcut: StandardKey.FullScreen
onTriggered: {
if (visibility === Window.FullScreen) {
showNormal()
} else {
showFullScreen()
}
}
}
Action {
shortcut: "Ctrl+M"
onTriggered: {
if (visibility === Window.Minimized) {
showNormal()
} else {
showMinimized()
}
}
}
Action {
shortcut: "Ctrl+W"
2021-04-20 20:15:16 +00:00
enabled: loader.item ? loader.item.currentView !== Utils.getAppSectionIndex(Constants.browser)
: true
2021-02-22 17:26:24 +00:00
onTriggered: {
applicationWindow.visible = false;
}
}
Action {
shortcut: "Ctrl+Q"
onTriggered: {
Qt.quit()
}
}
2021-10-16 19:03:01 +00:00
Connections {
target: startupModule
onAppStateChanged: {
mainModule.openStoreToKeychainPopup.connect(function(){
storeToKeychainConfirmationPopup.open()
})
if(localAccountSensitiveSettings.recentEmojis === "") {
localAccountSensitiveSettings.recentEmojis = [];
}
if (localAccountSensitiveSettings.whitelistedUnfurlingSites === "") {
localAccountSensitiveSettings.whitelistedUnfurlingSites = {};
}
if (localAccountSensitiveSettings.hiddenCommunityWelcomeBanners === "") {
localAccountSensitiveSettings.hiddenCommunityWelcomeBanners = [];
}
if (localAccountSensitiveSettings.hiddenCommunityBackUpBanners === "") {
localAccountSensitiveSettings.hiddenCommunityBackUpBanners = [];
}
2021-10-16 19:03:01 +00:00
}
}
//! Workaround for custom QQuickWindow
Connections {
2021-06-03 08:29:14 +00:00
target: applicationWindow
onClosing: {
if (Qt.platform.os === "osx") {
loader.sourceComponent = undefined
close.accepted = true
} else {
if (loader.sourceComponent == login) {
Qt.quit();
}
else if (loader.sourceComponent == app) {
if (localAccountSensitiveSettings.quitOnClose) {
Qt.quit();
} else {
applicationWindow.visible = false;
}
}
}
}
onActiveChanged: {
2021-05-19 18:31:18 +00:00
if (applicationWindow.active && currentlyHasANotification) {
currentlyHasANotification = false
// QML doesn't have a function to hide notifications, but this does the trick
systemTray.hide()
systemTray.show()
}
2021-02-22 22:10:47 +00:00
}
}
2021-09-06 13:37:00 +00:00
Connections {
target: singleInstance
onSecondInstanceDetected: {
console.log("User attempted to run the second instance of the application")
// activating this instance to give user visual feedback
applicationWindow.show()
applicationWindow.raise()
applicationWindow.requestActivate()
}
onEventReceived: {
let event = JSON.parse(eventStr)
if (event.hasOwnProperty("uri")) {
chatsModel.handleProtocolUri(event.uri)
} else {
console.warn("Unknown event received: " + eventStr)
}
}
2021-09-06 13:37:00 +00:00
}
Connections {
target: profileModel
ignoreUnknownSignals: true
enabled: removeMnemonicAfterLogin
onInitialized: {
mnemonicModule.remove()
}
}
// The easiest way to get current system theme (is it light or dark) without using
// OS native methods is to check lightness (0 - 1.0) of the window color.
// If it's too high (0.85+) means light theme is an active.
SystemPalette {
id: systemPalette
function isCurrentSystemThemeDark() {
return window.hslLightness < 0.85
}
}
function changeThemeFromOutside() {
Style.changeTheme(localAppSettings.theme, systemPalette.isCurrentSystemThemeDark())
}
2020-07-12 16:47:46 +00:00
Component.onCompleted: {
Style.changeTheme(localAppSettings.theme, systemPalette.isCurrentSystemThemeDark())
setX(Qt.application.screens[0].width / 2 - width / 2);
setY(Qt.application.screens[0].height / 2 - height / 2);
if (!localAppSettings.appSizeInitialized) {
width = Screen.desktopAvailableWidth - 125
height = Screen.desktopAvailableHeight - 125
localAppSettings.appSizeInitialized = true
}
applicationWindow.updatePosition();
2020-07-12 16:47:46 +00:00
}
signal navigateTo(string path)
property bool currentlyHasANotification: false
function makeStatusAppActive() {
applicationWindow.show()
applicationWindow.raise()
applicationWindow.requestActivate()
}
2020-05-18 15:07:30 +00:00
SystemTrayIcon {
id: systemTray
2020-05-18 15:07:30 +00:00
visible: true
icon.source: {
if (production) {
if (Qt.platform.os == "osx")
return "imports/assets/images/status-logo-round-rect.svg"
else
return "imports/assets/images/status-logo-circle.svg"
} else {
if (Qt.platform.os == "osx")
return "imports/assets/images/status-logo-dev-round-rect.svg"
else
return "imports/assets/images/status-logo-dev-circle.svg"
}
}
2020-05-18 15:07:30 +00:00
menu: Menu {
MenuItem {
//% "Open Status"
text: qsTrId("open-status")
onTriggered: {
applicationWindow.makeStatusAppActive()
}
}
MenuSeparator {
}
2020-05-18 15:07:30 +00:00
MenuItem {
//% "Quit"
text: qsTrId("quit")
2020-05-18 15:07:30 +00:00
onTriggered: Qt.quit()
}
}
onActivated: {
if (reason !== SystemTrayIcon.Context) {
applicationWindow.makeStatusAppActive()
}
2020-05-18 15:07:30 +00:00
}
}
2021-10-17 10:44:21 +00:00
function prepareForStoring(password, runStoreToKeychainPopup) {
if(Qt.platform.os == "osx")
{
2021-10-16 19:03:01 +00:00
storeToKeychainConfirmationPopup.password = password
2021-10-17 10:44:21 +00:00
if(runStoreToKeychainPopup)
storeToKeychainConfirmationPopup.open()
}
}
ConfirmationDialog {
id: storeToKeychainConfirmationPopup
property string password: ""
height: 200
confirmationText: qsTr("Would you like to store password to the Keychain?")
showRejectButton: true
showCancelButton: true
confirmButtonLabel: qsTr("Store")
rejectButtonLabel: qsTr("Not now")
cancelButtonLabel: qsTr("Never")
function finish()
{
password = ""
storeToKeychainConfirmationPopup.close()
}
onConfirmButtonClicked: {
2021-10-17 10:44:21 +00:00
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore
2021-10-16 19:03:01 +00:00
mainModule.storePassword(password)
finish()
}
onRejectButtonClicked: {
2021-10-17 10:44:21 +00:00
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNotNow
finish()
}
onCancelButtonClicked: {
2021-10-17 10:44:21 +00:00
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever
finish()
}
}
DSM.StateMachine {
id: stateMachine
initialState: onboardingState
running: true
DSM.State {
id: onboardingState
initialState: hasAccounts ? stateLogin : keysMainState
DSM.State {
id: keysMainState
onEntered: loader.sourceComponent = keysMain
DSM.SignalTransition {
targetState: genKeyState
signal: applicationWindow.navigateTo
guard: path === "GenKey"
}
}
DSM.State {
id: existingKeyState
onEntered: loader.sourceComponent = existingKey
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state == Constants.appState.main
}
}
DSM.State {
id: genKeyState
onEntered: loader.sourceComponent = genKey
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state == Constants.appState.main
}
}
2021-09-24 12:03:57 +00:00
DSM.State {
id: keycardState
2021-10-05 06:16:30 +00:00
onEntered: loader.sourceComponent = keycardFlowSelection
2021-09-24 12:03:57 +00:00
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state == Constants.appState.main
2021-09-24 12:03:57 +00:00
}
}
DSM.State {
id: stateLogin
onEntered: loader.sourceComponent = login
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state == Constants.appState.main
}
DSM.SignalTransition {
targetState: genKeyState
signal: applicationWindow.navigateTo
guard: path === "GenKey"
}
}
2020-06-12 20:47:44 +00:00
DSM.SignalTransition {
targetState: hasAccounts ? stateLogin : keysMainState
2020-06-12 20:47:44 +00:00
signal: applicationWindow.navigateTo
guard: path === "InitialState"
}
DSM.SignalTransition {
targetState: existingKeyState
signal: applicationWindow.navigateTo
guard: path === "ExistingKey"
}
DSM.SignalTransition {
targetState: keysMainState
signal: applicationWindow.navigateTo
guard: path === "KeysMain"
}
2021-09-24 12:03:57 +00:00
DSM.SignalTransition {
targetState: keycardState
signal: applicationWindow.navigateTo
2021-10-05 06:16:30 +00:00
guard: path === "KeycardFlowSelection"
2021-09-24 12:03:57 +00:00
}
DSM.FinalState {
id: onboardingDoneState
}
}
2020-09-22 15:12:48 +00:00
DSM.State {
id: appState
onEntered: loader.sourceComponent = app
DSM.SignalTransition {
targetState: stateLogin
2021-10-17 10:59:26 +00:00
signal: startupModule.logOut
}
}
}
Loader {
id: loader
2020-05-18 15:07:30 +00:00
anchors.fill: parent
}
2020-05-18 15:07:30 +00:00
DropArea {
id: dragTarget
signal droppedOnValidScreen(var drop)
property alias droppedUrls: rptDraggedPreviews.model
readonly property int chatView: Utils.getAppSectionIndex(Constants.chat)
readonly property int timelineView: Utils.getAppSectionIndex(Constants.timeline)
property bool enabled: !drag.source && !!loader.item && !!loader.item.appLayout &&
(
// in chat view
(loader.item.appLayout.appView.currentIndex === chatView &&
(
// in a one-to-one chat
chatsModel.channelView.activeChannel.chatType === Constants.chatTypeOneToOne ||
// in a private group chat
chatsModel.channelView.activeChannel.chatType === Constants.chatTypePrivateGroupChat
)
) ||
// in timeline view
loader.item.appLayout.appView.currentIndex === timelineView ||
// In community section
chatsModel.communities.activeCommunity.active
)
width: applicationWindow.width
height: applicationWindow.height
function cleanup() {
rptDraggedPreviews.model = []
}
onDropped: (drop) => {
if (enabled) {
droppedOnValidScreen(drop)
} else {
drop.accepted = false
}
cleanup()
}
onEntered: {
if (!enabled || !!drag.source) {
drag.accepted = false
return
}
// needed because drag.urls is not a normal js array
rptDraggedPreviews.model = drag.urls.filter(img => Utils.hasDragNDropImageExtension(img))
}
onPositionChanged: {
rptDraggedPreviews.x = drag.x
rptDraggedPreviews.y = drag.y
}
onExited: cleanup()
Rectangle {
id: dropRectangle
width: parent.width
height: parent.height
color: Style.current.transparent
opacity: 0.8
states: [
State {
when: dragTarget.enabled && dragTarget.containsDrag
PropertyChanges {
target: dropRectangle
color: Style.current.background
}
}
]
}
Repeater {
id: rptDraggedPreviews
Image {
source: modelData
width: 80
height: 80
sourceSize.width: 160
sourceSize.height: 160
fillMode: Image.PreserveAspectFit
x: index * 10 + rptDraggedPreviews.x
y: index * 10 + rptDraggedPreviews.y
z: 1
}
}
}
Component {
id: app
AppMain {}
}
Component {
id: keysMain
KeysMainView {
btnGenKey.onClicked: applicationWindow.navigateTo("GenKey")
btnExistingKey.onClicked: applicationWindow.navigateTo("ExistingKey")
2021-10-05 06:16:30 +00:00
btnKeycard.onClicked: applicationWindow.navigateTo("KeycardFlowSelection")
}
}
Component {
id: existingKey
ExistingKeyView {
2020-06-12 20:47:44 +00:00
onClosed: function () {
removeMnemonicAfterLogin = false
if (hasAccounts) {
applicationWindow.navigateTo("InitialState")
} else {
applicationWindow.navigateTo("KeysMain")
}
2020-06-12 20:47:44 +00:00
}
}
}
Component {
id: genKey
GenKeyView {
2020-06-13 15:17:54 +00:00
onClosed: function () {
if (hasAccounts) {
applicationWindow.navigateTo("InitialState")
} else {
applicationWindow.navigateTo("KeysMain")
}
2020-06-13 15:17:54 +00:00
}
}
}
2021-09-24 12:03:57 +00:00
Component {
2021-10-05 06:16:30 +00:00
id: keycardFlowSelection
KeycardFlowSelectionView {
2021-09-24 12:03:57 +00:00
onClosed: function () {
if (hasAccounts) {
applicationWindow.navigateTo("InitialState")
} else {
applicationWindow.navigateTo("KeysMain")
}
}
}
}
Component {
id: login
LoginView {
2020-06-12 20:47:44 +00:00
onGenKeyClicked: function () {
applicationWindow.navigateTo("GenKey")
}
onExistingKeyClicked: function () {
applicationWindow.navigateTo("ExistingKey")
}
}
}
NotificationWindow {
id: notificationWindow
}
2021-05-19 18:31:18 +00:00
MacTrafficLights {
2021-06-03 08:29:14 +00:00
// parent: Overlay.overlay
2021-05-19 18:31:18 +00:00
anchors.left: parent.left
anchors.top: parent.top
anchors.margins: 13
visible: Qt.platform.os === "osx" && !applicationWindow.isFullScreen
2021-06-03 08:29:14 +00:00
onClose: {
if (loader.sourceComponent == login) {
Qt.quit();
2021-06-03 08:29:14 +00:00
}
else if (loader.sourceComponent == app) {
if (localAccountSensitiveSettings.quitOnClose) {
2021-06-03 08:29:14 +00:00
Qt.quit();
} else {
applicationWindow.visible = false;
}
}
}
onMinimised: {
applicationWindow.showMinimized()
}
onMaximized: {
applicationWindow.toggleFullScreen()
}
2021-05-19 18:31:18 +00:00
}
}
/*##^##
Designer {
D{i:0;formeditorZoom:0.5}
}
##^##*/