feat(onboarding): ading create new keys screens

Closes #4956
This commit is contained in:
Alexandra Betouni 2022-03-08 00:59:38 +02:00 committed by Alexandra Betouni
parent 768b10e03f
commit dcab50fe09
32 changed files with 867 additions and 254 deletions

View File

@ -4,13 +4,15 @@ type
alias: string
identicon: string
address: string
pubKey: string
keyUid: string
proc initItem*(id, alias, identicon, address, keyUid: string): Item =
proc initItem*(id, alias, identicon, address, pubKey, keyUid: string): Item =
result.id = id
result.alias = alias
result.identicon = identicon
result.address = address
result.pubKey = pubKey
result.keyUid = keyUid
proc getId*(self: Item): string =
@ -25,5 +27,8 @@ proc getIdenticon*(self: Item): string =
proc getAddress*(self: Item): string =
return self.address
proc getPubKey*(self: Item): string =
return self.pubKey
proc getKeyUid*(self: Item): string =
return self.keyUid

View File

@ -8,6 +8,7 @@ type
Alias
Identicon
Address
PubKey
KeyUid
QtObject:
@ -35,6 +36,7 @@ QtObject:
ModelRole.Alias.int:"username",
ModelRole.Identicon.int:"identicon",
ModelRole.Address.int:"address",
ModelRole.PubKey.int:"pubKey",
ModelRole.KeyUid.int:"keyUid"
}.toTable
@ -57,6 +59,8 @@ QtObject:
result = newQVariant(item.getIdenticon())
of ModelRole.Address:
result = newQVariant(item.getAddress())
of ModelRole.PubKey:
result = newQVariant(item.getPubKey())
of ModelRole.KeyUid:
result = newQVariant(item.getKeyUid())

View File

@ -41,7 +41,7 @@ method load*(self: Module) =
let generatedAccounts = self.controller.getGeneratedAccounts()
var accounts: seq[Item]
for acc in generatedAccounts:
accounts.add(initItem(acc.id, acc.alias, acc.identicon, acc.address, acc.keyUid))
accounts.add(initItem(acc.id, acc.alias, acc.identicon, acc.address, acc.publicKey, acc.keyUid))
self.view.setAccountList(accounts)

View File

@ -60,6 +60,13 @@ QtObject:
read = getImportedAccountAddress
notify = importedAccountChanged
proc getImportedAccountPubKey*(self: View): string {.slot.} =
return self.delegate.getImportedAccount().publicKey
QtProperty[string] importedAccountPubKey:
read = getImportedAccountPubKey
notify = importedAccountChanged
proc setDisplayName*(self: View, displayName: string) {.slot.} =
self.delegate.setDisplayName(displayName)

View File

