feat: implement design on the login screen

This commit is contained in:
Jonathan Rainville 2020-06-11 17:23:27 -04:00 committed by Iuri Matias
parent eb9c948bb0
commit 67c7e9b0ca
13 changed files with 442 additions and 224 deletions

View File

@ -16,7 +16,8 @@ import core
type
AccountRoles {.pure.} = enum
Username = UserRole + 1,
Identicon = UserRole + 2
Identicon = UserRole + 2,
Address = UserRole + 3
QtObject:
type LoginView* = ref object of QAbstractListModel
@ -38,9 +39,22 @@ QtObject:
result.status = status
result.setup
proc getCurrentAccount*(self: LoginView): QVariant {.slot.} =
result = newQVariant(self.currentAccount)
proc setCurrentAccount*(self: LoginView, selectedAccountIdx: int) {.slot.} =
let currNodeAcct = self.accounts[selectedAccountIdx]
self.currentAccount.setAccount(GeneratedAccount(name: currNodeAcct.name, photoPath: currNodeAcct.photoPath, address: currNodeAcct.keyUid))
QtProperty[QVariant] currentAccount:
read = getCurrentAccount
write = setCurrentAccount
proc addAccountToList*(self: LoginView, account: NodeAccount) =
self.beginInsertRows(newQModelIndex(), self.accounts.len, self.accounts.len)
self.accounts.add(account)
if (self.accounts.len == 1):
self.setCurrentAccount(0)
self.endInsertRows()
proc removeAccounts*(self: LoginView) =
@ -62,25 +76,24 @@ QtObject:
case assetRole:
of AccountRoles.Username: result = newQVariant(asset.name)
of AccountRoles.Identicon: result = newQVariant(asset.photoPath)
of AccountRoles.Address: result = newQVariant(asset.keyUid)
method roleNames(self: LoginView): Table[int, string] =
{ AccountRoles.Username.int:"username",
AccountRoles.Identicon.int:"identicon" }.toTable
AccountRoles.Identicon.int:"identicon",
AccountRoles.Address.int:"address" }.toTable
proc getCurrentAccount*(self: LoginView): QVariant {.slot.} =
result = newQVariant(self.currentAccount)
proc login(self: LoginView, password: string): string {.slot.} =
var currentAccountId = 0
var i = 0
for account in self.accounts:
if (account.keyUid == self.currentAccount.address):
currentAccountId = i
break
i = i + 1
proc setCurrentAccount*(self: LoginView, selectedAccountIdx: int) {.slot.} =
let currNodeAcct = self.accounts[selectedAccountIdx]
self.currentAccount.setAccount(GeneratedAccount(name: currNodeAcct.name, photoPath: currNodeAcct.photoPath))
QtProperty[QVariant] currentAccount:
read = getCurrentAccount
write = setCurrentAccount
proc login(self: LoginView, selectedAccountIndex: int, password: string): string {.slot.} =
try:
result = self.status.accounts.login(selectedAccountIndex, password).toJson
result = self.status.accounts.login(currentAccountId, password).toJson
except:
let
e = getCurrentException()

View File

@ -33,7 +33,7 @@ QtObject:
read = identicon
notify = accountChanged
proc address*(self: AccountInfoView): string {.slot.} = result = ?.self.account.derived.whisper.publicKey
proc address*(self: AccountInfoView): string {.slot.} = result = ?.self.account.address
QtProperty[string] address:
read = address
notify = accountChanged

4
ui/app/img/refresh.svg Normal file
View File

