diff --git a/src/accountview/qml/deleter.qml b/src/accountview/qml/deleter.qml new file mode 100644 index 00000000..cc9187d1 --- /dev/null +++ b/src/accountview/qml/deleter.qml @@ -0,0 +1,61 @@ +/*************************************************************************** +* Copyright (C) 2017 by Bluesystems * +* Author : Emmanuel Lepage Vallee * +* * +* This program 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. * +* * +* This program 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 this program. If not, see . * +**************************************************************************/ +import QtQuick 2.8 +import QtQuick.Controls 2.2 as Controls + +Loader { + id: accountDeleter + + function deleteAccount(a) { + accountDeleter.name = a.alias + accountDeleter.account = a + accountDeleter.active = true + accountDeleter.item.open() + } + + property string name: "" + property var account: "" + active: false + sourceComponent: Controls.Dialog { + height: 150 + parent: applicationWindow().contentItem + x: applicationWindow().contentItem.width / 2 - width/2 + y: applicationWindow().contentItem.height / 2 - height/2 + standardButtons: Controls.Dialog.Ok | Controls.Dialog.Cancel + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + modal: true + title: i18n("Delete an account") + + Controls.Label { + text: i18n("
Are you sure you want to delete the account called ") + + name + i18n(".

This cannot be undone and you will lose the account permanently
") + } + + onAccepted: { + accountDeleter.active = false + RingSession.accountModel.remove(account) + RingSession.accountModel.save() + accountDeleter.account = null + } + + onRejected: { + accountDeleter.active = false + accountDeleter.account = null + } + } +} diff --git a/src/wizard/qml/buttonBar.qml b/src/wizard/qml/buttonBar.qml index 259ee3cf..a12c2375 100644 --- a/src/wizard/qml/buttonBar.qml +++ b/src/wizard/qml/buttonBar.qml @@ -1,177 +1,180 @@ /*************************************************************************** * Copyright (C) 2017 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program 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. * * * * This program 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 this program. If not, see . * **************************************************************************/ import QtQuick 2.0 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.3 import net.lvindustries.ringqtquick 1.0 as RingQtQuick Item { id: buttonBar + onStateChanged: { + console.log("\nSTATE!", state) + } + property alias backButton: backButton property alias nextButton: nextButton property bool displaySkip: RingSession.accountModel.size > 0 property bool displayBusy: false signal skip() Rectangle { id: rectangle color: "#0886a0" anchors.fill: parent // Left buttons RowLayout { anchors.fill: parent // Align left Controls.Button { id: backButton Layout.fillHeight: true text: i18n("Back") //visible: false //FIXME } Item { Layout.fillWidth: true } // Align right Controls.BusyIndicator { id: busyIndicator visible: false Layout.fillHeight: true } Controls.Label { id: missingFields text: i18n("Please fill the required fields") verticalAlignment: Text.AlignVCenter color: "#ff1111" font.bold: true visible: true Layout.fillHeight: true } Controls.Button { id: nextButton Layout.fillHeight: true text: i18n("Next") visible: false } Controls.Button { id: skipButton Layout.fillHeight: true text: i18n("Skip") visible: displaySkip onClicked: { skip() } } } } states: [ State { name: "shown" PropertyChanges { target: buttonBar visible: true - y: buttonBar.parent.height - buttonBar.height state: "nextAvailable" } }, State { name: "locked" PropertyChanges { target: buttonBar anchors.right: buttonBar.parent.right anchors.left: buttonBar.parent.left anchors.bottom: buttonBar.parent.bottom anchors.leftMargin: 0 visible: true } }, State { name: "firstStep" PropertyChanges { target: backButton visible: false } }, State { name: "busy" extend: "locked" PropertyChanges { target: missingFields text: i18n("Please wait") } PropertyChanges { target: busyIndicator visible: true } }, State { name: "nextAvailable" extend: "locked" PropertyChanges { target: nextButton visible: true } PropertyChanges { target: missingFields visible: false } }, State { name: i18n("finish") extend: "nextAvailable" PropertyChanges { target: skipButton visible: false } PropertyChanges { target: nextButton text: "Finish" } } ] /*transitions: [ Transition { from: "*"; to: "shown" // Slide-in the new account form NumberAnimation { target: buttonBar easing.type: Easing.OutQuad properties: "y"; duration: 700 } } ]*/ } diff --git a/src/wizard/qml/createRing.qml b/src/wizard/qml/createRing.qml index 77479003..07382980 100644 --- a/src/wizard/qml/createRing.qml +++ b/src/wizard/qml/createRing.qml @@ -1,298 +1,298 @@ /*************************************************************************** * Copyright (C) 2017 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program 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. * * * * This program 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 this program. If not, see . * **************************************************************************/ import QtQuick 2.9 import QtQuick.Controls 2.2 as Controls import QtQuick.Layouts 1.0 import org.kde.kirigami 2.2 as Kirigami import net.lvindustries.ringqtquick 1.0 as RingQtQuick Item { id: createRing property alias registerUserName: registerUserName property alias account: ringAccountBuilder.account property alias nextAvailable: ringAccountBuilder.canCreate property alias busy: ringAccountBuilder.creating signal registrationCompleted(QtObject account) width: Math.min(createForm.implicitWidth, parent.width - 20) height: createForm.implicitHeight function createAccount() { ringAccountBuilder.create() } function commitAccount() { ringAccountBuilder.commit() } /** * Business logic and validation logic */ RingQtQuick.RingAccountBuilder { id: ringAccountBuilder registerRingName : registerUserName.checked registeredRingName: userName.text repeatPassword : repeatPasswordTxt.text password : passwordTxt.text usePassword : usePassword.checked onFinished: { registrationCompleted(account) } } ColumnLayout { id: createForm width: parent.width Controls.Switch { id: registerUserName height: 40 text: i18n("Register a public username*") opacity: 1 Layout.fillWidth: true checked: true//ringAccountBuilder.registerRingName //FIXME race contentItem: Text { text: registerUserName.text font: registerUserName.font color: "white" verticalAlignment: Text.AlignVCenter leftPadding: registerUserName.indicator.width + registerUserName.spacing } } Controls.Label { id: label2 opacity: ringAccountBuilder.registerRingName ? 1 : 0 text: i18n("Enter an username") color: "white" anchors.leftMargin: 8 Layout.fillWidth: true Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad duration: 200 } } } Controls.TextField { id: userName color: "white" Layout.fillWidth: true opacity: ringAccountBuilder.registerRingName ? 1 : 0 enabled: ringAccountBuilder.registerRingName Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad duration: 200 } } } RowLayout { id: rowLayout clip: true Layout.fillHeight: false Layout.maximumHeight: 37 Layout.fillWidth: true opacity: ringAccountBuilder.registerRingName ? 1 : 0 spacing: 6 Item { width: 37 height: 37 Layout.fillHeight: true Layout.preferredWidth: 37 Controls.BusyIndicator { id: busyIndicator anchors.fill: parent visible: ringAccountBuilder.nameLookup } } Controls.Label { id: registerFoundLabel text: ringAccountBuilder.nameStatusMessage verticalAlignment: Text.AlignVCenter color: ringAccountBuilder.nameLookupError ? "#ff1111" : ( ringAccountBuilder.nameStatus == RingQtQuick.NameDirectory.NOT_FOUND ? "#66ff66" : "white" ) font.bold: ringAccountBuilder.nameLookupError || ( ringAccountBuilder.nameStatus == RingQtQuick.NameDirectory.NOT_FOUND ) Layout.fillHeight: true Layout.fillWidth: true Behavior on color { ColorAnimation {duration: 100} } } } Controls.Switch { id: usePassword text: i18n("Protect your crypto keys with a password") opacity: 1 enabled: !ringAccountBuilder.registerRingName Layout.fillWidth: true checked: ringAccountBuilder.usePassword contentItem: Text { text: usePassword.text font: usePassword.font color: "white" verticalAlignment: Text.AlignVCenter leftPadding: usePassword.indicator.width + usePassword.spacing } } Controls.Label { id: label1 text: i18n("Enter an archive password") opacity: ringAccountBuilder.usePassword ? 1 : 0 enabled: ringAccountBuilder.usePassword color: "white" Layout.fillWidth: true anchors.leftMargin: 8 Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad duration: 200 } } } Controls.TextField { id: passwordTxt echoMode: "Password" opacity: ringAccountBuilder.usePassword ? 1 : 0 enabled: ringAccountBuilder.usePassword text: ringAccountBuilder.password Layout.fillWidth: true color: "white" Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad duration: 200 } } } Controls.Label { id: label color: "white" opacity: ringAccountBuilder.usePassword ? 1 : 0 enabled: ringAccountBuilder.usePassword text: i18n("Repeat the new password") Layout.fillWidth: true anchors.leftMargin: 8 Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad duration: 200 } } } Controls.TextField { id: repeatPasswordTxt echoMode: "Password" opacity: ringAccountBuilder.usePassword ? 1 : 0 enabled: ringAccountBuilder.usePassword Layout.fillWidth: true text: ringAccountBuilder.repeatPassword color: "white" Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad duration: 200 } } } Controls.Label { id: label4 color: "#ff1111" font.bold: true text: i18n("Passwords don't match") verticalAlignment: Text.AlignVCenter Layout.fillHeight: true Layout.fillWidth: true opacity: ringAccountBuilder.passwordMatch ? 0 : 1 Behavior on opacity { NumberAnimation { easing.type: Easing.OutQuad duration: 200 } } } Item { height: Kirigami.Units.largeSpacing Layout.fillWidth: true } Controls.Label { id: labelInfo color: "white" text: i18n("(*) registered usernames can be reached by their username-string instead of their generated ring-id number only.") Layout.fillWidth: true wrapMode: Text.WordWrap } Rectangle { Layout.fillHeight: true } } Rectangle{ id: registrationPopup - width: Math.min(applicationWindow.width*0.8, implicitWidth + 50) + width: Math.min(wizardWindow.width*0.8, implicitWidth + 50) height: popupLayout.implicitHeight + 10 anchors.centerIn: createRing color: "#0886a0" visible: ringAccountBuilder.creating z: 200 RowLayout { id: popupLayout anchors.verticalCenter: parent.verticalCenter Controls.BusyIndicator { id: registrationIndicator Layout.fillHeight: false visible: ringAccountBuilder.creating } Controls.Label { id: registrationStatus text: ringAccountBuilder.creationStatusMessage wrapMode: Text.WordWrap verticalAlignment: Text.AlignVCenter Layout.fillHeight: true Layout.fillWidth: true color: "black" } } } } diff --git a/src/wizard/qml/startPage.ui.qml b/src/wizard/qml/startPage.ui.qml index 43c30fa3..d16687f2 100644 --- a/src/wizard/qml/startPage.ui.qml +++ b/src/wizard/qml/startPage.ui.qml @@ -1,130 +1,133 @@ /*************************************************************************** * Copyright (C) 2017 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program 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. * * * * This program 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 this program. If not, see . * **************************************************************************/ import QtQuick 2.7 import QtQuick.Controls 2.0 as Controls import org.kde.ringkde.jamiwizard 1.0 as JamiWizard Controls.Page { id: frontPage + + property real bottomMargin: 0 + Rectangle { anchors.fill: parent x: -1 color: "#004d61" } property alias importRingAccount: importRingAccount property alias createRingAccount: createRingAccount property alias createAnonRingAccount: createAnonRingAccount property alias importSIPAccount: importSIPAccount property alias createIp2IPAccount: createIp2IPAccount property alias createRing: createRing property alias importRing: importRing property alias profilePage: profilePage property alias welcomeMessage: text1 property alias logo: image Text { id: text1 anchors.horizontalCenter: parent.horizontalCenter y: 149 width: Math.min(423, frontPage.width - 114) height: 86 text: i18n("Welcome to Ring-KDE. Before you can contact your friend, you have to have an account. Don't worry, creating one is easy and doesn't require sharing any personal information. If you are in an office or your phone service provider offers a SIP account, you can also configure Ring-KDE to take your \"real\" phone calls.") wrapMode: Text.WordWrap font.pixelSize: 12 color: "white" } Controls.Button { id: createRingAccount anchors.horizontalCenter: parent.horizontalCenter y: 246 text: i18n("Create a new GNU Ring account") } Controls.Button { id: importRingAccount anchors.horizontalCenter: parent.horizontalCenter y: 300 text: i18n("Import an existing account") } Controls.Button { id: createAnonRingAccount anchors.horizontalCenter: parent.horizontalCenter y: 354 text: i18n("Use an anonymous account") visible: false } Controls.Button { id: importSIPAccount anchors.horizontalCenter: parent.horizontalCenter y: 408 text: i18n("Import a SIP account") visible: false } Controls.Button { id: createIp2IPAccount anchors.horizontalCenter: parent.horizontalCenter y: 462 text: i18n("Use on local network") visible: false } Image { id: image anchors.horizontalCenter: parent.horizontalCenter y: 43 width: 100 height: 100 source: "qrc:/wizard/ring-kde.svg" sourceSize.width: 100 sourceSize.height: 100 } JamiWizard.CreateRing { id: createRing anchors.top: parent.top anchors.topMargin: 120 //the logo height is 100 - anchors.bottomMargin: footer.height + 10 + anchors.bottomMargin: frontPage.bottomMargin + 10 anchors.horizontalCenter: parent.horizontalCenter opacity: 0 visible: false } JamiWizard.ImportRing { id: importRing opacity: 0 visible: false width: logo.width + text1.width x: frontPage.width/2 - width/2 - 20 y: frontPage.height/2 - height/2 } JamiWizard.ProfilePage { id: profilePage visible: false y: text1.height + 8 - height: parent.height - text1.height - 8 -45/*footer.height*/ + height: parent.height - text1.height - 8 - frontPage.bottomMargin width: parent.width - anchors.bottomMargin: 45/*footer.height*/ + anchors.bottomMargin: frontPage.bottomMargin anchors.topMargin: 12 } } diff --git a/src/wizard/qml/wizard.qml b/src/wizard/qml/wizard.qml index fb5357f5..36585e2c 100644 --- a/src/wizard/qml/wizard.qml +++ b/src/wizard/qml/wizard.qml @@ -1,410 +1,428 @@ /*************************************************************************** * Copyright (C) 2017 by Bluesystems * * Author : Emmanuel Lepage Vallee * * * * This program 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. * * * * This program 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 this program. If not, see . * **************************************************************************/ import QtQuick 2.7 import net.lvindustries.ringqtquick 1.0 as RingQtQuick import org.kde.ringkde.jamiwizard 1.0 as JamiWizard -Rectangle { - id: applicationWindow - visible: true - color: "#004d61" - - property string previousState: "" - signal wizardFinished() - - JamiWizard.Splash { - id: startingPage - anchors.fill: parent - anchors.bottomMargin: footer.height - z: 2 - - onVisibleChanged: { - if (!visible) { - // Force the geometry while the animation is running, anchor it after - footer.y = applicationWindow.height + footer.height - - // Begin the first animation - /*ANIM footer.state = "shown"*/ - footer.state = "shown" - } - } +Loader { + id: wizardLoader - onQuit: { - applicationWindow.visible = false - } + function activate() { + globalDrawer.drawerOpen = false + wizardLoader.active = true } - JamiWizard.StartPage { - id: frontPage - anchors.fill: parent - anchors.bottomMargin: footer.height + visible: active + active: false + z: 999999 + + sourceComponent: Rectangle { + id: wizardWindow visible: true - } + color: "#004d61" - JamiWizard.ButtonBar { - id: footer - height: 45 - width: applicationWindow.width - visible: false - } + property string previousState: "" + signal wizardFinished() - onWidthChanged: { //HACK - footer.width = width - } + onWizardFinished: { + wizardLoader.active = false + } + + JamiWizard.Splash { + id: startingPage + anchors.fill: parent + property bool wasVisible: wasVisible || visible + z: 2 + + onVisibleChanged: { + if (wasVisible && !visible) { + // Force the geometry while the animation is running, anchor it after + footer.y = wizardWindow.height + footer.height - // Display or hide the "next/finish" button in the bar - Connections { - target: frontPage - onNextAvailableChanged: { - footer.state = frontPage.nextAvailable ? "nextAvailable" : ( - frontPage.busy ? "busy" : "locked" - ) + // Begin the first animation + footer.state = "shown" + } + } + + onQuit: { + wizardWindow.visible = false + } } - } - // Use a better label when next is unavailable because it's busy - Connections { - target: frontPage - onBusyChanged: { - footer.state = frontPage.nextAvailable ? "nextAvailable" : ( - frontPage.busy ? "busy" : "locked" - ) + JamiWizard.StartPage { + id: frontPage + anchors.fill: parent + anchors.bottomMargin: footer.height + visible: true + bottomMargin: footer.height } - } - // Close the wizard and let the user configure things manually - Connections { - target: footer - onSkip: { - wizardFinished() + JamiWizard.ButtonBar { + id: footer + height: 45 + width: wizardWindow.width + visible: false + anchors.bottom: wizardWindow.bottom } - } - // Try to restore the previous page - Connections { - target: footer.backButton - onClicked: { - var oldPreviousState = previousState - previousState = stateGroup.state - - switch (stateGroup.state) { - case 'createRegRing': - case 'importRing': - stateGroup.state = "*" - break; - case 'showProfile': - previousState = "*" - footer.state = "nextUnavailable" //FIXME - stateGroup.state = oldPreviousState - break; + onWidthChanged: { //HACK + footer.width = width + } + + // Display or hide the "next/finish" button in the bar + Connections { + target: frontPage + onNextAvailableChanged: { + footer.state = frontPage.nextAvailable ? "nextAvailable" : ( + frontPage.busy ? "busy" : "locked" + ) } } - } - // Show the profile configuration - Connections { - target: footer.nextButton - onClicked: { - switch (stateGroup.state) { - case 'createRegRing': - frontPage.createRing.commitAccount() - break; - case 'importRing': - frontPage.importRing.createAccount() - break; - case '*': - stateGroup.state = "*" - stateGroup.state = "showProfile" - footer.state = "finish" - break; - case 'showProfile': - var acc = frontPage.createRing.account - if (acc && acc.alias == "") - frontPage.createRing.account.alias = frontPage.createRing.account.profile.formattedName - - frontPage.profilePage.save() - - wizardFinished() - break; + // Use a better label when next is unavailable because it's busy + Connections { + target: frontPage + onBusyChanged: { + footer.state = frontPage.nextAvailable ? "nextAvailable" : ( + frontPage.busy ? "busy" : "locked" + ) } } - } - Connections { - target: frontPage.createRing - onRegistrationCompleted: { - stateGroup.state = "showProfile" - footer.state = "finish" - account.createProfile() + // Close the wizard and let the user configure things manually + Connections { + target: footer + onSkip: { + wizardFinished() + } + } - frontPage.profilePage.individual = account.profile.individual + // Try to restore the previous page + Connections { + target: footer.backButton + onClicked: { + var oldPreviousState = previousState + previousState = stateGroup.state + + switch (stateGroup.state) { + case 'createRegRing': + case 'importRing': + stateGroup.state = "*" + break; + case 'showProfile': + previousState = "*" + footer.state = "nextUnavailable" //FIXME + stateGroup.state = oldPreviousState + break; + } + } } - onAccountChanged: { - var acc = frontPage.createRing.account - if ((!acc) || acc.registrationType != RingQtQuick.Account.READY) - return + // Show the profile configuration + Connections { + target: footer.nextButton + onClicked: { + switch (stateGroup.state) { + case 'createRegRing': + frontPage.createRing.commitAccount() + break; + case 'importRing': + frontPage.importRing.createAccount() + break; + case '*': + stateGroup.state = "*" + stateGroup.state = "showProfile" + footer.state = "finish" + break; + case 'showProfile': + var acc = frontPage.createRing.account + if (acc && acc.alias == "") + frontPage.createRing.account.alias = frontPage.createRing.account.profile.formattedName + + frontPage.profilePage.save() + + wizardFinished() + break; + } + } + } - if (!acc.profile) - acc.createProfile() + Connections { + target: frontPage.createRing + onRegistrationCompleted: { + stateGroup.state = "showProfile" + footer.state = "finish" + account.createProfile() - //frontPage.profilePage.currentPerson = acc.profile - frontPage.profilePage.individual = acc.profile.individual - } - } + frontPage.profilePage.individual = account.profile.individual + } + onAccountChanged: { + var acc = frontPage.createRing.account + + if ((!acc) || acc.registrationType != RingQtQuick.Account.READY) + return + + if (!acc.profile) + acc.createProfile() - Connections { - target: frontPage.importRing - onRegistrationCompleted: { - stateGroup.state = "showProfile" - footer.state = "finish" + //frontPage.profilePage.currentPerson = acc.profile + frontPage.profilePage.individual = acc.profile.individual + } } - onAccountChanged: { - var acc = frontPage.importRing.account - if (!acc) - return + Connections { + target: frontPage.importRing + onRegistrationCompleted: { + stateGroup.state = "showProfile" + footer.state = "finish" + } + onAccountChanged: { + var acc = frontPage.importRing.account + + if (!acc) + return - frontPage.profilePage.currentPerson = acc.profile + frontPage.profilePage.currentPerson = acc.profile + } } - } - StateGroup { - id: stateGroup - states: [ - // Hide all buttons - State { - name: "hideAccountTypes" - - PropertyChanges { - target: frontPage.createRingAccount - x: -436 - y: 322 - anchors.horizontalCenter: undefined - opacity: 0 - } + StateGroup { + id: stateGroup + states: [ + // Hide all buttons + State { + name: "hideAccountTypes" + + PropertyChanges { + target: frontPage.createRingAccount + x: -436 + y: 322 + anchors.horizontalCenter: undefined + opacity: 0 + } - PropertyChanges { - target: frontPage.createAnonRingAccount - x: 836 - y: -322 - anchors.horizontalCenter: undefined - opacity: 0 - } + PropertyChanges { + target: frontPage.createAnonRingAccount + x: 836 + y: -322 + anchors.horizontalCenter: undefined + opacity: 0 + } - PropertyChanges { - target: frontPage.importRingAccount - x: -383 - y: 575 - opacity: 0 - } + PropertyChanges { + target: frontPage.importRingAccount + x: -383 + y: 575 + opacity: 0 + } - PropertyChanges { - target: frontPage.importSIPAccount - x: 771 - y: 439 - opacity: 0 - text: i18n("Import a SIP account") - } + PropertyChanges { + target: frontPage.importSIPAccount + x: 771 + y: 439 + opacity: 0 + text: i18n("Import a SIP account") + } - PropertyChanges { - target: frontPage.createIp2IPAccount - x: 771 - y: 96 - opacity: 0 - } + PropertyChanges { + target: frontPage.createIp2IPAccount + x: 771 + y: 96 + opacity: 0 + } - PropertyChanges { - target: frontPage.logo - x: applicationWindow.width/2 - frontPage.welcomeMessage.width/2 - y: 8 - } + PropertyChanges { + target: frontPage.logo + x: wizardWindow.width/2 - frontPage.welcomeMessage.width/2 + y: 8 + } - PropertyChanges { - target: frontPage.welcomeMessage - x: applicationWindow.width/2 - 114/2 - y: 8 - } - }, + PropertyChanges { + target: frontPage.welcomeMessage + x: wizardWindow.width/2 - 114/2 + y: 8 + } + }, - // Create a new Ring account - State { - name: "createRegRing" - extend: "hideAccountTypes" + // Create a new Ring account + State { + name: "createRegRing" + extend: "hideAccountTypes" - PropertyChanges { - target: frontPage - state: "createRegRing" - } + PropertyChanges { + target: frontPage + state: "createRegRing" + } - PropertyChanges { - target: frontPage.createRing - opacity: 1 - visible: true + PropertyChanges { + target: frontPage.createRing + opacity: 1 + visible: true - // Use raw [x,y] to avoid following the animation - x: frontPage.width/2 - width/2 + // Use raw [x,y] to avoid following the animation + x: frontPage.width/2 - width/2 - } - }, + } + }, - // Import an existing account from another device - State { - name: "importRing" - extend: "hideAccountTypes" + // Import an existing account from another device + State { + name: "importRing" + extend: "hideAccountTypes" - PropertyChanges { - target: frontPage - state: "importRing" - } + PropertyChanges { + target: frontPage + state: "importRing" + } - PropertyChanges { - target: frontPage.importRing - opacity: 1 - visible: true + PropertyChanges { + target: frontPage.importRing + opacity: 1 + visible: true - // Use raw [x,y] to avoid following the animation - x: frontPage.width/2 - width/2 - } - }, - - // Last page, the shared profile information - State { - name: "showProfile" - extend: "hideAccountTypes" - PropertyChanges { - target: frontPage - state: "showProfile" - } + // Use raw [x,y] to avoid following the animation + x: frontPage.width/2 - width/2 + } + }, + + // Last page, the shared profile information + State { + name: "showProfile" + extend: "hideAccountTypes" + PropertyChanges { + target: frontPage + state: "showProfile" + } - PropertyChanges { - target: frontPage.profilePage - opacity: 1 - visible: true - } + PropertyChanges { + target: frontPage.profilePage + opacity: 1 + visible: true + } - PropertyChanges { - target: frontPage.importRing - opacity: 0 - visible: false - } + PropertyChanges { + target: frontPage.importRing + opacity: 0 + visible: false + } - PropertyChanges { - target: frontPage.createRing - opacity: 0 - visible: false - } - } - ] - - transitions: [ - Transition { - from: "*"; to: "hideAccountTypes" - NumberAnimation { - targets: [ - frontPage.importRingAccount, - frontPage.importSIPAccount, - frontPage.createIp2IPAccount, - frontPage.createRingAccount, - frontPage.createAnonRingAccount, - frontPage.logo, - frontPage.welcomeMessage - ] - easing.type: Easing.OutBounce - properties: "x,y"; - duration: 1000 + PropertyChanges { + target: frontPage.createRing + opacity: 0 + visible: false + } } + ] + + transitions: [ + Transition { + from: "*"; to: "hideAccountTypes" + NumberAnimation { + targets: [ + frontPage.importRingAccount, + frontPage.importSIPAccount, + frontPage.createIp2IPAccount, + frontPage.createRingAccount, + frontPage.createAnonRingAccount, + frontPage.logo, + frontPage.welcomeMessage + ] + easing.type: Easing.OutBounce + properties: "x,y"; + duration: 1000 + } - // Fade-out the irrelevant buttons - NumberAnimation { - targets: [ - frontPage.importRingAccount, - frontPage.importSIPAccount, - frontPage.createIp2IPAccount, - frontPage.createRingAccount, - frontPage.createAnonRingAccount, - frontPage.logo, - frontPage.welcomeMessage - ] - easing.type: Easing.Linear - properties: "opacity"; - duration: 500 - onStopped: { - importRingAccount.visible = false; - importSIPAccount.visible = false; - createIp2IPAccount.visible = false; - createRingAccount.visible = false; - createAnonRingAccount.visible = false; + // Fade-out the irrelevant buttons + NumberAnimation { + targets: [ + frontPage.importRingAccount, + frontPage.importSIPAccount, + frontPage.createIp2IPAccount, + frontPage.createRingAccount, + frontPage.createAnonRingAccount, + frontPage.logo, + frontPage.welcomeMessage + ] + easing.type: Easing.Linear + properties: "opacity"; + duration: 500 + onStopped: { + importRingAccount.visible = false; + importSIPAccount.visible = false; + createIp2IPAccount.visible = false; + createRingAccount.visible = false; + createAnonRingAccount.visible = false; + } } - } - }, - - // TO DIALOG - Transition { - from: "hideAccountTypes"; to: "createRegRing" - - // Slide-in the new account form - NumberAnimation { - target: frontPage.createRing - easing.type: Easing.OutQuad - properties: "x,opacity"; - duration: 700 - } - }, + }, + + // TO DIALOG + Transition { + from: "hideAccountTypes"; to: "createRegRing" + + // Slide-in the new account form + NumberAnimation { + target: frontPage.createRing + easing.type: Easing.OutQuad + properties: "x,opacity"; + duration: 700 + } + }, - Transition { - from: "hideAccountTypes"; to: "importRing" + Transition { + from: "hideAccountTypes"; to: "importRing" - // Slide-in the new account form - NumberAnimation { - target: frontPage.importRing - easing.type: Easing.OutQuad - properties: "x,opacity"; - duration: 700 - } - }, - - // BACK TO FIRST PAGE - Transition { - from: "createRegRing"; to: "*" - - // Slide-in the new account form - NumberAnimation { - target: frontPage.createRing - easing.type: Easing.OutQuad - properties: "x,opacity"; - duration: 700 - } - }, + // Slide-in the new account form + NumberAnimation { + target: frontPage.importRing + easing.type: Easing.OutQuad + properties: "x,opacity"; + duration: 700 + } + }, + + // BACK TO FIRST PAGE + Transition { + from: "createRegRing"; to: "*" + + // Slide-in the new account form + NumberAnimation { + target: frontPage.createRing + easing.type: Easing.OutQuad + properties: "x,opacity"; + duration: 700 + } + }, - Transition { - from: "importRing"; to: "*" + Transition { + from: "importRing"; to: "*" - // Slide-in the new account form - NumberAnimation { - target: frontPage.importRing - easing.type: Easing.OutQuad - properties: "x,opacity"; - duration: 700 + // Slide-in the new account form + NumberAnimation { + target: frontPage.importRing + easing.type: Easing.OutQuad + properties: "x,opacity"; + duration: 700 + } } - } - ] + ] + } } } diff --git a/views/basic/qml/basic.qml b/views/basic/qml/basic.qml index 873d4c5d..c3113a0e 100644 --- a/views/basic/qml/basic.qml +++ b/views/basic/qml/basic.qml @@ -1,219 +1,255 @@ /* * Copyright 2018 Fabian Riethmayer * Copyright 2019 Emmanuel Lepage * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 3, or * (at your option) any later version. * * This program 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 Library General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ - import QtQuick 2.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.2 import org.kde.kirigami 2.6 as Kirigami import net.lvindustries.ringqtquick.media 1.0 as RingQtMedia import net.lvindustries.ringqtquick 1.0 as RingQtQuick import org.kde.ringkde.basicview 1.0 as BasicView import org.kde.ringkde.jamicontactview 1.0 as JamiContactView import org.kde.ringkde.jamiwizard 1.0 as JamiWizard import org.kde.ringkde.jamikdeintegration 1.0 as JamiKDEIntegration -import QtQuick.Controls.Material 2.3 Kirigami.ApplicationWindow { width: 1024 height: 768 id: root // Localization is currently broken with the Material theme function i18n(t) {return t;} function showCallPage() { callpage.visible = true if (pageStack.currentItem == callpage) return showChat() for (var i = 0; i < pageStack.depth; i++) { if (pageStack.get(i) == callpage) { pageStack.currentIndex = i return } } pageStack.push(callpage) } function hideCall() { for (var i = 0; i < pageStack.depth; i++) { if (pageStack.get(i) == callpage) { pageStack.pop(callpage) pageStack.currentIndex = 0 return } } } function showChat() { if (pageStack.currentItem == callpage) return for (var i = 0; i < pageStack.depth; i++) { if (pageStack.get(i) == chat) { pageStack.currentIndex = Kirigami.Settings.isMobile ? 1 : 0 return } } pageStack.push(chat) pageStack.currentIndex = Kirigami.Settings.isMobile ? 1 : 0 } - // Check the network status and other sources to ensure actions *can* work + /** + * Track the network status and other information sources to ensure + * actions *can* work at any given time + */ RingQtMedia.AvailabilityTracker { id: availabilityTracker individual: mainPage.currentIndividual } - // Allows to free resources when viewing other individuals + /** + * This implements the workflow of having a single "current" Individual + * (abstract contact) at once. It hold some references to all the shared + * pointers to ensure QtQuick don't accidentally let them be freed. + */ RingQtQuick.SharedModelLocker { id: mainPage onCallChanged: { if (!call) hideCall() else showCallPage() } onIndividualChanged: { list.currentIndex = RingSession.peersTimelineModel.individualIndex( currentIndividual ).row } } + /** + * This is the QtQuick action collection, there is also: + * + * org.kde.ringkde.jamikdeintegration.actioncollection + * + * The difference is that this one has the actions that can be implemented + * in QML easily while the other one has the ones where the check and + * enabled state depends on the backend state and thus are better + * implemented in C++. + */ BasicView.ActionCollection { id: actionCollection } - BasicView.Contacts { +// sdfdssdfsd() + BasicView.Contacts {//FIXME id : mydata Component.onCompleted: { - chat.model = mydata.get(3) + chat.model = mydata.get(3) } } pageStack.initialPage: [list, chat] pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.ToolBar pageStack.globalToolBar.preferredHeight: Kirigami.Units.gridUnit * 3 pageStack.defaultColumnWidth: root.width < 320 ? root.width : 320 + /** + * + */ BasicView.ListPage { id: list } + /** + * This page holds the chat and "per individual" timeline. + * + * It also has a sidebar with some actions and statistics to perform on + * the individual (abstract contact). + */ BasicView.ChatPage { id: chat } + /** + * This is the multimedia page. + * + * It is used for audio, video and screencast communication. It is only in + * the PageRow stack when explicitly selected using a QmlAction or when + * there is an incoming (or active) communication. + */ BasicView.CallPage { id: callpage visible: false } contextDrawer: Kirigami.ContextDrawer { id: contextDrawer } globalDrawer: BasicView.GlobalDrawer { id: globalDrawer handleVisible: !wizardLoader.active } + /** + * This is a "wormhole" object where the signals sent by any instance, + * including in C++, ends up here. + * + * This is used as an abstraction to integrate the C++ backend with the QML + * frontend without any setContextProperty. + */ JamiKDEIntegration.WindowEvent { id: events function showDialog(path) { var component = Qt.createComponent(path) if (component.status == Component.Ready) { var window = component.createObject(applicationWindow().contentItem) window.open() } else console.log("ERROR", component.status, component.errorString()) } + // Dialogs and overlays onRequestsConfigureAccounts: showDialog("qrc:/account/qml/accountdialog.qml") onRequestsVideo: showDialog("qrc:/jamivideoview/qml/settingpopup.qml") + onRequestsWizard: wizardLoader.activate() - onRequestsHideWindow: { - hide() - } - - onRequestsWizard: { - globalDrawer.drawerOpen = false - wizardLoader.visible = true - wizardLoader.active = true - } + // Window events + onRequestsHideWindow: hide() } /** - * Display the wizard when all accounts are deleted. + * This object tracks when showing the wizard is required. Unlike Ring-KDE, + * Banji cannot work without an account and all component blindly expect + * one to exist. + * + * In Ring-KDE, having no account was requested (and implemented), but this + * made everything harder to maintain with long QML expressions checking + * everything, everywhere. It was a mistake and Banji will *never* support + * having no account. */ JamiWizard.Policies { id: wizardPolicies } - Loader { + /** + * Do common operations in a wizard instead of the settings. + * + * It is less error prone. + */ + JamiWizard.Wizard { id: wizardLoader - - active: false anchors.fill: parent - z: 999999 onActiveChanged: { - if ( list.displayWelcome && !active) + if (list.displayWelcome && !active) list.search() - - } - sourceComponent: JamiWizard.Wizard { - anchors.fill: parent - z: 999999 - onVisibleChanged: { - if (!visible) { - wizardLoader.visible = false - wizardLoader.active = false - } - } - onWizardFinished: { - wizardLoader.active = false - list.search() - } } } + /** + * Delay showing the wizard or welcome page until the next event loop + * iteration. This avoids having to handle a whole bunch of corner cases. + * + * It is important to keep in mind that net.lvindustries.ringqtquick is + * loaded by the QML files, not ahead of time. So during the first iteration, + * it isn't fully ready yet. + */ Timer { interval: 0 running: true repeat: false onTriggered: { if (wizardPolicies.displayWizard) - wizardLoader.active = true + wizardLoader.activate() else if (list.displayWelcome) list.search() } } }