mirror of
https://github.com/status-im/status-desktop.git
synced 2025-01-10 14:26:34 +00:00
f1e83f74bc
Allow up to 5 images to be dragged and dropped in to one-on-one chats and in the timeline. Can be combined with the existing upload button. The upload file dialog has been changed to allow multiple selections. Drag and dropped images adhere to the following rules, with corresponding validations messages: - Max 5 image - Image size must be 0.5 MB or less - File extension must be one of [".png", ".jpg", ".jpeg", ".heif", "tif", ".tiff"] Drag and drop and uploaded images are now also deduplicated.
399 lines
11 KiB
QML
399 lines
11 KiB
QML
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 "./onboarding"
|
|
import "./app"
|
|
import "./sounds"
|
|
import "./shared"
|
|
import "./imports"
|
|
|
|
ApplicationWindow {
|
|
property bool hasAccounts: !!loginModel.rowCount()
|
|
property bool removeMnemonicAfterLogin: false
|
|
property alias dragAndDrop: dragTarget
|
|
|
|
Universal.theme: Universal.System
|
|
|
|
id: applicationWindow
|
|
minimumWidth: 800
|
|
minimumHeight: 600
|
|
width: 1232
|
|
height: 770
|
|
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
|
|
|
|
ErrorSound {
|
|
id: errorSound
|
|
}
|
|
|
|
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"
|
|
onTriggered: {
|
|
applicationWindow.visible = false;
|
|
}
|
|
}
|
|
|
|
Action {
|
|
shortcut: "Ctrl+Q"
|
|
onTriggered: {
|
|
Qt.quit()
|
|
}
|
|
}
|
|
|
|
onClosing: {
|
|
if(loader.sourceComponent == app){
|
|
if(loader.item.appSettings.quitOnClose){
|
|
Qt.quit();
|
|
} else {
|
|
applicationWindow.visible = false;
|
|
close.accepted = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
// Change the theme to the system theme (dark/light) until we get the
|
|
// user's saved setting from status-go (after login)
|
|
Style.changeTheme(Universal.theme === Universal.Dark ? "dark" : "light")
|
|
setX(Qt.application.screens[0].width / 2 - width / 2);
|
|
setY(Qt.application.screens[0].height / 2 - height / 2);
|
|
}
|
|
|
|
signal navigateTo(string path)
|
|
|
|
property bool currentlyHasANotification: false
|
|
|
|
onActiveChanged: {
|
|
if (active && currentlyHasANotification) {
|
|
currentlyHasANotification = false
|
|
// QML doesn't have a function to hide notifications, but this does the trick
|
|
systemTray.hide()
|
|
systemTray.show()
|
|
}
|
|
}
|
|
|
|
SystemTrayIcon {
|
|
id: systemTray
|
|
visible: true
|
|
icon.source: applicationWindow.Universal.theme === Universal.Dark ?
|
|
"shared/img/status-logo.svg" :
|
|
"shared/img/status-logo-light-theme.svg";
|
|
menu: Menu {
|
|
MenuItem {
|
|
visible: !applicationWindow.visible
|
|
text: qsTr("Open Status")
|
|
onTriggered: {
|
|
applicationWindow.visible = true;
|
|
applicationWindow.requestActivate();
|
|
}
|
|
}
|
|
|
|
MenuSeparator {
|
|
visible: !applicationWindow.visible
|
|
}
|
|
|
|
MenuItem {
|
|
//% "Quit"
|
|
text: qsTrId("quit")
|
|
onTriggered: Qt.quit()
|
|
}
|
|
}
|
|
|
|
onActivated: {
|
|
applicationWindow.show()
|
|
applicationWindow.raise()
|
|
applicationWindow.requestActivate()
|
|
}
|
|
}
|
|
|
|
DSM.StateMachine {
|
|
id: stateMachine
|
|
initialState: onboardingState
|
|
running: true
|
|
|
|
DSM.State {
|
|
id: onboardingState
|
|
initialState: hasAccounts ? stateLogin : keysMainState
|
|
|
|
DSM.State {
|
|
id: stateIntro
|
|
onEntered: loader.sourceComponent = intro
|
|
}
|
|
|
|
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.loginResponseChanged
|
|
guard: !error
|
|
}
|
|
}
|
|
|
|
DSM.State {
|
|
id: genKeyState
|
|
onEntered: loader.sourceComponent = genKey
|
|
|
|
DSM.SignalTransition {
|
|
targetState: appState
|
|
signal: onboardingModel.loginResponseChanged
|
|
guard: !error
|
|
}
|
|
}
|
|
|
|
DSM.State {
|
|
id: stateLogin
|
|
onEntered: loader.sourceComponent = login
|
|
|
|
DSM.SignalTransition {
|
|
targetState: appState
|
|
signal: loginModel.loginResponseChanged
|
|
guard: !error
|
|
}
|
|
|
|
DSM.SignalTransition {
|
|
targetState: genKeyState
|
|
signal: applicationWindow.navigateTo
|
|
guard: path === "GenKey"
|
|
}
|
|
}
|
|
|
|
DSM.SignalTransition {
|
|
targetState: hasAccounts ? stateLogin : stateIntro
|
|
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 && loader.item &&
|
|
(
|
|
// in chat view
|
|
(loader.item.currentView === chatView &&
|
|
(
|
|
// in a one-to-one chat
|
|
chatsModel.activeChannel.chatType === Constants.chatTypeOneToOne ||
|
|
// in a private group chat
|
|
chatsModel.activeChannel.chatType === Constants.chatTypePrivateGroupChat
|
|
)
|
|
) ||
|
|
// in timeline view
|
|
loader.item.currentView === timelineView
|
|
)
|
|
|
|
width: applicationWindow.width
|
|
height: applicationWindow.height
|
|
|
|
function cleanup() {
|
|
rptDraggedPreviews.model = []
|
|
}
|
|
|
|
onDropped: (drop) => {
|
|
if (enabled) {
|
|
droppedOnValidScreen(drop)
|
|
}
|
|
cleanup()
|
|
}
|
|
onEntered: {
|
|
// 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: intro
|
|
Intro {
|
|
btnGetStarted.onClicked: applicationWindow.navigateTo("KeysMain")
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
/*##^##
|
|
Designer {
|
|
D{i:0;formeditorZoom:0.5}
|
|
}
|
|
##^##*/
|