@ -0,0 +1,4 @@
<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M6.85365 15.7033C6.52223 15.4548 6.05005 15.4497 5.75716 15.7426C5.46426 16.0355 5.46194 16.5142 5.78534 16.773C6.94028 17.6973 8.40549 18.25 9.9998 18.25C13.7277 18.25 16.7498 15.2279 16.7498 11.5V11.0176C16.7498 10.5721 17.2884 10.349 17.6034 10.664L18.4697 11.5303C18.7626 11.8232 19.2374 11.8232 19.5303 11.5303C19.8232 11.2374 19.8232 10.7626 19.5303 10.4697L16.5303 7.46967C16.2374 7.17678 15.7626 7.17678 15.4697 7.46967L12.4697 10.4697C12.1768 10.7626 12.1768 11.2374 12.4697 11.5303C12.7626 11.8232 13.2374 11.8232 13.5303 11.5303L14.3962 10.6644C14.7112 10.3494 15.2498 10.5725 15.2498 11.018V11.5C15.2498 14.3995 12.8993 16.75 9.9998 16.75C8.81964 16.75 7.73043 16.3606 6.85365 15.7033Z" fill="black"/>
<path d="M4.53033 10.5303L7.53033 7.53033C7.82322 7.23744 7.82322 6.76256 7.53033 6.46967C7.23744 6.17678 6.76256 6.17678 6.46967 6.46967L5.60278 7.33656C5.30293 7.63641 4.79024 7.42405 4.79024 7C4.79024 4.1005 7.14075 1.75 10.0402 1.75C11.2204 1.75 12.3096 2.1394 13.1864 2.79673C13.5178 3.04519 13.99 3.05025 14.2829 2.75736C14.5758 2.46447 14.5781 1.98584 14.2547 1.72703C13.0998 0.802734 11.6345 0.25 10.0402 0.25C6.31232 0.25 3.29024 3.27208 3.29024 7V7.02247C3.29024 7.46793 2.75167 7.69101 2.43669 7.37603L1.53033 6.46967C1.23744 6.17678 0.762563 6.17678 0.46967 6.46967C0.176777 6.76256 0.176777 7.23744 0.46967 7.53033L3.46967 10.5303C3.76256 10.8232 4.23744 10.8232 4.53033 10.5303Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -144,6 +144,7 @@ DISTFILES += \
onboarding/Login/AccountList.qml \
onboarding/Login/AccountSelection.qml \
onboarding/Login/AddressView.qml \
onboarding/Login/SelectAnotherAccountModal.qml \
onboarding/Login/qmldir \
onboarding/Login/samples/AccountsData.qml \
onboarding/Login/samples/qmldir \
@ -168,6 +169,7 @@ DISTFILES += \
shared/Input.qml \
shared/ModalPopup.qml \
shared/PopupMenu.qml \
shared/RoundImage.qml \
shared/Select.qml \
shared/Separator.qml \
shared/StatusTabButton.qml \

View File

