feat(@desktop/keycard): UI for the keycard settings in case user has set up a keycard

Keycard settings view - UI - developed in a way that list of keycards is displayed
if there is at least one keycard set up. If the a keycard is locked or gets locked it
will be correctly marked in red. Selecting keycard from the list, its details may be
seen and additional flows may be run for it (so far only unlock flow is developed).

Fixes: #7025
This commit is contained in:
Sale Djenic 2022-10-11 14:15:33 +02:00 committed by saledjenic
parent 86a2d963ad
commit c1f4874e18
25 changed files with 1074 additions and 176 deletions

View File

@ -5,6 +5,8 @@ import io_interface
import ../../../../core/eventemitter
import ../../../shared_modules/keycard_popup/io_interface as keycard_shared_module
import ../../../../../app_service/service/contacts/service as contact_service
import ../../../../../app_service/service/wallet_account/service as wallet_account_service
logScope:
topics = "profile-section-keycard-module-controller"
@ -15,13 +17,16 @@ type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
events: EventEmitter
walletAccountService: wallet_account_service.Service
proc newController*(delegate: io_interface.AccessInterface,
events: EventEmitter):
events: EventEmitter,
walletAccountService: wallet_account_service.Service):
Controller =
result = Controller()
result.delegate = delegate
result.events = events
result.walletAccountService = walletAccountService
proc delete*(self: Controller) =
discard
@ -38,3 +43,24 @@ proc init*(self: Controller) =
if args.uniqueIdentifier != UNIQUE_SETTING_KEYCARD_MODULE_IDENTIFIER:
return
self.delegate.onDisplayKeycardSharedModuleFlow()
self.events.on(SIGNAL_LOGGEDIN_USER_IMAGE_CHANGED) do(e: Args):
self.delegate.onLoggedInUserImageChanged()
self.events.on(SIGNAL_KEYCARD_LOCKED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardLocked(args.keycardUid)
self.events.on(SIGNAL_KEYCARD_UNLOCKED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardUnlocked(args.keycardUid)
self.events.on(SIGNAL_KEYCARD_UID_UPDATED) do(e: Args):
let args = KeycardActivityArgs(e)
self.delegate.onKeycardUidUpdated(args.keycardUid, args.keycardNewUid)
proc getAllMigratedKeyPairs*(self: Controller): seq[KeyPairDto] =
return self.walletAccountService.getAllMigratedKeyPairs()
proc getWalletAccounts*(self: Controller): seq[wallet_account_service.WalletAccountDto] =
return self.walletAccountService.fetchAccounts()

View File

@ -37,7 +37,7 @@ method runImportOrRestoreViaSeedPhrasePopup*(self: AccessInterface) {.base.} =
method runImportFromKeycardToAppPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method runUnlockKeycardPopup*(self: AccessInterface) {.base.} =
method runUnlockKeycardPopupForKeycardWithUid*(self: AccessInterface, keycardUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method runDisplayKeycardContentPopup*(self: AccessInterface) {.base.} =
@ -46,6 +46,36 @@ method runDisplayKeycardContentPopup*(self: AccessInterface) {.base.} =
method runFactoryResetPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method runRenameKeycardPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method runChangePinPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method runCreateBackupCopyOfAKeycardPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method runCreatePukPopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method runCreateNewPairingCodePopup*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onLoggedInUserImageChanged*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardLocked*(self: AccessInterface, keycardUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardUnlocked*(self: AccessInterface, keycardUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method onKeycardUidUpdated*(self: AccessInterface, keycardUid: string, keycardNewUid: string) {.base.} =
raise newException(ValueError, "No implementation available")
method getKeycardDetailsAsJson*(self: AccessInterface, keycardUid: string): string {.base.} =
raise newException(ValueError, "No implementation available")
# View Delegate Interface
# Delegate for the view must be declared here due to use of QtObject and multi

View File

@ -1,8 +1,9 @@
import NimQml, chronicles
import NimQml, chronicles, json, marshal
import ./io_interface, ./view, ./controller
import ../io_interface as delegate_interface
import ../../../../global/global_singleton
import ../../../../core/eventemitter
import ../../../../../app_service/service/keycard/service as keycard_service
@ -13,6 +14,7 @@ import ../../../../../app_service/service/wallet_account/service as wallet_accou
import ../../../../../app_service/service/keychain/service as keychain_service
import ../../../shared_modules/keycard_popup/module as keycard_shared_module
import ../../../shared_modules/keycard_popup/models/keycard_model
export io_interface
@ -35,6 +37,9 @@ type
keychainService: keychain_service.Service
keycardSharedModule: keycard_shared_module.AccessInterface
## Forward declarations
proc buildKeycardList(self: Module)
proc newModule*(delegate: delegate_interface.AccessInterface,
events: EventEmitter,
keycardService: keycard_service.Service,
@ -54,7 +59,7 @@ proc newModule*(delegate: delegate_interface.AccessInterface,
result.keychainService = keychainService
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, events)
result.controller = controller.newController(result, events, walletAccountService)
result.moduleLoaded = false
method delete*(self: Module) =
@ -67,6 +72,7 @@ method delete*(self: Module) =
method load*(self: Module) =
self.controller.init()
self.view.load()
self.buildKeycardList()
method isLoaded*(self: Module): bool =
return self.moduleLoaded
@ -113,10 +119,11 @@ method runImportOrRestoreViaSeedPhrasePopup*(self: Module) =
method runImportFromKeycardToAppPopup*(self: Module) =
info "TODO: Import from Keycard to Status Desktop..."
method runUnlockKeycardPopup*(self: Module) =
method runUnlockKeycardPopupForKeycardWithUid*(self: Module, keycardUid: string) =
self.createSharedKeycardModule()
if self.keycardSharedModule.isNil:
return
self.keycardSharedModule.setUidOfAKeycardWhichNeedToBeUnlocked(keycardUid)
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.UnlockKeycard)
method runDisplayKeycardContentPopup*(self: Module) =
@ -129,4 +136,93 @@ method runFactoryResetPopup*(self: Module) =
self.createSharedKeycardModule()
if self.keycardSharedModule.isNil:
return
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.FactoryReset)
self.keycardSharedModule.runFlow(keycard_shared_module.FlowType.FactoryReset)
method runRenameKeycardPopup*(self: Module) =
info "TODO: Rename Keycard..."
method runChangePinPopup*(self: Module) =
info "TODO: Change PIN for a Keycard..."
method runCreateBackupCopyOfAKeycardPopup*(self: Module) =
info "TODO: Create a Backup Copy of a Keycard..."
method runCreatePukPopup*(self: Module) =
info "TODO: Create PUK for a Keycard..."
method runCreateNewPairingCodePopup*(self: Module) =
info "TODO: Create New Pairing Code for a Keycard..."
proc buildKeycardList(self: Module) =
let findAccountByAccountAddress = proc(accounts: seq[WalletAccountDto], address: string): WalletAccountDto =
for i in 0 ..< accounts.len:
if(accounts[i].address == address):
return accounts[i]
return nil
let accounts = self.controller.getWalletAccounts()
var items: seq[KeycardItem]
let migratedKeyPairs = self.controller.getAllMigratedKeyPairs()
for kp in migratedKeyPairs:
var knownAccounts: seq[WalletAccountDto]
for accAddr in kp.accountsAddresses:
let account = findAccountByAccountAddress(accounts, accAddr)
if account.isNil:
## we should never be here cause we need to remove deleted accounts from the `keypairs` table and sync
## that state accross different app instances
continue
knownAccounts.add(account)
if knownAccounts.len == 0:
continue
var item = initKeycardItem(keycardUid = kp.keycardUid,
pubKey = knownAccounts[0].publicKey,
keyUid = kp.keyUid,
locked = kp.keycardLocked,
name = kp.keycardName,
derivedFrom = knownAccounts[0].derivedfrom)
for ka in knownAccounts:
if ka.walletType == WalletTypeDefaultStatusAccount:
item.setPairType(KeyPairType.Profile)
item.setPubKey(singletonInstance.userProfile.getPubKey())
item.setImage(singletonInstance.userProfile.getIcon())
if ka.walletType == WalletTypeSeed:
item.setPairType(KeyPairType.SeedImport)
item.setIcon("wallet")
if ka.walletType == WalletTypeKey:
item.setPairType(KeyPairType.PrivateKeyImport)
item.setIcon("wallet")
item.addAccount(ka.name, ka.path, ka.address, ka.emoji, ka.color, icon = "", balance = 0.0)
items.add(item)
self.view.setKeycardItems(items)
method onLoggedInUserImageChanged*(self: Module) =
self.view.keycardModel().setImage(singletonInstance.userProfile.getPubKey(), singletonInstance.userProfile.getIcon())
self.view.emitKeycardProfileChangedSignal()
method onKeycardLocked*(self: Module, keycardUid: string) =
self.view.keycardModel().setLocked(keycardUid, true)
self.view.emitKeycardDetailsChangedSignal(keycardUid)
method onKeycardUnlocked*(self: Module, keycardUid: string) =
self.view.keycardModel().setLocked(keycardUid, false)
self.view.emitKeycardDetailsChangedSignal(keycardUid)
method onKeycardUidUpdated*(self: Module, keycardUid: string, keycardNewUid: string) =
self.view.keycardModel().setKeycardUid(keycardUid, keycardNewUid)
self.view.emitKeycardUidChangedSignal(keycardUid, keycardNewUid)
method getKeycardDetailsAsJson*(self: Module, keycardUid: string): string =
let item = self.view.keycardModel().getItemByKeycardUid(keycardUid)
let jsonObj = %* {
"keycardUid": item.keycardUid,
"pubKey": item.pubkey,
"keyUid": item.keyUid,
"locked": item.locked,
"name": item.name,
"image": item.image,
"icon": item.icon,
"pairType": $item.pairType.int,
"derivedFrom": item.derivedFrom,
"accounts": $item.accounts
}
return $jsonObj

View File

@ -1,19 +1,31 @@
import NimQml
import ../../../shared_modules/keycard_popup/models/keycard_model
import ./io_interface
QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
keycardModel: KeycardModel
keycardModelVariant: QVariant
proc delete*(self: View) =
self.QObject.delete
if not self.keycardModel.isNil:
self.keycardModel.delete
if not self.keycardModelVariant.isNil:
self.keycardModelVariant.delete
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.QObject.setup
result.delegate = delegate
if result.keycardModel.isNil:
result.keycardModel = newKeycardModel()
if result.keycardModelVariant.isNil:
result.keycardModelVariant = newQVariant(result.keycardModel)
proc load*(self: View) =
self.delegate.viewDidLoad()
@ -43,11 +55,57 @@ QtObject:
proc runImportFromKeycardToAppPopup*(self: View) {.slot.} =
self.delegate.runImportFromKeycardToAppPopup()
proc runUnlockKeycardPopup*(self: View) {.slot.} =
self.delegate.runUnlockKeycardPopup()
proc runUnlockKeycardPopupForKeycardWithUid*(self: View, keycardUid: string) {.slot.} =
self.delegate.runUnlockKeycardPopupForKeycardWithUid(keycardUid)
proc runDisplayKeycardContentPopup*(self: View) {.slot.} =
self.delegate.runDisplayKeycardContentPopup()
proc runFactoryResetPopup*(self: View) {.slot.} =
self.delegate.runFactoryResetPopup()
self.delegate.runFactoryResetPopup()
proc runRenameKeycardPopup*(self: View) {.slot.} =
self.delegate.runRenameKeycardPopup()
proc runChangePinPopup*(self: View) {.slot.} =
self.delegate.runChangePinPopup()
proc runCreateBackupCopyOfAKeycardPopup*(self: View) {.slot.} =
self.delegate.runCreateBackupCopyOfAKeycardPopup()
proc runCreatePukPopup*(self: View) {.slot.} =
self.delegate.runCreatePukPopup()
proc runCreateNewPairingCodePopup*(self: View) {.slot.} =
self.delegate.runCreateNewPairingCodePopup()
proc keycardModel*(self: View): KeycardModel =
return self.keycardModel
proc keycardModelChanged(self: View) {.signal.}
proc getKeycardModel(self: View): QVariant {.slot.} =
if self.keycardModelVariant.isNil:
return newQVariant()
return self.keycardModelVariant
QtProperty[QVariant] keycardModel:
read = getKeycardModel
notify = keycardModelChanged
proc setKeycardItems*(self: View, items: seq[KeycardItem]) =
self.keycardModel.setItems(items)
self.keycardModelChanged()
proc getKeycardDetailsAsJson*(self: View, keycardUid: string): string {.slot.} =
return self.delegate.getKeycardDetailsAsJson(keycardUid)
proc keycardProfileChanged(self: View) {.signal.}
proc emitKeycardProfileChangedSignal*(self: View) =
self.keycardProfileChanged()
proc keycardUidChanged(self: View, oldKcUid: string, newKcUid: string) {.signal.}
proc emitKeycardUidChangedSignal*(self: View, oldKcUid: string, newKcUid: string) =
self.keycardUidChanged(oldKcUid, newKcUid)
proc keycardDetailsChanged(self: View, kcUid: string) {.signal.}
proc emitKeycardDetailsChangedSignal*(self: View, kcUid: string) =
self.keycardDetailsChanged(kcUid)

View File

@ -31,6 +31,7 @@ type
connectionKeycardResponse: UUID
tmpKeycardContainsMetadata: bool
tmpCardMetadata: CardMetadata
tmpKeycardUidForUnlocking: string
tmpPin: string
tmpPinMatch: bool
tmpPuk: string
@ -141,6 +142,12 @@ proc containsMetadata*(self: Controller): bool =
proc setContainsMetadata*(self: Controller, value: bool) =
self.tmpKeycardContainsMetadata = value
proc setUidOfAKeycardWhichNeedToBeUnlocked*(self: Controller, value: string) =
self.tmpKeycardUidForUnlocking = value
proc getUidOfAKeycardWhichNeedToBeUnlocked*(self: Controller): string =
return self.tmpKeycardUidForUnlocking
proc setPin*(self: Controller, value: string) =
self.tmpPin = value

View File

@ -26,7 +26,7 @@ proc doMigration(self: MigratingKeyPairState, controller: Controller) =
self.migrationSuccess = self.migrationSuccess and controller.convertSelectedKeyPairToKeycardAccount(password)
if not self.migrationSuccess:
return
controller.runStoreMetadataFlow(selectedKeyPairDto.keypairName, controller.getPin(),
controller.runStoreMetadataFlow(selectedKeyPairDto.keycardName, controller.getPin(),
controller.getSelectedKeyPairWalletPaths())
method executePrimaryCommand*(self: MigratingKeyPairState, controller: Controller) =

View File

@ -24,9 +24,15 @@ method executeTertiaryCommand*(self: ReadingKeycardState, controller: Controller
method getNextSecondaryState*(self: ReadingKeycardState, controller: Controller): State =
let (flowType, flowEvent) = controller.getLastReceivedKeycardData()
# this is used in case a keycard is not inserted in the moment when flow is run (we're animating an insertion)
return ensureReaderAndCardPresenceAndResolveNextState(self, flowType, flowEvent, controller)
return self.resolveKeycardNextState(flowType, flowEvent, controller)
method resolveKeycardNextState*(self: ReadingKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
controller: Controller): State =
if self.flowType == FlowType.UnlockKeycard:
let ensureKeycardPresenceState = ensureReaderAndCardPresence(self, keycardFlowType, keycardEvent, controller)
if ensureKeycardPresenceState.isNil: # means the keycard is inserted
let kcUid = controller.getUidOfAKeycardWhichNeedToBeUnlocked()
if kcUid.len > 0 and kcUid != keycardEvent.instanceUID:
return createState(StateType.WrongKeycard, self.flowType, nil)
# this is used in case a keycard is inserted and we jump to the first meaningful screen
return ensureReaderAndCardPresenceAndResolveNextState(self, keycardFlowType, keycardEvent, controller)

View File

@ -8,6 +8,11 @@ proc newWrongKeycardState*(flowType: FlowType, backState: State): WrongKeycardSt
proc delete*(self: WrongKeycardState) =
self.State.delete
method executePrimaryCommand*(self: WrongKeycardState, controller: Controller) =
if self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = true)
method executeTertiaryCommand*(self: WrongKeycardState, controller: Controller) =
if self.flowType == FlowType.Authentication:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)
if self.flowType == FlowType.Authentication or
self.flowType == FlowType.UnlockKeycard:
controller.terminateCurrentFlow(lastStepInTheCurrentFlow = false)

View File

@ -91,6 +91,9 @@ method onKeycardResponse*(self: AccessInterface, keycardFlowType: string, keycar
method runFlow*(self: AccessInterface, flowToRun: FlowType, keyUid = "", bip44Path = "", txHash = "") {.base.} =
raise newException(ValueError, "No implementation available")
method setUidOfAKeycardWhichNeedToBeUnlocked*(self: AccessInterface, value: string) {.base.} =
raise newException(ValueError, "No implementation available")
method setPin*(self: AccessInterface, value: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -28,7 +28,26 @@ type
derivedFrom: string
pairType: KeyPairType
accounts: seq[WalletAccountDetails]
proc setup*(self: KeyPairItem,
pubKey: string,
keyUid: string,
locked: bool,
name: string,
image: string,
icon: string,
pairType: KeyPairType,
derivedFrom: string
) =
self.pubKey = pubKey
self.keyUid = keyUid
self.locked = locked
self.name = name
self.image = image
self.icon = icon
self.pairType = pairType
self.derivedFrom = derivedFrom
proc initKeyPairItem*(
pubKey = "",
keyUid = "",
@ -40,19 +59,14 @@ proc initKeyPairItem*(
derivedFrom = ""
): KeyPairItem =
result = KeyPairItem()
result.pubKey = pubKey
result.keyUid = keyUid
result.name = name
result.image = image
result.icon = icon
result.pairType = pairType
result.derivedFrom = derivedFrom
result.setup(pubKey, keyUid, locked, name, image, icon, pairType, derivedFrom)
proc `$`*(self: KeyPairItem): string =
result = fmt"""KeyPairItem[
pubKey: {self.pubkey},
keyUid: {self.keyUid},
name: {self.name},
locked: {self.locked},
image: {self.image},
icon: {self.icon},
pairType: {$self.pairType},

View File

@ -0,0 +1,42 @@
import strformat
import key_pair_item
export key_pair_item
type
KeycardItem* = ref object of KeyPairItem
keycardUid: string
proc initKeycardItem*(
keycardUid = "",
pubKey = "",
keyUid = "",
locked = false,
name = "",
image = "",
icon = "",
pairType = KeyPairType.Unknown,
derivedFrom = ""
): KeycardItem =
result = KeycardItem()
result.KeyPairItem.setup(pubKey, keyUid, locked, name, image, icon, pairType, derivedFrom)
result.keycardUid = keycardUid
proc `$`*(self: KeycardItem): string =
result = fmt"""KeycardItem[
keycardUid: {self.keycardUid},
pubKey: {self.pubkey},
keyUid: {self.keyUid},
locked: {self.locked},
name: {self.name},
image: {self.image},
icon: {self.icon},
pairType: {$self.pairType},
derivedFrom: {self.derivedFrom},
accounts: {self.accounts}
]"""
proc keycardUid*(self: KeycardItem): string {.inline.} =
self.keycardUid
proc setKeycardUid*(self: KeycardItem, value: string) {.inline.} =
self.keycardUid = value

View File

@ -0,0 +1,128 @@
import NimQml, Tables, strformat
import keycard_item
export keycard_item
type
ModelRole {.pure.} = enum
PubKey = UserRole + 1
KeycardUid
Locked
Name
Image
Icon
PairType
Accounts
DerivedFrom
QtObject:
type
KeycardModel* = ref object of QAbstractListModel
items: seq[KeycardItem]
proc delete(self: KeycardModel) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: KeycardModel) =
self.QAbstractListModel.setup
proc newKeycardModel*(): KeycardModel =
new(result, delete)
result.setup
proc countChanged(self: KeycardModel) {.signal.}
proc getCount*(self: KeycardModel): int {.slot.} =
self.items.len
QtProperty[int]count:
read = getCount
notify = countChanged
proc setItems*(self: KeycardModel, items: seq[KeycardItem]) =
self.beginResetModel()
self.items = items
self.endResetModel()
self.countChanged()
proc `$`*(self: KeycardModel): string =
for i in 0 ..< self.items.len:
result &= fmt"""KeycardModel:
[{i}]:({$self.items[i]})
"""
method rowCount(self: KeycardModel, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: KeycardModel): Table[int, string] =
{
ModelRole.PubKey.int: "pubKey",
ModelRole.KeycardUid.int: "keycardUid",
ModelRole.Locked.int: "locked",
ModelRole.Name.int: "name",
ModelRole.Image.int: "image",
ModelRole.Icon.int: "icon",
ModelRole.PairType.int: "pairType",
ModelRole.Accounts.int: "accounts",
ModelRole.DerivedFrom.int: "derivedFrom"
}.toTable
method data(self: KeycardModel, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let item = self.items[index.row]
let enumRole = role.ModelRole
case enumRole:
of ModelRole.PubKey:
result = newQVariant(item.pubKey)
of ModelRole.KeycardUid:
result = newQVariant(item.keycardUid)
of ModelRole.Locked:
result = newQVariant(item.locked)
of ModelRole.Name:
result = newQVariant(item.name)
of ModelRole.Image:
result = newQVariant(item.image)
of ModelRole.Icon:
result = newQVariant(item.icon)
of ModelRole.PairType:
result = newQVariant(item.pairType.int)
of ModelRole.Accounts:
result = newQVariant(item.accounts)
of ModelRole.DerivedFrom:
result = newQVariant(item.derivedFrom)
proc getItemByKeycardUid*(self: KeycardModel, keycardUid: string): KeycardItem =
for i in 0 ..< self.items.len:
if(self.items[i].keycardUid == keycardUid):
return self.items[i]
return nil
proc findIndexForMember(self: KeycardModel, pubKey: string): int =
for i in 0 ..< self.items.len:
if(self.items[i].pubKey == pubKey):
return i
return -1
proc setImage*(self: KeycardModel, pubKey: string, image: string) =
let ind = self.findIndexForMember(pubKey)
if(ind == -1):
return
self.items[ind].setImage(image)
let index = self.createIndex(ind, 0, nil)
self.dataChanged(index, index, @[ModelRole.Image.int])
proc setLocked*(self: KeycardModel, keycardUid: string, locked: bool) =
for i in 0 ..< self.items.len:
if(self.items[i].keycardUid == keycardUid):
self.items[i].setLocked(locked)
let index = self.createIndex(i, 0, nil)
self.dataChanged(index, index, @[ModelRole.Locked.int])
proc setKeycardUid*(self: KeycardModel, keycardUid: string, keycardNewUid: string) =
for i in 0 ..< self.items.len:
if(self.items[i].keycardUid == keycardUid):
self.items[i].setKeycardUid(keycardNewUid)
let index = self.createIndex(i, 0, nil)
self.dataChanged(index, index, @[ModelRole.KeycardUid.int])

View File

@ -66,6 +66,9 @@ method getKeycardData*[T](self: Module[T]): string =
method setKeycardData*[T](self: Module[T], value: string) =
self.view.setKeycardData(value)
method setUidOfAKeycardWhichNeedToBeUnlocked*[T](self: Module[T], value: string) =
self.controller.setUidOfAKeycardWhichNeedToBeUnlocked(value)
method setPin*[T](self: Module[T], value: string) =
self.controller.setPin(value)
@ -349,7 +352,7 @@ method runFlow*[T](self: Module[T], flowToRun: FlowType, keyUid = "", bip44Path
method setSelectedKeyPair*[T](self: Module[T], item: KeyPairItem) =
var paths: seq[string]
var keyPairDto = KeyPairDto(keycardUid: "", # will be set during migration
keypairName: item.name,
keycardName: item.name,
keycardLocked: item.locked,
keyUid: item.keyUid)
for a in item.accountsAsArr():

View File

@ -4,7 +4,7 @@ include ../../common/json_utils
type KeyPairDto* = object
keycardUid*: string
keypairName*: string
keycardName*: string
keycardLocked*: bool
accountsAddresses*: seq[string]
keyUid*: string
@ -12,7 +12,7 @@ type KeyPairDto* = object
proc toKeyPairDto*(jsonObj: JsonNode): KeyPairDto =
result = KeyPairDto()
discard jsonObj.getProp("keycard-uid", result.keycardUid)
discard jsonObj.getProp("keypair-name", result.keypairName)
discard jsonObj.getProp("keycard-name", result.keycardName)
discard jsonObj.getProp("keycard-locked", result.keycardLocked)
discard jsonObj.getProp("key-uid", result.keyUid)

View File

@ -33,6 +33,10 @@ const SIGNAL_WALLET_ACCOUNT_NETWORK_ENABLED_UPDATED* = "walletAccount/networkEna
const SIGNAL_WALLET_ACCOUNT_DERIVED_ADDRESS_READY* = "walletAccount/derivedAddressesReady"
const SIGNAL_WALLET_ACCOUNT_TOKENS_REBUILT* = "walletAccount/tokensRebuilt"
const SIGNAL_KEYCARD_LOCKED* = "keycardLocked"
const SIGNAL_KEYCARD_UNLOCKED* = "keycardUnlocked"
const SIGNAL_KEYCARD_UID_UPDATED* = "keycardUidUpdated"
var
balanceCache {.threadvar.}: Table[string, float64]
@ -81,6 +85,10 @@ type DerivedAddressesArgs* = ref object of Args
type TokensPerAccountArgs* = ref object of Args
accountsTokens*: OrderedTable[string, seq[WalletTokenDto]] # [wallet address, list of tokens]
type KeycardActivityArgs* = ref object of Args
keycardUid*: string
keycardNewUid*: string
const CheckBalanceSlotExecuteIntervalInSeconds = 15 * 60 # 15 mins
const CheckBalanceTimerIntervalInMilliseconds = 5000 # 5 sec
@ -487,7 +495,7 @@ QtObject:
try:
let response = backend.addMigratedKeyPair(
keyPair.keycardUid,
keyPair.keyPairName,
keyPair.keycardName,
keyPair.keyUid,
keyPair.accountsAddresses)
return self.responseHasNoErrors("addMigratedKeyPair", response)
@ -522,7 +530,9 @@ QtObject:
proc setKeycardLocked*(self: Service, keycardUid: string): bool =
try:
let response = backend.keycardLocked(keycardUid)
return self.responseHasNoErrors("setKeycardLocked", response)
result = self.responseHasNoErrors("setKeycardLocked", response)
if result:
self.events.emit(SIGNAL_KEYCARD_LOCKED, KeycardActivityArgs(keycardUid: keycardUid))
except Exception as e:
error "error: ", procName="setKeycardLocked", errName = e.name, errDesription = e.msg
return false
@ -530,7 +540,9 @@ QtObject:
proc setKeycardUnlocked*(self: Service, keycardUid: string): bool =
try:
let response = backend.keycardUnlocked(keycardUid)
return self.responseHasNoErrors("setKeycardUnlocked", response)
result = self.responseHasNoErrors("setKeycardUnlocked", response)
if result:
self.events.emit(SIGNAL_KEYCARD_UNLOCKED, KeycardActivityArgs(keycardUid: keycardUid))
except Exception as e:
error "error: ", procName="setKeycardUnlocked", errName = e.name, errDesription = e.msg
return false
@ -538,7 +550,9 @@ QtObject:
proc updateKeycardUid*(self: Service, oldKeycardUid: string, newKeycardUid: string): bool =
try:
let response = backend.updateKeycardUID(oldKeycardUid, newKeycardUid)
return self.responseHasNoErrors("updateKeycardUid", response)
result = self.responseHasNoErrors("updateKeycardUid", response)
if result:
self.events.emit(SIGNAL_KEYCARD_UID_UPDATED, KeycardActivityArgs(keycardUid: oldKeycardUid, keycardNewUid: newKeycardUid))
except Exception as e:
error "error: ", procName="updateKeycardUid", errName = e.name, errDesription = e.msg
return false

View File

@ -32,6 +32,9 @@ StatusSectionLayout {
case 4:
walletView.resetStack();
break;
case Constants.settingsSubsection.keycard:
keycardView.handleBackAction();
break;
}
}
@ -87,7 +90,11 @@ StatusSectionLayout {
}
if (currentIndex === 1) {
root.store.backButtonName = root.store.getNameForSubsection(Constants.settingsSubsection.messaging);
} else {
}
else if (currentIndex === Constants.settingsSubsection.keycard) {
keycardView.handleBackAction();
}
else {
root.store.backButtonName = "";
}
}
@ -226,11 +233,14 @@ StatusSectionLayout {
}
KeycardView {
id: keycardView
implicitWidth: parent.width
implicitHeight: parent.height
profileSectionStore: root.store
keycardStore: root.store.keycardStore
sectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.keycard)
mainSectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.keycard)
contentWidth: d.contentWidth
}
}

View File

@ -22,8 +22,8 @@ QtObject {
root.keycardModule.runImportFromKeycardToAppPopup()
}
function runUnlockKeycardPopup() {
root.keycardModule.runUnlockKeycardPopup()
function runUnlockKeycardPopupForKeycardWithUid(keycardUid) {
root.keycardModule.runUnlockKeycardPopupForKeycardWithUid(keycardUid)
}
function runDisplayKeycardContentPopup() {
@ -33,4 +33,47 @@ QtObject {
function runFactoryResetPopup() {
root.keycardModule.runFactoryResetPopup()
}
function runRenameKeycardPopup() {
root.keycardModule.runRenameKeycardPopup()
}
function runChangePinPopup() {
root.keycardModule.runChangePinPopup()
}
function runCreateBackupCopyOfAKeycardPopup() {
root.keycardModule.runCreateBackupCopyOfAKeycardPopup()
}
function runCreatePukPopup() {
root.keycardModule.runCreatePukPopup()
}
function runCreateNewPairingCodePopup() {
root.keycardModule.runCreateNewPairingCodePopup()
}
function getKeycardDetailsAsJson(keycardUid) {
let jsonObj = root.keycardModule.getKeycardDetailsAsJson(keycardUid)
try {
let obj = JSON.parse(jsonObj)
return obj
}
catch (e) {
console.debug("error parsing keycard details for keycard uid: ", keycardUid, " error: ", e.message)
return {
keycardUid: keycardUid,
pubKey: "",
keyUid: "",
locked: false,
name: "",
image: "",
icon: "",
pairType: Constants.keycard.keyPairType.unknown,
derivedFrom: "",
accounts: [],
}
}
}
}

View File

@ -1,25 +1,21 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
import utils 1.0
import shared.panels 1.0
import shared.controls 1.0
import shared.status 1.0
import shared.popups.keycard 1.0
import "../stores"
import "./keycard"
SettingsContentBase {
id: root
property ProfileSectionStore profileSectionStore
property KeycardStore keycardStore
property string mainSectionTitle: ""
titleRowComponentLoader.sourceComponent: StatusButton {
text: qsTr("Get Keycard")
@ -28,9 +24,44 @@ SettingsContentBase {
}
}
ColumnLayout {
id: contentColumn
spacing: Constants.settingsSection.itemSpacing
function handleBackAction() {
if (stackLayout.currentIndex === d.detailsViewIndex) {
root.profileSectionStore.backButtonName = ""
root.sectionTitle = root.mainSectionTitle
stackLayout.currentIndex = d.mainViewIndex
}
}
StackLayout {
id: stackLayout
currentIndex: d.mainViewIndex
QtObject {
id: d
readonly property int mainViewIndex: 0
readonly property int detailsViewIndex: 1
property string observedKeycardUid: ""
}
MainView {
Layout.preferredWidth: root.contentWidth
keycardStore: root.keycardStore
onDisplayKeycardDetails: {
d.observedKeycardUid = keycardUid
root.profileSectionStore.backButtonName = root.mainSectionTitle
root.sectionTitle = keycardName
stackLayout.currentIndex = d.detailsViewIndex
}
}
DetailsView {
Layout.preferredWidth: root.contentWidth
keycardStore: root.keycardStore
keycardUid: d.observedKeycardUid
}
Connections {
target: root.keycardStore.keycardModule
@ -41,6 +72,12 @@ SettingsContentBase {
onDestroyKeycardSharedModuleFlow: {
keycardPopup.active = false
}
onKeycardUidChanged: {
if (d.observedKeycardUid === oldKcUid) {
d.observedKeycardUid = newKcUid
}
}
}
Loader {
@ -54,136 +91,5 @@ SettingsContentBase {
keycardPopup.item.open()
}
}
Image {
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
fillMode: Image.PreserveAspectFit
antialiasing: true
source: Style.png("keycard/security-keycard@2x")
mipmap: true
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: Style.current.halfPadding
}
StyledText {
Layout.alignment: Qt.AlignCenter
font.pixelSize: Constants.settingsSection.importantInfoFontSize
color: Style.current.directColor1
text: qsTr("Secure your funds. Keep your profile safe.")
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: Style.current.halfPadding
}
StatusListItem {
Layout.preferredWidth: root.contentWidth
title: qsTr("Setup a new Keycard with an existing account")
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runSetupKeycardPopup()
}
}
StatusSectionHeadline {
Layout.preferredWidth: root.contentWidth
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
text: qsTr("Create, import or restore a Keycard account")
}
StatusListItem {
Layout.preferredWidth: root.contentWidth
title: qsTr("Generate a seed phrase")
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runGenerateSeedPhrasePopup()
}
}
StatusListItem {
Layout.preferredWidth: root.contentWidth
title: qsTr("Import or restore via a seed phrase")
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runImportOrRestoreViaSeedPhrasePopup()
}
}
StatusListItem {
Layout.preferredWidth: root.contentWidth
title: qsTr("Import from Keycard to Status Desktop")
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runImportFromKeycardToAppPopup()
}
}
StatusSectionHeadline {
Layout.preferredWidth: root.contentWidth
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
text: qsTr("Other")
}
StatusListItem {
Layout.preferredWidth: root.contentWidth
title: qsTr("Check whats on a Keycard")
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runDisplayKeycardContentPopup()
}
}
StatusListItem {
Layout.preferredWidth: root.contentWidth
title: qsTr("Factory reset a Keycard")
components: [
StatusIcon {
icon: "chevron-down"
rotation: 270
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runFactoryResetPopup()
}
}
}
}

View File

@ -0,0 +1,183 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import utils 1.0
import shared.status 1.0
import shared.popups.keycard.helpers 1.0
import "../../stores"
ColumnLayout {
id: root
property KeycardStore keycardStore
property string keycardUid: ""
spacing: Constants.settingsSection.itemSpacing
QtObject {
id: d
property bool collapsed: true
function resetKeycardDetails() {
let kcItem = root.keycardStore.getKeycardDetailsAsJson(root.keycardUid)
keycardDetails.keycardName = kcItem.name
keycardDetails.keycardLocked = kcItem.locked
keycardDetails.keyPairType = kcItem.pairType
keycardDetails.keyPairIcon = kcItem.icon
keycardDetails.keyPairImage = kcItem.image
keycardDetails.keyPairAccounts = kcItem.accounts
}
}
onKeycardUidChanged: {
d.resetKeycardDetails()
}
Connections {
target: root.keycardStore.keycardModule
onKeycardProfileChanged: {
if (keycardDetails.keyPairType === Constants.keycard.keyPairType.profile) {
d.resetKeycardDetails()
}
}
onKeycardDetailsChanged: {
if (kcUid === root.keycardUid) {
d.resetKeycardDetails()
}
}
}
KeycardItem {
id: keycardDetails
Layout.fillWidth: true
displayChevronComponent: false
}
Item {
Layout.fillWidth: true
Layout.preferredHeight: Style.current.halfPadding
}
StatusSectionHeadline {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
text: qsTr("Configure your Keycard")
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Rename Keycard")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runRenameKeycardPopup()
}
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Change PIN")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runChangePinPopup()
}
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Create a backup copy of this Keycard")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runCreateBackupCopyOfAKeycardPopup()
}
}
StatusListItem {
visible: keycardDetails.keycardLocked
Layout.fillWidth: true
title: qsTr("Unlock Keycard")
components: [
StatusBadge {
value: 1 //always set to 1 if keycard is locked
border.width: 4
border.color: Theme.palette.dangerColor1
color: Theme.palette.dangerColor1
},
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runUnlockKeycardPopupForKeycardWithUid(root.keycardUid)
}
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Advanced")
statusListItemTitle.color: Style.current.secondaryText
components: [
StatusIcon {
icon: d.collapsed? "tiny/chevron-down" : "tiny/chevron-up"
color: Theme.palette.baseColor1
}
]
onClicked: {
d.collapsed = !d.collapsed
}
}
StatusListItem {
visible: !d.collapsed
Layout.fillWidth: true
title: qsTr("Create a 12-digit personal unblocking key (PUK)")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runCreatePukPopup()
}
}
StatusListItem {
visible: !d.collapsed
Layout.fillWidth: true
title: qsTr("Create a new pairing code")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runCreateNewPairingCodePopup()
}
}
}

View File

@ -0,0 +1,194 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
import utils 1.0
import shared.panels 1.0
import shared.controls 1.0
import shared.status 1.0
import shared.popups.keycard.helpers 1.0
import "../../stores"
ColumnLayout {
id: root
property KeycardStore keycardStore
signal displayKeycardDetails(string keycardUid, string keycardName)
spacing: Constants.settingsSection.itemSpacing
QtObject {
id: d
readonly property bool noKeycardsSet: root.keycardStore.keycardModule.keycardModel.count === 0
}
Image {
visible: d.noKeycardsSet
Layout.alignment: Qt.AlignCenter
Layout.preferredHeight: sourceSize.height
Layout.preferredWidth: sourceSize.width
fillMode: Image.PreserveAspectFit
antialiasing: true
source: Style.png("keycard/security-keycard@2x")
mipmap: true
}
Item {
visible: d.noKeycardsSet
Layout.fillWidth: true
Layout.preferredHeight: Style.current.halfPadding
}
StyledText {
visible: d.noKeycardsSet
Layout.alignment: Qt.AlignCenter
font.pixelSize: Constants.settingsSection.importantInfoFontSize
color: Theme.palette.directColor1
text: qsTr("Secure your funds. Keep your profile safe.")
}
Item {
visible: d.noKeycardsSet
Layout.fillWidth: true
Layout.preferredHeight: Style.current.halfPadding
}
StatusSectionHeadline {
visible: !d.noKeycardsSet
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
text: qsTr("Your Keycard(s)")
}
ListView {
visible: !d.noKeycardsSet
Layout.fillWidth: true
Layout.preferredHeight: 200
spacing: Style.current.padding
model: root.keycardStore.keycardModule.keycardModel
delegate: KeycardItem {
width: ListView.view.width
keycardName: model.name
keycardLocked: model.locked
keyPairType: model.pairType
keyPairIcon: model.icon
keyPairImage: model.image
keyPairAccounts: model.accounts
onKeycardSelected: {
root.displayKeycardDetails(model.keycardUid, model.name)
}
}
}
StatusListItem {
Layout.fillWidth: true
title: d.noKeycardsSet? qsTr("Setup a new Keycard with an existing account")
: qsTr("Migrate an existing account from Status Desktop to Keycard")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runSetupKeycardPopup()
}
}
StatusSectionHeadline {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
text: qsTr("Create, import or restore a Keycard account")
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Generate a seed phrase")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runGenerateSeedPhrasePopup()
}
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Import or restore via a seed phrase")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runImportOrRestoreViaSeedPhrasePopup()
}
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Import from Keycard to Status Desktop")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runImportFromKeycardToAppPopup()
}
}
StatusSectionHeadline {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
text: qsTr("Other")
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Check whats on a Keycard")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runDisplayKeycardContentPopup()
}
}
StatusListItem {
Layout.fillWidth: true
title: qsTr("Factory reset a Keycard")
components: [
StatusIcon {
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardStore.runFactoryResetPopup()
}
}
}

View File

@ -608,6 +608,7 @@ StatusModal {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardEmpty ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardAlreadyUnlocked ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.wrongKeycard ||
root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardSuccess)
return qsTr("Done")
if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.unlockKeycardOptions)

View File

@ -36,6 +36,7 @@ StatusListItem {
width: root.keyPairIcon? 24 : 40
height: root.keyPairIcon? 24 : 40
name: root.keyPairImage? root.keyPairImage : root.keyPairIcon
isImage: !!root.keyPairImage
color: root.keyPairType === Constants.keycard.keyPairType.profile?
Utils.colorForPubkey(d.myPublicKey) :
root.keyPairCardLocked? Theme.palette.dangerColor1 : Theme.palette.primaryColor1

View File

@ -0,0 +1,116 @@
import QtQuick 2.14
import QtQml.Models 2.14
import QtQuick.Controls 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import utils 1.0
StatusListItem {
id: root
property bool displayChevronComponent: true
property string keycardName: ""
property bool keycardLocked: false
property int keyPairType: Constants.keycard.keyPairType.unknown
property string keyPairIcon: ""
property string keyPairImage: ""
property string keyPairAccounts: ""
signal keycardSelected()
color: root.keycardLocked? Theme.palette.dangerColor3 : Style.current.grey
title: root.keycardName
statusListItemTitleAside.textFormat: Text.RichText
statusListItemTitleAside.visible: true
statusListItemTitleAside.text: {
let t = ""
if (root.keyPairType === Constants.keycard.keyPairType.profile) {
t = Utils.getElidedCompressedPk(d.myPublicKey)
}
if (root.keycardLocked) {
let label = qsTr("Keycard Locked")
t += `<font color="${Theme.palette.dangerColor1}" size="5">${label}</font>`
}
return t
}
asset {
width: root.keyPairIcon? 24 : 40
height: root.keyPairIcon? 24 : 40
name: root.keyPairImage? root.keyPairImage : root.keyPairIcon
isImage: !!root.keyPairImage
color: root.keyPairType === Constants.keycard.keyPairType.profile?
Utils.colorForPubkey(d.myPublicKey) : Theme.palette.primaryColor1
letterSize: Math.max(4, asset.width / 2.4)
charactersLen: 2
isLetterIdenticon: !root.keyPairIcon && !asset.name.toString()
bgColor: root.keycardLocked? Theme.palette.dangerColor2 : Theme.palette.primaryColor3
}
ringSettings {
ringSpecModel: root.keyPairType === Constants.keycard.keyPairType.profile?
Utils.getColorHashAsJson(d.myPublicKey) : []
ringPxSize: Math.max(asset.width / 24.0)
}
tagsModel: ListModel{}
tagsDelegate: StatusListItemTag {
color: model.color
height: Style.current.bigPadding
radius: 6
closeButtonVisible: false
asset {
emoji: model.emoji
emojiSize: Emoji.size.verySmall
isLetterIdenticon: !!model.emoji
name: model.icon
color: Theme.palette.indirectColor1
width: 16
height: 16
}
title: model.name
titleText.font.pixelSize: 12
titleText.color: Theme.palette.indirectColor1
}
components: [
StatusIcon {
visible: root.displayChevronComponent
icon: "tiny/chevron-right"
color: Theme.palette.baseColor1
}
]
onClicked: {
root.keycardSelected()
}
onKeyPairAccountsChanged: {
tagsModel.clear()
if (root.keyPairAccounts === "") {
// should never be here, as it's not possible to have keypair item without at least a single account
console.warning("accounts list is empty for selecting keycard pair")
return
}
let obj = JSON.parse(root.keyPairAccounts)
if (obj.error) {
console.warning("error parsing accounts for selecting keycard pair, error: ", obj.error)
return
}
for (var i=0; i<obj.length; i++) {
tagsModel.append({"name": obj[i].Field0, "color": obj[i].Field4, "emoji": obj[i].Field3, "icon": obj[i].Field5})
}
}
QtObject {
id: d
property string myPublicKey: userProfile.pubKey
}
}

View File

@ -1 +1,5 @@
KeycardImage 1.0 KeycardImage.qml
KeyPairItem 1.0 KeyPairItem.qml
KeyPairUnknownItem 1.0 KeyPairUnknownItem.qml
KeycardItem 1.0 KeycardItem.qml
KeyPairList 1.0 KeyPairList.qml

View File

@ -425,7 +425,15 @@ Item {
}
PropertyChanges {
target: message
text: qsTr("Keycard inserted does not match the Keycard below")
text: {
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.authentication) {
return qsTr("Keycard inserted does not match the Keycard below")
}
if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.unlockKeycard) {
return qsTr("Keycard inserted does not match the Keycard you're trying to unlock")
}
return ""
}
font.pixelSize: Constants.keycard.general.fontSize2
color: Theme.palette.dangerColor1
}