Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/app/boot/app_controller.nim
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import app_service/service/market/service as market_service
import app/modules/onboarding/module as onboarding_module
import app/modules/onboarding/post_onboarding/[keycard_replacement_task, keycard_convert_account, save_biometrics_task]
import app/modules/main/module as main_module
import app/modules/keycard_channel/module as keycard_channel_module
import app/core/notifications/notifications_manager
import app/global/global_singleton
import app/global/app_signals
Expand Down Expand Up @@ -105,6 +106,7 @@ type
# Modules
onboardingModule: onboarding_module.AccessInterface
mainModule: main_module.AccessInterface
keycardChannelModule: keycard_channel_module.AccessInterface

#################################################
# Forward declaration section
Expand Down Expand Up @@ -233,6 +235,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
result.marketService = market_service.newService(statusFoundation.events, result.settingsService)

# Modules
result.keycardChannelModule = keycard_channel_module.newModule(statusFoundation.events)
result.onboardingModule = onboarding_module.newModule[AppController](
result,
statusFoundation.events,
Expand Down Expand Up @@ -299,6 +302,9 @@ proc delete*(self: AppController) =
self.onboardingModule.delete
self.onboardingModule = nil
self.mainModule.delete
if not self.keycardChannelModule.isNil:
self.keycardChannelModule.delete
self.keycardChannelModule = nil

self.appSettingsVariant.delete
self.localAppSettingsVariant.delete
Expand Down Expand Up @@ -346,6 +352,9 @@ proc initializeQmlContext(self: AppController) =
singletonInstance.engine.setRootContextProperty("globalUtils", self.globalUtilsVariant)
singletonInstance.engine.setRootContextProperty("metrics", self.metricsVariant)

# Load keycard channel module (available before login for Session API)
self.keycardChannelModule.load()

singletonInstance.engine.load(newQUrl("qrc:///main.qml"))

proc onboardingDidLoad*(self: AppController) =
Expand Down
27 changes: 27 additions & 0 deletions src/app/modules/keycard_channel/controller.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import ./io_interface
import app/core/eventemitter
import app_service/service/keycardV2/service as keycard_serviceV2

type
Controller* = ref object of RootObj
delegate: io_interface.AccessInterface
events: EventEmitter

proc newController*(
delegate: io_interface.AccessInterface,
events: EventEmitter
): Controller =
result = Controller()
result.delegate = delegate
result.events = events

proc delete*(self: Controller) =
discard

proc init*(self: Controller) =
# Listen to channel state changes
self.events.on(keycard_serviceV2.SIGNAL_KEYCARD_CHANNEL_STATE_UPDATED) do(e: Args):
let args = keycard_serviceV2.KeycardChannelStateArg(e)
self.delegate.setKeycardChannelState(args.state)


23 changes: 23 additions & 0 deletions src/app/modules/keycard_channel/io_interface.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
type
AccessInterface* {.pure inheritable.} = ref object of RootObj
## Abstract class for any input/interaction with this module.

method delete*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

method load*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

method isLoaded*(self: AccessInterface): bool {.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
# inheritance, which is not well supported in Nim.
method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

method setKeycardChannelState*(self: AccessInterface, state: string) {.base.} =
raise newException(ValueError, "No implementation available")


47 changes: 47 additions & 0 deletions src/app/modules/keycard_channel/module.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import nimqml

import io_interface, view, controller
import app/global/global_singleton
import app/core/eventemitter

export io_interface

type
Module* = ref object of io_interface.AccessInterface
view: View
viewVariant: QVariant
controller: Controller
moduleLoaded: bool

proc newModule*(
events: EventEmitter,
): Module =
result = Module()
result.view = view.newView(result)
result.viewVariant = newQVariant(result.view)
result.controller = controller.newController(result, events)
result.moduleLoaded = false

singletonInstance.engine.setRootContextProperty("keycardChannelModule", result.viewVariant)

method delete*(self: Module) =
self.view.delete
self.viewVariant.delete
self.controller.delete

method load*(self: Module) =
self.controller.init()
self.view.load()

method isLoaded*(self: Module): bool =
return self.moduleLoaded

proc checkIfModuleDidLoad(self: Module) =
self.moduleLoaded = true

method viewDidLoad*(self: Module) =
self.checkIfModuleDidLoad()

method setKeycardChannelState*(self: Module, state: string) =
self.view.setKeycardChannelState(state)

42 changes: 42 additions & 0 deletions src/app/modules/keycard_channel/view.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import nimqml

import ./io_interface

QtObject:
type
View* = ref object of QObject
delegate: io_interface.AccessInterface
keycardChannelState: string # Operational channel state

proc setup(self: View)
proc delete*(self: View)
proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete)
result.delegate = delegate
result.setup()

proc load*(self: View) =
self.delegate.viewDidLoad()

proc keycardChannelStateChanged*(self: View) {.signal.}
proc setKeycardChannelState*(self: View, value: string) =
if self.keycardChannelState == value:
return
self.keycardChannelState = value
self.keycardChannelStateChanged()
proc getKeycardChannelState*(self: View): string {.slot.} =
return self.keycardChannelState
QtProperty[string] keycardChannelState:
read = getKeycardChannelState
write = setKeycardChannelState
notify = keycardChannelStateChanged

proc keycardDismissed*(self: View) {.slot.} =
self.setKeycardChannelState("")

proc setup(self: View) =
self.QObject.setup

proc delete*(self: View) =
self.QObject.delete

5 changes: 4 additions & 1 deletion src/app_service/service/keycard/constants.nim
Original file line number Diff line number Diff line change
Expand Up @@ -109,4 +109,7 @@ const ResponseParamWhisperKey* = RequestParamWhisperKey
const ResponseParamMnemonicIdxs* = RequestParamMnemonicIdxs
const ResponseParamTXSignature* = RequestParamTXSignature
const ResponseParamExportedKey* = RequestParamExportedKey
const ResponseParamMasterKeyAddress* = RequestParamMasterKeyAddress
const ResponseParamMasterKeyAddress* = RequestParamMasterKeyAddress

const SignalKeycardStatusChanged* = "status-changed"
const SignalKeycardChannelStateChanged* = "channel-state-changed"
3 changes: 3 additions & 0 deletions src/app_service/service/keycard/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,9 @@ QtObject:
return

let flowType = typeObj.getStr
if flowType == SignalKeycardChannelStateChanged:
return #nothing related to flows here

let flowEvent = toKeycardEvent(eventObj)
self.lastReceivedKeycardData = (flowType: flowType, flowEvent: flowEvent)
self.events.emit(SIGNAL_KEYCARD_RESPONSE, KeycardLibArgs(flowType: flowType, flowEvent: flowEvent))
Expand Down
15 changes: 12 additions & 3 deletions src/app_service/service/keycardV2/service.nim
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ./dto, rpc
featureGuard KEYCARD_ENABLED:
import keycard_go
import constants as status_const
import ../keycard/constants as keycard_constants

export dto

Expand All @@ -21,6 +22,7 @@ const PUKLengthForStatusApp* = 12
const KeycardLibCallsInterval = 500 # 0.5 seconds

const SIGNAL_KEYCARD_STATE_UPDATED* = "keycardStateUpdated"
const SIGNAL_KEYCARD_CHANNEL_STATE_UPDATED* = "keycardChannelStateUpdated"
const SIGNAL_KEYCARD_SET_PIN_FAILURE* = "keycardSetPinFailure"
const SIGNAL_KEYCARD_AUTHORIZE_FINISHED* = "keycardAuthorizeFinished"
const SIGNAL_KEYCARD_LOAD_MNEMONIC_FAILURE* = "keycardLoadMnemonicFailure"
Expand Down Expand Up @@ -60,6 +62,9 @@ type
KeycardExportedKeysArg* = ref object of Args
exportedKeys*: KeycardExportedKeysDto

KeycardChannelStateArg* = ref object of Args
state*: string

include utils
include app_service/common/async_tasks
include async_tasks
Expand Down Expand Up @@ -100,7 +105,6 @@ QtObject:
if status_const.IS_MACOS and status_const.IS_INTEL:
sleep 700
self.initializeRPC()
self.asyncStart(status_const.KEYCARDPAIRINGDATAFILE)
discard

proc initializeRPC(self: Service) {.slot, featureGuard(KEYCARD_ENABLED).} =
Expand All @@ -110,10 +114,15 @@ QtObject:
try:
# Since only one service can register to signals, we pass the signal to the old service too
var jsonSignal = signal.parseJson
if jsonSignal["type"].getStr == "status-changed":
let signalType = jsonSignal["type"].getStr

if signalType == keycard_constants.SignalKeycardStatusChanged:
let keycardEvent = jsonSignal["event"].toKeycardEventDto()

self.events.emit(SIGNAL_KEYCARD_STATE_UPDATED, KeycardEventArg(keycardEvent: keycardEvent))
elif signalType == keycard_constants.SignalKeycardChannelStateChanged:
let state = jsonSignal["event"]["state"].getStr
debug "keycardV2 service: emitting channel state update", state=state, signal=SIGNAL_KEYCARD_CHANNEL_STATE_UPDATED
self.events.emit(SIGNAL_KEYCARD_CHANNEL_STATE_UPDATED, KeycardChannelStateArg(state: state))
except Exception as e:
error "error receiving a keycard signal", err=e.msg, data = signal

Expand Down
Loading