@ -3,75 +3,161 @@ import QtQuick.Controls 2.4
import QtQuick.Layouts 1.11
import QtQuick.Window 2.11
import QtQuick.Dialogs 1.3
import QtGraphicalEffects 1.0
import "../shared"
import "../imports"
import "./Login"
SwipeView {
property alias btnGenKey: accountSelection.btnGenKey
Item {
property alias btnGenKey: genrateKeysLink
property bool loading: false
id: swipeView
id: loginView
anchors.fill: parent
currentIndex: 0
interactive: false
onCurrentItemChanged: {
if(currentItem.txtPassword) {
currentItem.txtPassword.textField.focus = true
Component.onCompleted: {
txtPassword.forceActiveFocus(Qt.MouseFocusReason)
}
}
AccountSelection {
id: accountSelection
onAccountSelect: function() {
loginModel.setCurrentAccount(this.selectedIndex)
swipeView.incrementCurrentIndex()
}
}
Item {
id: wizardStep2
property Item txtPassword: txtPassword
Text {
id: step2Title
text: "Enter password"
font.pointSize: 36
anchors.top: parent.top
anchors.topMargin: 20
id: element
width: 360
height: 200
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
}
Row {
RoundImage {
id: userImage
width: 40
height: 40
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: step2Title.bottom
anchors.topMargin: 30
Column {
Image {
source: loginModel.currentAccount.identicon
}
}
Column {
Text {
id: usernameText
text: loginModel.currentAccount.username
font.weight: Font.Bold
font.pixelSize: 17
anchors.top: userImage.bottom
anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter
}
SelectAnotherAccountModal {
id: selectAnotherAccountModal
onAccountSelect: function (index) {
loginModel.setCurrentAccount(index)
}
}
Rectangle {
property bool isHovered: false
id: changeAccountBtn
width: 24
height: 24
anchors.left: usernameText.right
anchors.leftMargin: 4
anchors.verticalCenter: usernameText.verticalCenter
color: isHovered ? Theme.grey : Theme.transparent
radius: 4
Image {
id: caretImg
width: 10
height: 6
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
source: "../app/img/caret.svg"
fillMode: Image.PreserveAspectFit
}
ColorOverlay {
anchors.fill: caretImg
source: caretImg
color: Theme.darkGrey
}
MouseArea {
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onEntered: {
changeAccountBtn.isHovered = true
}
onExited: {
changeAccountBtn.isHovered = false
}
onClicked: {
selectAnotherAccountModal.open()
// TODO add popup for when there are no other accounts
}
}
}
Text {
id: addressText
width: 90
color: Theme.darkGrey
text: loginModel.currentAccount.address
elide: Text.ElideMiddle
font.pixelSize: 15
anchors.top: usernameText.bottom
anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter
}
Input {
id: txtPassword
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: Theme.padding
anchors.leftMargin: Theme.padding
anchors.left: parent.left
anchors.right: parent.right
anchors.top: addressText.bottom
anchors.topMargin: Theme.padding * 2
placeholderText: "Enter password"
textField.echoMode: TextInput.Password
textField.focus: true
Keys.onReturnPressed: {
submitBtn.clicked()
}
}
Button {
id: submitBtn
visible: txtPassword.text.length > 0
width: 40
height: 40
anchors.left: txtPassword.right
anchors.leftMargin: Theme.padding
anchors.verticalCenter: txtPassword.verticalCenter
onClicked: {
if (loading) {
return;
}
loading = true
loginModel.login(txtPassword.textField.text)
}
Image {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
// TODO replace by a real loading image
source: loading ? "../app/img/refresh.svg" : "../app/img/arrowUp.svg"
width: 13.5
height: 17.5
fillMode: Image.PreserveAspectFit
rotation: 90
}
background: Rectangle {
color: Theme.blue
radius: 50
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
submitBtn.onClicked()
}
}
}
MessageDialog {
id: loginError
title: "Login failed"
@ -81,6 +167,7 @@ SwipeView {
onAccepted: {
txtPassword.textField.clear()
txtPassword.textField.focus = true
loading = false
}
}
@ -88,23 +175,26 @@ SwipeView {
target: loginModel
ignoreUnknownSignals: true
onLoginResponseChanged: {
if(error){
if (error) {
loginError.open()
}
}
}
StyledButton {
id: submitBtn
label: "Finish"
MouseArea {
id: genrateKeysLink
width: genrateKeysLinkText.width
height: genrateKeysLinkText.height
cursorShape: Qt.PointingHandCursor
anchors.top: txtPassword.bottom
anchors.topMargin: 26
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: 20
onClicked: {
const selectedAccountIndex = accountSelection.selectedIndex
const response = loginModel.login(selectedAccountIndex, txtPassword.textField.text)
// TODO: replace me with something graphical (ie spinner)
console.log("Logging in...")
Text {
id: genrateKeysLinkText
color: Theme.blue
text: qsTr("Generate new keys")
font.pixelSize: 13
}
}
}
@ -112,7 +202,7 @@ SwipeView {
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
D{i:0;autoSize:true;formeditorColor:"#ffffff";formeditorZoom:0.75;height:480;width:640}
}
##^##*/

View File

@ -3,34 +3,31 @@ import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import Qt.labs.platform 1.1
import "./samples/"
import "../../imports"
ListView {
property var accounts: AccountsData {}
property var onAccountSelect: function() {}
property var onAccountSelect: function () {}
id: addressesView
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
anchors.bottom: footer.top
anchors.bottomMargin: 0
anchors.top: title.bottom
anchors.topMargin: 16
contentWidth: 200
height: parent.height
anchors.fill: parent
model: accounts
focus: true
spacing: Theme.smallPadding
delegate: AddressView {
username: model.username
address: model.address
identicon: model.identicon
onAccountSelect: function(index) {
onAccountSelect: function (index) {
addressesView.onAccountSelect(index)
}
}
Layout.fillHeight: true
Layout.fillWidth: true
focus: true
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -2,34 +2,72 @@ import QtQuick 2.0
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import Qt.labs.platform 1.1
import "../../imports"
import "../../shared"
Item {
Rectangle {
property string username: "Jotaro Kujo"
property string identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
property string address: "0x123345677890987654321123456"
property url identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAg0lEQVR4nOzXwQmAMBAFURV7sQybsgybsgyr0QYUlE1g+Mw7ioQMe9lMQwhDaAyhMYTGEJqYkPnrj/t5XE/ft2UdW1yken7MRAyhMYTGEBpDaAyhKe9JbzvSX9WdLWYihtAYQuMLkcYQGkPUScxEDKExhMYQGkNoDKExhMYQmjsAAP//ZfIUZgXTZXQAAAAASUVORK5CYII="
property var onAccountSelect: function() {}
property bool selected: loginModel.currentAccount.address === address
property bool isHovered: false
id: addressViewDelegate
height: 56
height: 64
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
border.width: 0
color: selected || isHovered ? Theme.grey : Theme.transparent
radius: Theme.radius
Row {
RadioButton {
checked: index == 0 ? true : false
ButtonGroup.group: accountGroup
onClicked: { onAccountSelect(index) }
}
Column {
Image {
RoundImage {
id: accountImage
anchors.left: parent.left
anchors.leftMargin: Theme.padding
anchors.verticalCenter: parent.verticalCenter
source: identicon
}
}
Column {
Text {
id: usernameText
text: username
font.pixelSize: 17
anchors.top: accountImage.top
anchors.left: accountImage.right
anchors.leftMargin: Theme.padding
}
Text {
id: addressText
width: 108
text: address
elide: Text.ElideMiddle
anchors.bottom: accountImage.bottom
anchors.bottomMargin: 0
anchors.left: usernameText.left
anchors.leftMargin: 0
font.pixelSize: 15
color: Theme.darkGrey
}
MouseArea {
hoverEnabled: true
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
onAccountSelect(index)
}
onEntered: {
addressViewDelegate.isHovered = true
}
onExited: {
addressViewDelegate.isHovered = false
}
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#ffffff";height:64;width:450}
}
##^##*/

View File

@ -0,0 +1,41 @@
import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import "../../imports"
import "../../shared"
ModalPopup {
property var onAccountSelect: function () {}
id: popup
title: qsTr("Your accounts")
AccountList {
id: accountList
anchors.fill: parent
accounts: loginModel
onAccountSelect: function(index) {
popup.onAccountSelect(index)
popup.close()
}
}
footer: StyledButton {
anchors.top: parent.top
anchors.topMargin: Theme.padding
anchors.right: parent.right
anchors.rightMargin: Theme.padding
label: "Add another existing key"
onClicked : {
console.log('Open other popup for seed')
}
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#ffffff";height:500;width:400}
}
##^##*/

View File

@ -1,3 +1,4 @@
AccountSelection 1.0 AccountSelection.qml
AccountList 1.0 AccountList.qml
AddressView 1.0 AddressView.qml
SelectAnotherAccountModal 1.0 SelectAnotherAccountModal.qml

View File

@ -6,11 +6,13 @@ import Qt.labs.platform 1.1
ListModel {
ListElement {
username: "Ferocious Herringbone Sinewave2"
identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAg0lEQVR4nOzXwQmAMBAFURV7sQybsgybsgyr0QYUlE1g+Mw7ioQMe9lMQwhDaAyhMYTGEJqYkPnrj/t5XE/ft2UdW1yken7MRAyhMYTGEBpDaAyhKe9JbzvSX9WdLWYihtAYQuMLkcYQGkPUScxEDKExhMYQGkNoDKExhMYQmjsAAP//ZfIUZgXTZXQAAAAASUVORK5CYII="
address: "0x123456789009876543211234567890"
}
ListElement {
username: "Another Account"
identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII="
identicon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAg0lEQVR4nOzXwQmAMBAFURV7sQybsgybsgyr0QYUlE1g+Mw7ioQMe9lMQwhDaAyhMYTGEJqYkPnrj/t5XE/ft2UdW1yken7MRAyhMYTGEBpDaAyhKe9JbzvSX9WdLWYihtAYQuMLkcYQGkPUScxEDKExhMYQGkNoDKExhMYQmjsAAP//ZfIUZgXTZXQAAAAASUVORK5CYII="
address: "0x123456789009876543211234567890"
}
}

View File

@ -6,12 +6,12 @@ import "./"
Popup {
property string title
default property alias content : popupContent.children
default property alias content: popupContent.children
property alias header: headerContent.children
id: popup
modal: true
property alias footer : footerContent.children
property alias footer: footerContent.children
Overlay.modal: Rectangle {
color: "#60000000"
@ -81,10 +81,10 @@ Popup {
onExited: {
closeButton.color = Theme.white
}
onEntered:{
onEntered: {
closeButton.color = Theme.grey
}
onClicked : {
onClicked: {
popup.close()
}
}
@ -99,6 +99,7 @@ Popup {
id: popupContent
anchors.top: separator.bottom
anchors.topMargin: Theme.padding
anchors.bottom: separator2.top
anchors.bottomMargin: Theme.padding
anchors.left: parent.left
anchors.leftMargin: Theme.padding
@ -126,4 +127,3 @@ Popup {
}
}
}

29
ui/shared/RoundImage.qml Normal file
View File

@ -0,0 +1,29 @@
import QtQuick 2.3
import "../imports"
Rectangle {
id: roundedImage
property url source:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAg0lEQVR4nOzXwQmAMBAFURV7sQybsgybsgyr0QYUlE1g+Mw7ioQMe9lMQwhDaAyhMYTGEJqYkPnrj/t5XE/ft2UdW1yken7MRAyhMYTGEBpDaAyhKe9JbzvSX9WdLWYihtAYQuMLkcYQGkPUScxEDKExhMYQGkNoDKExhMYQmjsAAP//ZfIUZgXTZXQAAAAASUVORK5CYII="
width: 40
height: 40
color: Theme.white
radius: 50
border.width: 1
border.color: Theme.darkGrey
Image {
width: parent.width
height: parent.height
fillMode: Image.PreserveAspectFit
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
source: roundedImage.source
}
}
/*##^##
Designer {
D{i:0;formeditorColor:"#4c4e50";formeditorZoom:2}
}
##^##*/

View File

@ -8,3 +8,4 @@ TextWithLabel 1.0 TextWithLabel.qml
Input 1.0 Input.qml
Select 1.0 Select.qml
StyledTextArea 1.0 StyledTextArea.qml
RoundImage 1.0 RoundImage.qml