refactor: move onboarding logic out of the view

This commit is contained in:
Iuri Matias 2020-05-21 11:25:33 -04:00
parent 6e2f61b08e
commit 7cd9ceac28
6 changed files with 481 additions and 228 deletions

View File

@ -13,8 +13,14 @@ import uuids
import eventemitter import eventemitter
import view import view
proc storeAccountAndLogin(events: EventEmitter, selectedAccount: string, password: string): string = proc storeAccountAndLogin(model: AccountModel, selectedAccountIndex: int, password: string): string =
let account = to(json.parseJson(selectedAccount), Models.GeneratedAccount) # let account = to(json.parseJson(selectedAccount), Models.GeneratedAccount)
echo "account index selected is "
echo selectedAccountIndex
let account: GeneratedAccount = model.generatedAddresses[selectedAccountIndex]
echo "account selected is "
echo account
let password = "0x" & $keccak_256.digest(password) let password = "0x" & $keccak_256.digest(password)
let multiAccount = %* { let multiAccount = %* {
"accountID": account.id, "accountID": account.id,
@ -86,7 +92,7 @@ proc storeAccountAndLogin(events: EventEmitter, selectedAccount: string, passwor
let saveResult = result.parseJson let saveResult = result.parseJson
if saveResult["error"].getStr == "": if saveResult["error"].getStr == "":
events.emit("node:ready", Args()) model.events.emit("node:ready", Args())
echo "Account saved succesfully" echo "Account saved succesfully"
# TODO: this is temporary and will be removed once accounts import and creation is working # TODO: this is temporary and will be removed once accounts import and creation is working
@ -97,10 +103,13 @@ proc generateRandomAccountAndLogin*(events: EventEmitter) =
type OnboardingController* = ref object of SignalSubscriber type OnboardingController* = ref object of SignalSubscriber
view*: OnboardingView view*: OnboardingView
variant*: QVariant variant*: QVariant
model*: AccountModel
proc newController*(events: EventEmitter): OnboardingController = proc newController*(events: EventEmitter): OnboardingController =
result = OnboardingController() result = OnboardingController()
result.view = newOnboardingView(events, storeAccountAndLogin, generateRandomAccountAndLogin) # TODO: events should be specific to the model itself
result.model = newAccountModel(events)
result.view = newOnboardingView(result.model, storeAccountAndLogin, generateRandomAccountAndLogin)
result.variant = newQVariant(result.view) result.variant = newQVariant(result.view)
proc delete*(self: OnboardingController) = proc delete*(self: OnboardingController) =
@ -108,7 +117,17 @@ proc delete*(self: OnboardingController) =
delete self.variant delete self.variant
proc init*(self: OnboardingController) = proc init*(self: OnboardingController) =
discard # let addresses = parseJson(status_accounts.generateAddresses())
let accounts = self.model.generateAddresses()
for account in accounts:
# self.view.addAddressToList("account.username", "account.identicon", "account.key")
self.view.addAddressToList(account.username, account.identicon, account.key)
# echo address
# var username = $libstatus.generateAlias(address["publicKey"].str.toGoString)
# var identicon = $libstatus.identicon(address["publicKey"].str.toGoString)
# var generatedAddress = address["address"].str
# self.view.addAddressToList(username, identicon, generatedAddress)
# method onSignal(self: OnboardingController, data: Signal) = # method onSignal(self: OnboardingController, data: Signal) =
# echo "new signal received" # echo "new signal received"

View File

@ -1,5 +1,7 @@
import NimQml import NimQml
import Tables
import json import json
import eventemitter
import ../../status/accounts as status_accounts import ../../status/accounts as status_accounts
import nimcrypto import nimcrypto
import ../../status/utils import ../../status/utils
@ -7,28 +9,76 @@ import ../../status/libstatus
import ../../models/accounts as Models import ../../models/accounts as Models
import ../../constants/constants import ../../constants/constants
import uuids import uuids
import eventemitter
import ../../status/test as status_test import ../../status/test as status_test
type
AddressRoles {.pure.} = enum
Username = UserRole + 1,
Identicon = UserRole + 2,
Key = UserRole + 3
type
Address* = ref object of QObject
username*, identicon*, key*: string
QtObject: QtObject:
type OnboardingView* = ref object of QObject type OnboardingView* = ref object of QAbstractListModel
addresses*: seq[Address]
model: AccountModel
m_generatedAddresses: string m_generatedAddresses: string
events: EventEmitter doStoreAccountAndLogin: proc(model: AccountModel, selectedAccount: int, password: string): string
doStoreAccountAndLogin: proc(events: EventEmitter, selectedAccount: string, password: string): string
doGenerateRandomAccountAndLogin: proc(events: EventEmitter) doGenerateRandomAccountAndLogin: proc(events: EventEmitter)
proc setup(self: OnboardingView) = proc setup(self: OnboardingView) =
self.QObject.setup self.QAbstractListModel.setup
proc delete*(self: OnboardingView) = proc delete*(self: OnboardingView) =
self.QObject.delete self.QAbstractListModel.delete
for address in self.addresses:
address.delete
self.addresses = @[]
proc newOnboardingView*(events: EventEmitter, doStoreAccountAndLogin: proc, doGenerateRandomAccountAndLogin: proc(events: EventEmitter)): OnboardingView = proc newOnboardingView*(model: AccountModel, doStoreAccountAndLogin: proc, doGenerateRandomAccountAndLogin: proc(events: EventEmitter)): OnboardingView =
new(result, delete) new(result, delete)
result.events = events result.model = model
result.doStoreAccountAndLogin = doStoreAccountAndLogin result.doStoreAccountAndLogin = doStoreAccountAndLogin
result.doGenerateRandomAccountAndLogin = doGenerateRandomAccountAndLogin result.doGenerateRandomAccountAndLogin = doGenerateRandomAccountAndLogin
result.setup() result.addresses = @[]
result.setup
proc addAddressToList*(self: OnboardingView, username: string, identicon: string, key: string) {.slot.} =
self.beginInsertRows(newQModelIndex(), self.addresses.len, self.addresses.len)
self.addresses.add(Address(username : username,
identicon : identicon,
key : key))
self.endInsertRows()
method rowCount(self: OnboardingView, index: QModelIndex = nil): int =
return self.addresses.len
method data(self: OnboardingView, index: QModelIndex, role: int): QVariant =
if not index.isValid:
return
if index.row < 0 or index.row >= self.addresses.len:
return
let asset = self.addresses[index.row]
let assetRole = role.AddressRoles
case assetRole:
of AddressRoles.Username: result = newQVariant(asset.username)
of AddressRoles.Identicon: result = newQVariant(asset.identicon)
of AddressRoles.Key: result = newQVariant(asset.key)
method roleNames(self: OnboardingView): Table[int, string] =
{ AddressRoles.Username.int:"username",
AddressRoles.Identicon.int:"identicon",
AddressRoles.Key.int:"key" }.toTable
proc getGeneratedAddresses*(self: OnboardingView): string {.slot.} = proc getGeneratedAddresses*(self: OnboardingView): string {.slot.} =
result = self.m_generatedAddresses result = self.m_generatedAddresses
@ -57,9 +107,16 @@ QtObject:
proc identicon*(self: OnboardingView, publicKey: string): string {.slot.} = proc identicon*(self: OnboardingView, publicKey: string): string {.slot.} =
result = $libstatus.identicon(publicKey.toGoString) result = $libstatus.identicon(publicKey.toGoString)
proc storeAccountAndLogin(self: OnboardingView, selectedAccount: string, password: string): string {.slot.} = # proc storeAccountAndLogin(self: OnboardingView, selectedAccount: string, password: string): string {.slot.} =
result = self.doStoreAccountAndLogin(self.events, selectedAccount, password) proc storeAccountAndLogin(self: OnboardingView, selectedAccountIndex: int, password: string): string {.slot.} =
echo "--------------------"
echo "--------------------"
echo selectedAccountIndex
echo "--------------------"
echo "--------------------"
# var selectedAccountIndex = self.addresses
result = self.doStoreAccountAndLogin(self.model, selectedAccountIndex, password)
# TODO: this is temporary and will be removed once accounts import and creation is working # TODO: this is temporary and will be removed once accounts import and creation is working
proc generateRandomAccountAndLogin*(self: OnboardingView) {.slot.} = proc generateRandomAccountAndLogin*(self: OnboardingView) {.slot.} =
self.doGenerateRandomAccountAndLogin(self.events) self.doGenerateRandomAccountAndLogin(self.model.events)

View File

@ -1,4 +1,9 @@
import json import json
import eventemitter
import ../status/libstatus
import ../status/accounts as status_accounts
import ../constants/constants
import ../status/utils
type type
GeneratedAccount* = object GeneratedAccount* = object
@ -8,3 +13,49 @@ type
keyUid*: string keyUid*: string
mnemonic*: string mnemonic*: string
derived*: JsonNode derived*: JsonNode
username*: string
key*: string
identicon*: string
type
AccountModel* = ref object
generatedAddresses*: seq[GeneratedAccount]
events*: EventEmitter
proc newAccountModel*(events: EventEmitter): AccountModel =
result = AccountModel()
result.events = events
result.generatedAddresses = @[]
proc delete*(self: AccountModel) =
# delete self.generatedAddresses
discard
proc generateAddresses*(self: AccountModel): seq[GeneratedAccount] =
let accounts = parseJson(status_accounts.generateAddresses())
echo "----- generating accounts"
for account in accounts:
echo account
var generatedAccount = GeneratedAccount()
generatedAccount.publicKey = account["publicKey"].str
generatedAccount.address = account["address"].str
generatedAccount.id = account["id"].str
generatedAccount.keyUid = account["keyUid"].str
generatedAccount.mnemonic = account["mnemonic"].str
generatedAccount.derived = account["derived"]
generatedAccount.username = $libstatus.generateAlias(account["publicKey"].str.toGoString)
generatedAccount.identicon = $libstatus.identicon(account["publicKey"].str.toGoString)
generatedAccount.key = account["address"].str
# var generatedAccount = cast[GeneratedAccount](account.to(GeneratedAccountBase))
# generatedAccount.username = $libstatus.generateAlias(account["publicKey"].str.toGoString)
# generatedAccount.identicon = $libstatus.identicon(account["publicKey"].str.toGoString)
# generatedAccount.key = account["address"].str
self.generatedAddresses.add(generatedAccount)
self.generatedAddresses

View File

@ -77,7 +77,7 @@ proc mainProc() =
var onboarding = onboarding.newController(events) var onboarding = onboarding.newController(events)
# onboarding.init() onboarding.init()
engine.setRootContextProperty("onboardingLogic", onboarding.variant) engine.setRootContextProperty("onboardingLogic", onboarding.variant)
engine.setRootContextProperty("onboardingModel", onboarding.variant) engine.setRootContextProperty("onboardingModel", onboarding.variant)

View File

@ -10,8 +10,9 @@ SwipeView {
currentIndex: 0 currentIndex: 0
property string strGeneratedAccounts: onboardingLogic.generatedAddresses property string strGeneratedAccounts: onboardingLogic.generatedAddresses
property var generatedAccounts: {} // property var generatedAccounts: {}
signal storeAccountAndLoginResult(response: var) // signal storeAccountAndLoginResult(response: var)
signal storeAccountAndLoginResult()
onCurrentItemChanged: { onCurrentItemChanged: {
currentItem.txtPassword.focus = true; currentItem.txtPassword.focus = true;
@ -25,6 +26,11 @@ SwipeView {
id: wizardStep2 id: wizardStep2
property int selectedIndex: 0 property int selectedIndex: 0
ColumnLayout {
id: columnLayout
width: 620
height: 427
Text { Text {
text: "Generated accounts" text: "Generated accounts"
font.pointSize: 36 font.pointSize: 36
@ -33,42 +39,82 @@ SwipeView {
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
} }
Item { // Item {
anchors.top: parent.top // Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
anchors.topMargin: 50 // transformOrigin: Item.Center
// anchors.top: parent.top
// anchors.topMargin: 50
Column { Row {
Layout.fillHeight: true
Layout.fillWidth: true
spacing: 10 spacing: 10
ButtonGroup { ButtonGroup {
id: accountGroup id: accountGroup
} }
Repeater { Component {
model: generatedAccountsModel id: addressViewDelegate
Rectangle {
height: 32 // Item {
width: 32 // id: addressViewContainer
anchors.leftMargin: 20 // height: 56
anchors.rightMargin: 20 // anchors.right: parent.right
// anchors.rightMargin: 0
// anchors.left: parent.left
// anchors.leftMargin: 0
// Text {
// text: "address"
// font.pointSize: 24
// anchors.verticalCenter: parent.verticalCenter
// font.pixelSize: 14
// font.strikeout: false
// anchors.left: parent.left
// anchors.leftMargin: 72
// }
// }
Item {
height: 56
// anchors.leftMargin: 20
// anchors.rightMargin: 20
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
// Text {
// id: keyValue
// text: key
// anchors.verticalCenter: parent.verticalCenter
// font.pixelSize: 14
// font.strikeout: false
// anchors.left: parent.left
// anchors.leftMargin: 72
// }
Row { Row {
RadioButton { RadioButton {
checked: index == 0 ? true : false // checked: index == 0 ? true : false
checked: false
ButtonGroup.group: accountGroup ButtonGroup.group: accountGroup
onClicked: { // onClicked: {
wizardStep2.selectedIndex = index; // wizardStep2.selectedIndex = index;
} // }
} }
Column { Column {
Image { Image {
source: identicon source: identicon
// source: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAAF0lEQVR42mPk+c9Qz0ACYBzVMKoBOwAA3IgShVgwlIUAAAAASUVORK5CYII="
} }
} }
Column { Column {
Text { Text {
text: alias text: username
} }
Text { Text {
text: publicKey text: key
width: 160 width: 160
elide: Text.ElideMiddle elide: Text.ElideMiddle
} }
@ -76,11 +122,70 @@ SwipeView {
} }
} }
} }
} }
ListView {
id: addressesView
contentWidth: 200
model: onboardingModel
delegate: addressViewDelegate
Layout.fillHeight: true
Layout.fillWidth: true
anchors.topMargin: 36
anchors.fill: parent
// model: ListModel {
// ListElement {
// username: "Bill Smith"
// key: "0x123"
// }
// ListElement {
// username: "Slushy Welltodo Woodborer"
// key: "0x234"
// }
// }
}
// Repeater {
// model: generatedAccountsModel
// Rectangle {
// height: 32
// width: 32
// anchors.leftMargin: 20
// anchors.rightMargin: 20
// Row {
// RadioButton {
// checked: index == 0 ? true : false
// ButtonGroup.group: accountGroup
// onClicked: {
// wizardStep2.selectedIndex = index;
// }
// }
// Column {
// Image {
// source: identicon
// }
// }
// Column {
// Text {
// text: alias
// }
// Text {
// text: publicKey
// width: 160
// elide: Text.ElideMiddle
// }
// }
// }
// }
// }
} }
} // }
Button { Button {
text: "Select" text: "Select"
@ -93,6 +198,10 @@ SwipeView {
swipeView.incrementCurrentIndex(); swipeView.incrementCurrentIndex();
} }
} }
}
} }
Item { Item {
@ -179,11 +288,16 @@ SwipeView {
} }
} }
MessageDialog { MessageDialog {
id: storeAccountAndLoginError id: storeAccountAndLoginError
title: "Error storing account and logging in" title: "Error storing account and logging in"
text: "An error occurred while storing your account and logging in: " text: "An error occurred while storing your account and logging in: "
icon: StandardIcon.Error
// icon: StandardIcon.Error
standardButtons: StandardButton.Ok standardButtons: StandardButton.Ok
} }
@ -199,42 +313,54 @@ SwipeView {
return passwordsDontMatchError.open(); return passwordsDontMatchError.open();
} }
const selectedAccount = swipeView.generatedAccounts[wizardStep2.selectedIndex]; const selectedAccountIndex = wizardStep2.selectedIndex
const storeResponse = onboardingModel.storeAccountAndLogin(JSON.stringify(selectedAccount), txtPassword.text)
const storeResponse = onboardingModel.storeAccountAndLogin(selectedAccountIndex, txtPassword.text)
// const storeResponse = onboardingModel.storeAccountAndLogin(JSON.stringify(selectedAccount), txtPassword.text)
// const selectedAccount = swipeView.generatedAccounts[wizardStep2.selectedIndex];
// const storeResponse = onboardingModel.storeAccountAndLogin(JSON.stringify(selectedAccount), txtPassword.text)
const response = JSON.parse(storeResponse); const response = JSON.parse(storeResponse);
if (response.error) { // if (response.error) {
storeAccountAndLoginError.text += response.error; // storeAccountAndLoginError.text += response.error;
return storeAccountAndLoginError.open(); // return storeAccountAndLoginError.open();
} // }
swipeView.storeAccountAndLoginResult(response); console.log("=======");
console.log(storeResponse);
console.log("=======")
// swipeView.storeAccountAndLoginResult(response);
swipeView.storeAccountAndLoginResult();
} }
} }
} }
// handle the serialised result coming from node and deserialise into JSON // handle the serialised result coming from node and deserialise into JSON
// TODO: maybe we should figure out a clever to avoid this? // TODO: maybe we should figure out a clever to avoid this?
onStrGeneratedAccountsChanged: { // onStrGeneratedAccountsChanged: {
if (generatedAccounts === null || generatedAccounts === "") { // if (generatedAccounts === null || generatedAccounts === "") {
return; // return;
} // }
swipeView.generatedAccounts = JSON.parse(strGeneratedAccounts); // swipeView.generatedAccounts = JSON.parse(strGeneratedAccounts);
} // }
// handle deserialised data coming from the node // handle deserialised data coming from the node
onGeneratedAccountsChanged: { // onGeneratedAccountsChanged: {
generatedAccountsModel.clear(); // generatedAccountsModel.clear();
generatedAccounts.forEach(acc => { // generatedAccounts.forEach(acc => {
generatedAccountsModel.append({ // generatedAccountsModel.append({
publicKey: acc.publicKey, // publicKey: acc.publicKey,
alias: onboardingLogic.generateAlias(acc.publicKey), // alias: onboardingLogic.generateAlias(acc.publicKey),
identicon: onboardingLogic.identicon(acc.publicKey) // identicon: onboardingLogic.identicon(acc.publicKey)
}); // });
}); // });
} // }
} }
/*##^## /*##^##
Designer { Designer {
D{i:0;autoSize:true;height:480;width:640} D{i:0;autoSize:true;height:480;width:640}
} }
##^##*/ ##^##*/

View File

@ -58,7 +58,7 @@ Page {
DSM.SignalTransition { DSM.SignalTransition {
targetState: appState targetState: appState
signal: genKey.storeAccountAndLoginResult signal: genKey.storeAccountAndLoginResult
guard: !response.error // guard: !response.error
} }
} }