diff --git a/Makefile b/Makefile index 8178ca920c..338243c2fb 100644 --- a/Makefile +++ b/Makefile @@ -263,7 +263,7 @@ $(STATUSQ_CMAKE_CACHE): | deps -Wno-dev \ $(HANDLE_OUTPUT) -statusq-configure: | $(STATUSQ_CMAKE_CACHE) +statusq-configure: | $(STATUSQ_CMAKE_CACHE) statusq-build: | statusq-configure echo -e "\033[92mBuilding:\033[39m StatusQ" @@ -328,7 +328,7 @@ run-statusq-tests: statusq-tests ifneq ($(detected_OS),Windows) DOTHERSIDE_CMAKE_CONFIG_PARAMS += -DENABLE_DYNAMIC_LIBS=OFF -DENABLE_STATIC_LIBS=ON -# NIM_PARAMS += +# NIM_PARAMS += else DOTHERSIDE_LIBFILE := vendor/DOtherSide/build/lib/$(COMMON_CMAKE_BUILD_TYPE)/DOtherSide.dll DOTHERSIDE_CMAKE_CONFIG_PARAMS += -DENABLE_DYNAMIC_LIBS=ON -DENABLE_STATIC_LIBS=OFF @@ -354,7 +354,7 @@ $(DOTHERSIDE_CMAKE_CACHE): | deps -Wno-dev \ $(HANDLE_OUTPUT) -dotherside-configure: | $(DOTHERSIDE_CMAKE_CACHE) +dotherside-configure: | $(DOTHERSIDE_CMAKE_CACHE) dotherside-build: | dotherside-configure echo -e "\033[92mBuilding:\033[39m DOtherSide" @@ -392,11 +392,16 @@ STATUSKEYCARDGO := vendor/status-keycard-go/build/libkeycard/libkeycard.$(LIBSTA STATUSKEYCARDGO_LIBDIR := $(shell pwd)/$(shell dirname "$(STATUSKEYCARDGO)") export STATUSKEYCARDGO_LIBDIR +STATUSKEYCARDGO_RULE := build-lib +ifeq ($(TEST_ENVIRONMENT),true) + STATUSKEYCARDGO_RULE := build-mocked-lib +endif + status-keycard-go: $(STATUSKEYCARDGO) $(STATUSKEYCARDGO): | deps echo -e $(BUILD_MSG) "status-keycard-go" + cd vendor/status-keycard-go && \ - $(MAKE) build-lib $(STATUSKEYCARDGO_MAKE_PARAMS) $(HANDLE_OUTPUT) + $(MAKE) $(STATUSKEYCARDGO_RULE) $(STATUSKEYCARDGO_MAKE_PARAMS) $(HANDLE_OUTPUT) QRCODEGEN := vendor/QR-Code-generator/c/libqrcodegen.a diff --git a/src/app/global/local_app_settings.nim b/src/app/global/local_app_settings.nim index a2d7a67bcb..c59580b177 100644 --- a/src/app/global/local_app_settings.nim +++ b/src/app/global/local_app_settings.nim @@ -1,4 +1,4 @@ -import NimQml, os +import NimQml, strutils, os import ../../constants @@ -142,7 +142,8 @@ QtObject: proc getTestEnvironment*(self: LocalAppSettings): bool {.slot.} = - return existsEnv(TEST_ENVIRONMENT_VAR) + let value = getEnv(TEST_ENVIRONMENT_VAR) + return value.toUpperAscii() == "TRUE" or value == "1" QtProperty[bool] testEnvironment: read = getTestEnvironment @@ -150,7 +151,7 @@ QtObject: proc fakeLoadingScreenEnabledChanged*(self: LocalAppSettings) {.signal.} proc getFakeLoadingScreenEnabled*(self: LocalAppSettings): bool {.slot.} = self.settings.value(LAS_KEY_FAKE_LOADING_SCREEN_ENABLED, newQVariant(DEFAULT_FAKE_LOADING_SCREEN_ENABLED)).boolVal - + proc setFakeLoadingScreenEnabled*(self: LocalAppSettings, enabled: bool) {.slot.} = self.settings.setValue(LAS_KEY_FAKE_LOADING_SCREEN_ENABLED, newQVariant(enabled)) self.fakeLoadingScreenEnabledChanged() diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 2e678e996c..bdc59c8978 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -353,6 +353,23 @@ method windowDeactivated*(self: AccessInterface) {.base.} = method communityMembersRevealedAccountsLoaded*(self: AccessInterface, communityId: string, membersRevealedAccounts: MembersRevealedAccounts) {.base.} = raise newException(ValueError, "No implementation available") +## Used in test env only, for testing keycard flows +method registerMockedKeycard*(self: AccessInterface, cardIndex: int, readerState: int, keycardState: int, + mockedKeycard: string, mockedKeycardHelper: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method pluginMockedReaderAction*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method unplugMockedReaderAction*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method insertMockedKeycardAction*(self: AccessInterface, cardIndex: int) {.base.} = + raise newException(ValueError, "No implementation available") + +method removeMockedKeycardAction*(self: AccessInterface) {.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/main/module.nim b/src/app/modules/main/module.nim index 0610b4b812..c2a690f1cc 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -1424,3 +1424,19 @@ method communityMembersRevealedAccountsLoaded*[T](self: Module[T], communityId: self.view.model.setMembersAirdropAddress(communityId, communityMembersAirdropAddress) +## Used in test env only, for testing keycard flows +method registerMockedKeycard*[T](self: Module[T], cardIndex: int, readerState: int, keycardState: int, + mockedKeycard: string, mockedKeycardHelper: string) = + self.keycardService.registerMockedKeycard(cardIndex, readerState, keycardState, mockedKeycard, mockedKeycardHelper) + +method pluginMockedReaderAction*[T](self: Module[T]) = + self.keycardService.pluginMockedReaderAction() + +method unplugMockedReaderAction*[T](self: Module[T]) = + self.keycardService.unplugMockedReaderAction() + +method insertMockedKeycardAction*[T](self: Module[T], cardIndex: int) = + self.keycardService.insertMockedKeycardAction(cardIndex) + +method removeMockedKeycardAction*[T](self: Module[T]) = + self.keycardService.removeMockedKeycardAction() \ No newline at end of file diff --git a/src/app/modules/main/view.nim b/src/app/modules/main/view.nim index 62b2103f28..e0f5cdd259 100644 --- a/src/app/modules/main/view.nim +++ b/src/app/modules/main/view.nim @@ -295,4 +295,21 @@ QtObject: proc showNetworkEndpointUpdated*(self: View, name: string, isTest: bool) {.signal.} proc showIncludeWatchOnlyAccountUpdated*(self: View, includeWatchOnly: bool) {.signal.} proc showToastKeypairRemoved*(self: View, keypairName: string) {.signal.} - proc showToastKeypairsImported*(self: View, keypairName: string, keypairsCount: int, error: string) {.signal.} \ No newline at end of file + proc showToastKeypairsImported*(self: View, keypairName: string, keypairsCount: int, error: string) {.signal.} + + ## Used in test env only, for testing keycard flows + proc registerMockedKeycard*(self: View, cardIndex: int, readerState: int, keycardState: int, + mockedKeycard: string, mockedKeycardHelper: string) {.slot.} = + self.delegate.registerMockedKeycard(cardIndex, readerState, keycardState, mockedKeycard, mockedKeycardHelper) + + proc pluginMockedReaderAction*(self: View) {.slot.} = + self.delegate.pluginMockedReaderAction() + + proc unplugMockedReaderAction*(self: View) {.slot.} = + self.delegate.unplugMockedReaderAction() + + proc insertMockedKeycardAction*(self: View, cardIndex: int) {.slot.} = + self.delegate.insertMockedKeycardAction(cardIndex) + + proc removeMockedKeycardAction*(self: View) {.slot.} = + self.delegate.removeMockedKeycardAction() diff --git a/src/app/modules/startup/io_interface.nim b/src/app/modules/startup/io_interface.nim index ab88403773..b2d440629d 100644 --- a/src/app/modules/startup/io_interface.nim +++ b/src/app/modules/startup/io_interface.nim @@ -195,6 +195,23 @@ method onReencryptionProcessStarted*(self: AccessInterface) {.base.} = method onReencryptionProcessFinished*(self: AccessInterface) {.base.} = raise newException(ValueError, "No implementation available") +## Used in test env only, for testing keycard flows +method registerMockedKeycard*(self: AccessInterface, cardIndex: int, readerState: int, keycardState: int, + mockedKeycard: string, mockedKeycardHelper: string) {.base.} = + raise newException(ValueError, "No implementation available") + +method pluginMockedReaderAction*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method unplugMockedReaderAction*(self: AccessInterface) {.base.} = + raise newException(ValueError, "No implementation available") + +method insertMockedKeycardAction*(self: AccessInterface, cardIndex: int) {.base.} = + raise newException(ValueError, "No implementation available") + +method removeMockedKeycardAction*(self: AccessInterface) {.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/startup/module.nim b/src/app/modules/startup/module.nim index 316f897f32..f241ddb8f3 100644 --- a/src/app/modules/startup/module.nim +++ b/src/app/modules/startup/module.nim @@ -552,4 +552,21 @@ method onReencryptionProcessFinished*[T](self: Module[T]) = currStateObj.stateType() == StateType.LoginKeycardConvertedToRegularAccount: self.moveToStartupState() return - self.moveToLoadingAppState() \ No newline at end of file + self.moveToLoadingAppState() + +## Used in test env only, for testing keycard flows +method registerMockedKeycard*[T](self: Module[T], cardIndex: int, readerState: int, keycardState: int, + mockedKeycard: string, mockedKeycardHelper: string) = + self.keycardService.registerMockedKeycard(cardIndex, readerState, keycardState, mockedKeycard, mockedKeycardHelper) + +method pluginMockedReaderAction*[T](self: Module[T]) = + self.keycardService.pluginMockedReaderAction() + +method unplugMockedReaderAction*[T](self: Module[T]) = + self.keycardService.unplugMockedReaderAction() + +method insertMockedKeycardAction*[T](self: Module[T], cardIndex: int) = + self.keycardService.insertMockedKeycardAction(cardIndex) + +method removeMockedKeycardAction*[T](self: Module[T]) = + self.keycardService.removeMockedKeycardAction() \ No newline at end of file diff --git a/src/app/modules/startup/view.nim b/src/app/modules/startup/view.nim index c5406b47ba..3c7e825a23 100644 --- a/src/app/modules/startup/view.nim +++ b/src/app/modules/startup/view.nim @@ -373,3 +373,20 @@ QtObject: proc validateLocalPairingConnectionString*(self: View, connectionString: string): string {.slot.} = return self.delegate.validateLocalPairingConnectionString(connectionString) + + ## Used in test env only, for testing keycard flows + proc registerMockedKeycard*(self: View, cardIndex: int, readerState: int, keycardState: int, + mockedKeycard: string, mockedKeycardHelper: string) {.slot.} = + self.delegate.registerMockedKeycard(cardIndex, readerState, keycardState, mockedKeycard, mockedKeycardHelper) + + proc pluginMockedReaderAction*(self: View) {.slot.} = + self.delegate.pluginMockedReaderAction() + + proc unplugMockedReaderAction*(self: View) {.slot.} = + self.delegate.unplugMockedReaderAction() + + proc insertMockedKeycardAction*(self: View, cardIndex: int) {.slot.} = + self.delegate.insertMockedKeycardAction(cardIndex) + + proc removeMockedKeycardAction*(self: View) {.slot.} = + self.delegate.removeMockedKeycardAction() \ No newline at end of file diff --git a/src/app_service/service/keycard/service.nim b/src/app_service/service/keycard/service.nim index 9896ea4dba..182e4958cd 100644 --- a/src/app_service/service/keycard/service.nim +++ b/src/app_service/service/keycard/service.nim @@ -1,7 +1,8 @@ import NimQml, json, os, chronicles, random, strutils import keycard_go -import ../../../app/core/eventemitter -import ../../../app/core/tasks/[qt, threadpool] +import app/global/global_singleton +import app/core/eventemitter +import app/core/tasks/[qt, threadpool] import ../../../constants as status_const import constants @@ -148,6 +149,50 @@ QtObject: if self.doLogging: debug "keycardCancelFlow", kcServiceCurrFlow=($self.currentFlow), response=response + ########################################################## + ## Used in test env only, for testing keycard flows + proc registerMockedKeycard*(self: Service, cardIndex: int, readerState: int, keycardState: int, + mockedKeycard: string, mockedKeycardHelper: string) = + if not singletonInstance.localAppSettings.getTestEnvironment(): + error "registerMockedKeycard can be used only in test env" + return + let response = keycard_go.mockedLibRegisterKeycard(cardIndex, readerState, keycardState, mockedKeycard, mockedKeycardHelper) + if self.doLogging: + debug "mockedLibRegisterKeycard", kcServiceCurrFlow=($self.currentFlow), cardIndex=cardIndex, readerState=readerState, keycardState=keycardState, mockedKeycard=mockedKeycard, mockedKeycardHelper=mockedKeycardHelper, response=response + + proc pluginMockedReaderAction*(self: Service) = + if not singletonInstance.localAppSettings.getTestEnvironment(): + error "pluginMockedReaderAction can be used only in test env" + return + let response = keycard_go.mockedLibReaderPluggedIn() + if self.doLogging: + debug "mockedLibReaderPluggedIn", kcServiceCurrFlow=($self.currentFlow), response=response + + proc unplugMockedReaderAction*(self: Service) = + if not singletonInstance.localAppSettings.getTestEnvironment(): + error "unplugMockedReaderAction can be used only in test env" + return + let response = keycard_go.mockedLibReaderUnplugged() + if self.doLogging: + debug "mockedLibReaderUnplugged", kcServiceCurrFlow=($self.currentFlow), response=response + + proc insertMockedKeycardAction*(self: Service, cardIndex: int) = + if not singletonInstance.localAppSettings.getTestEnvironment(): + error "insertMockedKeycardAction can be used only in test env" + return + let response = keycard_go.mockedLibKeycardInserted(cardIndex) + if self.doLogging: + debug "mockedLibKeycardInserted", kcServiceCurrFlow=($self.currentFlow), cardIndex=cardIndex, response=response + + proc removeMockedKeycardAction*(self: Service) = + if not singletonInstance.localAppSettings.getTestEnvironment(): + error "removeMockedKeycardAction can be used only in test env" + return + let response = keycard_go.mockedLibKeycardRemoved() + if self.doLogging: + debug "mockedLibKeycardRemoved", kcServiceCurrFlow=($self.currentFlow), response=response + ########################################################## + proc generateRandomPUK*(self: Service): string = randomize() for i in 0 ..< PUKLengthForStatusApp: diff --git a/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml b/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml index 7efea804fb..525b00cbb0 100644 --- a/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml +++ b/ui/app/AppLayouts/Onboarding/OnboardingLayout.qml @@ -8,6 +8,7 @@ import StatusQ.Core.Theme 0.1 import utils 1.0 import shared.popups.keycard 1.0 +import shared.panels 1.0 import "controls" import "views" @@ -30,6 +31,16 @@ OnboardingBasePage { loader.sourceComponent = undefined } + MockedKeycardLibFlowController { + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: Style.current.bigPadding + anchors.leftMargin: 4 * Style.current.bigPadding + visible: localAppSettings.testEnvironment + + relatedModule: root.startupStore.startupModuleInst + } + Loader { id: loader anchors.fill: parent diff --git a/ui/app/mainui/AppMain.qml b/ui/app/mainui/AppMain.qml index 98f4fa769c..2133bb503d 100644 --- a/ui/app/mainui/AppMain.qml +++ b/ui/app/mainui/AppMain.qml @@ -1622,4 +1622,20 @@ Item { onClosed: userAgreementLoader.active = false } } + + Loader { + id: mockedKeycardLibInitialController + anchors.right: parent.right + anchors.bottom: parent.bottom + active: localAppSettings.testEnvironment + + sourceComponent: MockedKeycardLibInitialController { + width: 450 + height: 500 + + onClose: { + mockedKeycardLibInitialController.active = false + } + } + } } diff --git a/ui/imports/shared/controls/MockedKeycardReaderStateSelector.qml b/ui/imports/shared/controls/MockedKeycardReaderStateSelector.qml new file mode 100644 index 0000000000..b7844252d2 --- /dev/null +++ b/ui/imports/shared/controls/MockedKeycardReaderStateSelector.qml @@ -0,0 +1,85 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Popups 0.1 + +ColumnLayout { + id: root + + property string title + property int selectedState: MockedKeycardReaderStateSelector.State.NoReader + + enum State { + NoReader, + NoKeycard, + KeycardInserted + } + + QtObject { + id: d + + readonly property string readerStateReaderUnplugged: qsTr("Reader Unplugged") + readonly property string readerStateKeycardNotInserted: qsTr("Keycard Not Inserted") + readonly property string readerStateKeycardInserted: qsTr("Keycard Inserted") + } + + StatusBaseText { + text: root.title + } + + StatusButton { + id: selectReaderStateButton + + text: { + switch (root.selectedState) { + case MockedKeycardReaderStateSelector.State.NoReader: + return d.readerStateReaderUnplugged + case MockedKeycardReaderStateSelector.State.NoKeycard: + return d.readerStateKeycardNotInserted + case MockedKeycardReaderStateSelector.State.KeycardInserted: + return d.readerStateKeycardInserted + } + + return "" + } + + icon.name: "chevron-down" + + onClicked: { + if (initialReaderState.opened) { + initialReaderState.close() + } else { + initialReaderState.popup(selectReaderStateButton.x, selectReaderStateButton.y + selectReaderStateButton.height + 8) + } + } + } + + StatusMenu { + id: initialReaderState + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + + StatusAction { + text: d.readerStateReaderUnplugged + onTriggered: { + root.selectedState = MockedKeycardReaderStateSelector.State.NoReader + } + } + + StatusAction { + text: d.readerStateKeycardNotInserted + onTriggered: { + root.selectedState = MockedKeycardReaderStateSelector.State.NoKeycard + } + } + + StatusAction { + text: d.readerStateKeycardInserted + onTriggered: { + root.selectedState = MockedKeycardReaderStateSelector.State.KeycardInserted + } + } + } +} diff --git a/ui/imports/shared/controls/MockedKeycardStateSelector.qml b/ui/imports/shared/controls/MockedKeycardStateSelector.qml new file mode 100644 index 0000000000..29d5168594 --- /dev/null +++ b/ui/imports/shared/controls/MockedKeycardStateSelector.qml @@ -0,0 +1,140 @@ +import QtQuick 2.14 +import QtQuick.Controls 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Popups 0.1 + +ColumnLayout { + id: root + + property string title + property int selectedState: MockedKeycardStateSelector.State.EmptyKeycard + + enum State { + NotStatusKeycard, + EmptyKeycard, + MaxPairingSlotsReached, + MaxPINRetriesReached, + MaxPUKRetriesReached, + KeycardWithMnemonicOnly, + KeycardWithMnemonicAndMedatada, + CustomKeycard // should be always the last option + } + + QtObject { + id: d + + readonly property string kcStateNotStatusKeycard: qsTr("Not Status Keycard") + readonly property string kcStateEmptyKeycard: qsTr("Empty Keycard") + readonly property string kcStateMaxPairingSlotsReached: qsTr("Max Pairing Slots Reached") + readonly property string kcStateMaxPINRetriesReached: qsTr("Max PIN Retries Reached") + readonly property string kcStateMaxPUKRetriesReached: qsTr("Max PUK Retries Reached") + readonly property string kcStateKeycardWithMnemonicOnly: qsTr("Keycard With Mnemonic Only") + readonly property string kcStateKeycardWithMnemonicAndMedatada: qsTr("Keycard With Mnemonic & Metadata") + readonly property string kcStateCustomKeycard: qsTr("Custom Keycard") + } + + StatusBaseText { + text: root.title + } + + StatusButton { + id: selectKeycardsStateButton + + text: { + switch (root.selectedState) { + case MockedKeycardStateSelector.State.NotStatusKeycard: + return d.kcStateNotStatusKeycard + case MockedKeycardStateSelector.State.EmptyKeycard: + return d.kcStateEmptyKeycard + case MockedKeycardStateSelector.State.MaxPairingSlotsReached: + return d.kcStateMaxPairingSlotsReached + case MockedKeycardStateSelector.State.MaxPINRetriesReached: + return d.kcStateMaxPINRetriesReached + case MockedKeycardStateSelector.State.MaxPUKRetriesReached: + return d.kcStateMaxPUKRetriesReached + case MockedKeycardStateSelector.State.KeycardWithMnemonicOnly: + return d.kcStateKeycardWithMnemonicOnly + case MockedKeycardStateSelector.State.KeycardWithMnemonicAndMedatada: + return d.kcStateKeycardWithMnemonicAndMedatada + case MockedKeycardStateSelector.State.CustomKeycard: + return d.kcStateCustomKeycard + } + + return "" + } + + icon.name: "chevron-down" + + onClicked: { + if (initialKeycardState.opened) { + initialKeycardState.close() + } else { + initialKeycardState.popup(selectKeycardsStateButton.x, selectKeycardsStateButton.y + selectKeycardsStateButton.height + 8) + } + } + } + + StatusMenu { + id: initialKeycardState + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent + + StatusAction { + text: d.kcStateNotStatusKeycard + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.NotStatusKeycard + } + } + + StatusAction { + text: d.kcStateEmptyKeycard + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.EmptyKeycard + } + } + + StatusAction { + text: d.kcStateMaxPairingSlotsReached + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.MaxPairingSlotsReached + } + } + + StatusAction { + text: d.kcStateMaxPINRetriesReached + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.MaxPINRetriesReached + } + } + + StatusAction { + text: d.kcStateMaxPUKRetriesReached + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.MaxPUKRetriesReached + } + } + + StatusAction { + text: d.kcStateKeycardWithMnemonicOnly + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.KeycardWithMnemonicOnly + } + } + + StatusAction { + text: d.kcStateKeycardWithMnemonicAndMedatada + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.KeycardWithMnemonicAndMedatada + } + } + + StatusAction { + text: d.kcStateCustomKeycard + onTriggered: { + root.selectedState = MockedKeycardStateSelector.State.CustomKeycard + } + } + } +} diff --git a/ui/imports/shared/controls/qmldir b/ui/imports/shared/controls/qmldir index 7d7f19de26..76865aa1d0 100644 --- a/ui/imports/shared/controls/qmldir +++ b/ui/imports/shared/controls/qmldir @@ -41,3 +41,5 @@ TransactionAddressTile 1.0 TransactionAddressTile.qml TransactionDataTile 1.0 TransactionDataTile.qml TransactionDelegate 1.0 TransactionDelegate.qml TransactionDetailsHeader.qml 1.0 TransactionDetailsHeader.qml +MockedKeycardReaderStateSelector 1.0 MockedKeycardReaderStateSelector.qml +MockedKeycardStateSelector 1.0 MockedKeycardStateSelector.qml \ No newline at end of file diff --git a/ui/imports/shared/panels/MockedKeycardLibFlowController.qml b/ui/imports/shared/panels/MockedKeycardLibFlowController.qml new file mode 100644 index 0000000000..cf53e46495 --- /dev/null +++ b/ui/imports/shared/panels/MockedKeycardLibFlowController.qml @@ -0,0 +1,65 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Controls 0.1 + +RowLayout { + id: root + + property var relatedModule + + StatusButton { + text: qsTr("Plugin R") + + onClicked: { + if (!!root.relatedModule) { + root.relatedModule.pluginMockedReaderAction() + } + } + } + + StatusButton { + text: qsTr("Unplug R") + + onClicked: { + if (!!root.relatedModule) { + root.relatedModule.unplugMockedReaderAction() + } + } + } + + StatusButton { + text: qsTr("Ins Kc 1") + + onClicked: { + if (!!root.relatedModule) { + root.relatedModule.insertMockedKeycardAction(1) + } + } + } + + StatusButton { + text: qsTr("Ins Kc 2") + + onClicked: { + if (!!root.relatedModule) { + root.relatedModule.insertMockedKeycardAction(2) + } + } + } + + StatusButton { + text: qsTr("Remove Kc") + + onClicked: { + if (!!root.relatedModule) { + root.relatedModule.removeMockedKeycardAction() + } + } + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 1 + } +} diff --git a/ui/imports/shared/panels/MockedKeycardLibInitialController.qml b/ui/imports/shared/panels/MockedKeycardLibInitialController.qml new file mode 100644 index 0000000000..e3dc8a38b7 --- /dev/null +++ b/ui/imports/shared/panels/MockedKeycardLibInitialController.qml @@ -0,0 +1,213 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Core.Theme 0.1 + +import shared.controls 1.0 + +import utils 1.0 + +Rectangle { + id: root + + color: Style.current.modalBackground + radius: Style.current.radius + border.color: Style.current.grey3 + border.width: 2 + + signal close() + + QtObject { + id: d + + property int btnWidth: 30 + property int btnHeight: 30 + property int margin: 8 + + property bool minimized: false + property int maxWidth + property int maxHeight + onMinimizedChanged: { + if (minimized) { + d.maxWidth = root.width + d.maxHeight = root.height + root.width = header.implicitWidth + 2 * d.margin + root.height = header.implicitHeight + 2 * d.margin + return + } + root.width = d.maxWidth + root.height = d.maxHeight + } + } + + Row { + id: header + anchors.right: parent.right + anchors.rightMargin: d.margin + anchors.top: parent.top + anchors.topMargin: d.margin + spacing: d.margin + + StatusFlatRoundButton { + type: StatusFlatRoundButton.Type.Secondary + icon.name: d.minimized? "chevron-up" : "chevron-down" + icon.color: Theme.palette.directColor1 + implicitWidth: d.btnWidth + implicitHeight: d.btnHeight + onClicked: { + d.minimized = !d.minimized + } + } + + StatusFlatRoundButton { + type: StatusFlatRoundButton.Type.Secondary + icon.name: "close" + icon.color: Theme.palette.directColor1 + implicitWidth: d.btnWidth + implicitHeight: d.btnHeight + onClicked: { + root.close() + } + } + } + + MockedKeycardReaderStateSelector { + id: readerState + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: Style.current.bigPadding + anchors.leftMargin: Style.current.bigPadding + visible: !d.minimized + title: qsTr("Initial reader state") + } + + StatusTabBar { + id: tabBar + anchors.top: readerState.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Style.current.bigPadding + visible: !d.minimized + + StatusTabButton { + width: implicitWidth + leftPadding: 0 + text: qsTr("Keycard-1") + } + + StatusTabButton { + width: implicitWidth + text: qsTr("Keycard-2") + } + } + + StackLayout { + anchors.top: tabBar.bottom + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: Style.current.halfPadding + visible: !d.minimized + currentIndex: tabBar.currentIndex + + KeycardSettingsTab { + cardIndex: 1 + + onRegisterKeycard: { + mainModule.registerMockedKeycard(cardIndex, readerState.selectedState, kcState, kc, kcHelper) + } + } + + KeycardSettingsTab { + cardIndex: 2 + + onRegisterKeycard: { + mainModule.registerMockedKeycard(cardIndex, MockedKeycardReaderStateSelector.NoKeycard, kcState, kc, kcHelper) + } + } + } + + component KeycardSettingsTab: StatusScrollView { + id: keycardSettingsTabRoot + + property int cardIndex + + signal registerKeycard(int kcState, string kc, string kcHelper) + + ColumnLayout { + spacing: 16 + + MockedKeycardStateSelector { + id: keycardState + title: qsTr("Keycard %1 - initial keycard state").arg(keycardSettingsTabRoot.cardIndex) + } + + Column { + id: customSection + visible: keycardState.selectedState === MockedKeycardStateSelector.CustomKeycard + spacing: 16 + + StatusInput { + id: mockedKeycard + label: qsTr("Mocked Keycard") + implicitWidth: 400 + minimumHeight: 200 + maximumHeight: 200 + input.multiline: true + input.verticalAlignment: TextEdit.AlignTop + placeholderText: qsTr("Enter json form of status-go MockedKeycard") + errorMessageCmp.text: qsTr("Invalid json format") + } + + StatusInput { + id: mockedKeycardHelper + label: qsTr("Specific keycard details") + implicitWidth: 400 + minimumHeight: 200 + maximumHeight: 200 + input.multiline: true + input.verticalAlignment: TextEdit.AlignTop + placeholderText: qsTr("Enter json form of status-go MockedKeycard") + errorMessageCmp.text: qsTr("Invalid json format") + } + } + + StatusButton { + text: qsTr("Register Keycard") + onClicked: { + if (customSection.visible) { + mockedKeycard.input.valid = true + mockedKeycardHelper.input.valid = true + + if (mockedKeycard.text != "") { + try { + JSON.parse(mockedKeycard.text) + } + catch(e) { + mockedKeycard.input.valid = false + } + } + if (mockedKeycardHelper.text != "") { + try { + JSON.parse(mockedKeycardHelper.text) + } + catch(e) { + mockedKeycardHelper.input.valid = false + } + } + + if (!mockedKeycard.input.valid || !mockedKeycardHelper.input.valid) { + return + } + } + + keycardSettingsTabRoot.registerKeycard(keycardState.selectedState, + mockedKeycard.text, + mockedKeycardHelper.text) + } + } + } + } +} diff --git a/ui/imports/shared/panels/qmldir b/ui/imports/shared/panels/qmldir index 5e50318d7f..5d14fd9b18 100644 --- a/ui/imports/shared/panels/qmldir +++ b/ui/imports/shared/panels/qmldir @@ -25,3 +25,5 @@ StatusAssetSelector 1.0 StatusAssetSelector.qml StyledText 1.0 StyledText.qml TextWithLabel 1.0 TextWithLabel.qml DropAndEditImagePanel 1.0 DropAndEditImagePanel.qml +MockedKeycardLibFlowController 1.0 MockedKeycardLibFlowController.qml +MockedKeycardLibInitialController 1.0 MockedKeycardLibInitialController.qml diff --git a/ui/imports/shared/popups/keycard/KeycardPopup.qml b/ui/imports/shared/popups/keycard/KeycardPopup.qml index a84987763c..cfc784239e 100644 --- a/ui/imports/shared/popups/keycard/KeycardPopup.qml +++ b/ui/imports/shared/popups/keycard/KeycardPopup.qml @@ -83,21 +83,23 @@ StatusModal { id: content width: scrollView.availableWidth implicitHeight: { + let additionalHeight = localAppSettings.testEnvironment? 60 : 0 + // for all flows if (root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.keycardMetadataDisplay || root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.factoryResetConfirmationDisplayMetadata) { if (!root.sharedKeycardModule.keyPairStoredOnKeycardIsKnown) { - return Constants.keycard.general.popupBiggerHeight + return Constants.keycard.general.popupBiggerHeight + additionalHeight } } if (root.sharedKeycardModule.currentState.flowType === Constants.keycardSharedFlow.importFromKeycard && root.sharedKeycardModule.currentState.stateType === Constants.keycardSharedState.manageKeycardAccounts && root.sharedKeycardModule.keyPairHelper.accounts.count > 1) { - return Constants.keycard.general.popupBiggerHeight + return Constants.keycard.general.popupBiggerHeight + additionalHeight } - return Constants.keycard.general.popupHeight + return Constants.keycard.general.popupHeight + additionalHeight } sharedKeycardModule: root.sharedKeycardModule diff --git a/ui/imports/shared/popups/keycard/KeycardPopupContent.qml b/ui/imports/shared/popups/keycard/KeycardPopupContent.qml index 49aba7f028..11bc902105 100644 --- a/ui/imports/shared/popups/keycard/KeycardPopupContent.qml +++ b/ui/imports/shared/popups/keycard/KeycardPopupContent.qml @@ -1,6 +1,7 @@ import QtQuick 2.14 import utils 1.0 +import shared.panels 1.0 import "./states" @@ -18,9 +19,26 @@ Item { property bool primaryButtonEnabled: false } + MockedKeycardLibFlowController { + id: mockedLibFlowController + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: Style.current.padding + anchors.rightMargin: Style.current.padding + visible: localAppSettings.testEnvironment + + relatedModule: mainModule + } + Loader { id: loader - anchors.fill: parent + + anchors.top: mockedLibFlowController.visible? mockedLibFlowController.bottom : parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: parent.right + sourceComponent: { switch (root.sharedKeycardModule.currentState.stateType) { case Constants.keycardSharedState.biometrics: diff --git a/vendor/nim-keycard-go b/vendor/nim-keycard-go index 6a4bed51d5..a1724bad72 160000 --- a/vendor/nim-keycard-go +++ b/vendor/nim-keycard-go @@ -1 +1 @@ -Subproject commit 6a4bed51d53f3ef8d1eaff038e08449c288c8334 +Subproject commit a1724bad72e83ca83df2632d83a631cd8d8ff0f6 diff --git a/vendor/status-keycard-go b/vendor/status-keycard-go index c6b7095a85..1a86f57a96 160000 --- a/vendor/status-keycard-go +++ b/vendor/status-keycard-go @@ -1 +1 @@ -Subproject commit c6b7095a85f04fe798e4847406967beb0d3566d3 +Subproject commit 1a86f57a96932ff248f767487ce41575ee1c4b7c