diff --git a/src/app/boot/app_controller.nim b/src/app/boot/app_controller.nim index f19d633d4e..3ccd75b87b 100644 --- a/src/app/boot/app_controller.nim +++ b/src/app/boot/app_controller.nim @@ -3,6 +3,7 @@ import NimQml, sequtils, sugar, chronicles, uuids import app_service/service/general/service as general_service import app_service/service/keychain/service as keychain_service import app_service/service/keycard/service as keycard_service +import app_service/service/keycardV2/service as keycard_serviceV2 import app_service/service/accounts/service as accounts_service import app_service/service/contacts/service as contacts_service import app_service/service/language/service as language_service @@ -70,6 +71,7 @@ type # Services generalService: general_service.Service keycardService*: keycard_service.Service + keycardServiceV2*: keycard_serviceV2.Service keychainService: keychain_service.Service accountsService: accounts_service.Service contactsService: contacts_service.Service @@ -169,6 +171,7 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = # Services result.generalService = general_service.newService(statusFoundation.events, statusFoundation.threadpool) result.keycardService = keycard_service.newService(statusFoundation.events, statusFoundation.threadpool) + result.keycardServiceV2 = keycard_serviceV2.newService(statusFoundation.events, statusFoundation.threadpool) result.nodeConfigurationService = node_configuration_service.newService(statusFoundation.fleetConfiguration, result.settingsService, statusFoundation.events) result.keychainService = keychain_service.newService(statusFoundation.events) @@ -255,6 +258,10 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController = result.onboardingModule = onboarding_module.newModule[AppController]( result, statusFoundation.events, + result.generalService, + result.accountsService, + result.devicesService, + result.keycardServiceV2, ) result.mainModule = main_module.newModule[AppController]( result, @@ -353,6 +360,7 @@ proc delete*(self: AppController) = self.ensService.delete self.tokensService.delete self.keycardService.delete + self.keycardServiceV2.delete self.networkConnectionService.delete self.metricsService.delete @@ -421,6 +429,7 @@ proc mainDidLoad*(self: AppController) = proc start*(self: AppController) = self.keycardService.init() + self.keycardServiceV2.init() self.keychainService.init() self.generalService.init() self.accountsService.init() diff --git a/src/app/modules/main/profile_section/devices/controller.nim b/src/app/modules/main/profile_section/devices/controller.nim index 0ac02a2ed4..49364987c0 100644 --- a/src/app/modules/main/profile_section/devices/controller.nim +++ b/src/app/modules/main/profile_section/devices/controller.nim @@ -106,5 +106,5 @@ proc validateConnectionString*(self: Controller, connectionString: string): stri proc getConnectionStringForBootstrappingAnotherDevice*(self: Controller, password, chatKey: string): string = return self.devicesService.getConnectionStringForBootstrappingAnotherDevice(password, chatKey) -proc inputConnectionStringForBootstrapping*(self: Controller, connectionString: string): string = - return self.devicesService.inputConnectionStringForBootstrapping(connectionString) +proc inputConnectionStringForBootstrapping*(self: Controller, connectionString: string) = + self.devicesService.inputConnectionStringForBootstrapping(connectionString) diff --git a/src/app/modules/main/profile_section/devices/io_interface.nim b/src/app/modules/main/profile_section/devices/io_interface.nim index c1c3ddaf94..aa7df95c31 100644 --- a/src/app/modules/main/profile_section/devices/io_interface.nim +++ b/src/app/modules/main/profile_section/devices/io_interface.nim @@ -59,7 +59,7 @@ method onLoggedInUserAuthenticated*(self: AccessInterface, pin: string, password proc validateConnectionString*(self: AccessInterface, connectionString: string): string = raise newException(ValueError, "No implementation available") -method inputConnectionStringForBootstrapping*(self: AccessInterface, connectionString: string): string {.base.} = +method inputConnectionStringForBootstrapping*(self: AccessInterface, connectionString: string) {.base.} = raise newException(ValueError, "No implementation available") method onLocalPairingStatusUpdate*(self: AccessInterface, status: LocalPairingStatus) {.base.} = diff --git a/src/app/modules/main/profile_section/devices/module.nim b/src/app/modules/main/profile_section/devices/module.nim index 3dddef6220..79bb2a7612 100644 --- a/src/app/modules/main/profile_section/devices/module.nim +++ b/src/app/modules/main/profile_section/devices/module.nim @@ -120,8 +120,8 @@ method onLoggedInUserAuthenticated*(self: Module, pin: string, password: string, proc validateConnectionString*(self: Module, connectionString: string): string = return self.controller.validateConnectionString(connectionString) -method inputConnectionStringForBootstrapping*(self: Module, connectionString: string): string = - return self.controller.inputConnectionStringForBootstrapping(connectionString) +method inputConnectionStringForBootstrapping*(self: Module, connectionString: string) = + self.controller.inputConnectionStringForBootstrapping(connectionString) method onLocalPairingStatusUpdate*(self: Module, status: LocalPairingStatus) = self.view.onLocalPairingStatusUpdate(status) diff --git a/src/app/modules/main/profile_section/devices/view.nim b/src/app/modules/main/profile_section/devices/view.nim index 2856e302e7..4c09ca9c0b 100644 --- a/src/app/modules/main/profile_section/devices/view.nim +++ b/src/app/modules/main/profile_section/devices/view.nim @@ -139,5 +139,5 @@ QtObject: proc validateConnectionString*(self: View, connectionString: string): string {.slot.} = return self.delegate.validateConnectionString(connectionString) - proc inputConnectionStringForBootstrapping*(self: View, connectionString: string): string {.slot.} = - return self.delegate.inputConnectionStringForBootstrapping(connectionString) + proc inputConnectionStringForBootstrapping*(self: View, connectionString: string) {.slot.} = + self.delegate.inputConnectionStringForBootstrapping(connectionString) diff --git a/src/app/modules/onboarding/controller.nim b/src/app/modules/onboarding/controller.nim index c013fa413d..edfa1bc741 100644 --- a/src/app/modules/onboarding/controller.nim +++ b/src/app/modules/onboarding/controller.nim @@ -1,7 +1,12 @@ -import chronicles +import chronicles, strutils import io_interface import app/core/eventemitter +import app_service/service/general/service as general_service +import app_service/service/accounts/service as accounts_service +import app_service/service/accounts/dto/image_crop_rectangle +import app_service/service/devices/service as devices_service +import app_service/service/keycardV2/service as keycard_serviceV2 logScope: topics = "onboarding-controller" @@ -10,15 +15,80 @@ type Controller* = ref object of RootObj delegate: io_interface.AccessInterface events: EventEmitter + generalService: general_service.Service + accountsService: accounts_service.Service + devicesService: devices_service.Service + keycardServiceV2: keycard_serviceV2.Service -proc newController*(delegate: io_interface.AccessInterface, events: EventEmitter): +proc newController*( + delegate: io_interface.AccessInterface, + events: EventEmitter, + generalService: general_service.Service, + accountsService: accounts_service.Service, + devicesService: devices_service.Service, + keycardServiceV2: keycard_serviceV2.Service, + ): Controller = result = Controller() result.delegate = delegate result.events = events + result.generalService = generalService + result.accountsService = accountsService + result.devicesService = devicesService + result.keycardServiceV2 = keycardServiceV2 proc delete*(self: Controller) = discard proc init*(self: Controller) = discard + +proc setPin*(self: Controller, pin: string): bool = + self.keycardServiceV2.setPin(pin) + discard + +proc getPasswordStrengthScore*(self: Controller, password, userName: string): int = + return self.generalService.getPasswordStrengthScore(password, userName) + +proc validMnemonic*(self: Controller, mnemonic: string): bool = + let (_, err) = self.accountsService.validateMnemonic(mnemonic) + if err.len == 0: + return true + return false + +proc buildSeedPhrasesFromIndexes(self: Controller, seedPhraseIndexes: seq[int]): string = + if seedPhraseIndexes.len == 0: + error "keycard error: cannot generate mnemonic" + return + let sp = self.keycardServiceV2.buildSeedPhrasesFromIndexes(seedPhraseIndexes) + return sp.join(" ") + +proc getMnemonic*(self: Controller): string = + let indexes = self.keycardServiceV2.getMnemonicIndexes() + return self.buildSeedPhrasesFromIndexes(indexes) + +proc validateLocalPairingConnectionString*(self: Controller, connectionString: string): bool = + let err = self.devicesService.validateConnectionString(connectionString) + return err.len == 0 + +proc inputConnectionStringForBootstrapping*(self: Controller, connectionString: string) = + self.devicesService.inputConnectionStringForBootstrapping(connectionString) + +proc createAccountAndLogin*(self: Controller, password: string): string = + return self.accountsService.createAccountAndLogin( + password, + displayName = "", + imagePath = "", + ImageCropRectangle(), + ) + +proc restoreAccountAndLogin*(self: Controller, password, mnemonic: string, recoverAccount: bool, keycardInstanceUID: string): string = + return self.accountsService.importAccountAndLogin( + mnemonic, + password, + recoverAccount, + displayName = "", + imagePath = "", + ImageCropRectangle(), + keycardInstanceUID, + ) \ No newline at end of file diff --git a/src/app/modules/onboarding/io_interface.nim b/src/app/modules/onboarding/io_interface.nim index ad9d4c7fbf..b9b515f5bc 100644 --- a/src/app/modules/onboarding/io_interface.nim +++ b/src/app/modules/onboarding/io_interface.nim @@ -10,6 +10,27 @@ method onAppLoaded*(self: AccessInterface) {.base.} = method load*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") +method setPin*(self: AccessInterface, pin: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getPasswordStrengthScore*(self: AccessInterface, password, userName: string): int {.base.} = + raise newException(ValueError, "No implementation available") + +method validMnemonic*(self: AccessInterface, mnemonic: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method getMnemonic*(self: AccessInterface): string {.base.} = + raise newException(ValueError, "No implementation available") + +method validateLocalPairingConnectionString*(self: AccessInterface, connectionString: string): bool {.base.} = + raise newException(ValueError, "No implementation available") + +method inputConnectionStringForBootstrapping*(self: AccessInterface, connectionString: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method finishOnboardingFlow*(self: AccessInterface, primaryFlowInt, secondaryFlowInt: int, dataJson: string): string {.base.} = + raise newException(ValueError, "No implementation available") + # This way (using concepts) is used only for the modules managed by AppController type DelegateInterface* = concept c diff --git a/src/app/modules/onboarding/module.nim b/src/app/modules/onboarding/module.nim index 398a68a888..03b69a2a8e 100644 --- a/src/app/modules/onboarding/module.nim +++ b/src/app/modules/onboarding/module.nim @@ -5,12 +5,32 @@ import view, controller import app/global/global_singleton import app/core/eventemitter +import app_service/service/general/service as general_service +import app_service/service/accounts/service as accounts_service +import app_service/service/devices/service as devices_service +import app_service/service/keycardV2/service as keycard_serviceV2 export io_interface logScope: topics = "onboarding-module" +type PrimaryFlow* {.pure} = enum + Unknown = 0, + CreateProfile, + Login + +type SecondaryFlow* {.pure} = enum + Unknown = 0, + CreateProfileWithPassword, + CreateProfileWithSeedphrase, + CreateProfileWithKeycard, + CreateProfileWithKeycardNewSeedphrase, + CreateProfileWithKeycardExistingSeedphrase, + LoginWithSeedphrase, + LoginWithSyncing, + LoginWithKeycard + type Module*[T: io_interface.DelegateInterface] = ref object of io_interface.AccessInterface delegate: T @@ -18,12 +38,26 @@ type viewVariant: QVariant controller: Controller -proc newModule*[T](delegate: T, events: EventEmitter): Module[T] = +proc newModule*[T]( + delegate: T, + events: EventEmitter, + generalService: general_service.Service, + accountsService: accounts_service.Service, + devicesService: devices_service.Service, + keycardServiceV2: keycard_serviceV2.Service, + ): Module[T] = result = Module[T]() result.delegate = delegate result.view = view.newView(result) result.viewVariant = newQVariant(result.view) - result.controller = controller.newController(result, events) + result.controller = controller.newController( + result, + events, + generalService, + accountsService, + devicesService, + keycardServiceV2, + ) {.push warning[Deprecated]: off.} @@ -46,4 +80,83 @@ method load*[T](self: Module[T]) = self.controller.init() self.delegate.onboardingDidLoad() +method setPin*[T](self: Module[T], pin: string): bool = + self.controller.setPin(pin) + +method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int = + self.controller.getPasswordStrengthScore(password, userName) + +method validMnemonic*[T](self: Module[T], mnemonic: string): bool = + self.controller.validMnemonic(mnemonic) + +method getMnemonic*[T](self: Module[T]): string = + self.controller.getMnemonic() + +method validateLocalPairingConnectionString*[T](self: Module[T], connectionString: string): bool = + self.controller.validateLocalPairingConnectionString(connectionString) + +method inputConnectionStringForBootstrapping*[T](self: Module[T], connectionString: string) = + self.controller.inputConnectionStringForBootstrapping(connectionString) + +method finishOnboardingFlow*[T](self: Module[T], primaryFlowInt, secondaryFlowInt: int, dataJson: string): string = + try: + let primaryFlow = PrimaryFlow(primaryFlowInt) + let secondaryFlow = SecondaryFlow(secondaryFlowInt) + + let data = parseJson(dataJson) + let password = data["password"].str + let seedPhrase = data["seedPhrase"].str + + var err = "" + + # CREATE PROFILE PRIMARY FLOW + if primaryFlow == PrimaryFlow.CreateProfile: + case secondaryFlow: + of SecondaryFlow.CreateProfileWithPassword: + err = self.controller.createAccountAndLogin(password) + of SecondaryFlow.CreateProfileWithSeedphrase: + err = self.controller.restoreAccountAndLogin( + password, + seedPhrase, + recoverAccount = false, + keycardInstanceUID = "", + ) + of SecondaryFlow.CreateProfileWithKeycard: + # TODO implement keycard function + discard + of SecondaryFlow.CreateProfileWithKeycardNewSeedphrase: + # TODO implement keycard function + discard + of SecondaryFlow.CreateProfileWithKeycardExistingSeedphrase: + # TODO implement keycard function + discard + else: + raise newException(ValueError, "Unknown secondary flow for CreateProfile: " & $secondaryFlow) + + # LOGIN PRIMARY FLOW + elif primaryFlow == PrimaryFlow.Login: + case secondaryFlow: + of SecondaryFlow.LoginWithSeedphrase: + err = self.controller.restoreAccountAndLogin( + password, + seedPhrase, + recoverAccount = true, + keycardInstanceUID = "", + ) + of SecondaryFlow.LoginWithSyncing: + self.controller.inputConnectionStringForBootstrapping(data["connectionString"].str) + of SecondaryFlow.LoginWithKeycard: + # TODO implement keycard function + discard + else: + raise newException(ValueError, "Unknown secondary flow for Login: " & $secondaryFlow) + if err != "": + raise newException(ValueError, err) + else: + raise newException(ValueError, "Unknown primary flow: " & $primaryFlow) + + except Exception as e: + error "Error finishing Onboarding Flow", msg = e.msg + return e.msg + {.pop.} diff --git a/src/app/modules/onboarding/view.nim b/src/app/modules/onboarding/view.nim index 650594a238..f33ec74654 100644 --- a/src/app/modules/onboarding/view.nim +++ b/src/app/modules/onboarding/view.nim @@ -1,10 +1,14 @@ -import NimQml, json +import NimQml import io_interface QtObject: type View* = ref object of QObject delegate: io_interface.AccessInterface + syncState: int + keycardState: int + keycardRemainingPinAttempts: int + addKeyPairState: int proc delete*(self: View) = self.QObject.delete @@ -13,3 +17,72 @@ QtObject: new(result, delete) result.QObject.setup result.delegate = delegate + + proc syncStateChanged*(self: View) {.signal.} + proc getSyncState(self: View): int {.slot.} = + return self.syncState + QtProperty[int] syncState: + read = getSyncState + notify = syncStateChanged + proc setSyncState(self: View, syncState: int) = + self.syncState = syncState + self.syncStateChanged() + + proc keycardStateChanged*(self: View) {.signal.} + proc getKeycardState(self: View): int {.slot.} = + return self.keycardState + QtProperty[int] keycardState: + read = getKeycardState + notify = keycardStateChanged + proc setKeycardState(self: View, keycardState: int) = + self.keycardState = keycardState + self.keycardStateChanged() + + proc keycardRemainingPinAttemptsChanged*(self: View) {.signal.} + proc getKeycardRemainingPinAttempts(self: View): int {.slot.} = + return self.keycardRemainingPinAttempts + QtProperty[int] keycardRemainingPinAttempts: + read = getKeycardRemainingPinAttempts + notify = keycardRemainingPinAttemptsChanged + proc setKeycardRemainingPinAttempts(self: View, keycardRemainingPinAttempts: int) = + self.keycardRemainingPinAttempts = keycardRemainingPinAttempts + self.keycardRemainingPinAttemptsChanged() + + proc addKeyPairStateChanged*(self: View) {.signal.} + proc getAddKeyPairState(self: View): int {.slot.} = + return self.addKeyPairState + QtProperty[int] addKeyPairState: + read = getAddKeyPairState + notify = addKeyPairStateChanged + proc setAddKeyPairState(self: View, addKeyPairState: int) = + self.addKeyPairState = addKeyPairState + self.addKeyPairStateChanged() + + proc setPin(self: View, pin: string): bool {.slot.} = + return self.delegate.setPin(pin) + + # TODO find what does this do + # proc startKeypairTransfer(self: View) {.slot.} = + # self.delegate.startKeypairTransfer() + + proc getPasswordStrengthScore(self: View, password: string, userName: string): int {.slot.} = + return self.delegate.getPasswordStrengthScore(password, userName) + + proc validMnemonic(self: View, mnemonic: string): bool {.slot.} = + return self.delegate.validMnemonic(mnemonic) + + proc getMnemonic(self: View): string {.slot.} = + return self.delegate.getMnemonic() + + proc validateLocalPairingConnectionString(self: View, connectionString: string): bool {.slot.} = + return self.delegate.validateLocalPairingConnectionString(connectionString) + + proc inputConnectionStringForBootstrapping(self: View, connectionString: string) {.slot.} = + self.delegate.inputConnectionStringForBootstrapping(connectionString) + + # TODO find what does this do + # proc mnemonicWasShown(self: View): string {.slot.} = + # return self.delegate.getMnemonic() + + proc finishOnboardingFlow(self: View, primaryFlowInt: int, secondaryFlowInt: int, dataJson: string): string {.slot.} = + self.delegate.finishOnboardingFlow(primaryFlowInt, secondaryFlowInt, dataJson) diff --git a/src/app/modules/startup/controller.nim b/src/app/modules/startup/controller.nim index 1090911570..47be149f07 100644 --- a/src/app/modules/startup/controller.nim +++ b/src/app/modules/startup/controller.nim @@ -609,8 +609,8 @@ proc setConnectionString*(self: Controller, connectionString: string) = proc validateLocalPairingConnectionString*(self: Controller, connectionString: string): string = return self.devicesService.validateConnectionString(connectionString) -proc inputConnectionStringForBootstrapping*(self: Controller, connectionString: string): string = - return self.devicesService.inputConnectionStringForBootstrapping(connectionString) +proc inputConnectionStringForBootstrapping*(self: Controller, connectionString: string) = + self.devicesService.inputConnectionStringForBootstrapping(connectionString) proc setLoggedInAccount*(self: Controller, account: AccountDto) = self.accountsService.setLoggedInAccount(account) diff --git a/src/app/modules/startup/internal/sync_device_with_sync_code.nim b/src/app/modules/startup/internal/sync_device_with_sync_code.nim index d15e2017f7..e0d4c805e0 100644 --- a/src/app/modules/startup/internal/sync_device_with_sync_code.nim +++ b/src/app/modules/startup/internal/sync_device_with_sync_code.nim @@ -10,7 +10,7 @@ proc delete*(self: SyncDeviceWithSyncCodeState) = method executePrimaryCommand*(self: SyncDeviceWithSyncCodeState, controller: Controller) = let connectionString = controller.getConnectionString() - discard controller.inputConnectionStringForBootstrapping(connectionString) + controller.inputConnectionStringForBootstrapping(connectionString) method getNextPrimaryState*(self: SyncDeviceWithSyncCodeState, controller: Controller): State = return createState(StateType.SyncDeviceResult, self.flowType, self) \ No newline at end of file diff --git a/src/app_service/service/devices/service.nim b/src/app_service/service/devices/service.nim index b8a7769023..94f39c8655 100644 --- a/src/app_service/service/devices/service.nim +++ b/src/app_service/service/devices/service.nim @@ -233,7 +233,7 @@ QtObject: self.localPairingStatus = newLocalPairingStatus(PairingType.AppSync, LocalPairingMode.Sender) return status_go.getConnectionStringForBootstrappingAnotherDevice($configJSON) - proc inputConnectionStringForBootstrapping*(self: Service, connectionString: string): string = + proc inputConnectionStringForBootstrapping*(self: Service, connectionString: string) = let configJSON = %* { "receiverConfig": %* { "createAccount": %*accounts_service.defaultCreateAccountRequest(), diff --git a/src/app_service/service/keycardV2/async_tasks.nim b/src/app_service/service/keycardV2/async_tasks.nim new file mode 100644 index 0000000000..de621bbeb7 --- /dev/null +++ b/src/app_service/service/keycardV2/async_tasks.nim @@ -0,0 +1,16 @@ +type + AsyncSetPinTaskArg = ref object of QObjectTaskArg + pin: string + +proc asyncSetPinTask(argEncoded: string) {.gcsafe, nimcall.} = + let arg = decode[AsyncSetPinTaskArg](argEncoded) + try: + # TODO Call function from keycard_go + echo "Set pin ", arg.pin + arg.finish(%*{ + "error": "" + }) + except Exception as e: + arg.finish(%* { + "error": e.msg, + }) diff --git a/src/app_service/service/keycardV2/service.nim b/src/app_service/service/keycardV2/service.nim new file mode 100644 index 0000000000..7afe435633 --- /dev/null +++ b/src/app_service/service/keycardV2/service.nim @@ -0,0 +1,67 @@ +import NimQml, json, chronicles, strutils +# import keycard_go +import app/global/global_singleton +import app/core/eventemitter +import app/core/tasks/[qt, threadpool] +include ../../common/mnemonics +include async_tasks + +logScope: + topics = "keycardV2-service" + +QtObject: + type Service* = ref object of QObject + events: EventEmitter + threadpool: ThreadPool + + proc delete*(self: Service) = + self.QObject.delete + + proc newService*(events: EventEmitter, threadpool: ThreadPool): Service = + new(result, delete) + result.QObject.setup + result.events = events + result.threadpool = threadpool + + proc init*(self: Service) = + discard + + proc receiveKeycardSignal(self: Service, signal: string) {.slot.} = + var jsonSignal: JsonNode + try: + jsonSignal = signal.parseJson + except: + error "Invalid signal received", data = signal + return + + debug "keycard_signal", response=signal + + proc buildSeedPhrasesFromIndexes*(self: Service, seedPhraseIndexes: seq[int]): seq[string] = + var seedPhrase: seq[string] + for ind in seedPhraseIndexes: + seedPhrase.add(englishWords[ind]) + return seedPhrase + + proc getMnemonicIndexes*(self: Service): seq[int] = + # TODO call lib to get mnemonic indexes + echo "Get mnemonic indexes" + return @[] + + proc setPin*(self: Service, pin: string) = + let arg = AsyncSetPinTaskArg( + tptr: asyncSetPinTask, + vptr: cast[ByteAddress](self.vptr), + slot: "onAsyncSetPinResponse", + pin: pin, + ) + self.threadpool.start(arg) + + proc onAsyncSetPinResponse*(self: Service, response: string) {.slot.} = + try: + let rpcResponseObj = response.parseJson + echo "Set the pin ", response + + if (rpcResponseObj{"error"}.kind != JNull and rpcResponseObj{"error"}.getStr != ""): + raise newException(CatchableError, rpcResponseObj{"error"}.getStr) + except Exception as e: + error "error set pin: ", msg = e.msg \ No newline at end of file diff --git a/ui/app/AppLayouts/Profile/stores/DevicesStore.qml b/ui/app/AppLayouts/Profile/stores/DevicesStore.qml index 5a99605017..d0a02c48dd 100644 --- a/ui/app/AppLayouts/Profile/stores/DevicesStore.qml +++ b/ui/app/AppLayouts/Profile/stores/DevicesStore.qml @@ -46,6 +46,6 @@ QtObject { } function inputConnectionStringForBootstrapping(connectionString) { - return root.devicesModule.inputConnectionStringForBootstrapping(connectionString) + root.devicesModule.inputConnectionStringForBootstrapping(connectionString) } }