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 import QtQuick.Window 2.0 import QtQuick.Controls.Universal 2.12 import DotherSide 0.1 import "./onboarding" import "./app" import "./sounds" import "./shared" import "./imports" StatusWindow { property bool hasAccounts: !!loginModel.rowCount() property bool removeMnemonicAfterLogin: false property alias dragAndDrop: dragTarget property bool popupOpened: false property bool displayBeforeGetStartedModal: !hasAccounts Universal.theme: Universal.System Settings { id: globalSettings category: "global" fileName: profileModel.settings.globalSettingsFile property string locale: "en" property int theme: 2 Component.onCompleted: { profileModel.changeLocale(locale) } } id: applicationWindow objectName: "mainWindow" minimumWidth: 900 minimumHeight: 600 width: Math.min(1232, Screen.desktopAvailableWidth - 64) height: Math.min(770, Screen.desktopAvailableHeight - 64) 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 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" enabled: loader.item ? loader.item.currentView !== Utils.getAppSectionIndex(Constants.browser) : true onTriggered: { applicationWindow.visible = false; } } Action { shortcut: "Ctrl+Q" onTriggered: { Qt.quit() } } //! Workaround for custom QQuickWindow Connections { target: applicationWindow onClosing: { if (loader.sourceComponent == login) { applicationWindow.visible = false close.accepted = false } else if (loader.sourceComponent == app) { if (loader.item.appSettings.quitOnClose) { close.accepted = true } else { applicationWindow.visible = false close.accepted = false } } } onActiveChanged: { if (applicationWindow.active && currentlyHasANotification) { currentlyHasANotification = false // QML doesn't have a function to hide notifications, but this does the trick systemTray.hide() systemTray.show() } } } // 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(globalSettings.theme, systemPalette.isCurrentSystemThemeDark()) } Component.onCompleted: { Style.changeTheme(globalSettings.theme, systemPalette.isCurrentSystemThemeDark()) setX(Qt.application.screens[0].width / 2 - width / 2); setY(Qt.application.screens[0].height / 2 - height / 2); applicationWindow.updatePosition(); } signal navigateTo(string path) property bool currentlyHasANotification: false SystemTrayIcon { id: systemTray visible: true icon.source: { if (production) { if (Qt.platform.os == "osx") return "shared/img/status-logo-round-rect.svg" else return "shared/img/status-logo-circle.svg" } else { if (Qt.platform.os == "osx") return "shared/img/status-logo-dev-round-rect.svg" else return "shared/img/status-logo-dev-circle.svg" } } function openStatusWindow() { applicationWindow.show() applicationWindow.raise() applicationWindow.requestActivate() } menu: Menu { MenuItem { //% "Open Status" text: qsTrId("open-status") onTriggered: { systemTray.openStatusWindow() } } MenuSeparator { } MenuItem { //% "Quit" text: qsTrId("quit") onTriggered: Qt.quit() } } onActivated: { if (reason !== SystemTrayIcon.Context) { openStatusWindow() } } } 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: onboardingModel.moveToAppState } } DSM.State { id: genKeyState onEntered: loader.sourceComponent = genKey DSM.SignalTransition { targetState: appState signal: onboardingModel.moveToAppState } } DSM.State { id: stateLogin onEntered: loader.sourceComponent = login DSM.SignalTransition { targetState: appState signal: loginModel.moveToAppState } DSM.SignalTransition { targetState: genKeyState signal: applicationWindow.navigateTo guard: path === "GenKey" } } DSM.SignalTransition { targetState: hasAccounts ? stateLogin : keysMainState 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" } DSM.FinalState { id: onboardingDoneState } } DSM.State { id: appState onEntered: loader.sourceComponent = app DSM.SignalTransition { targetState: stateLogin signal: loginModel.onLoggedOut } } } Loader { id: loader anchors.fill: parent property var appSettings } 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: containsDrag && drag.source.objectName !== "chatItem" && drag.source.objectName !== "chatListCategory" && loader.item && ( // in chat view (loader.item.currentView === 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.currentView === 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.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 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 KeysMain { btnGenKey.onClicked: applicationWindow.navigateTo("GenKey") btnExistingKey.onClicked: applicationWindow.navigateTo("ExistingKey") } } Component { id: existingKey ExistingKey { onClosed: function () { removeMnemonicAfterLogin = false if (hasAccounts) { applicationWindow.navigateTo("InitialState") } else { applicationWindow.navigateTo("KeysMain") } } } } Component { id: genKey GenKey { onClosed: function () { if (hasAccounts) { applicationWindow.navigateTo("InitialState") } else { applicationWindow.navigateTo("KeysMain") } } } } Component { id: login Login { onGenKeyClicked: function () { applicationWindow.navigateTo("GenKey") } onExistingKeyClicked: function () { applicationWindow.navigateTo("ExistingKey") } } } NotificationWindow { id: notificationWindow } MacTrafficLights { // parent: Overlay.overlay anchors.left: parent.left anchors.top: parent.top anchors.margins: 13 visible: Qt.platform.os === "osx" && !applicationWindow.isFullScreen onClose: { if (loader.sourceComponent == login) { Qt.quit(); } else if (loader.sourceComponent == app) { if (loader.item.appSettings.quitOnClose) { Qt.quit(); } else { applicationWindow.visible = false; } } } onMinimised: { applicationWindow.showMinimized() } onMaximized: { applicationWindow.toggleFullScreen() } } } /*##^## Designer { D{i:0;formeditorZoom:0.5} } ##^##*/