feat: connect existing status ens username
- Show welcome page only when there are no ens names registered - If you already have an ens username for the current account, connect and mark it as preferred name - State machine navigation
This commit is contained in:
parent
613c70c4a6
commit
43f4f8775b
|
@ -11,6 +11,7 @@ import ../../status/chat as status_chat
|
|||
import ../../status/devices
|
||||
import ../../status/chat/chat
|
||||
import view
|
||||
import views/ens_manager
|
||||
import chronicles
|
||||
|
||||
type ProfileController* = ref object of SignalSubscriber
|
||||
|
@ -31,10 +32,6 @@ proc delete*(self: ProfileController) =
|
|||
proc init*(self: ProfileController, account: Account) =
|
||||
let profile = account.toProfileModel()
|
||||
|
||||
# (rramos) TODO: I added this because I needed the public key
|
||||
# Ideally, this module should call getSettings once, and fill the
|
||||
# profile with all the information comming from the settings.
|
||||
let response = status_settings.getSettings()
|
||||
let pubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0")
|
||||
let mnemonic = status_settings.getSetting[string](Setting.Mnemonic, "")
|
||||
let network = status_settings.getSetting[string](Setting.Networks_CurrentNetwork, constants.DEFAULT_NETWORK_NAME)
|
||||
|
@ -48,6 +45,7 @@ proc init*(self: ProfileController, account: Account) =
|
|||
self.view.setNewProfile(profile)
|
||||
self.view.setMnemonic(mnemonic)
|
||||
self.view.setNetwork(network)
|
||||
self.view.ens.init()
|
||||
|
||||
var mailservers = status_mailservers.getMailservers()
|
||||
for mailserver_config in mailservers:
|
||||
|
|
|
@ -49,7 +49,7 @@ QtObject:
|
|||
result.addedContacts = newContactList()
|
||||
result.blockedContacts = newContactList()
|
||||
result.deviceList = newDeviceList()
|
||||
result.ens = newEnsManager()
|
||||
result.ens = newEnsManager(status)
|
||||
result.mnemonic = ""
|
||||
result.network = ""
|
||||
result.status = status
|
||||
|
|
|
@ -1,50 +1,95 @@
|
|||
import NimQml
|
||||
import Tables
|
||||
import json
|
||||
import json_serialization
|
||||
import sequtils
|
||||
from ../../../status/libstatus/types import Setting
|
||||
import ../../../status/threads
|
||||
import ../../../status/ens as status_ens
|
||||
import ../../../status/libstatus/settings as status_settings
|
||||
import ../../../status/libstatus/types
|
||||
import ../../../status/status
|
||||
|
||||
type
|
||||
EnsRoles {.pure.} = enum
|
||||
UserName = UserRole + 1
|
||||
|
||||
QtObject:
|
||||
type EnsManager* = ref object of QAbstractListModel
|
||||
usernames*: seq[string]
|
||||
status: Status
|
||||
|
||||
proc setup(self: EnsManager) = self.QAbstractListModel.setup
|
||||
|
||||
proc delete(self: EnsManager) =
|
||||
self.usernames = @[]
|
||||
self.QAbstractListModel.delete
|
||||
|
||||
proc newEnsManager*(): EnsManager =
|
||||
proc newEnsManager*(status: Status): EnsManager =
|
||||
new(result, delete)
|
||||
result.usernames = @[]
|
||||
result.status = status
|
||||
result.setup
|
||||
|
||||
proc validate*(self: EnsManager, ens: string, isStatus: bool) {.slot.} =
|
||||
spawnAndSend(self, "ensResolved") do:
|
||||
var username = ens
|
||||
if(isStatus): username = username & status_ens.domain
|
||||
proc init*(self: EnsManager) =
|
||||
self.usernames = status_settings.getSetting[seq[string]](Setting.Usernames, @[])
|
||||
|
||||
proc ensWasResolved*(self: EnsManager, ensResult: string) {.signal.}
|
||||
|
||||
proc ensResolved(self: EnsManager, ensResult: string) {.slot.} =
|
||||
self.ensWasResolved(ensResult)
|
||||
|
||||
proc validate*(self: EnsManager, ens: string, isStatus: bool) {.slot.} =
|
||||
let username = ens & (if(isStatus): status_ens.domain else: "")
|
||||
if self.usernames.filter(proc(x: string):bool = x == username).len > 0:
|
||||
self.ensResolved("already-connected")
|
||||
else:
|
||||
spawnAndSend(self, "ensResolved") do:
|
||||
let ownerAddr = status_ens.owner(username)
|
||||
var output = ""
|
||||
if ownerAddr == "" and isStatus:
|
||||
output = "available"
|
||||
else:
|
||||
if not isStatus:
|
||||
let userPubKey = status_settings.getSetting[string](Setting.PublicKey, "0x0")
|
||||
if ownerAddr != "":
|
||||
let userWallet = status_settings.getSetting[string](Setting.WalletRootAddress, "0x0")
|
||||
let pubkey = status_ens.pubkey(ens)
|
||||
if ownerAddr != "":
|
||||
if pubkey == "":
|
||||
output = "owned"
|
||||
else:
|
||||
if pubkey == userPubKey:
|
||||
output = "owned" # "Continuing will connect this username with your chat key."
|
||||
elif pubkey == userPubkey:
|
||||
output = "connected"
|
||||
else:
|
||||
output = "connected-different-key"
|
||||
elif ownerAddr == userWallet:
|
||||
output = "connected-different-key" # "Continuing will require a transaction to connect the username with your current chat key.",
|
||||
else:
|
||||
output = "taken"
|
||||
else:
|
||||
output = "taken"
|
||||
output
|
||||
|
||||
proc ensWasResolved*(self: EnsManager, ensResult: string) {.signal.}
|
||||
proc connect(self: EnsManager, username: string) {.slot.} =
|
||||
let ensUsername = username & status_ens.domain
|
||||
var usernames = status_settings.getSetting[seq[string]](Setting.Usernames, @[])
|
||||
let preferredUsername = status_settings.getSetting[string](Setting.PreferredUsername, "")
|
||||
usernames.add ensUsername
|
||||
discard status_settings.saveSetting(Setting.Usernames, %*usernames)
|
||||
discard status_settings.saveSetting(Setting.PreferredUsername, ensUsername)
|
||||
|
||||
proc ensResolved(self: EnsManager, ensResult: string) {.slot.} =
|
||||
self.ensWasResolved(ensResult)
|
||||
method rowCount(self: EnsManager, index: QModelIndex = nil): int =
|
||||
return self.usernames.len
|
||||
|
||||
method data(self: EnsManager, index: QModelIndex, role: int): QVariant =
|
||||
if not index.isValid:
|
||||
return
|
||||
if index.row < 0 or index.row >= self.usernames.len:
|
||||
return
|
||||
let username = self.usernames[index.row]
|
||||
result = newQVariant(username)
|
||||
|
||||
method roleNames(self: EnsManager): Table[int, string] =
|
||||
{
|
||||
EnsRoles.UserName.int:"username"
|
||||
}.toTable
|
||||
|
||||
proc add*(self: EnsManager, username: string) =
|
||||
self.beginInsertRows(newQModelIndex(), self.usernames.len, self.usernames.len)
|
||||
self.usernames.add(username)
|
||||
self.endInsertRows()
|
||||
|
|
|
@ -150,6 +150,8 @@ type
|
|||
Stickers_Recent = "stickers/recent-stickers"
|
||||
WalletRootAddress = "wallet-root-address"
|
||||
LatestDerivedPath = "latest-derived-path"
|
||||
PreferredUsername = "preferred-name"
|
||||
Usernames = "usernames"
|
||||
|
||||
UpstreamConfig* = ref object
|
||||
enabled* {.serializedFieldName("Enabled").}: bool
|
||||
|
|
|
@ -46,9 +46,17 @@ SplitView {
|
|||
address: profileModel.profile.address
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if(visibleChildren[0] === ensContainer){
|
||||
ensContainer.goToStart();
|
||||
}
|
||||
}
|
||||
|
||||
ContactsContainer {}
|
||||
|
||||
EnsContainer {}
|
||||
EnsContainer {
|
||||
id: ensContainer
|
||||
}
|
||||
|
||||
PrivacyContainer {}
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.14
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
|
||||
Item {
|
||||
property string ensUsername: ""
|
||||
property var onClick: function(){}
|
||||
|
||||
StyledText {
|
||||
id: sectionTitle
|
||||
//% "ENS usernames"
|
||||
text: qsTrId("ens-usernames")
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 24
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 24
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
|
||||
Rectangle {
|
||||
id: circle
|
||||
anchors.top: sectionTitle.bottom
|
||||
anchors.topMargin: 24
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: 60
|
||||
height: 60
|
||||
radius: 120
|
||||
color: Style.current.blue
|
||||
|
||||
StyledText {
|
||||
text: "✓"
|
||||
opacity: 0.7
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 18
|
||||
color: Style.current.white
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: title
|
||||
text: qsTr("Username added")
|
||||
anchors.top: circle.bottom
|
||||
anchors.topMargin: 24
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 24
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: subtitle
|
||||
text: qsTr("%1 is now connected with your chat key and can be used in Status.").arg(ensUsername)
|
||||
anchors.top: title.bottom
|
||||
anchors.topMargin: 24
|
||||
font.pixelSize: 14
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
StyledButton {
|
||||
id: startBtn
|
||||
anchors.top: subtitle.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
label: qsTr("Ok, got it")
|
||||
onClicked: onClick()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.14
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
|
||||
Item {
|
||||
property var onClick: function(){}
|
||||
|
||||
StyledText {
|
||||
id: sectionTitle
|
||||
//% "ENS usernames"
|
||||
text: qsTrId("ens-usernames")
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 24
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 24
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
Item {
|
||||
id: addUsername
|
||||
anchors.top: sectionTitle.bottom
|
||||
anchors.topMargin: Style.current.bigPadding
|
||||
width: addButton.width + usernameText.width + Style.current.padding
|
||||
height: addButton.height
|
||||
|
||||
AddButton {
|
||||
id: addButton
|
||||
clickable: false
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 40
|
||||
height: 40
|
||||
}
|
||||
|
||||
StyledText {
|
||||
id: usernameText
|
||||
text: qsTr("Add username")
|
||||
color: Style.current.blue
|
||||
anchors.left: addButton.right
|
||||
anchors.leftMargin: Style.current.padding
|
||||
anchors.verticalCenter: addButton.verticalCenter
|
||||
font.pixelSize: 15
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: onClick()
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: sview
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
contentHeight: contentItem.childrenRect.height
|
||||
anchors.top: addUsername.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Item {
|
||||
id: contentItem
|
||||
anchors.right: parent.right;
|
||||
anchors.left: parent.left;
|
||||
|
||||
StyledText {
|
||||
id: title
|
||||
text: "TODO: Show ENS username list"
|
||||
anchors.top: parent.top
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,9 +6,13 @@ import "../../../../../shared"
|
|||
|
||||
Item {
|
||||
id: searchENS
|
||||
|
||||
property var onClick: function(){}
|
||||
|
||||
property string validationMessage: ""
|
||||
property bool valid: false
|
||||
property bool isStatus: true
|
||||
property string ensStatus: ""
|
||||
|
||||
property var validateENS: Backpressure.debounce(searchENS, 500, function (ensName, isStatus){
|
||||
profileModel.ens.validate(ensName, isStatus)
|
||||
|
@ -17,6 +21,7 @@ Item {
|
|||
function validate() {
|
||||
validationMessage = "";
|
||||
valid = false;
|
||||
ensStatus = "";
|
||||
if (ensUsername.text.length < 4) {
|
||||
validationMessage = qsTr("At least 4 characters. Latin letters, numbers, and lowercase only.");
|
||||
} else if(isStatus && !ensUsername.text.match(/^[a-z0-9]+$/)){
|
||||
|
@ -80,25 +85,32 @@ Item {
|
|||
Connections {
|
||||
target: profileModel.ens
|
||||
onEnsWasResolved: {
|
||||
valid = false
|
||||
valid = false;
|
||||
ensStatus = ensResult;
|
||||
switch(ensResult){
|
||||
case "available":
|
||||
valid = true;
|
||||
validationMessage = qsTr("✓ Username available!");
|
||||
break;
|
||||
case "owned":
|
||||
console.log("TODO: -");
|
||||
console.log("TODO: - Continuing will connect this username with your chat key.");
|
||||
case "taken":
|
||||
validationMessage = !isStatus ?
|
||||
qsTr("Username doesn’t belong to you :(")
|
||||
:
|
||||
qsTr("Username already taken :(");
|
||||
break;
|
||||
case "already-connected":
|
||||
validationMessage = qsTr("Username is already connected with your chat key and can be used inside Status.");
|
||||
break;
|
||||
case "connected":
|
||||
validationMessage = qsTr("This user name is owned by you and connected with your chat key.");
|
||||
valid = true;
|
||||
validationMessage = qsTr("This user name is owned by you and connected with your chat key. Continue to set `Show my ENS username in chats`.");
|
||||
break;
|
||||
case "connected-different-key":
|
||||
validationMessage = qsTr("Username doesn’t belong to you :(");
|
||||
valid = true;
|
||||
validationMessage = qsTr("Continuing will require a transaction to connect the username with your current chat key.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,7 +137,17 @@ Item {
|
|||
anchors.fill: parent
|
||||
onClicked : {
|
||||
if(!valid) return;
|
||||
console.log("TODO: show ens T&C")
|
||||
|
||||
if(ensStatus === "connected"){
|
||||
profileModel.ens.connect(ensUsername.text);
|
||||
onClick(ensStatus, ensUsername.text);
|
||||
return;
|
||||
}
|
||||
|
||||
if(ensStatus === "available"){
|
||||
onClick(ensStatus, ensUsername.text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import QtQuick 2.14
|
||||
import QtQuick.Layouts 1.3
|
||||
import QtQuick.Controls 2.14
|
||||
import "../../../../../imports"
|
||||
import "../../../../../shared"
|
||||
|
||||
Item {
|
||||
property var onClick: function(){}
|
||||
|
||||
StyledText {
|
||||
id: sectionTitle
|
||||
//% "ENS usernames"
|
||||
text: qsTrId("ens-usernames")
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 24
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 24
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 20
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
id: sview
|
||||
clip: true
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
contentHeight: contentItem.childrenRect.height
|
||||
anchors.top: sectionTitle.bottom
|
||||
anchors.topMargin: Style.current.padding
|
||||
anchors.bottom: startBtn.top
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Item {
|
||||
id: contentItem
|
||||
anchors.right: parent.right;
|
||||
anchors.left: parent.left;
|
||||
|
||||
StyledText {
|
||||
id: title
|
||||
text: qsTr("TODO: show T&C and confirmation screen for acquiring a ens username")
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 24
|
||||
font.weight: Font.Bold
|
||||
font.pixelSize: 24
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledButton {
|
||||
id: startBtn
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Style.current.padding
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
label: qsTr("Ok")
|
||||
onClicked: onClick()
|
||||
}
|
||||
}
|
|
@ -12,9 +12,21 @@ Item {
|
|||
Layout.fillWidth: true
|
||||
|
||||
property bool showSearchScreen: false
|
||||
property string addedUsername: ""
|
||||
|
||||
signal next()
|
||||
signal back()
|
||||
signal next(output: string)
|
||||
signal connect(ensUsername: string)
|
||||
|
||||
signal goToWelcome();
|
||||
signal goToList();
|
||||
|
||||
function goToStart(){
|
||||
if(profileModel.ens.rowCount() > 0){
|
||||
goToList();
|
||||
} else {
|
||||
goToWelcome();
|
||||
}
|
||||
}
|
||||
|
||||
DSM.StateMachine {
|
||||
id: stateMachine
|
||||
|
@ -28,15 +40,84 @@ Item {
|
|||
targetState: searchState
|
||||
signal: next
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: listState
|
||||
signal: goToList
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: searchState
|
||||
onEntered: loader.sourceComponent = search
|
||||
DSM.SignalTransition {
|
||||
targetState: tAndCState
|
||||
signal: next
|
||||
guard: output === "available"
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: addedState
|
||||
signal: connect
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: listState
|
||||
signal: goToList
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: welcomeState
|
||||
signal: goToWelcome
|
||||
}
|
||||
}
|
||||
|
||||
DSM.FinalState {
|
||||
id: ensFinalState
|
||||
DSM.State {
|
||||
id: addedState
|
||||
onEntered: {
|
||||
loader.sourceComponent = added;
|
||||
loader.item.ensUsername = addedUsername;
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: listState
|
||||
signal: next
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: listState
|
||||
signal: goToList
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: welcomeState
|
||||
signal: goToWelcome
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: listState
|
||||
onEntered: {
|
||||
loader.sourceComponent = list;
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: searchState
|
||||
signal: next
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: listState
|
||||
signal: goToList
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: welcomeState
|
||||
signal: goToWelcome
|
||||
}
|
||||
}
|
||||
|
||||
DSM.State {
|
||||
id: tAndCState
|
||||
onEntered:loader.sourceComponent = termsAndConditions
|
||||
DSM.SignalTransition {
|
||||
targetState: listState
|
||||
signal: goToList
|
||||
}
|
||||
DSM.SignalTransition {
|
||||
targetState: welcomeState
|
||||
signal: goToWelcome
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -49,13 +130,55 @@ Item {
|
|||
id: welcome
|
||||
Welcome {
|
||||
onClick: function(){
|
||||
next();
|
||||
next(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: search
|
||||
Search {}
|
||||
Search {
|
||||
onClick: function(output, username){
|
||||
if(output === "connected"){
|
||||
connect(username)
|
||||
} else {
|
||||
next(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: termsAndConditions
|
||||
TermsAndConditions {
|
||||
onClick: function(output){
|
||||
next(output);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: added
|
||||
Added {
|
||||
onClick: function(){
|
||||
next(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: list
|
||||
List {
|
||||
onClick: function(){
|
||||
next(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: ensContainer
|
||||
onConnect: {
|
||||
addedUsername = ensUsername;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue