[Settings]: Added change password view (#13284)
* [Settings]: Added change password view Closes #10037 Adding configuration options to PasswordView * feat(ChangePassword): Integrate ConfirmChangePasswordModal 1. Integrate with backend 2. Clean unused components * feat: Add support to restart application 1. Adding restart app support in DOtherSide 2. Integrating nimqml 3. Expose in qml in Utils * chore: Move changeDatabasePassword call to threadpool * chore(squish): Fix failing tests due to settings index changes --------- Co-authored-by: Alex Jbanca <alexjb@status.im>
This commit is contained in:
parent
d29e5406de
commit
480985ca4e
|
@ -110,6 +110,9 @@ QtObject:
|
|||
proc downloadImageByUrl*(self: Utils, url: string, path: string) {.slot.} =
|
||||
downloadImageByUrl(url, path)
|
||||
|
||||
proc restartApplication*(self: Utils) {.slot.} =
|
||||
restartApplication()
|
||||
|
||||
proc generateQRCodeSVG*(self: Utils, text: string, border: int = 0): string =
|
||||
var qr0: array[0..qrcodegen_BUFFER_LEN_MAX, uint8]
|
||||
var tempBuffer: array[0..qrcodegen_BUFFER_LEN_MAX, uint8]
|
||||
|
|
|
@ -67,6 +67,9 @@ method mnemonicBackedUp*(self: Module) =
|
|||
self.view.emitMnemonicBackedUpSignal()
|
||||
|
||||
method onPasswordChanged*(self: Module, success: bool, errorMsg: string) =
|
||||
if singletonInstance.localAccountSettings.getStoreToKeychainValue() != LS_VALUE_NEVER:
|
||||
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NOT_NOW)
|
||||
|
||||
self.view.emitPasswordChangedSignal(success, errorMsg)
|
||||
|
||||
method getMnemonic*(self: Module): string =
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import ../../../backend/privacy as status_privacy
|
||||
|
||||
type
|
||||
ChangeDatabasePasswordTaskArg = ref object of QObjectTaskArg
|
||||
accountId: string
|
||||
currentPassword: string
|
||||
newPassword: string
|
||||
|
||||
const changeDatabasePasswordTask*: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||
let arg = decode[ChangeDatabasePasswordTaskArg](argEncoded)
|
||||
let output = %* {
|
||||
"error": "",
|
||||
"result": ""
|
||||
}
|
||||
|
||||
try:
|
||||
let result = status_privacy.changeDatabasePassword(arg.accountId, arg.currentPassword, arg.newPassword)
|
||||
output["result"] = %result.result
|
||||
except Exception as e:
|
||||
output["error"] = %e.msg
|
||||
|
||||
arg.finish(output)
|
|
@ -5,12 +5,15 @@ import ../settings/service as settings_service
|
|||
import ../accounts/service as accounts_service
|
||||
|
||||
import ../../../app/core/eventemitter
|
||||
import ../../../app/core/tasks/[qt, threadpool]
|
||||
|
||||
import ../../../backend/eth as status_eth
|
||||
import ../../../backend/privacy as status_privacy
|
||||
|
||||
import ../../common/utils as common_utils
|
||||
|
||||
include ./async_tasks
|
||||
|
||||
logScope:
|
||||
topics = "privacy-service"
|
||||
|
||||
|
@ -27,6 +30,7 @@ QtObject:
|
|||
events: EventEmitter
|
||||
settingsService: settings_service.Service
|
||||
accountsService: accounts_service.Service
|
||||
threadpool: threadpool.ThreadPool
|
||||
|
||||
proc delete*(self: Service) =
|
||||
self.QObject.delete
|
||||
|
@ -58,6 +62,36 @@ QtObject:
|
|||
except Exception as e:
|
||||
error "error: ", procName="getDefaultAccount", errName = e.name, errDesription = e.msg
|
||||
|
||||
proc onChangeDatabasePasswordResponse(self: Service, responseStr: string) {.slot.} =
|
||||
var data = OperationSuccessArgs(success: false, errorMsg: "")
|
||||
try:
|
||||
let response = responseStr.parseJson
|
||||
|
||||
# nim runtime error
|
||||
let error = response["error"].getStr
|
||||
if error != "":
|
||||
data.errorMsg = error
|
||||
self.events.emit(SIGNAL_PASSWORD_CHANGED, data)
|
||||
return;
|
||||
|
||||
let result = response["result"]
|
||||
|
||||
if(result.contains("error")):
|
||||
let errMsg = result["error"].getStr
|
||||
if(errMsg.len == 0):
|
||||
data.success = true
|
||||
else:
|
||||
# backend runtime error
|
||||
data.errorMsg = errMsg
|
||||
error "error: ", procName="changePassword", errDesription = errMsg
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", procName="changePassword", errName = e.name, errDesription = e.msg
|
||||
data.errorMsg = e.msg
|
||||
|
||||
self.events.emit(SIGNAL_PASSWORD_CHANGED, data)
|
||||
|
||||
|
||||
proc changePassword*(self: Service, password: string, newPassword: string) =
|
||||
try:
|
||||
var data = OperationSuccessArgs(success: false, errorMsg: "")
|
||||
|
@ -76,16 +110,15 @@ QtObject:
|
|||
return
|
||||
|
||||
let loggedInAccount = self.accountsService.getLoggedInAccount()
|
||||
let response = status_privacy.changeDatabasePassword(loggedInAccount.keyUid, common_utils.hashPassword(password), common_utils.hashPassword(newPassword))
|
||||
|
||||
if(response.result.contains("error")):
|
||||
let errMsg = response.result["error"].getStr
|
||||
if(errMsg.len == 0):
|
||||
data.success = true
|
||||
else:
|
||||
error "error: ", procName="changePassword", errDesription = errMsg
|
||||
|
||||
self.events.emit(SIGNAL_PASSWORD_CHANGED, data)
|
||||
let arg = ChangeDatabasePasswordTaskArg(
|
||||
tptr: cast[ByteAddress](changeDatabasePasswordTask),
|
||||
vptr: cast[ByteAddress](self.vptr),
|
||||
slot: "onChangeDatabasePasswordResponse",
|
||||
accountId: loggedInAccount.keyUid,
|
||||
currentPassword: common_utils.hashPassword(password),
|
||||
newPassword: common_utils.hashPassword(newPassword)
|
||||
)
|
||||
self.threadpool.start(arg)
|
||||
|
||||
except Exception as e:
|
||||
error "error: ", procName="changePassword", errName = e.name, errDesription = e.msg
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import AppLayouts.Profile.popups 1.0
|
||||
|
||||
import Storybook 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
PopupBackground {
|
||||
id: popupBg
|
||||
|
||||
property var popupIntance: null
|
||||
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
Button {
|
||||
id: reopenButton
|
||||
anchors.centerIn: parent
|
||||
text: "Reopen"
|
||||
enabled: globalUtilsMock.ready
|
||||
|
||||
onClicked: modal.open()
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: globalUtilsMock
|
||||
|
||||
property bool ready: false
|
||||
property var globalUtils: QtObject {
|
||||
function restartApplication() {
|
||||
if (popupBg.popupIntance)
|
||||
popupBg.popupIntance.close()
|
||||
}
|
||||
}
|
||||
Component.onCompleted: {
|
||||
Utils.globalUtilsInst = globalUtilsMock.globalUtils
|
||||
globalUtilsMock.ready = true
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmChangePasswordModal {
|
||||
id: modal
|
||||
visible: true
|
||||
onChangePasswordRequested: {
|
||||
passwordChangedTimer.start()
|
||||
}
|
||||
Component.onCompleted: {
|
||||
popupBg.popupIntance = modal
|
||||
}
|
||||
Timer {
|
||||
id: passwordChangedTimer
|
||||
interval: 2000
|
||||
repeat: false
|
||||
onTriggered: {
|
||||
if (successFlow.checked) {
|
||||
modal.passwordSuccessfulyChanged()
|
||||
} else {
|
||||
modal.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LogsAndControlsPanel {
|
||||
SplitView.minimumHeight: 100
|
||||
SplitView.preferredHeight: 200
|
||||
SplitView.preferredWidth: 300
|
||||
|
||||
ColumnLayout {
|
||||
CheckBox {
|
||||
id: successFlow
|
||||
text: "%1 in 2 seconds".arg(successFlow.checked ? "Success" : "Error")
|
||||
checked: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// category: Popups
|
||||
|
||||
// https://www.figma.com/file/d0G7m8X6ELjQlFOEKQpn1g/Profile-WIP?type=design&node-id=11-111195&mode=design&t=j3guZtz78wkceVda-0
|
|
@ -12,21 +12,22 @@ _EXTRA_MENU_ITEM_OBJ_NAME = "-ExtraMenuItem"
|
|||
# These values are used to determine the dynamic `objectName` of the subsection item instead of using "design" properties like `text`.
|
||||
class SettingsSubsection(Enum):
|
||||
PROFILE: str = "0" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
CONTACTS: str = "1" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
ENS_USERNAMES: str = "2" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
MESSAGING: str = "3" + _APP_MENU_ITEM_OBJ_NAME
|
||||
WALLET: str = "4" + _APP_MENU_ITEM_OBJ_NAME
|
||||
APPEARANCE: str = "5" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
LANGUAGE: str = "6" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
NOTIFICATIONS: str = "7" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
DEVICE_SETTINGS: str = "8" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
BROWSER: str = "9" + _APP_MENU_ITEM_OBJ_NAME
|
||||
ADVANCED: str = "10" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
ABOUT: str = "11" + _EXTRA_MENU_ITEM_OBJ_NAME
|
||||
COMMUNITY: str = "12" + _APP_MENU_ITEM_OBJ_NAME
|
||||
KEYCARD: str = "13" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
SIGNOUT: str = "16" + _EXTRA_MENU_ITEM_OBJ_NAME
|
||||
BACKUP_SEED: str = "17" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
PASSWORD: str = "1" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
CONTACTS: str = "2" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
ENS_USERNAMES: str = "3" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
MESSAGING: str = "4" + _APP_MENU_ITEM_OBJ_NAME
|
||||
WALLET: str = "5" + _APP_MENU_ITEM_OBJ_NAME
|
||||
APPEARANCE: str = "6" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
LANGUAGE: str = "7" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
NOTIFICATIONS: str = "8" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
DEVICE_SETTINGS: str = "9" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
BROWSER: str = "10" + _APP_MENU_ITEM_OBJ_NAME
|
||||
ADVANCED: str = "11" + _SETTINGS_MENU_ITEM_OBJ_NAME
|
||||
ABOUT: str = "12" + _EXTRA_MENU_ITEM_OBJ_NAME
|
||||
COMMUNITY: str = "13" + _APP_MENU_ITEM_OBJ_NAME
|
||||
KEYCARD: str = "14" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
SIGNOUT: str = "17" + _EXTRA_MENU_ITEM_OBJ_NAME
|
||||
BACKUP_SEED: str = "18" + _MAIN_MENU_ITEM_OBJ_NAME
|
||||
|
||||
# Main:
|
||||
navBarListView_Settings_navbar_StatusNavBarTabButton = {"checkable": True, "container": mainWindow_navBarListView_ListView, "objectName": "Settings-navbar", "type": "StatusNavBarTabButton", "visible": True}
|
||||
|
|
|
@ -93,6 +93,7 @@ Rectangle {
|
|||
property alias subTitleBadgeComponent: subTitleBadgeLoader.sourceComponent
|
||||
property alias errorIcon: errorIcon
|
||||
property alias statusListItemTagsRowLayout: statusListItemSubtitleTagsRow
|
||||
property bool showLoadingIndicator: false
|
||||
|
||||
property int subTitleBadgeLoaderAlignment: Qt.AlignVCenter
|
||||
|
||||
|
@ -113,7 +114,9 @@ Rectangle {
|
|||
}
|
||||
return Math.max(64, statusListItemTitleArea.height + 90)
|
||||
}
|
||||
color: {
|
||||
color: bgColor
|
||||
|
||||
property color bgColor: {
|
||||
if (sensor.containsMouse || root.highlighted) {
|
||||
switch(type) {
|
||||
case StatusListItem.Type.Primary:
|
||||
|
@ -153,6 +156,8 @@ Rectangle {
|
|||
acceptedButtons: Qt.NoButton
|
||||
hoverEnabled: true
|
||||
|
||||
|
||||
|
||||
StatusSmartIdenticon {
|
||||
id: iconOrImage
|
||||
anchors.left: parent.left
|
||||
|
@ -160,9 +165,9 @@ Rectangle {
|
|||
anchors.verticalCenter: parent.verticalCenter
|
||||
asset: root.asset
|
||||
name: root.title
|
||||
active: root.asset.isLetterIdenticon ||
|
||||
active: ((root.asset.isLetterIdenticon ||
|
||||
!!root.asset.name ||
|
||||
!!root.asset.emoji
|
||||
!!root.asset.emoji) && !root.showLoadingIndicator)
|
||||
badge.border.color: root.color
|
||||
ringSettings: root.ringSettings
|
||||
loading: root.loading
|
||||
|
@ -170,6 +175,19 @@ Rectangle {
|
|||
onClicked: root.iconClicked(mouse)
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: loadingIndicator
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: root.leftPadding
|
||||
anchors.top: statusListItemTitleArea.top
|
||||
active: root.showLoadingIndicator
|
||||
sourceComponent: StatusLoadingIndicator {
|
||||
width: 24
|
||||
height: 24
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: statusListItemTitleArea
|
||||
|
||||
|
@ -181,9 +199,9 @@ Rectangle {
|
|||
return !root.titleAsideText && !isIconsRowVisible ? statusListItemTitleArea.right : undefined
|
||||
}
|
||||
|
||||
anchors.left: iconOrImage.active ? iconOrImage.right : parent.left
|
||||
anchors.left: iconOrImage.active ? iconOrImage.right : loadingIndicator.active ? loadingIndicator.right : parent.left
|
||||
anchors.right: statusListItemLabel.visible ? statusListItemLabel.left : statusListItemComponentsSlot.left
|
||||
anchors.leftMargin: iconOrImage.active ? 16 : root.leftPadding
|
||||
anchors.leftMargin: iconOrImage.active ? 16 : loadingIndicator.active ? 6 : root.leftPadding
|
||||
anchors.rightMargin: Math.max(root.rightPadding, titleIconsRow.requiredWidth)
|
||||
anchors.verticalCenter: bottomModel.length === 0 ? parent.verticalCenter : undefined
|
||||
|
||||
|
@ -291,7 +309,7 @@ Rectangle {
|
|||
Loader {
|
||||
id: subTitleBadgeLoader
|
||||
Layout.alignment: root.subTitleBadgeLoaderAlignment
|
||||
visible: sourceComponent
|
||||
visible: sourceComponent && !root.showLoadingIndicator
|
||||
}
|
||||
|
||||
StatusTextWithLoadingState {
|
||||
|
|
|
@ -59,6 +59,7 @@ Loader {
|
|||
root.asset.bgColor
|
||||
image.fillMode: Image.PreserveAspectCrop
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: root.asset.imgStatus === Image.Error ||
|
||||
|
|
|
@ -7,7 +7,6 @@ import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|||
import utils 1.0
|
||||
import shared.views 1.0
|
||||
|
||||
import "../../Profile/views"
|
||||
import "../controls"
|
||||
import "../stores"
|
||||
|
||||
|
@ -44,6 +43,7 @@ Item {
|
|||
PasswordView {
|
||||
id: view
|
||||
Layout.preferredWidth: root.width - 2 * Style.current.bigPadding
|
||||
Layout.maximumWidth: 460
|
||||
Layout.fillHeight: true
|
||||
passwordStrengthScoreFunction: root.startupStore.getPasswordStrengthScore
|
||||
highSizeIntro: true
|
||||
|
|
|
@ -6,7 +6,7 @@ import QtQuick.Window 2.15
|
|||
import utils 1.0
|
||||
import shared 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.stores 1.0
|
||||
import shared.stores 1.0 as SharedStores
|
||||
import shared.popups.keycard 1.0
|
||||
import shared.stores.send 1.0
|
||||
|
||||
|
@ -38,7 +38,7 @@ StatusSectionLayout {
|
|||
required property TransactionStore transactionStore
|
||||
required property WalletAssetsStore walletAssetsStore
|
||||
required property CollectiblesStore collectiblesStore
|
||||
required property CurrenciesStore currencyStore
|
||||
required property SharedStores.CurrenciesStore currencyStore
|
||||
|
||||
backButtonName: root.store.backButtonName
|
||||
notificationCount: activityCenterStore.unreadNotificationsCount
|
||||
|
@ -147,6 +147,19 @@ StatusSectionLayout {
|
|||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: false
|
||||
asynchronous: true
|
||||
sourceComponent: ChangePasswordView {
|
||||
implicitWidth: parent.width
|
||||
implicitHeight: parent.height
|
||||
privacyStore: root.store.privacyStore
|
||||
passwordStrengthScoreFunction: SharedStores.RootStore.getPasswordStrengthScore
|
||||
contentWidth: d.contentWidth
|
||||
sectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.password)
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
active: false
|
||||
asynchronous: true
|
||||
|
|
|
@ -1,115 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
|
||||
import utils 1.0
|
||||
import shared 1.0
|
||||
import shared.views 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import "../views"
|
||||
|
||||
StatusModal {
|
||||
id: root
|
||||
|
||||
property var privacyStore
|
||||
signal passwordChanged()
|
||||
|
||||
function onChangePasswordResponse(success, errorMsg) {
|
||||
if (success) {
|
||||
if (Qt.platform.os === Constants.mac && localAccountSettings.storeToKeychainValue !== Constants.keychain.storedValue.never) {
|
||||
localAccountSettings.storeToKeychainValue = Constants.keychain.storedValue.notNow;
|
||||
}
|
||||
passwordChanged()
|
||||
}
|
||||
else {
|
||||
view.reset()
|
||||
view.errorMsgText = errorMsg
|
||||
console.warn("TODO: Display error message when change password action failure! ")
|
||||
}
|
||||
d.passwordProcessing = "";
|
||||
submitBtn.loading = false;
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
// We temporarly store the password during "changePassword" call
|
||||
// to store it to KeyChain after successfull change operation.
|
||||
property string passwordProcessing: ""
|
||||
|
||||
function submit() {
|
||||
submitBtn.loading = true
|
||||
// ChangePassword operation blocks the UI so loading = true; will never have any affect until changePassword/createPassword is done.
|
||||
// Getting around it with a small pause (timer) in order to get the desired behavior
|
||||
pause.start()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.privacyStore.privacyModule
|
||||
function onPasswordChanged(success: bool, errorMsg: string) {
|
||||
onChangePasswordResponse(success, errorMsg)
|
||||
}
|
||||
}
|
||||
|
||||
width: 480
|
||||
height: 546
|
||||
closePolicy: submitBtn.loading? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
hasCloseButton: !submitBtn.loading
|
||||
headerSettings.title: qsTr("Change password")
|
||||
|
||||
onOpened: view.reset()
|
||||
|
||||
PasswordView {
|
||||
id: view
|
||||
anchors {
|
||||
fill: parent
|
||||
topMargin: Style.current.padding
|
||||
bottomMargin: Style.current.padding
|
||||
leftMargin: Style.current.padding
|
||||
rightMargin: Style.current.padding
|
||||
}
|
||||
passwordStrengthScoreFunction: RootStore.getPasswordStrengthScore
|
||||
titleVisible: false
|
||||
introText: qsTr("Change password used to unlock Status on this device & sign transactions.")
|
||||
fixIntroTextWidth: true
|
||||
createNewPsw: false
|
||||
onReturnPressed: if(submitBtn.enabled) d.submit()
|
||||
}
|
||||
|
||||
rightButtons: [
|
||||
StatusButton {
|
||||
id: submitBtn
|
||||
objectName: "changePasswordModalSubmitButton"
|
||||
text: qsTr("Change password and restart Status")
|
||||
enabled: !submitBtn.loading && view.ready
|
||||
|
||||
property Timer sim: Timer {
|
||||
id: pause
|
||||
interval: 20
|
||||
onTriggered: {
|
||||
// Change current password call action to the backend
|
||||
d.passwordProcessing = view.newPswText
|
||||
root.privacyStore.changePassword(view.currentPswText, view.newPswText)
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: { d.submit() }
|
||||
}
|
||||
]
|
||||
|
||||
// By clicking anywhere outside password entries fields or focusable element in the view, it is needed to check if passwords entered matches
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
z: view.zBehind // Behind focusable components in the view
|
||||
onClicked: { view.checkPasswordMatches() }
|
||||
}
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
import QtQuick 2.13
|
||||
import QtQuick.Controls 2.13
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Popups 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
StatusModal {
|
||||
id: root
|
||||
width: 400
|
||||
height: 248
|
||||
|
||||
closePolicy: Popup.NoAutoClose
|
||||
hasCloseButton: false
|
||||
|
||||
showHeader: false
|
||||
contentItem: ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 45
|
||||
spacing: Style.current.halfPadding
|
||||
StatusIcon {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: 26
|
||||
Layout.preferredHeight: 26
|
||||
icon: "checkmark"
|
||||
color: Style.current.green
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: 18
|
||||
text: qsTr("<b>Password changed</b>")
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.pixelSize: 13
|
||||
color: Theme.palette.baseColor1
|
||||
text: qsTr("You need to sign in again using the new password.")
|
||||
}
|
||||
|
||||
StatusButton {
|
||||
id: submitBtn
|
||||
objectName:"changePasswordSuccessModalSignOutAndQuitButton"
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: qsTr("Sign out & Quit")
|
||||
onClicked: {
|
||||
//quits the app TODO: change this to logout instead when supported
|
||||
Qt.quit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ ModalPopup {
|
|||
type: StatusBaseButton.Type.Danger
|
||||
text: qsTr("Restart")
|
||||
anchors.bottom: parent.bottom
|
||||
onClicked: Qt.quit()
|
||||
onClicked: Utils.restartApplication();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.12
|
||||
import QtQml.Models 2.15
|
||||
|
||||
|
||||
import utils 1.0
|
||||
import shared 1.0
|
||||
import shared.views 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Popups.Dialog 0.1
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
import "../views"
|
||||
|
||||
StatusDialog {
|
||||
id: root
|
||||
|
||||
signal changePasswordRequested()
|
||||
// Currently this modal handles only the happy path
|
||||
// The error is handled in the caller
|
||||
function passwordSuccessfulyChanged() {
|
||||
d.dbEncryptionInProgress = false;
|
||||
d.passwordChanged = true;
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
function reset() {
|
||||
d.dbEncryptionInProgress = false;
|
||||
d.passwordChanged = false;
|
||||
}
|
||||
|
||||
property bool passwordChanged: false
|
||||
property bool dbEncryptionInProgress: false
|
||||
}
|
||||
|
||||
onClosed: {
|
||||
d.reset();
|
||||
}
|
||||
|
||||
width: 480
|
||||
height: 546
|
||||
closePolicy: d.passwordChanged || d.dbEncryptionInProgress ? Popup.NoAutoClose : Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 20
|
||||
StatusBaseText {
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
text: qsTr("Your data must now be re-encrypted with your new password. This process may take some time, during which you won’t be able to interact with the app. Do not quit the app or turn off your device. Doing so will lead to data corruption, loss of your Status profile and the inability to restart Status.")
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 76
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
visible: d.passwordChanged
|
||||
border.color: Theme.palette.successColor1
|
||||
color: Theme.palette.successColor1
|
||||
radius: 8
|
||||
opacity: .1
|
||||
}
|
||||
|
||||
StatusListItem {
|
||||
id: listItem
|
||||
anchors.fill: parent
|
||||
sensor.enabled: false
|
||||
visible: (d.dbEncryptionInProgress || d.passwordChanged)
|
||||
title: !d.dbEncryptionInProgress ? qsTr("Re-encryption complete") :
|
||||
qsTr("Re-encrypting your data with your new password...")
|
||||
subTitle: !d.dbEncryptionInProgress ? qsTr("Restart Status and log in using your new password") :
|
||||
qsTr("Do not quit the app of turn off your device")
|
||||
statusListItemSubTitle.customColor: !d.passwordChanged ? Style.current.red : Theme.palette.successColor1
|
||||
statusListItemIcon.active: d.passwordChanged
|
||||
asset.name: "checkmark-circle"
|
||||
asset.width: 24
|
||||
asset.height: 24
|
||||
asset.bgWidth: 0
|
||||
asset.bgHeight: 0
|
||||
asset.color: Theme.palette.successColor1
|
||||
showLoadingIndicator: (d.dbEncryptionInProgress && !d.passwordChanged)
|
||||
asset.isLetterIdenticon: false
|
||||
border.width: !d.passwordChanged ? 1 : 0
|
||||
border.color: Theme.palette.baseColor5
|
||||
color: d.passwordChanged ? "transparent" : bgColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
header: StatusDialogHeader {
|
||||
visible: true
|
||||
headline.title: qsTr("Change password")
|
||||
actions.closeButton.visible: !(d.passwordChanged || d.dbEncryptionInProgress)
|
||||
actions.closeButton.onClicked: root.close()
|
||||
}
|
||||
|
||||
footer: StatusDialogFooter {
|
||||
id: footer
|
||||
leftButtons: ObjectModel {
|
||||
StatusFlatButton {
|
||||
text: qsTr("Cancel")
|
||||
visible: !d.dbEncryptionInProgress && !d.passwordChanged
|
||||
textColor: Style.current.darkGrey
|
||||
onClicked: { root.close(); }
|
||||
}
|
||||
}
|
||||
rightButtons: ObjectModel {
|
||||
StatusButton {
|
||||
id: submitBtn
|
||||
objectName: "changePasswordModalSubmitButton"
|
||||
text: !d.dbEncryptionInProgress && !d.passwordChanged ? qsTr("Re-encrypt data using new password") : qsTr("Restart status")
|
||||
enabled: !d.dbEncryptionInProgress
|
||||
onClicked: {
|
||||
if (d.passwordChanged) {
|
||||
Utils.restartApplication();
|
||||
} else {
|
||||
d.dbEncryptionInProgress = true
|
||||
root.changePasswordRequested()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,3 +8,4 @@ RemoveKeypairPopup 1.0 RemoveKeypairPopup.qml
|
|||
TokenListPopup 1.0 TokenListPopup.qml
|
||||
WalletKeypairAccountMenu 1.0 WalletKeypairAccountMenu.qml
|
||||
WalletAddressMenu 1.0 WalletAddressMenu.qml
|
||||
ConfirmChangePasswordModal 1.0 ConfirmChangePasswordModal.qml
|
||||
|
|
|
@ -94,6 +94,9 @@ QtObject {
|
|||
append({subsection: Constants.settingsSubsection.profile,
|
||||
text: qsTr("Profile"),
|
||||
icon: "profile"})
|
||||
append({subsection: Constants.settingsSubsection.password,
|
||||
text: qsTr("Password"),
|
||||
icon: "profile"})
|
||||
append({subsection: Constants.settingsSubsection.keycard,
|
||||
text: qsTr("Keycard"),
|
||||
icon: "keycard"})
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Controls 2.14
|
||||
import QtQuick.Layouts 1.12
|
||||
|
||||
import shared.panels 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.stores 1.0
|
||||
import shared.views 1.0
|
||||
import utils 1.0
|
||||
|
||||
import StatusQ.Controls 0.1
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import StatusQ.Components 0.1
|
||||
|
||||
import AppLayouts.Profile.popups 1.0
|
||||
|
||||
SettingsContentBase {
|
||||
id: root
|
||||
|
||||
property var privacyStore
|
||||
property var passwordStrengthScoreFunction: function () {}
|
||||
|
||||
//TODO https://github.com/status-im/status-desktop/issues/13302
|
||||
// titleRowComponentLoader.sourceComponent: Item {
|
||||
// implicitWidth: 226
|
||||
// implicitHeight: 38
|
||||
// StatusSwitch {
|
||||
// LayoutMirroring.enabled: true
|
||||
// text: qsTr("Enable biometrics")
|
||||
// onToggled: {
|
||||
// //
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
PasswordView {
|
||||
id: choosePasswordForm
|
||||
|
||||
width: 507
|
||||
height: 660
|
||||
|
||||
createNewPsw: false
|
||||
title: qsTr("Change your password.")
|
||||
titleSize: 17
|
||||
contentAlignment: Qt.AlignLeft
|
||||
|
||||
passwordStrengthScoreFunction: root.passwordStrengthScoreFunction
|
||||
onReadyChanged: {
|
||||
submitBtn.enabled = ready
|
||||
}
|
||||
|
||||
onReturnPressed: {
|
||||
if (ready) {
|
||||
confirmPasswordChangePopup.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
StatusLinkText {
|
||||
text: qsTr("Clear & cancel")
|
||||
onClicked: {
|
||||
choosePasswordForm.reset();
|
||||
}
|
||||
}
|
||||
Item { Layout.fillWidth: true }
|
||||
StatusButton {
|
||||
id: submitBtn
|
||||
Layout.alignment: Qt.AlignRight
|
||||
objectName: "changePasswordModalSubmitButton"
|
||||
text: qsTr("Change password")
|
||||
enabled: choosePasswordForm.ready
|
||||
onClicked: { confirmPasswordChangePopup.open(); }
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmChangePasswordModal {
|
||||
id: confirmPasswordChangePopup
|
||||
onChangePasswordRequested: {
|
||||
root.privacyStore.changePassword(choosePasswordForm.currentPswText, choosePasswordForm.newPswText);
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.privacyStore.privacyModule
|
||||
function onPasswordChanged(success: bool, errorMsg: string) {
|
||||
if (success) {
|
||||
confirmPasswordChangePopup.passwordSuccessfulyChanged()
|
||||
return
|
||||
}
|
||||
|
||||
choosePasswordForm.reset()
|
||||
choosePasswordForm.errorMsgText = errorMsg
|
||||
confirmPasswordChangePopup.close()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -233,19 +233,6 @@ SettingsContentBase {
|
|||
socialLinksModel: root.profileStore.temporarySocialLinksModel
|
||||
}
|
||||
|
||||
Component {
|
||||
id: changePasswordModal
|
||||
ChangePasswordModal {
|
||||
privacyStore: root.privacyStore
|
||||
onPasswordChanged: Global.openPopup(successPopup)
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: successPopup
|
||||
ChangePasswordSuccessModal {}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: profilePreview
|
||||
ProfileDialog {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
AboutView 1.0 AboutView.qml
|
||||
LanguageView 1.0 LanguageView.qml
|
||||
AppearanceView 1.0 AppearanceView.qml
|
||||
NotificationsView 1.0 NotificationsView.qml
|
||||
CommunitiesView 1.0 CommunitiesView.qml
|
||||
BrowserView 1.0 BrowserView.qml
|
||||
ChangePasswordView 1.0 ChangePasswordView.qml
|
||||
CommunitiesView 1.0 CommunitiesView.qml
|
||||
LanguageView 1.0 LanguageView.qml
|
||||
NotificationsView 1.0 NotificationsView.qml
|
||||
SyncingView 1.0 SyncingView.qml
|
||||
|
|
|
@ -22,11 +22,11 @@ Item {
|
|||
spacing: Style.current.padding
|
||||
|
||||
PasswordView {
|
||||
Layout.minimumWidth: 460
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
passwordStrengthScoreFunction: RootStore.getPasswordStrengthScore
|
||||
highSizeIntro: true
|
||||
fixIntroTextWidth: true
|
||||
|
||||
newPswText: root.sharedKeycardModule.getNewPassword()
|
||||
confirmationPswText: root.sharedKeycardModule.getNewPassword()
|
||||
|
|
|
@ -19,11 +19,13 @@ ColumnLayout {
|
|||
property bool createNewPsw: true
|
||||
property string title: qsTr("Create a password")
|
||||
property bool titleVisible: true
|
||||
property real titleSize: 22
|
||||
property string introText: qsTr("Create a password to unlock Status on this device & sign transactions.")
|
||||
property string recoverText: qsTr("You will not be able to recover this password if it is lost.")
|
||||
property string strengthenText: qsTr("Minimum %n character(s). To strengthen your password consider including:", "", Constants.minPasswordLength)
|
||||
property bool highSizeIntro: false
|
||||
property bool fixIntroTextWidth: false
|
||||
|
||||
property int contentAlignment: Qt.AlignHCenter
|
||||
|
||||
property var passwordStrengthScoreFunction: function () {}
|
||||
|
||||
|
@ -80,8 +82,6 @@ ColumnLayout {
|
|||
readonly property var validatorRegexp: /^[!-~]{0,64}$/
|
||||
readonly property string validatorErrMessage: qsTr("Only letters, numbers, underscores and hyphens allowed")
|
||||
|
||||
readonly property int defaultInputWidth: 416
|
||||
|
||||
// Password strength categorization / validation
|
||||
function lowerCaseValidator(text) { return (/[a-z]/.test(text)) }
|
||||
function upperCaseValidator(text) { return (/[A-Z]/.test(text)) }
|
||||
|
@ -142,16 +142,17 @@ ColumnLayout {
|
|||
function isTooShort() { return newPswInput.text.length < Constants.minPasswordLength }
|
||||
}
|
||||
|
||||
implicitWidth: 460
|
||||
spacing: Style.current.bigPadding
|
||||
z: root.zFront
|
||||
|
||||
// View visual content:
|
||||
StatusBaseText {
|
||||
id: title
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.alignment: root.contentAlignment
|
||||
visible: root.titleVisible
|
||||
text: root.title
|
||||
font.pixelSize: 22
|
||||
font.pixelSize: root.titleSize
|
||||
font.bold: true
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
@ -159,16 +160,16 @@ ColumnLayout {
|
|||
ColumnLayout {
|
||||
id: introColumn
|
||||
|
||||
Layout.preferredWidth: root.fixIntroTextWidth ? d.defaultInputWidth : parent.width
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: root.contentAlignment
|
||||
spacing: 4
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.alignment: root.contentAlignment
|
||||
|
||||
text: root.introText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
horizontalAlignment: root.contentAlignment
|
||||
font.pixelSize: root.highSizeIntro ? Style.current.primaryTextFontSize : Style.current.tertiaryTextFontSize
|
||||
wrapMode: Text.WordWrap
|
||||
color: Theme.palette.baseColor1
|
||||
|
@ -176,10 +177,10 @@ ColumnLayout {
|
|||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.alignment: root.contentAlignment
|
||||
|
||||
text: root.recoverText
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
horizontalAlignment: root.contentAlignment
|
||||
font.pixelSize: root.highSizeIntro ? Style.current.primaryTextFontSize : Style.current.tertiaryTextFontSize
|
||||
wrapMode: Text.WordWrap
|
||||
color: Theme.palette.dangerColor1
|
||||
|
@ -194,8 +195,8 @@ ColumnLayout {
|
|||
|
||||
z: root.zFront
|
||||
visible: !root.createNewPsw
|
||||
Layout.preferredWidth: d.defaultInputWidth
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: root.contentAlignment
|
||||
placeholderText: qsTr("Current password")
|
||||
echoMode: showPassword ? TextInput.Normal : TextInput.Password
|
||||
rightPadding: showHideCurrentIcon.width + showHideCurrentIcon.anchors.rightMargin + Style.current.padding / 2
|
||||
|
@ -219,7 +220,8 @@ ColumnLayout {
|
|||
ColumnLayout {
|
||||
spacing: 4
|
||||
z: root.zFront
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: root.contentAlignment
|
||||
|
||||
StatusPasswordInput {
|
||||
id: newPswInput
|
||||
|
@ -227,8 +229,8 @@ ColumnLayout {
|
|||
|
||||
property bool showPassword
|
||||
|
||||
Layout.preferredWidth: d.defaultInputWidth
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.alignment: root.contentAlignment
|
||||
Layout.fillWidth: true
|
||||
placeholderText: qsTr("New password")
|
||||
echoMode: showPassword ? TextInput.Normal : TextInput.Password
|
||||
rightPadding: showHideNewIcon.width + showHideNewIcon.anchors.rightMargin + Style.current.padding / 2
|
||||
|
@ -269,6 +271,7 @@ ColumnLayout {
|
|||
|
||||
StatusPasswordStrengthIndicator {
|
||||
id: strengthInditactor
|
||||
Layout.fillWidth: true
|
||||
value: Math.min(Constants.minPasswordLength, newPswInput.text.length)
|
||||
from: 0
|
||||
to: Constants.minPasswordLength
|
||||
|
@ -280,46 +283,66 @@ ColumnLayout {
|
|||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: strengthenTxt
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
text: root.strengthenText
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.baseColor1
|
||||
clip: true
|
||||
}
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.minimumHeight: 80
|
||||
border.color: Theme.palette.baseColor2
|
||||
border.width: 1
|
||||
radius: Style.current.radius
|
||||
implicitHeight: strengthColumn.implicitHeight
|
||||
implicitWidth: strengthColumn.implicitWidth
|
||||
|
||||
RowLayout {
|
||||
spacing: Style.current.padding
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
ColumnLayout {
|
||||
id: strengthColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Style.current.padding
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Style.current.padding
|
||||
|
||||
StatusBaseText {
|
||||
id: lowerCaseTxt
|
||||
text: "• " + qsTr("Lower case")
|
||||
font.pixelSize: 12
|
||||
color: d.containsLower ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
id: strengthenTxt
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
text: root.strengthenText
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.baseColor1
|
||||
clip: true
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: upperCaseTxt
|
||||
text: "• " + qsTr("Upper case")
|
||||
font.pixelSize: 12
|
||||
color: d.containsUpper ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
RowLayout {
|
||||
spacing: Style.current.padding
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
StatusBaseText {
|
||||
id: numbersTxt
|
||||
text: "• " + qsTr("Numbers")
|
||||
font.pixelSize: 12
|
||||
color: d.containsNumbers ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
StatusBaseText {
|
||||
id: lowerCaseTxt
|
||||
text: "• " + qsTr("Lower case")
|
||||
font.pixelSize: 12
|
||||
color: d.containsLower ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: symbolsTxt
|
||||
text: "• " + qsTr("Symbols")
|
||||
font.pixelSize: 12
|
||||
color: d.containsSymbols ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
StatusBaseText {
|
||||
id: upperCaseTxt
|
||||
text: "• " + qsTr("Upper case")
|
||||
font.pixelSize: 12
|
||||
color: d.containsUpper ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: numbersTxt
|
||||
text: "• " + qsTr("Numbers")
|
||||
font.pixelSize: 12
|
||||
color: d.containsNumbers ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: symbolsTxt
|
||||
text: "• " + qsTr("Symbols")
|
||||
font.pixelSize: 12
|
||||
color: d.containsSymbols ? Theme.palette.successColor1 : Theme.palette.baseColor1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -330,8 +353,8 @@ ColumnLayout {
|
|||
property bool showPassword
|
||||
|
||||
z: root.zFront
|
||||
Layout.preferredWidth: d.defaultInputWidth
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: root.contentAlignment
|
||||
placeholderText: qsTr("Confirm password")
|
||||
echoMode: showPassword ? TextInput.Normal : TextInput.Password
|
||||
rightPadding: showHideConfirmIcon.width + showHideConfirmIcon.anchors.rightMargin + Style.current.padding / 2
|
||||
|
@ -376,7 +399,7 @@ ColumnLayout {
|
|||
|
||||
StatusBaseText {
|
||||
id: errorTxt
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.alignment: root.contentAlignment
|
||||
Layout.fillHeight: true
|
||||
font.pixelSize: 12
|
||||
color: Theme.palette.dangerColor1
|
||||
|
|
|
@ -331,25 +331,26 @@ QtObject {
|
|||
|
||||
readonly property QtObject settingsSubsection: QtObject {
|
||||
readonly property int profile: 0
|
||||
readonly property int contacts: 1
|
||||
readonly property int ensUsernames: 2
|
||||
readonly property int messaging: 3
|
||||
readonly property int wallet: 4
|
||||
readonly property int appearance: 5
|
||||
readonly property int language: 6
|
||||
readonly property int notifications: 7
|
||||
readonly property int syncingSettings: 8
|
||||
readonly property int browserSettings: 9
|
||||
readonly property int advanced: 10
|
||||
readonly property int about: 11
|
||||
readonly property int communitiesSettings: 12
|
||||
readonly property int keycard: 13
|
||||
readonly property int about_terms: 14 // a subpage under "About"
|
||||
readonly property int about_privacy: 15 // a subpage under "About"
|
||||
readonly property int password: 1
|
||||
readonly property int contacts: 2
|
||||
readonly property int ensUsernames: 3
|
||||
readonly property int messaging: 4
|
||||
readonly property int wallet:5
|
||||
readonly property int appearance: 6
|
||||
readonly property int language: 7
|
||||
readonly property int notifications: 8
|
||||
readonly property int syncingSettings: 9
|
||||
readonly property int browserSettings: 10
|
||||
readonly property int advanced: 11
|
||||
readonly property int about: 12
|
||||
readonly property int communitiesSettings: 13
|
||||
readonly property int keycard: 14
|
||||
readonly property int about_terms: 15 // a subpage under "About"
|
||||
readonly property int about_privacy: 16 // a subpage under "About"
|
||||
|
||||
// special treatment; these do not participate in the main settings' StackLayout
|
||||
readonly property int signout: 16
|
||||
readonly property int backUpSeed: 17
|
||||
readonly property int signout: 17
|
||||
readonly property int backUpSeed: 18
|
||||
}
|
||||
|
||||
readonly property QtObject walletSettingsSubsection: QtObject {
|
||||
|
|
|
@ -17,6 +17,10 @@ QtObject {
|
|||
readonly property int maxImgSizeBytes: Constants.maxUploadFilesizeMB * 1048576 /* 1 MB in bytes */
|
||||
readonly property int communityIdLength: 68
|
||||
|
||||
function restartApplication() {
|
||||
globalUtilsInst.restartApplication()
|
||||
}
|
||||
|
||||
function isDigit(value) {
|
||||
return /^\d$/.test(value);
|
||||
}
|
||||
|
|
|
@ -127,6 +127,9 @@ DOS_API void DOS_CALL dos_qguiapplication_icon(const char *filename);
|
|||
/// \note A QGuiApplication should have been already created through dos_qguiapplication_create()
|
||||
DOS_API void DOS_CALL dos_qguiapplication_quit(void);
|
||||
|
||||
/// @brief Calls the QGuiApplication::quit() function of the current QGuiApplication and QProcess::startDetached to spawn another process
|
||||
DOS_API void DOS_CALL dos_qguiapplication_restart(void);
|
||||
|
||||
/// \brief Free the memory of the current QGuiApplication
|
||||
/// \note A QGuiApplication should have been already created through dos_qguiapplication_create()
|
||||
DOS_API void DOS_CALL dos_qguiapplication_delete(void);
|
||||
|
|
|
@ -371,6 +371,12 @@ void dos_qguiapplication_quit()
|
|||
QMetaObject::invokeMethod(qGuiApp, "quit", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void dos_qguiapplication_restart()
|
||||
{
|
||||
QProcess::startDetached(QCoreApplication::applicationFilePath());
|
||||
dos_qguiapplication_quit();
|
||||
}
|
||||
|
||||
void dos_qguiapplication_icon(const char *filename)
|
||||
{
|
||||
qGuiApp->setWindowIcon(QIcon(filename));
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2d733c5ec6977edac451c4d0017911b59d01a310
|
||||
Subproject commit 13a8890db484d3ff40b410c2ff4b3e3bd2e0e880
|
Loading…
Reference in New Issue