@ -8,6 +8,8 @@ import "stores"
QtObject {
id: root
property bool hasAccounts
property string keysMainSetState: ""
signal loadApp()
signal onBoardingStepChanged(var view, string state)
@ -18,38 +20,78 @@ QtObject {
DSM.State {
id: onboardingState
initialState: root.hasAccounts ? stateLogin : keysMainState
initialState: root.hasAccounts ? stateLogin : welcomeMainState
DSM.State {
id: welcomeMainState
onEntered: { onBoardingStepChanged(welcomeMain, ""); }
DSM.SignalTransition {
targetState: keysMainState
signal: Global.applicationWindow.navigateTo
guard: path === "KeyMain"
}
}
DSM.State {
id: keysMainState
onEntered: { onBoardingStepChanged(welcomeMain, ""); }
onEntered: { onBoardingStepChanged(keysMain, root.keysMainSetState); }
DSM.SignalTransition {
targetState: genKeyState
signal: Global.applicationWindow.navigateTo
guard: path === "GenKey"
}
DSM.SignalTransition {
targetState: welcomeMainState
signal: Global.applicationWindow.navigateTo
guard: path === "Welcome"
}
}
DSM.State {
id: existingKeyState
onEntered: { onBoardingStepChanged(existingKey, ""); }
DSM.SignalTransition {
targetState: genKeyState
signal: Global.applicationWindow.navigateTo
guard: path === "GenKey"
}
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state === Constants.appState.main
}
DSM.SignalTransition {
targetState: welcomeMainState
signal: Global.applicationWindow.navigateTo
guard: path === "Welcome"
}
}
DSM.State {
id: genKeyState
onEntered: { onBoardingStepChanged(genKey, ""); }
DSM.SignalTransition {
targetState: welcomeMainState
signal: Global.applicationWindow.navigateTo
guard: path === "Welcome"
}
DSM.SignalTransition {
targetState: appState
signal: startupModule.appStateChanged
guard: state === Constants.appState.main
signal: Global.applicationWindow.navigateTo
guard: path === "LoggedIn"
}
DSM.SignalTransition {
targetState: stateLogin
signal: Global.applicationWindow.navigateTo
guard: path === "LogIn"
}
}
@ -109,36 +151,24 @@ QtObject {
guard: path === "InitialState"
}
DSM.SignalTransition {
targetState: existingKeyState
signal: Global.applicationWindow.navigateTo
guard: path === "ExistingKey"
}
DSM.SignalTransition {
targetState: keysMainState
signal: Global.applicationWindow.navigateTo
guard: path === "KeysMain"
}
DSM.SignalTransition {
targetState: existingKeyState
signal: Global.applicationWindow.navigateTo
guard: path === "ExistingKey"
}
DSM.SignalTransition {
targetState: keycardState
signal: Global.applicationWindow.navigateTo
guard: path === "KeycardFlowSelection"
}
DSM.SignalTransition {
targetState: createPasswordState
signal: applicationWindow.navigateTo
guard: path === "CreatePassword"
}
DSM.SignalTransition {
targetState: confirmPasswordState
signal: applicationWindow.navigateTo
guard: path === "ConfirmPassword"
}
DSM.FinalState {
id: onboardingDoneState
}
@ -159,10 +189,12 @@ QtObject {
id: welcomeMain
WelcomeView {
onBtnNewUserClicked: {
onBoardingStepChanged(keysMain, "getkeys");
root.keysMainSetState = "getkeys";
Global.applicationWindow.navigateTo("KeyMain");
}
onBtnExistingUserClicked: {
onBoardingStepChanged(keysMain, "connectkeys");
root.keysMainSetState = "connectkeys";
Global.applicationWindow.navigateTo("KeyMain");
}
}
}
@ -180,7 +212,7 @@ QtObject {
Global.applicationWindow.navigateTo("ExistingKey");
}
onBackClicked: {
onBoardingStepChanged(welcomeMain, "");
Global.applicationWindow.navigateTo("Welcome");
}
}
}
@ -188,7 +220,9 @@ QtObject {
property var existingKeyComponent: Component {
id: existingKey
ExistingKeyView {
onShowCreatePasswordView: { Global.applicationWindow.navigateTo("CreatePassword") }
onShowCreatePasswordView: {
Global.applicationWindow.navigateTo("GenKey");
}
onClosed: function () {
if (root.hasAccounts) {
Global.applicationWindow.navigateTo("InitialState")
@ -202,14 +236,16 @@ QtObject {
property var genKeyComponent: Component {
id: genKey
GenKeyView {
onShowCreatePasswordView: { Global.applicationWindow.navigateTo("CreatePassword") }
onClosed: function () {
if (root.hasAccounts) {
Global.applicationWindow.navigateTo("InitialState")
onFinished: {
if (LoginStore.currentAccount.username !== "") {
Global.applicationWindow.navigateTo("LogIn");
} else {
Global.applicationWindow.navigateTo("KeysMain")
Global.applicationWindow.navigateTo("KeysMain");
}
}
onKeysGenerated: {
Global.applicationWindow.navigateTo("LoggedIn")
}
}
}
@ -237,39 +273,4 @@ QtObject {
}
}
}
property var d: QtObject {
property string newPassword
property string confirmationPassword
}
property var createPasswordComponent: Component {
id: createPassword
CreatePasswordView {
store: OnboardingStore
newPassword: d.newPassword
confirmationPassword: d.confirmationPassword
onPasswordCreated: {
d.newPassword = newPassword
d.confirmationPassword = confirmationPassword
applicationWindow.navigateTo("ConfirmPassword")
}
onBackClicked: {
d.newPassword = ""
d.confirmationPassword = ""
applicationWindow.navigateTo("InitialState");
console.warn("TODO: Integration with onboarding flow!")
}
}
}
property var confirmPasswordComponent: Component {
id: confirmPassword
ConfirmPasswordView {
password: d.newPassword
onBackClicked: { applicationWindow.navigateTo("CreatePassword") }
}
}
}

View File

@ -0,0 +1,27 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import StatusQ.Controls 0.1
import utils 1.0
Page {
id: root
signal backClicked()
signal finished()
background: Rectangle {
color: Style.current.background
}
StatusRoundButton {
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.current.padding
icon.name: "arrow-left"
onClicked: {
root.backClicked();
}
}
}

View File

@ -43,7 +43,7 @@ ModalPopup {
anchors.top: info.bottom
anchors.topMargin: Style.current.bigPadding
anchors.horizontalCenter: parent.horizontalCenter
image.source: OnboardingStore.onBoardingModul.importedAccountIdenticon
image.source: OnboardingStore.onboardingModuleInst.importedAccountIdenticon
image.width: 60
image.height: 60
}
@ -53,7 +53,7 @@ ModalPopup {
anchors.top: identicon.bottom
anchors.topMargin: Style.current.padding
anchors.horizontalCenter: identicon.horizontalCenter
text: OnboardingStore.onBoardingModul.importedAccountAlias
text: OnboardingStore.onboardingModuleInst.importedAccountAlias
font.weight: Font.Bold
font.pixelSize: 15
}
@ -62,7 +62,7 @@ ModalPopup {
anchors.top: username.bottom
anchors.topMargin: Style.current.halfPadding
anchors.horizontalCenter: username.horizontalCenter
text: OnboardingStore.onBoardingModul.importedAccountAddress
text: OnboardingStore.onboardingModuleInst.importedAccountAddress
width: 120
}

View File

@ -0,0 +1,98 @@
import QtQuick 2.13
import QtQuick.Dialogs 1.3
import utils 1.0
import StatusQ.Controls 0.1
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import "../stores"
// TODO: replace with StatusModal
ModalPopup {
id: popup
title: qsTr("Upload profile picture")
property string selectedImage
property string uploadError
onSelectedImageChanged: {
if (!selectedImage) {
return;
}
cropImageModal.open();
}
Item {
anchors.fill: parent
RoundedImage {
id: profilePic
source: selectedImage
width: 160
height: 160
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
border.width: 1
border.color: Style.current.border
onClicked: imageDialog.open();
}
StyledText {
visible: !!uploadError
text: uploadError
anchors.left: parent.left
anchors.right: parent.right
anchors.top: profilePic.bottom
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 13
wrapMode: Text.WordWrap
anchors.topMargin: 13
font.weight: Font.Thin
color: Style.current.danger
}
ImageCropperModal {
id: cropImageModal
selectedImage: popup.selectedImage
ratio: "1:1"
onCropFinished: {
OnboardingStore.uploadImage(selectedImage, aX, aY, bX, bY);
}
}
}
footer: Item {
width: parent.width
height: uploadBtn.height
StatusButton {
id: uploadBtn
text: !!selectedImage ? qsTr("Done") : qsTr("Upload")
anchors.right: parent.right
anchors.bottom: parent.bottom
onClicked: {
if (!!selectedImage) {
close();
} else {
imageDialog.open();
}
}
FileDialog {
id: imageDialog
title: qsTrId("please-choose-an-image")
folder: shortcuts.pictures
nameFilters: [
qsTrId("image-files----jpg---jpeg---png-")
]
onAccepted: {
selectedImage = imageDialog.fileUrls[0];
}
}
}
}
}

View File

@ -10,6 +10,8 @@ import shared.panels 1.0
import shared.popups 1.0
import shared.controls 1.0
import "../stores"
// TODO: replace with StatusModal
ModalPopup {
property var privacyStore
@ -153,7 +155,7 @@ ModalPopup {
}
Connections {
target: onboardingModule
target: OnboardingStore.onboardingModuleInst
onAccountSetupError: {
importLoginError.open()
}
@ -169,15 +171,15 @@ ModalPopup {
passwordValidationError = qsTr("Incorrect password")
}
else {
Global.applicationWindow.prepareForStoring(repeatPasswordField.text, true)
//Global.applicationWindow.prepareForStoring(repeatPasswordField.text, true)
popup.close()
}
}
else
{
loading = true
onboardingModule.storeSelectedAccountAndLogin(repeatPasswordField.text);
Global.applicationWindow.prepareForStoring(repeatPasswordField.text, false)
OnboardingStore.onboardingModuleInst.storeSelectedAccountAndLogin(repeatPasswordField.text);
//Global.applicationWindow.prepareForStoring(repeatPasswordField.text, false)
}
}
}

View File

@ -1,22 +1,69 @@
pragma Singleton
import QtQuick 2.13
import utils 1.0
QtObject {
property var onBoardingModul: onboardingModule
id: root
property var profileSectionModuleInst: profileSectionModule
property var profileModule: profileSectionModuleInst.profileModule
property var onboardingModuleInst: onboardingModule
property var mainModuleInst: !!mainModule ? mainModule : undefined
property var accountSettings: localAccountSettings
property var privacyModule: profileSectionModuleInst.privacyModule
property string displayName: userProfile !== undefined ? userProfile.displayName : ""
property url profImgUrl: ""
property real profImgAX: 0.0
property real profImgAY: 0.0
property real profImgBX: 0.0
property real profImgBY: 0.0
property bool accountCreated: false
property bool showBeforeGetStartedPopup: true
function importMnemonic(mnemonic) {
onBoardingModul.importMnemonic(mnemonic)
onboardingModuleInst.importMnemonic(mnemonic)
}
function setCurrentAccountAndDisplayName(selectedAccountIdx, displayName) {
onBoardingModul.setDisplayName(displayName)
onBoardingModul.setSelectedAccountByIndex(selectedAccountIdx)
onboardingModuleInst.setDisplayName(displayName)
onboardingModuleInst.setSelectedAccountByIndex(selectedAccountIdx)
}
function getPasswordStrengthScore(password) {
let userName = onBoardingModul.importedAccountAlias
return onBoardingModul.getPasswordStrengthScore(password, userName)
function updatedDisplayName(displayName) {
if (displayName !== root.displayName) {
print(displayName, root.displayName)
root.profileModule.setDisplayName(displayName);
}
}
function saveImage() {
root.profileModule.upload(root.profImgUrl, root.profImgAX, root.profImgAY, root.profImgBX, root.profImgBY);
}
function uploadImage(source, aX, aY, bX, bY) {
root.profImgUrl = source;
root.profImgAX = aX;
root.profImgAY = aY;
root.profImgBX = bX;
root.profImgBY = bY;
}
function removeImage() {
return root.profileModule.remove();
}
function finishCreatingAccount(pass) {
root.onboardingModuleInst.storeSelectedAccountAndLogin(pass);
}
function storeToKeyChain(pass) {
mainModule.storePassword(pass);
}
function changePassword(password, newPassword) {
root.privacyModule.changePassword(password, newPassword)
}
property ListModel accountsSampleData: ListModel {

View File

@ -12,18 +12,15 @@ import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import "../stores"
import "../controls"
Page {
OnboardingBasePage {
id: root
property string password
signal backClicked()
anchors.fill: parent
background: null
Component.onCompleted: confPswInput.forceActiveFocus(Qt.MouseFocusReason)
property string tmpPass
property string displayName
function forcePswInputFocus() { confPswInput.forceActiveFocus(Qt.MouseFocusReason)}
Column {
id: view
@ -73,9 +70,7 @@ Page {
width: parent.width
enabled: !submitBtn.loading
placeholderText: submitBtn.loading ?
qsTr("Connecting...") :
qsTr("Confirm you password (again)")
placeholderText: qsTr("Confirm you password (again)")
textField.echoMode: showPassword ? TextInput.Normal : TextInput.Password
textField.validator: RegExpValidator { regExp: /^[!-~]{0,64}$/ } // That incudes NOT extended ASCII printable characters less space and a maximum of 64 characters allowed
keepHeight: true
@ -106,24 +101,34 @@ Page {
id: submitBtn
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Finalize Status Password Creation")
enabled: !submitBtn.loading && confPswInput.text === root.password
enabled:!submitBtn.loading && confPswInput.text === root.password
property Timer sim: Timer {
id: pause
interval: 20
onTriggered: {
// Create new password call action to the backend
OnboardingStore.onBoardingModul.storeSelectedAccountAndLogin(root.password)
Global.applicationWindow.prepareForStoring(root.password, false)
// Create account operation blocks the UI so loading = true; will never have any affect until it is done.
// Getting around it with a small pause (timer) in order to get the desired behavior
OnboardingStore.finishCreatingAccount(root.password)
}
}
onClicked: {
confPswInput.text = ""
submitBtn.loading = true
// Create password 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()
//confPswInput.text = ""
if (OnboardingStore.accountCreated) {
if (root.password !== root.tmpPass) {
OnboardingStore.changePassword(root.tmpPass, root.password);
root.tmpPass = root.password;
} else {
submitBtn.loading = false
root.finished();
}
} else {
root.tmpPass = root.password;
submitBtn.loading = true
OnboardingStore.setCurrentAccountAndDisplayName(0, root.displayName);
pause.start();
}
}
}
}
@ -138,4 +143,28 @@ Page {
icon.name: "arrow-left"
onClicked: { root.backClicked() }
}
Connections {
target: startupModule
onAppStateChanged: {
if (state === Constants.appState.main) {
if (!!OnboardingStore.profImgUrl) {
OnboardingStore.saveImage()
OnboardingStore.accountCreated = true;
}
submitBtn.loading = false
root.finished()
}
}
}
Connections {
target: OnboardingStore.privacyModule
onPasswordChanged: {
if (success) {
submitBtn.loading = false
root.finished();
}
}
}
}

View File

@ -1,28 +1,20 @@
import QtQuick 2.0
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.12
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import utils 1.0
import shared.views 1.0
import "../../Profile/views"
import "../controls"
Page {
OnboardingBasePage {
id: root
property var store
property string newPassword
property string confirmationPassword
signal passwordCreated(string newPassword, string confirmationPassword)
signal backClicked()
anchors.fill: parent
background: null
Component.onCompleted: { view.forceNewPswInputFocus() }
function forceNewPswInputFocus() { view.forceNewPswInputFocus() }
QtObject {
id: d
@ -34,21 +26,23 @@ Page {
spacing: 4 * Style.current.padding
anchors.centerIn: parent
z: view.zFront
PasswordView {
id: view
store: root.store
onboarding: true
newPswText: root.newPassword
confirmationPswText: root.confirmationPassword
}
StatusButton {
id: submitBtn
z: d.zFront
anchors.horizontalCenter: parent.horizontalCenter
text: qsTr("Create password")
enabled: view.ready
onClicked: { passwordCreated(view.newPswText, view.confirmationPswText) }
onClicked: {
root.newPassword = view.newPswText
root.confirmationPassword = view.confirmationPswText
root.finished()
}
}
}
@ -62,7 +56,6 @@ Page {
icon.name: "arrow-left"
onClicked: { root.backClicked() }
}
// 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

View File

@ -1,33 +1,102 @@
import QtQuick 2.13
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import "../popups"
import shared.panels 1.0
import utils 1.0
import "../controls"
import "../panels"
import "../stores"
import "../shared"
Item {
property var onClosed: function () {}
signal showCreatePasswordView()
id: genKeyView
OnboardingBasePage {
id: root
anchors.fill: parent
Behavior on opacity { NumberAnimation { duration: 200 }}
state: "username"
Component.onCompleted: {
genKeyModal.open()
signal keysGenerated()
function gotoKeysStack(stackIndex) { createKeysStack.currentIndex = stackIndex }
enum KeysStack {
DETAILS,
CREATE_PWD,
CONFRIM_PWD,
TOUCH_ID
}
GenKeyModal {
property bool wentNext: false
id: genKeyModal
onNextClick: function (selectedIndex, displayName) {
wentNext = true
OnboardingStore.setCurrentAccountAndDisplayName(selectedIndex, displayName)
showCreatePasswordView()
QtObject {
id: d
property string newPassword
property string confirmationPassword
}
StackLayout {
id: createKeysStack
anchors.fill: parent
currentIndex: GenKeyView.KeysStack.DETAILS
onCurrentIndexChanged: {
// Set focus:
if(currentIndex === GenKeyView.KeysStack.CREATE_PWD)
createPswView.forceNewPswInputFocus()
else if(currentIndex === GenKeyView.KeysStack.CONFRIM_PWD)
confirmPswView.forcePswInputFocus()
}
onClosed: function () {
if (!wentNext) {
genKeyView.onClosed()
InsertDetailsView {
id: userDetailsPanel
onCreatePassword: { gotoKeysStack(GenKeyView.KeysStack.CREATE_PWD) }
}
CreatePasswordView {
id: createPswView
newPassword: d.newPassword
confirmationPassword: d.confirmationPassword
onFinished: {
d.newPassword = newPassword
d.confirmationPassword = confirmationPassword
gotoKeysStack(GenKeyView.KeysStack.CONFRIM_PWD)
}
onBackClicked: {
d.newPassword = ""
d.confirmationPassword = ""
gotoKeysStack(GenKeyView.KeysStack.DETAILS)
}
}
ConfirmPasswordView {
id: confirmPswView
password: d.newPassword
displayName: userDetailsPanel.displayName
onFinished: {
if (Qt.platform.os == "osx") {
gotoKeysStack(GenKeyView.KeysStack.TOUCH_ID);
} else {
root.keysGenerated();
}
}
onBackClicked: { gotoKeysStack(GenKeyView.KeysStack.CREATE_PWD) }
}
TouchIDAuthView {
userPass: d.newPassword
onBackClicked: { gotoKeysStack(GenKeyView.KeysStack.CONFRIM_PWD) }
onGenKeysDone: { root.keysGenerated() }
}
}
onBackClicked: {
if (userDetailsPanel.state === "chatkey") {
userDetailsPanel.state = "username";
} else {
root.finished();
}
}
}

View File

@ -0,0 +1,251 @@
import QtQuick 2.13
import QtQuick.Layouts 1.12
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import shared.panels 1.0
import utils 1.0
import shared.controls 1.0
import "../popups"
import "../stores"
Item {
id: root
property string pubKey
property string address
property string displayName
signal createPassword()
state: "username"
ListView {
id: accountsList
model: OnboardingStore.onboardingModuleInst.accountsModel
delegate: Item {
Component.onCompleted: {
root.pubKey = model.pubKey;
root.address = model.address;
}
}
}
ColumnLayout {
anchors.centerIn: parent
spacing: Style.current.padding
StyledText {
id: usernameText
text: qsTr("Your profile")
font.weight: Font.Bold
font.pixelSize: 22
Layout.alignment: Qt.AlignHCenter
}
StyledText {
id: txtDesc
Layout.preferredWidth: (root.state === "username") ? 338 : 643
color: Style.current.secondaryText
text: qsTr("Longer and unusual names are better as they are less likely to be used by someone else.")
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
Layout.alignment: Qt.AlignHCenter
font.pixelSize: 15
}
Item {
implicitWidth: 100
implicitHeight: 100
Layout.alignment: Qt.AlignHCenter
StatusSmartIdenticon {
id: userImage
image.width: 80
image.height: 80
icon.width: 80
icon.height: 80
icon.letterSize: 32
icon.color: Theme.palette.miscColor5
icon.charactersLen: 2
image.isIdenticon: false
image.source: uploadProfilePicPopup.selectedImage
ringSettings { ringSpecModel: Utils.getColorHashAsJson(root.pubKey) }
}
StatusRoundButton {
id: updatePicButton
width: 40
height: 40
anchors.top: parent.top
anchors.right: parent.right
type: StatusFlatRoundButton.Type.Secondary
icon.name: "add"
onClicked: {
uploadProfilePicPopup.open();
}
}
}
StatusInput {
id: nameInput
implicitWidth: 328
Layout.alignment: Qt.AlignHCenter
input.placeholderText: qsTr("Display name")
input.edit.font.capitalization: Font.Capitalize
input.rightComponent: RoundedIcon {
width: 14
height: 14
iconWidth: 14
iconHeight: 14
color: "transparent"
source: Style.svg("close-filled")
onClicked: {
nameInput.input.edit.clear();
}
}
onTextChanged: {
userImage.name = text;
}
}
StyledText {
id: chatKeyTxt
color: Style.current.secondaryText
text: "Chatkey:" + root.address
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
Layout.alignment: Qt.AlignHCenter
font.pixelSize: 15
}
Item {
id: chainsChatKeyImg
Layout.alignment: Qt.AlignHCenter
Layout.preferredWidth: 181
Layout.preferredHeight: 84
Image {
anchors.horizontalCenter: parent.horizontalCenter
source: Style.png("onboarding/chains")
}
EmojiHash {
anchors.bottom: parent.bottom
publicKey: root.pubKey
}
StatusSmartIdenticon {
id: userImageCopy
anchors.bottom: parent.bottom
anchors.right: parent.right
icon.width: 44
icon.height: 44
icon.color: "transparent"
ringSettings { ringSpecModel: Utils.getColorHashAsJson(root.pubKey) }
}
}
StatusButton {
Layout.alignment: Qt.AlignHCenter | Qt.AlignTop
Layout.topMargin: 125
enabled: !!nameInput.text
text: qsTr("Next")
onClicked: {
if (root.state === "username") {
if (OnboardingStore.accountCreated) {
OnboardingStore.updatedDisplayName(nameInput.text);
}
root.displayName = nameInput.text;
root.state = "chatkey";
} else {
createPassword();
}
}
}
UploadProfilePicModal {
id: uploadProfilePicPopup
}
}
states: [
State {
name: "username"
PropertyChanges {
target: usernameText
text: qsTr("Your profile")
}
PropertyChanges {
target: txtDesc
text: qsTr("Longer and unusual names are better as they are less likely to be used by someone else.")
}
PropertyChanges {
target: chatKeyTxt
visible: false
}
PropertyChanges {
target: chainsChatKeyImg
visible: false
}
PropertyChanges {
target: userImageCopy
visible: false
}
PropertyChanges {
target: updatePicButton
visible: true
}
PropertyChanges {
target: nameInput
visible: true
}
},
State {
name: "chatkey"
PropertyChanges {
target: usernameText
text: qsTr("Your emojihash and identicon ring")
}
PropertyChanges {
target: txtDesc
text: qsTr("This set of emojis and coloured ring around your avatar are unique and represent your chat key, so your friends can easily distinguish you from potential impersonators.")
}
PropertyChanges {
target: chatKeyTxt
visible: true
}
PropertyChanges {
target: chainsChatKeyImg
visible: true
}
PropertyChanges {
target: userImageCopy
visible: true
}
PropertyChanges {
target: updatePicButton
visible: false
}
PropertyChanges {
target: nameInput
visible: false
}
}
]
transitions: [
Transition {
from: "*"
to: "*"
SequentialAnimation {
PropertyAction {
target: root
property: "opacity"
value: 0.0
}
PropertyAction {
target: root
property: "opacity"
value: 1.0
}
}
}
]
}

View File

@ -10,31 +10,16 @@ import StatusQ.Core.Theme 0.1
import shared 1.0
import shared.panels 1.0
import "../popups"
import "../controls"
import utils 1.0
Page {
OnboardingBasePage {
id: root
signal buttonClicked()
signal keycardLinkClicked()
signal seedLinkClicked()
signal backClicked()
background: Rectangle {
color: Style.current.background
}
Component.onCompleted: {
if(displayBeforeGetStartedModal) {
displayBeforeGetStartedModal = false
beforeGetStartedModal.open()
}
}
BeforeGetStartedModal {
id: beforeGetStartedModal
}
Item {
id: container
@ -141,17 +126,6 @@ Page {
}
}
StatusRoundButton {
anchors.left: parent.left
anchors.leftMargin: Style.current.padding
anchors.bottom: parent.bottom
anchors.bottomMargin: Style.current.padding
icon.name: "arrow-left"
onClicked: {
root.backClicked();
}
}
states: [
State {
name: "connectkeys"
@ -168,6 +142,7 @@ Page {
PropertyChanges {
target: button
text: qsTr("Scan sync code")
enabled: false
}
PropertyChanges {

View File

@ -31,7 +31,6 @@ Item {
loading = true
LoginStore.login(password)
Global.applicationWindow.prepareForStoring(password, false)
txtPassword.textField.clear()
}

View File

@ -0,0 +1,129 @@
import QtQuick 2.13
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import shared.panels 1.0
import utils 1.0
import "../controls"
import "../panels"
import "../stores"
OnboardingBasePage {
id: root
property string userPass
signal genKeysDone();
Item {
id: container
enabled: !dimBackground.active
anchors.centerIn: parent
width: 425
height: {
let h = 0
const children = this.children
Object.keys(children).forEach(function (key) {
const child = children[key]
h += child.height + Style.current.padding
})
return h
}
Image {
id: keysImg
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
fillMode: Image.PreserveAspectFit
source: Style.png("onboarding/fingerprint")
width: 160
height: 160
mipmap: true
}
StyledText {
id: txtTitle
text: qsTr("Biometrics")
anchors.topMargin: Style.current.padding
font.bold: true
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: keysImg.bottom
font.letterSpacing: -0.2
font.pixelSize: 22
}
StyledText {
id: txtDesc
width: 426
anchors.top: txtTitle.bottom
anchors.topMargin: Style.current.padding
color: Style.current.secondaryText
text: qsTrId("Would you like to use your TouchID to login to Status?")
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
font.pixelSize: 15
}
ColumnLayout {
anchors.topMargin: 40
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: txtDesc.bottom
spacing: Style.current.bigPadding
StatusButton {
id: button
Layout.alignment: Qt.AlignHCenter
text: qsTr("Yes, use TouchID ")
onClicked: {
OnboardingStore.accountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore;
dimBackground.active = true;
OnboardingStore.storeToKeyChain(userPass);
}
}
StatusBaseText {
id: keycardLink
Layout.alignment: Qt.AlignHCenter
color: Theme.palette.primaryColor1
text: qsTr("I prefer to use my PIN")
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
parent.font.underline = true
}
onExited: {
parent.font.underline = false
}
onClicked: {
OnboardingStore.accountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever;
root.genKeysDone();
}
}
}
}
}
Loader {
id: dimBackground
anchors.fill: parent
active: false
sourceComponent: Rectangle {
color: Qt.rgba(0, 0, 0, 0.4)
}
}
Connections {
enabled: !!OnboardingStore.mainModuleInst
target: OnboardingStore.mainModuleInst
onStoringPasswordSuccess: {
dimBackground.active = false;
root.genKeysDone();
}
onStoringPasswordError: {
dimBackground.active = false;
}
}
}

View File

@ -7,6 +7,7 @@ import StatusQ.Controls 0.1
import shared 1.0
import shared.panels 1.0
import "../popups"
import "../stores"
import utils 1.0
@ -21,14 +22,16 @@ Page {
}
Component.onCompleted: {
if(displayBeforeGetStartedModal) {
displayBeforeGetStartedModal = false
beforeGetStartedModal.open()
if (OnboardingStore.showBeforeGetStartedPopup) {
beforeGetStartedModal.open();
}
}
BeforeGetStartedModal {
id: beforeGetStartedModal
onClosed: {
OnboardingStore.showBeforeGetStartedPopup = false;
}
}
Item {
@ -52,7 +55,7 @@ Page {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
fillMode: Image.PreserveAspectFit
source: Style.png("welcome")
source: Style.png("onboarding/welcome")
width: 256
height: 256
mipmap: true

View File

@ -5,6 +5,7 @@ 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
@ -45,7 +46,6 @@ StatusModal {
PasswordView {
id: view
store: root.privacyStore
anchors.topMargin: Style.current.padding
anchors.centerIn: parent
titleVisible: false

View File

@ -58,7 +58,9 @@ ModalPopup {
checked: localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueStore
onCheckedChanged: {
if (checked && localAccountSettings.storeToKeychainValue !== Constants.storeToKeychainValueStore) {
// TODO: REFACTOR TO NEW PASWORD VIEW
// TODO: REFACTOR TO NEW PASWORD VIEW AND
// DELETE StoreToKeychainSelectionModal.qml
// AND CreatePasswordModal.qml IF NOT NEEDED
var storePassPopup = Global.openPopup(storePasswordModal)
if(storePassPopup)
{

View File

@ -28,8 +28,4 @@ QtObject {
function validatePassword(password) {
return root.privacyModule.validatePassword(password)
}
function getPasswordStrengthScore(password) {
return root.privacyModule.getPasswordStrengthScore(password)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -0,0 +1,24 @@
import QtQuick 2.13
import StatusQ.Core.Utils 0.1 as StatusQUtils
import utils 1.0
Text {
id: root
property string publicKey
property string size: "14x14"
renderType: Text.NativeRendering
font.pointSize: 1 // make sure there is no padding for emojis due to 'style: "vertical-align: top"'
text: {
const emojiHash = Utils.getEmojiHashAsJson(root.publicKey);
var emojiHashFirstLine = "";
var emojiHashSecondLine = "";
for (var i = 0; i < 7; i++) {
emojiHashFirstLine += emojiHash[i];
}
for (var j = 7; j < emojiHash.length; j++) {
emojiHashSecondLine += emojiHash[j];
}
return StatusQUtils.Emoji.parse(emojiHashFirstLine, size) + "<br>" +
StatusQUtils.Emoji.parse(emojiHashSecondLine, size)
}
}

View File

@ -3,6 +3,7 @@ import QtQuick.Layouts 1.14
import utils 1.0
import shared.panels 1.0
import shared.controls 1.0
import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
@ -88,33 +89,14 @@ Item {
color: Style.current.secondaryText
}
Text {
EmojiHash {
id: emojihash
readonly property size finalSize: supersampling ? Qt.size(emojiSize.width * 2, emojiSize.height * 2) : emojiSize
property string size: `${finalSize.width}x${finalSize.height}`
Layout.fillWidth: true
renderType: Text.NativeRendering
scale: supersampling ? 0.5 : 1
text: {
const emojiHash = Utils.getEmojiHashAsJson(root.pubkey)
var emojiHashFirstLine = ""
var emojiHashSecondLine = ""
for (var i = 0; i < 7; i++) {
emojiHashFirstLine += emojiHash[i]
}
for (var i = 7; i < emojiHash.length; i++) {
emojiHashSecondLine += emojiHash[i]
}
return StatusQUtils.Emoji.parse(emojiHashFirstLine, size) + "<br>" +
StatusQUtils.Emoji.parse(emojiHashSecondLine, size)
}
horizontalAlignment: Text.AlignHCenter
font.pointSize: 1 // make sure there is no padding for emojis due to 'style: "vertical-align: top"'
publicKey: root.pubkey
readonly property size finalSize: supersampling ? Qt.size(emojiSize.width * 2, emojiSize.height * 2) : emojiSize
size: `${finalSize.width}x${finalSize.height}`
scale: supersampling ? 0.5 : 1
}
}
}

View File

@ -23,3 +23,4 @@ StyledTextEdit 1.0 StyledTextEdit.qml
StyledTextField 1.0 StyledTextField.qml
Timer 1.0 Timer.qml
TransactionFormGroup 1.0 TransactionFormGroup.qml
EmojiHash 1.0 EmojiHash.qml

View File

@ -1,4 +1,4 @@
import QtQuick 2.3
import QtQuick 2.13
import QtGraphicalEffects 1.13
import StatusQ.Components 0.1
@ -53,7 +53,8 @@ Rectangle {
]
Connections {
target: mainModule
enabled: !!mainModule
target: enabled ? mainModule : undefined
onOnlineStatusChanged: {
if (connected && root.state !== "ready" &&
root.visible &&

View File

@ -10,6 +10,9 @@ QtObject {
// property var keycardModelInst: !!keycardModel ? keycardModel : null
// property var profileModelInst: !!profileModel ? profileModel : null
property var profileSectionModuleInst: profileSectionModule
property var privacyModule: profileSectionModuleInst.privacyModule
property var onboardingModuleInst: onboardingModule
property var userProfileInst: !!userProfile ? userProfile : null
property var walletSectionInst: !!walletSection ? walletSection : null
property var appSettings: !!localAppSettings ? localAppSettings : null
@ -97,4 +100,13 @@ QtObject {
function addToRecentsGif(id) {
chatSectionChatContentInputArea.addToRecentsGif(id)
}
function getPasswordStrengthScore(password, onboarding = false) {
if (onboarding) {
let userName = root.onboardingModuleInst.importedAccountAlias;
return root.onboardingModuleInst.getPasswordStrengthScore(password, userName);
} else {
return root.privacyModule.getPasswordStrengthScore(password);
}
}
}

View File

@ -4,6 +4,7 @@ import QtQuick.Layouts 1.12
import shared.panels 1.0
import shared.controls 1.0
import shared.stores 1.0
import utils 1.0
import StatusQ.Controls 0.1
@ -13,7 +14,6 @@ import StatusQ.Components 0.1
Column {
id: root
property var store
property bool ready: newPswInput.text.length >= root.minPswLen && newPswInput.text === confirmPswInput.text && errorTxt.text === ""
property int minPswLen: 6
property bool createNewPsw: true
@ -22,6 +22,7 @@ Column {
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 6 characers. To strengthen your password consider including:")
property bool onboarding: false
readonly property int zBehind: 1
readonly property int zFront: 100
@ -208,7 +209,7 @@ Column {
d.containsSymbols = d.symbolsValidator(text)
// Update strength indicator:
strengthInditactor.strength = d.convertStrength(root.store.getPasswordStrengthScore(newPswInput.text))
strengthInditactor.strength = d.convertStrength(RootStore.getPasswordStrengthScore(newPswInput.text, root.onboarding))
}
StatusFlatRoundButton {

View File

@ -5,3 +5,4 @@ SearchResults 1.0 SearchResults.qml
TransactionPreview 1.0 TransactionPreview.qml
TransactionSigner 1.0 TransactionSigner.qml
TransactionStackView 1.0 TransactionStackView.qml
PasswordView 1.0 PasswordView.qml

View File

@ -20,7 +20,6 @@ import AppLayouts.Onboarding 1.0
StatusWindow {
property bool hasAccounts: startupModule.appState !== Constants.appState.onboarding
property bool displayBeforeGetStartedModal: !hasAccounts
property bool appIsReady: false
Universal.theme: Universal.System
@ -109,9 +108,6 @@ StatusWindow {
// We set main module to the Global singleton once user is logged in and we move to the main app.
Global.mainModuleInst = mainModule
mainModule.openStoreToKeychainPopup.connect(function(){
storeToKeychainConfirmationPopup.open()
})
if(localAccountSensitiveSettings.recentEmojis === "") {
localAccountSensitiveSettings.recentEmojis = [];
}
@ -253,50 +249,6 @@ StatusWindow {
}
}
function prepareForStoring(password, runStoreToKeychainPopup) {
if(Qt.platform.os == "osx")
{
storeToKeychainConfirmationPopup.password = password
if(runStoreToKeychainPopup)
storeToKeychainConfirmationPopup.open()
}
}
ConfirmationDialog {
id: storeToKeychainConfirmationPopup
property string password: ""
height: 200
confirmationText: qsTr("Would you like to store password to the Keychain?")
showRejectButton: true
showCancelButton: true
confirmButtonLabel: qsTr("Store")
rejectButtonLabel: qsTr("Not now")
cancelButtonLabel: qsTr("Never")
function finish()
{
password = ""
storeToKeychainConfirmationPopup.close()
}
onConfirmButtonClicked: {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore
mainModule.storePassword(password)
finish()
}
onRejectButtonClicked: {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNotNow
finish()
}
onCancelButtonClicked: {
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever
finish()
}
}
Loader {
id: loader
anchors.fill: parent
@ -321,7 +273,9 @@ StatusWindow {
onOnBoardingStepChanged: {
loader.sourceComponent = view;
loader.item.state = state;
if (!!state) {
loader.item.state = state;
}
}
}