feat(@desktop/keycard): initial keycard implementation
Keycard implementation affected onboarding/login flows. - new user - first run - new keys into keycard - new user - first run - import seed phrase into keycard - old user - first run - login importing from keycard - login the app using keycard Fixes: #5972
|
@ -118,3 +118,6 @@
|
||||||
[submodule "vendor/nim-keycard-go"]
|
[submodule "vendor/nim-keycard-go"]
|
||||||
path = vendor/nim-keycard-go
|
path = vendor/nim-keycard-go
|
||||||
url = https://github.com/status-im/nim-keycard-go
|
url = https://github.com/status-im/nim-keycard-go
|
||||||
|
[submodule "vendor/status-keycard-go"]
|
||||||
|
path = vendor/status-keycard-go
|
||||||
|
url = https://github.com/status-im/status-keycard-go.git
|
||||||
|
|
32
Makefile
|
@ -35,7 +35,7 @@ BUILD_SYSTEM_DIR := vendor/nimbus-build-system
|
||||||
run-macos \
|
run-macos \
|
||||||
run-windows \
|
run-windows \
|
||||||
status-go \
|
status-go \
|
||||||
keycard-go \
|
status-keycard-go \
|
||||||
update
|
update
|
||||||
|
|
||||||
ifeq ($(NIM_PARAMS),)
|
ifeq ($(NIM_PARAMS),)
|
||||||
|
@ -223,15 +223,15 @@ $(STATUSGO): | deps
|
||||||
$(MAKE) statusgo-shared-library $(HANDLE_OUTPUT)
|
$(MAKE) statusgo-shared-library $(HANDLE_OUTPUT)
|
||||||
|
|
||||||
|
|
||||||
KEYCARDGO := vendor/nim-keycard-go/go/keycard/build/libkeycard/libkeycard.$(LIBSTATUS_EXT)
|
STATUSKEYCARDGO := vendor/status-keycard-go/build/libkeycard/libkeycard.$(LIBSTATUS_EXT)
|
||||||
KEYCARDGO_LIBDIR := $(shell pwd)/$(shell dirname "$(KEYCARDGO)")
|
STATUSKEYCARDGO_LIBDIR := $(shell pwd)/$(shell dirname "$(STATUSKEYCARDGO)")
|
||||||
export KEYCARDGO_LIBDIR
|
export STATUSKEYCARDGO_LIBDIR
|
||||||
|
|
||||||
keycard-go: $(KEYCARDGO)
|
status-keycard-go: $(STATUSKEYCARDGO)
|
||||||
$(KEYCARDGO): | deps
|
$(STATUSKEYCARDGO): | deps
|
||||||
echo -e $(BUILD_MSG) "keycard-go"
|
echo -e $(BUILD_MSG) "status-keycard-go"
|
||||||
+ cd vendor/nim-keycard-go && \
|
+ cd vendor/status-keycard-go && \
|
||||||
$(MAKE) build-keycard-go $(HANDLE_OUTPUT)
|
$(MAKE) build-lib $(HANDLE_OUTPUT)
|
||||||
|
|
||||||
QRCODEGEN := vendor/QR-Code-generator/c/libqrcodegen.a
|
QRCODEGEN := vendor/QR-Code-generator/c/libqrcodegen.a
|
||||||
|
|
||||||
|
@ -331,9 +331,9 @@ else
|
||||||
endif
|
endif
|
||||||
|
|
||||||
$(NIM_STATUS_CLIENT): NIM_PARAMS += $(RESOURCES_LAYOUT)
|
$(NIM_STATUS_CLIENT): NIM_PARAMS += $(RESOURCES_LAYOUT)
|
||||||
$(NIM_STATUS_CLIENT): $(NIM_SOURCES) | $(DOTHERSIDE) $(STATUSGO) $(KEYCARDGO) $(QRCODEGEN) $(FLEETS) rcc $(QM_BINARIES) deps
|
$(NIM_STATUS_CLIENT): $(NIM_SOURCES) | $(DOTHERSIDE) $(STATUSGO) $(STATUSKEYCARDGO) $(QRCODEGEN) $(FLEETS) rcc $(QM_BINARIES) deps
|
||||||
echo -e $(BUILD_MSG) "$@" && \
|
echo -e $(BUILD_MSG) "$@" && \
|
||||||
$(ENV_SCRIPT) nim c $(NIM_PARAMS) --passL:"-L$(STATUSGO_LIBDIR)" --passL:"-lstatus" --passL:"-L$(KEYCARDGO_LIBDIR)" --passL:"-lkeycard" $(NIM_EXTRA_PARAMS) --passL:"$(QRCODEGEN)" --passL:"-lm" src/nim_status_client.nim && \
|
$(ENV_SCRIPT) nim c $(NIM_PARAMS) --passL:"-L$(STATUSGO_LIBDIR)" --passL:"-lstatus" --passL:"-L$(STATUSKEYCARDGO_LIBDIR)" --passL:"-lkeycard" $(NIM_EXTRA_PARAMS) --passL:"$(QRCODEGEN)" --passL:"-lm" src/nim_status_client.nim && \
|
||||||
[[ $$? = 0 ]] && \
|
[[ $$? = 0 ]] && \
|
||||||
(([[ $(detected_OS) = Darwin ]] && \
|
(([[ $(detected_OS) = Darwin ]] && \
|
||||||
install_name_tool -change \
|
install_name_tool -change \
|
||||||
|
@ -407,7 +407,7 @@ $(STATUS_CLIENT_APPIMAGE): nim_status_client $(APPIMAGE_TOOL) nim-status.desktop
|
||||||
cp -r /usr/lib/x86_64-linux-gnu/gstreamer1.0 tmp/linux/dist/usr/lib/
|
cp -r /usr/lib/x86_64-linux-gnu/gstreamer1.0 tmp/linux/dist/usr/lib/
|
||||||
cp vendor/status-go/build/bin/libstatus.so tmp/linux/dist/usr/lib/
|
cp vendor/status-go/build/bin/libstatus.so tmp/linux/dist/usr/lib/
|
||||||
cp vendor/status-go/build/bin/libstatus.so.0 tmp/linux/dist/usr/lib/
|
cp vendor/status-go/build/bin/libstatus.so.0 tmp/linux/dist/usr/lib/
|
||||||
cp $(KEYCARDGO) tmp/linux/dist/usr/lib/
|
cp $(STATUSKEYCARDGO) tmp/linux/dist/usr/lib/
|
||||||
|
|
||||||
echo -e $(BUILD_MSG) "AppImage"
|
echo -e $(BUILD_MSG) "AppImage"
|
||||||
linuxdeployqt tmp/linux/dist/nim-status.desktop -no-copy-copyright-files -qmldir=ui -qmlimport=$(QTDIR)/qml -bundle-non-qt-libs
|
linuxdeployqt tmp/linux/dist/nim-status.desktop -no-copy-copyright-files -qmldir=ui -qmlimport=$(QTDIR)/qml -bundle-non-qt-libs
|
||||||
|
@ -530,7 +530,7 @@ $(STATUS_CLIENT_EXE): nim_status_client nim_windows_launcher $(NIM_WINDOWS_PREBU
|
||||||
cp bin/nim_windows_launcher.exe $(OUTPUT)/Status.exe
|
cp bin/nim_windows_launcher.exe $(OUTPUT)/Status.exe
|
||||||
rcedit $(OUTPUT)/bin/Status.exe --set-icon $(OUTPUT)/resources/status.ico
|
rcedit $(OUTPUT)/bin/Status.exe --set-icon $(OUTPUT)/resources/status.ico
|
||||||
rcedit $(OUTPUT)/Status.exe --set-icon $(OUTPUT)/resources/status.ico
|
rcedit $(OUTPUT)/Status.exe --set-icon $(OUTPUT)/resources/status.ico
|
||||||
cp $(DOTHERSIDE) $(STATUSGO) $(KEYCARDGO) tmp/windows/tools/*.dll $(OUTPUT)/bin/
|
cp $(DOTHERSIDE) $(STATUSGO) $(STATUSKEYCARDGO) tmp/windows/tools/*.dll $(OUTPUT)/bin/
|
||||||
cp "$(shell which libgcc_s_seh-1.dll)" $(OUTPUT)/bin/
|
cp "$(shell which libgcc_s_seh-1.dll)" $(OUTPUT)/bin/
|
||||||
cp "$(shell which libwinpthread-1.dll)" $(OUTPUT)/bin/
|
cp "$(shell which libwinpthread-1.dll)" $(OUTPUT)/bin/
|
||||||
echo -e $(BUILD_MSG) "deployable folder"
|
echo -e $(BUILD_MSG) "deployable folder"
|
||||||
|
@ -575,7 +575,7 @@ pkg-windows: check-pkg-target-windows $(STATUS_CLIENT_EXE)
|
||||||
zip-windows: check-pkg-target-windows $(STATUS_CLIENT_7Z)
|
zip-windows: check-pkg-target-windows $(STATUS_CLIENT_7Z)
|
||||||
|
|
||||||
clean: | clean-common
|
clean: | clean-common
|
||||||
rm -rf bin/* node_modules bottles/* pkg/* tmp/* $(STATUSGO) $(KEYCARDGO)
|
rm -rf bin/* node_modules bottles/* pkg/* tmp/* $(STATUSGO) $(STATUSKEYCARDGO)
|
||||||
+ $(MAKE) -C vendor/DOtherSide/build --no-print-directory clean
|
+ $(MAKE) -C vendor/DOtherSide/build --no-print-directory clean
|
||||||
|
|
||||||
force-rebuild-status-go:
|
force-rebuild-status-go:
|
||||||
|
@ -594,7 +594,7 @@ $(ICON_TOOL):
|
||||||
|
|
||||||
run-linux: nim_status_client
|
run-linux: nim_status_client
|
||||||
echo -e "\e[92mRunning:\e[39m bin/nim_status_client"
|
echo -e "\e[92mRunning:\e[39m bin/nim_status_client"
|
||||||
LD_LIBRARY_PATH="$(QT5_LIBDIR)":"$(STATUSGO_LIBDIR)":"$(KEYCARDGO_LIBDIR)" \
|
LD_LIBRARY_PATH="$(QT5_LIBDIR)":"$(STATUSGO_LIBDIR)":"$(STATUSKEYCARDGO_LIBDIR)" \
|
||||||
./bin/nim_status_client
|
./bin/nim_status_client
|
||||||
|
|
||||||
run-macos: nim_status_client $(ICON_TOOL)
|
run-macos: nim_status_client $(ICON_TOOL)
|
||||||
|
@ -609,7 +609,7 @@ run-macos: nim_status_client $(ICON_TOOL)
|
||||||
|
|
||||||
run-windows: nim_status_client $(NIM_WINDOWS_PREBUILT_DLLS)
|
run-windows: nim_status_client $(NIM_WINDOWS_PREBUILT_DLLS)
|
||||||
echo -e "\e[92mRunning:\e[39m bin/nim_status_client.exe"
|
echo -e "\e[92mRunning:\e[39m bin/nim_status_client.exe"
|
||||||
PATH="$(shell pwd)"/"$(shell dirname "$(DOTHERSIDE)")":"$(STATUSGO_LIBDIR)":"$(KEYCARDGO_LIBDIR)":"$(shell pwd)"/"$(shell dirname "$(NIM_WINDOWS_PREBUILT_DLLS)")":"$(PATH)" \
|
PATH="$(shell pwd)"/"$(shell dirname "$(DOTHERSIDE)")":"$(STATUSGO_LIBDIR)":"$(STATUSKEYCARDGO_LIBDIR)":"$(shell pwd)"/"$(shell dirname "$(NIM_WINDOWS_PREBUILT_DLLS)")":"$(PATH)" \
|
||||||
./bin/nim_status_client.exe
|
./bin/nim_status_client.exe
|
||||||
|
|
||||||
endif # "variables.mk" was not included
|
endif # "variables.mk" was not included
|
||||||
|
|
|
@ -16,7 +16,7 @@ if defined(macosx):
|
||||||
# note: macdeployqt rewrites rpath appropriately when building the .app bundle
|
# note: macdeployqt rewrites rpath appropriately when building the .app bundle
|
||||||
switch("passL", "-rpath" & " " & getEnv("QT5_LIBDIR"))
|
switch("passL", "-rpath" & " " & getEnv("QT5_LIBDIR"))
|
||||||
switch("passL", "-rpath" & " " & getEnv("STATUSGO_LIBDIR"))
|
switch("passL", "-rpath" & " " & getEnv("STATUSGO_LIBDIR"))
|
||||||
switch("passL", "-rpath" & " " & getEnv("KEYCARDGO_LIBDIR"))
|
switch("passL", "-rpath" & " " & getEnv("STATUSKEYCARDGO_LIBDIR"))
|
||||||
# statically link these libs
|
# statically link these libs
|
||||||
switch("passL", "bottles/openssl@1.1/lib/libcrypto.a")
|
switch("passL", "bottles/openssl@1.1/lib/libcrypto.a")
|
||||||
switch("passL", "bottles/openssl@1.1/lib/libssl.a")
|
switch("passL", "bottles/openssl@1.1/lib/libssl.a")
|
||||||
|
|
|
@ -2,6 +2,7 @@ import NimQml, chronicles
|
||||||
|
|
||||||
import ../../app_service/service/general/service as general_service
|
import ../../app_service/service/general/service as general_service
|
||||||
import ../../app_service/service/keychain/service as keychain_service
|
import ../../app_service/service/keychain/service as keychain_service
|
||||||
|
import ../../app_service/service/keycard/service as keycard_service
|
||||||
import ../../app_service/service/accounts/service as accounts_service
|
import ../../app_service/service/accounts/service as accounts_service
|
||||||
import ../../app_service/service/contacts/service as contacts_service
|
import ../../app_service/service/contacts/service as contacts_service
|
||||||
import ../../app_service/service/language/service as language_service
|
import ../../app_service/service/language/service as language_service
|
||||||
|
@ -56,6 +57,7 @@ type
|
||||||
|
|
||||||
# Services
|
# Services
|
||||||
generalService: general_service.Service
|
generalService: general_service.Service
|
||||||
|
keycardService*: keycard_service.Service
|
||||||
keychainService: keychain_service.Service
|
keychainService: keychain_service.Service
|
||||||
accountsService: accounts_service.Service
|
accountsService: accounts_service.Service
|
||||||
contactsService: contacts_service.Service
|
contactsService: contacts_service.Service
|
||||||
|
@ -126,7 +128,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
||||||
result.globalUtilsVariant = newQVariant(singletonInstance.utils)
|
result.globalUtilsVariant = newQVariant(singletonInstance.utils)
|
||||||
|
|
||||||
# Services
|
# Services
|
||||||
result.generalService = general_service.newService()
|
result.generalService = general_service.newService()
|
||||||
|
result.keycardService = keycard_service.newService(statusFoundation.events, statusFoundation.threadpool)
|
||||||
result.nodeConfigurationService = node_configuration_service.newService(statusFoundation.fleetConfiguration,
|
result.nodeConfigurationService = node_configuration_service.newService(statusFoundation.fleetConfiguration,
|
||||||
result.settingsService)
|
result.settingsService)
|
||||||
result.keychainService = keychain_service.newService(statusFoundation.events)
|
result.keychainService = keychain_service.newService(statusFoundation.events)
|
||||||
|
@ -189,7 +192,8 @@ proc newAppController*(statusFoundation: StatusFoundation): AppController =
|
||||||
result.keychainService,
|
result.keychainService,
|
||||||
result.accountsService,
|
result.accountsService,
|
||||||
result.generalService,
|
result.generalService,
|
||||||
result.profileService
|
result.profileService,
|
||||||
|
result.keycardService
|
||||||
)
|
)
|
||||||
result.mainModule = main_module.newModule[AppController](
|
result.mainModule = main_module.newModule[AppController](
|
||||||
result,
|
result,
|
||||||
|
@ -272,6 +276,7 @@ proc delete*(self: AppController) =
|
||||||
self.generalService.delete
|
self.generalService.delete
|
||||||
self.ensService.delete
|
self.ensService.delete
|
||||||
self.gifService.delete
|
self.gifService.delete
|
||||||
|
self.keycardService.delete
|
||||||
|
|
||||||
proc startupDidLoad*(self: AppController) =
|
proc startupDidLoad*(self: AppController) =
|
||||||
singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant)
|
singletonInstance.engine.setRootContextProperty("localAppSettings", self.localAppSettingsVariant)
|
||||||
|
@ -290,6 +295,7 @@ proc mainDidLoad*(self: AppController) =
|
||||||
self.mainModule.checkForStoringPassword()
|
self.mainModule.checkForStoringPassword()
|
||||||
|
|
||||||
proc start*(self: AppController) =
|
proc start*(self: AppController) =
|
||||||
|
self.keycardService.init()
|
||||||
self.keychainService.init()
|
self.keychainService.init()
|
||||||
self.generalService.init()
|
self.generalService.init()
|
||||||
self.accountsService.init()
|
self.accountsService.init()
|
||||||
|
|
|
@ -5,8 +5,6 @@ import ../../constants
|
||||||
# Local Account Settings keys:
|
# Local Account Settings keys:
|
||||||
const LS_KEY_STORE_TO_KEYCHAIN* = "storeToKeychain"
|
const LS_KEY_STORE_TO_KEYCHAIN* = "storeToKeychain"
|
||||||
const DEFAULT_STORE_TO_KEYCHAIN = "notNow"
|
const DEFAULT_STORE_TO_KEYCHAIN = "notNow"
|
||||||
const LS_KEY_IS_KEYCARD_ENABLED* = "isKeycardEnabled"
|
|
||||||
const DEFAULT_IS_KEYCARD_ENABLED = false
|
|
||||||
# Local Account Settings values:
|
# Local Account Settings values:
|
||||||
const LS_VALUE_STORE* = "store"
|
const LS_VALUE_STORE* = "store"
|
||||||
const LS_VALUE_NOTNOW* = "notNow"
|
const LS_VALUE_NOTNOW* = "notNow"
|
||||||
|
@ -35,23 +33,17 @@ QtObject:
|
||||||
proc setFileName*(self: LocalAccountSettings, fileName: string) =
|
proc setFileName*(self: LocalAccountSettings, fileName: string) =
|
||||||
if(not self.settings.isNil):
|
if(not self.settings.isNil):
|
||||||
self.settings.delete
|
self.settings.delete
|
||||||
|
|
||||||
let filePath = os.joinPath(self.settingsFileDir, fileName)
|
let filePath = os.joinPath(self.settingsFileDir, fileName)
|
||||||
self.settings = newQSettings(filePath, QSettingsFormat.IniFormat)
|
self.settings = newQSettings(filePath, QSettingsFormat.IniFormat)
|
||||||
|
|
||||||
proc storeToKeychainValueChanged*(self: LocalAccountSettings) {.signal.}
|
proc storeToKeychainValueChanged*(self: LocalAccountSettings) {.signal.}
|
||||||
proc isKeycardEnabledChanged*(self: LocalAccountSettings) {.signal.}
|
|
||||||
|
|
||||||
proc removeKey*(self: LocalAccountSettings, key: string) =
|
proc removeKey*(self: LocalAccountSettings, key: string) =
|
||||||
if(self.settings.isNil):
|
if(self.settings.isNil):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.settings.remove(key)
|
self.settings.remove(key)
|
||||||
|
|
||||||
if(key == LS_KEY_STORE_TO_KEYCHAIN):
|
if(key == LS_KEY_STORE_TO_KEYCHAIN):
|
||||||
self.storeToKeychainValueChanged()
|
self.storeToKeychainValueChanged()
|
||||||
elif(key == LS_KEY_IS_KEYCARD_ENABLED):
|
|
||||||
self.isKeycardEnabledChanged()
|
|
||||||
|
|
||||||
proc getStoreToKeychainValue*(self: LocalAccountSettings): string {.slot.} =
|
proc getStoreToKeychainValue*(self: LocalAccountSettings): string {.slot.} =
|
||||||
if(self.settings.isNil):
|
if(self.settings.isNil):
|
||||||
|
@ -70,22 +62,3 @@ QtObject:
|
||||||
read = getStoreToKeychainValue
|
read = getStoreToKeychainValue
|
||||||
write = setStoreToKeychainValue
|
write = setStoreToKeychainValue
|
||||||
notify = storeToKeychainValueChanged
|
notify = storeToKeychainValueChanged
|
||||||
|
|
||||||
|
|
||||||
proc getIsKeycardEnabled*(self: LocalAccountSettings): bool {.slot.} =
|
|
||||||
if(self.settings.isNil):
|
|
||||||
return DEFAULT_IS_KEYCARD_ENABLED
|
|
||||||
|
|
||||||
self.settings.value(LS_KEY_IS_KEYCARD_ENABLED).boolVal
|
|
||||||
|
|
||||||
proc setIsKeycardEnabled*(self: LocalAccountSettings, value: bool) {.slot.} =
|
|
||||||
if(self.settings.isNil):
|
|
||||||
return
|
|
||||||
|
|
||||||
self.settings.setValue(LS_KEY_IS_KEYCARD_ENABLED, newQVariant(value))
|
|
||||||
self.isKeycardEnabledChanged()
|
|
||||||
|
|
||||||
QtProperty[bool] isKeycardEnabled:
|
|
||||||
read = getIsKeycardEnabled
|
|
||||||
write = setIsKeycardEnabled
|
|
||||||
notify = isKeycardEnabledChanged
|
|
||||||
|
|
|
@ -91,8 +91,10 @@ proc init*(self: Controller) =
|
||||||
|
|
||||||
self.events.on(SIGNAL_KEYCHAIN_SERVICE_ERROR) do(e:Args):
|
self.events.on(SIGNAL_KEYCHAIN_SERVICE_ERROR) do(e:Args):
|
||||||
let args = KeyChainServiceArg(e)
|
let args = KeyChainServiceArg(e)
|
||||||
singletonInstance.localAccountSettings.removeKey(LS_KEY_STORE_TO_KEYCHAIN)
|
# with the following condition we guard unintentional props deletion from the `.ini` file
|
||||||
self.delegate.emitStoringPasswordError(args.errDescription)
|
if self.accountsService.getLoggedInAccount().isValid():
|
||||||
|
singletonInstance.localAccountSettings.removeKey(LS_KEY_STORE_TO_KEYCHAIN)
|
||||||
|
self.delegate.emitStoringPasswordError(args.errDescription)
|
||||||
|
|
||||||
self.events.on(SIGNAL_COMMUNITY_JOINED) do(e:Args):
|
self.events.on(SIGNAL_COMMUNITY_JOINED) do(e:Args):
|
||||||
let args = CommunityArgs(e)
|
let args = CommunityArgs(e)
|
||||||
|
@ -253,7 +255,7 @@ proc storePassword*(self: Controller, password: string) =
|
||||||
if (value != LS_VALUE_STORE or account.name.len == 0):
|
if (value != LS_VALUE_STORE or account.name.len == 0):
|
||||||
return
|
return
|
||||||
|
|
||||||
self.keychainService.storePassword(account.name, password)
|
self.keychainService.storeData(account.name, password)
|
||||||
|
|
||||||
proc getActiveSectionId*(self: Controller): string =
|
proc getActiveSectionId*(self: Controller): string =
|
||||||
result = self.activeSectionId
|
result = self.activeSectionId
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import chronicles
|
import chronicles, strutils, os
|
||||||
|
|
||||||
import io_interface
|
import io_interface
|
||||||
|
|
||||||
|
@ -9,12 +9,14 @@ import ../../../app_service/service/general/service as general_service
|
||||||
import ../../../app_service/service/accounts/service as accounts_service
|
import ../../../app_service/service/accounts/service as accounts_service
|
||||||
import ../../../app_service/service/keychain/service as keychain_service
|
import ../../../app_service/service/keychain/service as keychain_service
|
||||||
import ../../../app_service/service/profile/service as profile_service
|
import ../../../app_service/service/profile/service as profile_service
|
||||||
|
import ../../../app_service/service/keycard/service as keycard_service
|
||||||
|
|
||||||
logScope:
|
logScope:
|
||||||
topics = "startup-controller"
|
topics = "startup-controller"
|
||||||
|
|
||||||
type ProfileImageDetails = object
|
type ProfileImageDetails = object
|
||||||
url*: string
|
url*: string
|
||||||
|
croppedImage*: string
|
||||||
x1*: int
|
x1*: int
|
||||||
y1*: int
|
y1*: int
|
||||||
x2*: int
|
x2*: int
|
||||||
|
@ -28,18 +30,29 @@ type
|
||||||
accountsService: accounts_service.Service
|
accountsService: accounts_service.Service
|
||||||
keychainService: keychain_service.Service
|
keychainService: keychain_service.Service
|
||||||
profileService: profile_service.Service
|
profileService: profile_service.Service
|
||||||
|
keycardService: keycard_service.Service
|
||||||
tmpProfileImageDetails: ProfileImageDetails
|
tmpProfileImageDetails: ProfileImageDetails
|
||||||
tmpDisplayName: string
|
tmpDisplayName: string
|
||||||
tmpPassword: string
|
tmpPassword: string
|
||||||
tmpMnemonic: string
|
|
||||||
tmpSelectedLoginAccountKeyUid: string
|
tmpSelectedLoginAccountKeyUid: string
|
||||||
|
tmpPin: string
|
||||||
|
tmpPinMatch: bool
|
||||||
|
tmpPuk: string
|
||||||
|
tmpValidPuk: bool
|
||||||
|
tmpSeedPhrase: string
|
||||||
|
tmpSeedPhraseLength: int
|
||||||
|
tmpKeyUid: string
|
||||||
|
tmpKeycardEvent: KeycardEvent
|
||||||
|
tmpKeychainErrorOccurred: bool
|
||||||
|
tmpRecoverUsingSeedPhraseWhileLogin: bool
|
||||||
|
|
||||||
proc newController*(delegate: io_interface.AccessInterface,
|
proc newController*(delegate: io_interface.AccessInterface,
|
||||||
events: EventEmitter,
|
events: EventEmitter,
|
||||||
generalService: general_service.Service,
|
generalService: general_service.Service,
|
||||||
accountsService: accounts_service.Service,
|
accountsService: accounts_service.Service,
|
||||||
keychainService: keychain_service.Service,
|
keychainService: keychain_service.Service,
|
||||||
profileService: profile_service.Service):
|
profileService: profile_service.Service,
|
||||||
|
keycardService: keycard_service.Service):
|
||||||
Controller =
|
Controller =
|
||||||
result = Controller()
|
result = Controller()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
|
@ -48,6 +61,14 @@ proc newController*(delegate: io_interface.AccessInterface,
|
||||||
result.accountsService = accountsService
|
result.accountsService = accountsService
|
||||||
result.keychainService = keychainService
|
result.keychainService = keychainService
|
||||||
result.profileService = profileService
|
result.profileService = profileService
|
||||||
|
result.keycardService = keycardService
|
||||||
|
result.tmpPinMatch = false
|
||||||
|
result.tmpSeedPhraseLength = 0
|
||||||
|
result.tmpKeychainErrorOccurred = true
|
||||||
|
result.tmpRecoverUsingSeedPhraseWhileLogin = false
|
||||||
|
|
||||||
|
# Forward declaration
|
||||||
|
proc cleanTmpData(self: Controller)
|
||||||
|
|
||||||
proc delete*(self: Controller) =
|
proc delete*(self: Controller) =
|
||||||
discard
|
discard
|
||||||
|
@ -56,10 +77,12 @@ proc init*(self: Controller) =
|
||||||
self.events.on(SignalType.NodeLogin.event) do(e:Args):
|
self.events.on(SignalType.NodeLogin.event) do(e:Args):
|
||||||
let signal = NodeSignal(e)
|
let signal = NodeSignal(e)
|
||||||
self.delegate.onNodeLogin(signal.event.error)
|
self.delegate.onNodeLogin(signal.event.error)
|
||||||
|
self.cleanTmpData()
|
||||||
|
|
||||||
self.events.on(SignalType.NodeStopped.event) do(e:Args):
|
self.events.on(SignalType.NodeStopped.event) do(e:Args):
|
||||||
self.events.emit("nodeStopped", Args())
|
self.events.emit("nodeStopped", Args())
|
||||||
self.accountsService.clear()
|
self.accountsService.clear()
|
||||||
|
self.cleanTmpData()
|
||||||
self.delegate.emitLogOut()
|
self.delegate.emitLogOut()
|
||||||
|
|
||||||
self.events.on(SignalType.NodeReady.event) do(e:Args):
|
self.events.on(SignalType.NodeReady.event) do(e:Args):
|
||||||
|
@ -71,11 +94,12 @@ proc init*(self: Controller) =
|
||||||
|
|
||||||
self.events.on(SIGNAL_KEYCHAIN_SERVICE_ERROR) do(e:Args):
|
self.events.on(SIGNAL_KEYCHAIN_SERVICE_ERROR) do(e:Args):
|
||||||
let args = KeyChainServiceArg(e)
|
let args = KeyChainServiceArg(e)
|
||||||
# We are notifying user only about keychain errors.
|
self.tmpKeychainErrorOccurred = true
|
||||||
if (args.errType == ERROR_TYPE_AUTHENTICATION):
|
self.delegate.emitObtainingPasswordError(args.errDescription, args.errType)
|
||||||
return
|
|
||||||
singletonInstance.localAccountSettings.removeKey(LS_KEY_STORE_TO_KEYCHAIN)
|
self.events.on(SignalKeycardResponse) do(e: Args):
|
||||||
self.delegate.emitObtainingPasswordError(args.errDescription)
|
let args = KeycardArgs(e)
|
||||||
|
self.delegate.onKeycardResponse(args.flowType, args.flowEvent)
|
||||||
|
|
||||||
proc shouldStartWithOnboardingScreen*(self: Controller): bool =
|
proc shouldStartWithOnboardingScreen*(self: Controller): bool =
|
||||||
return self.accountsService.openedAccounts().len == 0
|
return self.accountsService.openedAccounts().len == 0
|
||||||
|
@ -86,22 +110,22 @@ proc getGeneratedAccounts*(self: Controller): seq[GeneratedAccountDto] =
|
||||||
proc getImportedAccount*(self: Controller): GeneratedAccountDto =
|
proc getImportedAccount*(self: Controller): GeneratedAccountDto =
|
||||||
return self.accountsService.getImportedAccount()
|
return self.accountsService.getImportedAccount()
|
||||||
|
|
||||||
proc generateImages*(self: Controller, image: string, aX: int, aY: int, bX: int, bY: int): seq[general_service.Image] =
|
|
||||||
return self.generalService.generateImages(image, aX, aY, bX, bY)
|
|
||||||
|
|
||||||
proc getPasswordStrengthScore*(self: Controller, password, userName: string): int =
|
proc getPasswordStrengthScore*(self: Controller, password, userName: string): int =
|
||||||
return self.generalService.getPasswordStrengthScore(password, userName)
|
return self.generalService.getPasswordStrengthScore(password, userName)
|
||||||
|
|
||||||
proc generateImage*(self: Controller, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
proc generateImage*(self: Controller, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
||||||
let formatedImg = singletonInstance.utils.formatImagePath(imageUrl)
|
let formatedImg = singletonInstance.utils.formatImagePath(imageUrl)
|
||||||
let images = self.generateImages(formatedImg, aX, aY, bX, bY)
|
let images = self.generalService.generateImages(formatedImg, aX, aY, bX, bY)
|
||||||
if(images.len == 0):
|
if(images.len == 0):
|
||||||
return
|
return
|
||||||
for img in images:
|
for img in images:
|
||||||
if(img.imgType == "large"):
|
if(img.imgType == "large"):
|
||||||
self.tmpProfileImageDetails = ProfileImageDetails(url: imageUrl, x1: aX, y1: aY, x2: bX, y2: bY)
|
self.tmpProfileImageDetails = ProfileImageDetails(url: imageUrl, croppedImage: img.uri, x1: aX, y1: aY, x2: bX, y2: bY)
|
||||||
return img.uri
|
return img.uri
|
||||||
|
|
||||||
|
proc getCroppedProfileImage*(self: Controller): string =
|
||||||
|
return self.tmpProfileImageDetails.croppedImage
|
||||||
|
|
||||||
proc setDisplayName*(self: Controller, value: string) =
|
proc setDisplayName*(self: Controller, value: string) =
|
||||||
self.tmpDisplayName = value
|
self.tmpDisplayName = value
|
||||||
|
|
||||||
|
@ -114,12 +138,83 @@ proc setPassword*(self: Controller, value: string) =
|
||||||
proc getPassword*(self: Controller): string =
|
proc getPassword*(self: Controller): string =
|
||||||
return self.tmpPassword
|
return self.tmpPassword
|
||||||
|
|
||||||
|
proc setPin*(self: Controller, value: string) =
|
||||||
|
self.tmpPin = value
|
||||||
|
|
||||||
|
proc getPin*(self: Controller): string =
|
||||||
|
return self.tmpPin
|
||||||
|
|
||||||
|
proc setPinMatch*(self: Controller, value: bool) =
|
||||||
|
self.tmpPinMatch = value
|
||||||
|
|
||||||
|
proc getPinMatch*(self: Controller): bool =
|
||||||
|
return self.tmpPinMatch
|
||||||
|
|
||||||
|
proc setPuk*(self: Controller, value: string) =
|
||||||
|
self.tmpPuk = value
|
||||||
|
|
||||||
|
proc getPuk*(self: Controller): string =
|
||||||
|
return self.tmpPuk
|
||||||
|
|
||||||
|
proc setPukValid*(self: Controller, value: bool) =
|
||||||
|
self.tmpValidPuk = value
|
||||||
|
|
||||||
|
proc getValidPuk*(self: Controller): bool =
|
||||||
|
return self.tmpValidPuk
|
||||||
|
|
||||||
|
proc setSeedPhrase*(self: Controller, value: string) =
|
||||||
|
let words = value.split(" ")
|
||||||
|
self.tmpSeedPhrase = value
|
||||||
|
self.tmpSeedPhraseLength = words.len
|
||||||
|
|
||||||
|
proc getSeedPhrase*(self: Controller): string =
|
||||||
|
return self.tmpSeedPhrase
|
||||||
|
|
||||||
|
proc getSeedPhraseLength*(self: Controller): int =
|
||||||
|
return self.tmpSeedPhraseLength
|
||||||
|
|
||||||
|
proc setKeyUid*(self: Controller, value: string) =
|
||||||
|
self.tmpKeyUid = value
|
||||||
|
|
||||||
|
proc setKeycardData*(self: Controller, value: string) =
|
||||||
|
self.delegate.setKeycardData(value)
|
||||||
|
|
||||||
|
proc setKeycardEvent*(self: Controller, value: KeycardEvent) =
|
||||||
|
self.tmpKeycardEvent = value
|
||||||
|
|
||||||
|
proc keychainErrorOccurred*(self: Controller): bool =
|
||||||
|
return self.tmpKeychainErrorOccurred
|
||||||
|
|
||||||
|
proc setRecoverUsingSeedPhraseWhileLogin*(self: Controller, value: bool) =
|
||||||
|
self.tmpRecoverUsingSeedPhraseWhileLogin = value
|
||||||
|
|
||||||
|
proc getRecoverUsingSeedPhraseWhileLogin*(self: Controller): bool =
|
||||||
|
return self.tmpRecoverUsingSeedPhraseWhileLogin
|
||||||
|
|
||||||
|
proc cleanTmpData(self: Controller) =
|
||||||
|
self.tmpSelectedLoginAccountKeyUid = ""
|
||||||
|
self.tmpProfileImageDetails = ProfileImageDetails()
|
||||||
|
self.tmpKeychainErrorOccurred = true
|
||||||
|
self.setDisplayName("")
|
||||||
|
self.setPassword("")
|
||||||
|
self.setPin("")
|
||||||
|
self.setPinMatch(false)
|
||||||
|
self.setPuk("")
|
||||||
|
self.setPukValid(false)
|
||||||
|
self.setSeedPhrase("")
|
||||||
|
self.setKeyUid("")
|
||||||
|
self.setKeycardEvent(KeycardEvent())
|
||||||
|
self.setRecoverUsingSeedPhraseWhileLogin(false)
|
||||||
|
|
||||||
proc storePasswordToKeychain(self: Controller) =
|
proc storePasswordToKeychain(self: Controller) =
|
||||||
let account = self.accountsService.getLoggedInAccount()
|
let account = self.accountsService.getLoggedInAccount()
|
||||||
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
||||||
if (value != LS_VALUE_STORE or account.name.len == 0):
|
if (value != LS_VALUE_STORE or account.name.len == 0):
|
||||||
return
|
return
|
||||||
self.keychainService.storePassword(account.name, self.tmpPassword)
|
#TODO: we should store PubKey of this account instead of display name (display name is not unique)
|
||||||
|
# and we may run into a problem if 2 accounts with the same display name are generated.
|
||||||
|
let data = if self.tmpPassword.len > 0: self.tmpPassword else: self.tmpPin
|
||||||
|
self.keychainService.storeData(account.name, data)
|
||||||
|
|
||||||
proc storeIdentityImage*(self: Controller) =
|
proc storeIdentityImage*(self: Controller) =
|
||||||
if self.tmpProfileImageDetails.url.len == 0:
|
if self.tmpProfileImageDetails.url.len == 0:
|
||||||
|
@ -130,8 +225,24 @@ proc storeIdentityImage*(self: Controller) =
|
||||||
self.tmpProfileImageDetails.y1, self.tmpProfileImageDetails.x2, self.tmpProfileImageDetails.y2)
|
self.tmpProfileImageDetails.y1, self.tmpProfileImageDetails.x2, self.tmpProfileImageDetails.y2)
|
||||||
self.tmpProfileImageDetails = ProfileImageDetails()
|
self.tmpProfileImageDetails = ProfileImageDetails()
|
||||||
|
|
||||||
proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool) =
|
proc validMnemonic*(self: Controller, mnemonic: string): bool =
|
||||||
let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName)
|
let err = self.accountsService.validateMnemonic(mnemonic)
|
||||||
|
if err.len == 0:
|
||||||
|
self.setSeedPhrase(mnemonic)
|
||||||
|
return true
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc importMnemonic*(self: Controller): bool =
|
||||||
|
let error = self.accountsService.importMnemonic(self.tmpSeedPhrase)
|
||||||
|
if(error.len == 0):
|
||||||
|
self.delegate.importAccountSuccess()
|
||||||
|
return true
|
||||||
|
else:
|
||||||
|
self.delegate.importAccountError(error)
|
||||||
|
return false
|
||||||
|
|
||||||
|
proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool, keycardUsage: bool) =
|
||||||
|
let error = self.accountsService.setupAccount(accountId, self.tmpPassword, self.tmpDisplayName, keycardUsage)
|
||||||
if error != "":
|
if error != "":
|
||||||
self.delegate.setupAccountError(error)
|
self.delegate.setupAccountError(error)
|
||||||
else:
|
else:
|
||||||
|
@ -140,8 +251,6 @@ proc setupAccount(self: Controller, accountId: string, storeToKeychain: bool) =
|
||||||
self.storePasswordToKeychain()
|
self.storePasswordToKeychain()
|
||||||
else:
|
else:
|
||||||
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER)
|
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER)
|
||||||
self.setPassword("")
|
|
||||||
self.setDisplayName("")
|
|
||||||
|
|
||||||
proc storeGeneratedAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
proc storeGeneratedAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
||||||
let accounts = self.getGeneratedAccounts()
|
let accounts = self.getGeneratedAccounts()
|
||||||
|
@ -149,27 +258,30 @@ proc storeGeneratedAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
||||||
error "list of generated accounts is empty"
|
error "list of generated accounts is empty"
|
||||||
return
|
return
|
||||||
let accountId = accounts[0].id
|
let accountId = accounts[0].id
|
||||||
self.setupAccount(accountId, storeToKeychain)
|
self.setupAccount(accountId, storeToKeychain, keycardUsage = false)
|
||||||
|
|
||||||
proc storeImportedAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
proc storeImportedAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
||||||
let accountId = self.getImportedAccount().id
|
let accountId = self.getImportedAccount().id
|
||||||
self.setupAccount(accountId, storeToKeychain)
|
self.setupAccount(accountId, storeToKeychain, keycardUsage = false)
|
||||||
|
|
||||||
proc validMnemonic*(self: Controller, mnemonic: string): bool =
|
proc storeKeycardAccountAndLogin*(self: Controller, storeToKeychain: bool) =
|
||||||
let err = self.accountsService.validateMnemonic(mnemonic)
|
if self.importMnemonic():
|
||||||
if err.len == 0:
|
let accountId = self.getImportedAccount().id
|
||||||
self.tmpMnemonic = mnemonic
|
self.setupAccount(accountId, storeToKeychain, keycardUsage = true)
|
||||||
return true
|
|
||||||
return false
|
|
||||||
|
|
||||||
proc importMnemonic*(self: Controller): bool =
|
|
||||||
let error = self.accountsService.importMnemonic(self.tmpMnemonic)
|
|
||||||
if(error.len == 0):
|
|
||||||
self.delegate.importAccountSuccess()
|
|
||||||
return true
|
|
||||||
else:
|
else:
|
||||||
self.delegate.importAccountError(error)
|
error "an error ocurred while importing mnemonic"
|
||||||
return false
|
|
||||||
|
proc setupKeycardAccount*(self: Controller, storeToKeychain: bool) =
|
||||||
|
if self.tmpSeedPhrase.len > 0:
|
||||||
|
# if `tmpSeedPhrase` is not empty means user has recovered keycard via seed phrase
|
||||||
|
self.storeKeycardAccountAndLogin(storeToKeychain)
|
||||||
|
else:
|
||||||
|
self.accountsService.setupAccountKeycard(self.tmpKeycardEvent)
|
||||||
|
if storeToKeychain:
|
||||||
|
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_STORE)
|
||||||
|
self.storePasswordToKeychain()
|
||||||
|
else:
|
||||||
|
singletonInstance.localAccountSettings.setStoreToKeychainValue(LS_VALUE_NEVER)
|
||||||
|
|
||||||
proc getOpenedAccounts*(self: Controller): seq[AccountDto] =
|
proc getOpenedAccounts*(self: Controller): seq[AccountDto] =
|
||||||
return self.accountsService.openedAccounts()
|
return self.accountsService.openedAccounts()
|
||||||
|
@ -180,21 +292,95 @@ proc getSelectedLoginAccount(self: Controller): AccountDto =
|
||||||
if(acc.keyUid == self.tmpSelectedLoginAccountKeyUid):
|
if(acc.keyUid == self.tmpSelectedLoginAccountKeyUid):
|
||||||
return acc
|
return acc
|
||||||
|
|
||||||
|
proc keyUidMatch*(self: Controller, keyUid: string): bool =
|
||||||
|
return self.tmpSelectedLoginAccountKeyUid == keyUid
|
||||||
|
|
||||||
proc setSelectedLoginAccountKeyUid*(self: Controller, keyUid: string) =
|
proc setSelectedLoginAccountKeyUid*(self: Controller, keyUid: string) =
|
||||||
self.tmpSelectedLoginAccountKeyUid = keyUid
|
self.tmpSelectedLoginAccountKeyUid = keyUid
|
||||||
|
let selectedAccount = self.getSelectedLoginAccount()
|
||||||
|
singletonInstance.localAccountSettings.setFileName(selectedAccount.name)
|
||||||
|
|
||||||
|
proc isKeycardCreatedAccountSelectedOne*(self: Controller): bool =
|
||||||
|
let selectedAccount = self.getSelectedLoginAccount()
|
||||||
|
return selectedAccount.keycardPairing.len > 0
|
||||||
|
|
||||||
|
proc tryToObtainDataFromKeychain*(self: Controller) =
|
||||||
# Dealing with Keychain is the MacOS only feature
|
# Dealing with Keychain is the MacOS only feature
|
||||||
if(not defined(macosx)):
|
if(not defined(macosx)):
|
||||||
return
|
return
|
||||||
let selectedAccount = self.getSelectedLoginAccount()
|
|
||||||
singletonInstance.localAccountSettings.setFileName(selectedAccount.name)
|
|
||||||
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
||||||
if (value != LS_VALUE_STORE):
|
if (value != LS_VALUE_STORE):
|
||||||
return
|
return
|
||||||
self.keychainService.tryToObtainPassword(selectedAccount.name)
|
self.tmpKeychainErrorOccurred = false
|
||||||
|
let selectedAccount = self.getSelectedLoginAccount()
|
||||||
|
self.keychainService.tryToObtainData(selectedAccount.name)
|
||||||
|
|
||||||
proc login*(self: Controller) =
|
proc login*(self: Controller) =
|
||||||
let selectedAccount = self.getSelectedLoginAccount()
|
let selectedAccount = self.getSelectedLoginAccount()
|
||||||
let error = self.accountsService.login(selectedAccount, self.tmpPassword)
|
let error = self.accountsService.login(selectedAccount, self.tmpPassword)
|
||||||
self.setPassword("")
|
|
||||||
if(error.len > 0):
|
if(error.len > 0):
|
||||||
self.delegate.emitAccountLoginError(error)
|
self.delegate.emitAccountLoginError(error)
|
||||||
|
|
||||||
|
proc loginAccountKeycard*(self: Controller) =
|
||||||
|
let error = self.accountsService.loginAccountKeycard(self.tmpKeycardEvent)
|
||||||
|
if(error.len > 0):
|
||||||
|
self.delegate.emitAccountLoginError(error)
|
||||||
|
|
||||||
|
proc cancelCurrentFlow(self: Controller) =
|
||||||
|
self.keycardService.cancelCurrentFlow()
|
||||||
|
# in most cases we're running another flow after canceling the current one,
|
||||||
|
# this way we're giving to the keycard some time to cancel the current flow
|
||||||
|
sleep(200)
|
||||||
|
|
||||||
|
proc runLoadAccountFlow*(self: Controller, factoryReset = false) =
|
||||||
|
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
|
||||||
|
self.keycardService.startLoadAccountFlow(factoryReset)
|
||||||
|
|
||||||
|
proc runLoadAccountFlowWithSeedPhrase*(self: Controller, seedPhraseLength: int, seedPhrase: string, factoryReset = false) =
|
||||||
|
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
|
||||||
|
self.keycardService.startLoadAccountFlowWithSeedPhrase(seedPhraseLength, seedPhrase, factoryReset)
|
||||||
|
|
||||||
|
proc runLoginFlow*(self: Controller) =
|
||||||
|
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
|
||||||
|
self.keycardService.startLoginFlow()
|
||||||
|
|
||||||
|
proc startLoginFlowAutomatically*(self: Controller, pin: string) =
|
||||||
|
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
|
||||||
|
self.keycardService.startLoginFlowAutomatically(pin)
|
||||||
|
|
||||||
|
proc runRecoverAccountFlow*(self: Controller) =
|
||||||
|
self.cancelCurrentFlow() # before running into any flow we're making sure that the previous flow is canceled
|
||||||
|
self.keycardService.startRecoverAccountFlow()
|
||||||
|
|
||||||
|
proc resumeCurrentFlow*(self: Controller) =
|
||||||
|
self.keycardService.resumeCurrentFlow()
|
||||||
|
|
||||||
|
proc resumeCurrentFlowLater*(self: Controller) =
|
||||||
|
self.keycardService.resumeCurrentFlowLater()
|
||||||
|
|
||||||
|
proc factoryReset*(self: Controller) =
|
||||||
|
self.keycardService.factoryReset()
|
||||||
|
|
||||||
|
proc storePinToKeycard*(self: Controller, pin: string, puk: string) =
|
||||||
|
self.keycardService.storePin(pin, puk)
|
||||||
|
|
||||||
|
proc enterKeycardPin*(self: Controller, pin: string) =
|
||||||
|
self.keycardService.enterPin(pin)
|
||||||
|
|
||||||
|
proc enterKeycardPuk*(self: Controller, puk: string) =
|
||||||
|
self.keycardService.enterPuk(puk)
|
||||||
|
|
||||||
|
proc storeSeedPhraseToKeycard*(self: Controller, seedPhraseLength: int, seedPhrase: string) =
|
||||||
|
self.keycardService.storeSeedPhrase(seedPhraseLength, seedPhrase)
|
||||||
|
|
||||||
|
proc buildSeedPhrasesFromIndexes*(self: Controller, seedPhraseIndexes: seq[int]) =
|
||||||
|
if seedPhraseIndexes.len == 0:
|
||||||
|
let err = "cannot generate mnemonic"
|
||||||
|
error "keycard error: ", err
|
||||||
|
## TODO: we should not be ever in this block, but maybe we can cancel flow and reset states (as the app was just strated)
|
||||||
|
return
|
||||||
|
let sp = self.keycardService.buildSeedPhrasesFromIndexes(seedPhraseIndexes)
|
||||||
|
self.setSeedPhrase(sp.join(" "))
|
||||||
|
|
||||||
|
proc generateRandomPUK*(self: Controller): string =
|
||||||
|
return self.keycardService.generateRandomPUK()
|
|
@ -1,6 +1,3 @@
|
||||||
import state
|
|
||||||
import ../controller
|
|
||||||
|
|
||||||
type
|
type
|
||||||
BiometricsState* = ref object of State
|
BiometricsState* = ref object of State
|
||||||
|
|
||||||
|
@ -11,28 +8,36 @@ proc newBiometricsState*(flowType: FlowType, backState: State): BiometricsState
|
||||||
proc delete*(self: BiometricsState) =
|
proc delete*(self: BiometricsState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method moveToNextPrimaryState*(self: BiometricsState): bool =
|
|
||||||
return false
|
|
||||||
|
|
||||||
method moveToNextSecondaryState*(self: BiometricsState): bool =
|
|
||||||
return false
|
|
||||||
|
|
||||||
method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
|
method executePrimaryCommand*(self: BiometricsState, controller: Controller) =
|
||||||
|
let storeToKeychain = true # true, cause we have support for keychain for mac os
|
||||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||||
controller.storeGeneratedAccountAndLogin(storeToKeychain = true) # true, cause we have support for keychain for mac os
|
controller.storeGeneratedAccountAndLogin(storeToKeychain)
|
||||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||||
controller.storeImportedAccountAndLogin(storeToKeychain = true) # true, cause we have support for keychain for mac os
|
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||||
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||||
## This should not be the correct call for this flow, this is an issue, but since current implementation is like that
|
## This should not be the correct call for this flow, this is an issue,
|
||||||
## and this is not a bug fixing issue, left as it is.
|
## but since current implementation is like that and this is not a bug fixing issue, left as it is.
|
||||||
controller.storeImportedAccountAndLogin(storeToKeychain = true) # true, cause we have support for keychain for mac os
|
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||||
|
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
controller.storeKeycardAccountAndLogin(storeToKeychain)
|
||||||
|
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
controller.storeKeycardAccountAndLogin(storeToKeychain)
|
||||||
|
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.setupKeycardAccount(storeToKeychain)
|
||||||
|
|
||||||
method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
|
method executeSecondaryCommand*(self: BiometricsState, controller: Controller) =
|
||||||
|
let storeToKeychain = false # false, cause we don't have keychain support for other than mac os
|
||||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||||
controller.storeGeneratedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
controller.storeGeneratedAccountAndLogin(storeToKeychain)
|
||||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||||
controller.storeImportedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||||
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||||
## This should not be the correct call for this flow, this is an issue, but since current implementation is like that
|
## This should not be the correct call for this flow, this is an issue,
|
||||||
## and this is not a bug fixing issue, left as it is.
|
## but since current implementation is like that and this is not a bug fixing issue, left as it is.
|
||||||
controller.storeImportedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||||
|
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
controller.storeKeycardAccountAndLogin(storeToKeychain)
|
||||||
|
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
controller.storeKeycardAccountAndLogin(storeToKeychain)
|
||||||
|
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.setupKeycardAccount(storeToKeychain)
|
|
@ -0,0 +1,23 @@
|
||||||
|
type
|
||||||
|
KeycardCreatePinState* = ref object of State
|
||||||
|
pinValid: bool
|
||||||
|
|
||||||
|
proc newKeycardCreatePinState*(flowType: FlowType, backState: State): KeycardCreatePinState =
|
||||||
|
result = KeycardCreatePinState()
|
||||||
|
result.setup(flowType, StateType.KeycardCreatePin, backState)
|
||||||
|
result.pinValid = false
|
||||||
|
|
||||||
|
proc delete*(self: KeycardCreatePinState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executeBackCommand*(self: KeycardCreatePinState, controller: Controller) =
|
||||||
|
controller.setPin("")
|
||||||
|
controller.setPinMatch(false)
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: KeycardCreatePinState, controller: Controller): State =
|
||||||
|
if not self.pinValid:
|
||||||
|
return nil
|
||||||
|
return createState(StateType.KeycardRepeatPin, self.flowType, self.getBackState)
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardCreatePinState, controller: Controller) =
|
||||||
|
self.pinValid = controller.getPin().len == PINLengthForStatusApp
|
|
@ -0,0 +1,16 @@
|
||||||
|
type
|
||||||
|
KeycardDisplaySeedPhraseState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardDisplaySeedPhraseState*(flowType: FlowType, backState: State): KeycardDisplaySeedPhraseState =
|
||||||
|
result = KeycardDisplaySeedPhraseState()
|
||||||
|
result.setup(flowType, StateType.KeycardDisplaySeedPhrase, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardDisplaySeedPhraseState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executeBackCommand*(self: KeycardDisplaySeedPhraseState, controller: Controller) =
|
||||||
|
controller.setPin("")
|
||||||
|
controller.setPinMatch(false)
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: KeycardDisplaySeedPhraseState, controller: Controller): State =
|
||||||
|
return createState(StateType.KeycardEnterSeedPhraseWords, self.flowType, self.getBackState)
|
|
@ -0,0 +1,24 @@
|
||||||
|
type
|
||||||
|
KeycardEmptyState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardEmptyState*(flowType: FlowType, backState: State): KeycardEmptyState =
|
||||||
|
result = KeycardEmptyState()
|
||||||
|
result.setup(flowType, StateType.KeycardEmpty, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardEmptyState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardEmptyState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.runLoadAccountFlow(true)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardEmptyState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return createState(StateType.KeycardPluginReader, FlowType.FirstRunNewUserNewKeycardKeys, self)
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, FlowType.FirstRunNewUserNewKeycardKeys, self)
|
|
@ -0,0 +1,45 @@
|
||||||
|
type
|
||||||
|
KeycardEnterPinState* = ref object of State
|
||||||
|
pinValid: bool
|
||||||
|
|
||||||
|
proc newKeycardEnterPinState*(flowType: FlowType, backState: State): KeycardEnterPinState =
|
||||||
|
result = KeycardEnterPinState()
|
||||||
|
result.setup(flowType, StateType.KeycardEnterPin, backState)
|
||||||
|
result.pinValid = false
|
||||||
|
|
||||||
|
proc delete*(self: KeycardEnterPinState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: KeycardEnterPinState, controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
return createState(StateType.KeycardDisplaySeedPhrase, self.flowType, self.getBackState)
|
||||||
|
elif self.flowType == FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
return createState(StateType.UserProfileCreate, self.flowType, self.getBackState)
|
||||||
|
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
method executeBackCommand*(self: KeycardEnterPinState, controller: Controller) =
|
||||||
|
controller.setPin("")
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardEnterPinState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
self.pinValid = controller.getPin().len == PINLengthForStatusApp
|
||||||
|
if self.pinValid:
|
||||||
|
controller.enterKeycardPin(controller.getPin())
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardEnterPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPIN:
|
||||||
|
controller.setKeycardData($keycardEvent.pinRetries)
|
||||||
|
if keycardEvent.pinRetries > 0:
|
||||||
|
return createState(StateType.KeycardWrongPin, self.flowType, self.getBackState)
|
||||||
|
return createState(StateType.KeycardMaxPinRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
if not defined(macosx):
|
||||||
|
controller.setupKeycardAccount(false)
|
||||||
|
return nil
|
||||||
|
return createState(StateType.Biometrics, self.flowType, self)
|
|
@ -0,0 +1,44 @@
|
||||||
|
type
|
||||||
|
KeycardEnterPukState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardEnterPukState*(flowType: FlowType, backState: State): KeycardEnterPukState =
|
||||||
|
result = KeycardEnterPukState()
|
||||||
|
result.setup(flowType, StateType.KeycardEnterPuk, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardEnterPukState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardEnterPukState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if controller.getPuk().len == PUKLengthForStatusApp:
|
||||||
|
controller.enterKeycardPuk(controller.getPuk())
|
||||||
|
elif self.flowType == FlowType.AppLogin:
|
||||||
|
if controller.getPuk().len == PUKLengthForStatusApp:
|
||||||
|
controller.enterKeycardPuk(controller.getPuk())
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardEnterPukState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorUnblocking:
|
||||||
|
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUK:
|
||||||
|
controller.setKeycardData($keycardEvent.pukRetries)
|
||||||
|
if keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.KeycardWrongPuk, self.flowType, self.getBackState)
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorUnblocking:
|
||||||
|
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUK:
|
||||||
|
controller.setKeycardData($keycardEvent.pukRetries)
|
||||||
|
if keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.KeycardWrongPuk, self.flowType, self.getBackState)
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
|
@ -0,0 +1,24 @@
|
||||||
|
type
|
||||||
|
KeycardEnterSeedPhraseWordsState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardEnterSeedPhraseWordsState*(flowType: FlowType, backState: State): KeycardEnterSeedPhraseWordsState =
|
||||||
|
result = KeycardEnterSeedPhraseWordsState()
|
||||||
|
result.setup(flowType, StateType.KeycardEnterSeedPhraseWords, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardEnterSeedPhraseWordsState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executeBackCommand*(self: KeycardEnterSeedPhraseWordsState, controller: Controller) =
|
||||||
|
controller.setPin("")
|
||||||
|
controller.setPinMatch(false)
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardEnterSeedPhraseWordsState, controller: Controller) =
|
||||||
|
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardEnterSeedPhraseWordsState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.keyUid.len > 0:
|
||||||
|
controller.setKeyUid(keycardEvent.keyUid)
|
||||||
|
return createState(StateType.UserProfileCreate, self.flowType, self)
|
|
@ -0,0 +1,15 @@
|
||||||
|
type
|
||||||
|
KeycardInsertKeycardState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardInsertKeycardState*(flowType: FlowType, backState: State): KeycardInsertKeycardState =
|
||||||
|
result = KeycardInsertKeycardState()
|
||||||
|
result.setup(flowType, StateType.KeycardInsertKeycard, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardInsertKeycardState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardInsertKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if keycardFlowType == ResponseTypeValueCardInserted:
|
||||||
|
return createState(StateType.KeycardReadingKeycard, self.flowType, self.getBackState)
|
||||||
|
return nil
|
|
@ -0,0 +1,12 @@
|
||||||
|
type
|
||||||
|
KeycardLockedState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardLockedState*(flowType: FlowType, backState: State): KeycardLockedState =
|
||||||
|
result = KeycardLockedState()
|
||||||
|
result.setup(flowType, StateType.KeycardLocked, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardLockedState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: KeycardLockedState, controller: Controller): State =
|
||||||
|
return createState(StateType.KeycardEnterSeedPhraseWords, self.flowType, self)
|
|
@ -0,0 +1,28 @@
|
||||||
|
type
|
||||||
|
KeycardMaxPairingSlotsReachedState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardMaxPairingSlotsReachedState*(flowType: FlowType, backState: State): KeycardMaxPairingSlotsReachedState =
|
||||||
|
result = KeycardMaxPairingSlotsReachedState()
|
||||||
|
result.setup(flowType, StateType.KeycardMaxPairingSlotsReached, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardMaxPairingSlotsReachedState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardMaxPairingSlotsReachedState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.runLoadAccountFlow(true)
|
||||||
|
|
||||||
|
method executeSecondaryCommand*(self: KeycardMaxPairingSlotsReachedState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.resumeCurrentFlow()
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardMaxPairingSlotsReachedState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return createState(StateType.KeycardPluginReader, FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard, self.getBackState)
|
|
@ -0,0 +1,12 @@
|
||||||
|
type
|
||||||
|
KeycardMaxPinRetriesReachedState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardMaxPinRetriesReachedState*(flowType: FlowType, backState: State): KeycardMaxPinRetriesReachedState =
|
||||||
|
result = KeycardMaxPinRetriesReachedState()
|
||||||
|
result.setup(flowType, StateType.KeycardMaxPinRetriesReached, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardMaxPinRetriesReachedState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: KeycardMaxPinRetriesReachedState, controller: Controller): State =
|
||||||
|
return createState(StateType.KeycardRecover, self.flowType(), self.getBackState)
|
|
@ -0,0 +1,40 @@
|
||||||
|
type
|
||||||
|
KeycardMaxPukRetriesReachedState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardMaxPukRetriesReachedState*(flowType: FlowType, backState: State): KeycardMaxPukRetriesReachedState =
|
||||||
|
result = KeycardMaxPukRetriesReachedState()
|
||||||
|
result.setup(flowType, StateType.KeycardMaxPukRetriesReached, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardMaxPukRetriesReachedState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardMaxPukRetriesReachedState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.runLoadAccountFlow(true)
|
||||||
|
elif self.flowType == FlowType.AppLogin:
|
||||||
|
controller.runLoadAccountFlow(true)
|
||||||
|
|
||||||
|
method executeSecondaryCommand*(self: KeycardMaxPukRetriesReachedState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.resumeCurrentFlow()
|
||||||
|
elif self.flowType == FlowType.AppLogin:
|
||||||
|
controller.resumeCurrentFlow()
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardMaxPukRetriesReachedState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return createState(StateType.KeycardPluginReader, FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard, self.getBackState)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return createState(StateType.KeycardPluginReader, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, self.flowType, self.getBackState)
|
|
@ -0,0 +1,31 @@
|
||||||
|
type
|
||||||
|
KeycardNotEmptyState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardNotEmptyState*(flowType: FlowType, backState: State): KeycardNotEmptyState =
|
||||||
|
result = KeycardNotEmptyState()
|
||||||
|
result.setup(flowType, StateType.KeycardNotEmpty, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardNotEmptyState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardNotEmptyState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys or
|
||||||
|
self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
controller.runLoadAccountFlow(true)
|
||||||
|
|
||||||
|
method executeSecondaryCommand*(self: KeycardNotEmptyState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
controller.resumeCurrentFlow()
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardNotEmptyState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorRequireInit:
|
||||||
|
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorRequireInit:
|
||||||
|
return createState(StateType.UserProfileEnterSeedPhrase, self.flowType, self.getBackState)
|
|
@ -0,0 +1,40 @@
|
||||||
|
type
|
||||||
|
KeycardPinSetState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardPinSetState*(flowType: FlowType, backState: State): KeycardPinSetState =
|
||||||
|
result = KeycardPinSetState()
|
||||||
|
result.setup(flowType, StateType.KeycardPinSet, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardPinSetState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executeBackCommand*(self: KeycardPinSetState, controller: Controller) =
|
||||||
|
controller.setPin("")
|
||||||
|
controller.setPinMatch(false)
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: KeycardPinSetState, controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
return createState(StateType.KeycardDisplaySeedPhrase, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
return createState(StateType.UserProfileCreate, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if controller.getValidPuk():
|
||||||
|
if not defined(macosx):
|
||||||
|
return nil
|
||||||
|
return createState(StateType.Biometrics, self.flowType, self.getBackState)
|
||||||
|
return createState(StateType.KeycardWrongPuk, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if controller.getRecoverUsingSeedPhraseWhileLogin():
|
||||||
|
return createState(StateType.LoginKeycardReadingKeycard, self.flowType, nil)
|
||||||
|
if controller.getValidPuk():
|
||||||
|
controller.loginAccountKeycard()
|
||||||
|
return nil
|
||||||
|
return createState(StateType.KeycardWrongPuk, self.flowType, self.getBackState)
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardPinSetState, controller: Controller) =
|
||||||
|
if controller.getValidPuk() and not defined(macosx):
|
||||||
|
controller.setupKeycardAccount(false)
|
||||||
|
return
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if controller.getRecoverUsingSeedPhraseWhileLogin():
|
||||||
|
controller.startLoginFlowAutomatically(controller.getPin())
|
|
@ -0,0 +1,27 @@
|
||||||
|
type
|
||||||
|
KeycardPluginReaderState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardPluginReaderState*(flowType: FlowType, backState: State): KeycardPluginReaderState =
|
||||||
|
result = KeycardPluginReaderState()
|
||||||
|
result.setup(flowType, StateType.KeycardPluginReader, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardPluginReaderState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardPluginReaderState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
controller.runLoadAccountFlow()
|
||||||
|
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
controller.runLoadAccountFlow()
|
||||||
|
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.runRecoverAccountFlow()
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardPluginReaderState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return nil
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, self.flowType, self.getBackState)
|
|
@ -0,0 +1,72 @@
|
||||||
|
type
|
||||||
|
KeycardReadingKeycardState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardReadingKeycardState*(flowType: FlowType, backState: State): KeycardReadingKeycardState =
|
||||||
|
result = KeycardReadingKeycardState()
|
||||||
|
result.setup(flowType, StateType.KeycardReadingKeycard, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardReadingKeycardState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardReadingKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorRequireInit:
|
||||||
|
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
return createState(StateType.KeycardNotEmpty, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorHasKeys:
|
||||||
|
return createState(StateType.KeycardNotEmpty, self.flowType, self.getBackState)
|
||||||
|
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorRequireInit:
|
||||||
|
return createState(StateType.UserProfileEnterSeedPhrase, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
return createState(StateType.KeycardNotEmpty, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorHasKeys:
|
||||||
|
return createState(StateType.KeycardNotEmpty, self.flowType, self.getBackState)
|
||||||
|
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
return createState(StateType.KeycardEnterPin, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.KeycardMaxPinRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorNoKeys:
|
||||||
|
return createState(StateType.KeycardEmpty, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUKRetries:
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamFreeSlots:
|
||||||
|
return createState(StateType.KeycardMaxPairingSlotsReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorRequireInit:
|
||||||
|
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)
|
||||||
|
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUKRetries:
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorRequireInit:
|
||||||
|
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)
|
|
@ -0,0 +1,23 @@
|
||||||
|
type
|
||||||
|
KeycardRecoverState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardRecoverState*(flowType: FlowType, backState: State): KeycardRecoverState =
|
||||||
|
result = KeycardRecoverState()
|
||||||
|
result.setup(flowType, StateType.KeycardRecover, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardRecoverState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: KeycardRecoverState, controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
return createState(StateType.UserProfileEnterSeedPhrase, self.flowType, self)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
controller.setRecoverUsingSeedPhraseWhileLogin(true)
|
||||||
|
return createState(StateType.UserProfileEnterSeedPhrase, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: KeycardRecoverState, controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
return createState(StateType.KeycardEnterPuk, self.flowType, self)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
controller.setRecoverUsingSeedPhraseWhileLogin(false)
|
||||||
|
return createState(StateType.KeycardEnterPuk, self.flowType, self)
|
|
@ -0,0 +1,65 @@
|
||||||
|
type
|
||||||
|
KeycardRepeatPinState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardRepeatPinState*(flowType: FlowType, backState: State): KeycardRepeatPinState =
|
||||||
|
result = KeycardRepeatPinState()
|
||||||
|
result.setup(flowType, StateType.KeycardRepeatPin, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardRepeatPinState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executeBackCommand*(self: KeycardRepeatPinState, controller: Controller) =
|
||||||
|
controller.setPin("")
|
||||||
|
controller.setPinMatch(false)
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardRepeatPinState, controller: Controller) =
|
||||||
|
if not controller.getPinMatch():
|
||||||
|
return
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
controller.storePinToKeycard(controller.getPin(), controller.generateRandomPUK())
|
||||||
|
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
controller.storePinToKeycard(controller.getPin(), controller.generateRandomPUK())
|
||||||
|
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.storePinToKeycard(controller.getPin(), puk = "")
|
||||||
|
elif self.flowType == FlowType.AppLogin:
|
||||||
|
controller.storePinToKeycard(controller.getPin(), puk = "")
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardRepeatPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterMnemonic and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorLoadingKeys:
|
||||||
|
controller.buildSeedPhrasesFromIndexes(keycardEvent.seedPhraseIndexes)
|
||||||
|
return createState(StateType.KeycardPinSet, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.keyUid.len > 0:
|
||||||
|
controller.setKeyUid(keycardEvent.keyUid)
|
||||||
|
return createState(StateType.KeycardPinSet, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUK:
|
||||||
|
controller.setKeycardData($keycardEvent.pukRetries)
|
||||||
|
controller.setPukValid(false)
|
||||||
|
if keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.KeycardPinSet, self.flowType, self.getBackState)
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
controller.setPukValid(true)
|
||||||
|
return createState(StateType.KeycardPinSet, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUK:
|
||||||
|
controller.setKeycardData($keycardEvent.pukRetries)
|
||||||
|
controller.setPukValid(false)
|
||||||
|
if keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.KeycardPinSet, self.flowType, self.getBackState)
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
controller.setPukValid(true)
|
||||||
|
return createState(StateType.KeycardPinSet, self.flowType, self.getBackState)
|
|
@ -0,0 +1,41 @@
|
||||||
|
type
|
||||||
|
KeycardWrongPinState* = ref object of State
|
||||||
|
pinValid: bool
|
||||||
|
|
||||||
|
proc newKeycardWrongPinState*(flowType: FlowType, backState: State): KeycardWrongPinState =
|
||||||
|
result = KeycardWrongPinState()
|
||||||
|
result.setup(flowType, StateType.KeycardWrongPin, backState)
|
||||||
|
result.pinValid = false
|
||||||
|
|
||||||
|
proc delete*(self: KeycardWrongPinState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executeBackCommand*(self: KeycardWrongPinState, controller: Controller) =
|
||||||
|
controller.setPin("")
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardWrongPinState, controller: Controller) =
|
||||||
|
self.pinValid = controller.getPin().len == PINLengthForStatusApp
|
||||||
|
if self.pinValid:
|
||||||
|
controller.enterKeycardPin(controller.getPin())
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardWrongPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPIN:
|
||||||
|
controller.setKeycardData($keycardEvent.pinRetries)
|
||||||
|
if keycardEvent.pinRetries > 0:
|
||||||
|
return self
|
||||||
|
return createState(StateType.KeycardMaxPinRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.KeycardMaxPinRetriesReached, self.flowType, self.getBackState)
|
||||||
|
return nil
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
if not defined(macosx):
|
||||||
|
controller.setupKeycardAccount(false)
|
||||||
|
return nil
|
||||||
|
return createState(StateType.Biometrics, self.flowType, self)
|
|
@ -0,0 +1,58 @@
|
||||||
|
type
|
||||||
|
KeycardWrongPukState* = ref object of State
|
||||||
|
|
||||||
|
proc newKeycardWrongPukState*(flowType: FlowType, backState: State): KeycardWrongPukState =
|
||||||
|
result = KeycardWrongPukState()
|
||||||
|
result.setup(flowType, StateType.KeycardWrongPuk, backState)
|
||||||
|
|
||||||
|
proc delete*(self: KeycardWrongPukState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: KeycardWrongPukState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if controller.getPuk().len == PUKLengthForStatusApp:
|
||||||
|
controller.enterKeycardPuk(controller.getPuk())
|
||||||
|
elif self.flowType == FlowType.AppLogin:
|
||||||
|
if controller.getPuk().len == PUKLengthForStatusApp:
|
||||||
|
controller.enterKeycardPuk(controller.getPuk())
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: KeycardWrongPukState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUK:
|
||||||
|
controller.setKeycardData($keycardEvent.pukRetries)
|
||||||
|
controller.setPukValid(false)
|
||||||
|
if keycardEvent.pukRetries > 0:
|
||||||
|
return nil
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUKRetries:
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
controller.setPukValid(true)
|
||||||
|
if not defined(macosx):
|
||||||
|
controller.setupKeycardAccount(false)
|
||||||
|
return nil
|
||||||
|
return createState(StateType.Biometrics, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUK:
|
||||||
|
controller.setKeycardData($keycardEvent.pukRetries)
|
||||||
|
controller.setPukValid(false)
|
||||||
|
if keycardEvent.pukRetries > 0:
|
||||||
|
return nil
|
||||||
|
return createState(StateType.KeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUKRetries:
|
||||||
|
return createState(StateType.LoginKeycardMaxPukRetriesReached, self.flowType, self.getBackState)
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
controller.setPukValid(true)
|
||||||
|
controller.loginAccountKeycard()
|
||||||
|
return nil
|
|
@ -0,0 +1,30 @@
|
||||||
|
type
|
||||||
|
LoginKeycardEmptyState* = ref object of State
|
||||||
|
|
||||||
|
proc newLoginKeycardEmptyState*(flowType: FlowType, backState: State): LoginKeycardEmptyState =
|
||||||
|
result = LoginKeycardEmptyState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardEmpty, backState)
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardEmptyState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: LoginKeycardEmptyState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
controller.runLoadAccountFlow(true)
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: LoginKeycardEmptyState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: LoginKeycardEmptyState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: LoginKeycardEmptyState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return createState(StateType.KeycardPluginReader, FlowType.FirstRunNewUserNewKeycardKeys, self)
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, FlowType.FirstRunNewUserNewKeycardKeys, self)
|
|
@ -0,0 +1,40 @@
|
||||||
|
type
|
||||||
|
LoginKeycardEnterPinState* = ref object of State
|
||||||
|
|
||||||
|
proc newLoginKeycardEnterPinState*(flowType: FlowType, backState: State): LoginKeycardEnterPinState =
|
||||||
|
result = LoginKeycardEnterPinState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardEnterPin, backState)
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardEnterPinState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: LoginKeycardEnterPinState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if controller.getPin().len == PINLengthForStatusApp:
|
||||||
|
controller.enterKeycardPin(controller.getPin())
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: LoginKeycardEnterPinState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: LoginKeycardEnterPinState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: LoginKeycardEnterPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
controller.loginAccountKeycard()
|
||||||
|
return
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPIN:
|
||||||
|
controller.setKeycardData($keycardEvent.pinRetries)
|
||||||
|
if keycardEvent.pinRetries > 0:
|
||||||
|
return createState(StateType.LoginKeycardWrongPin, self.flowType, nil)
|
||||||
|
return createState(StateType.LoginKeycardMaxPinRetriesReached, self.flowType, nil)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.LoginKeycardMaxPinRetriesReached, self.flowType, nil)
|
|
@ -0,0 +1,21 @@
|
||||||
|
type
|
||||||
|
LoginKeycardInsertKeycardState* = ref object of State
|
||||||
|
|
||||||
|
proc newLoginKeycardInsertKeycardState*(flowType: FlowType, backState: State): LoginKeycardInsertKeycardState =
|
||||||
|
result = LoginKeycardInsertKeycardState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardInsertKeycard, backState)
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardInsertKeycardState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: LoginKeycardInsertKeycardState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: LoginKeycardInsertKeycardState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: LoginKeycardInsertKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if keycardFlowType == ResponseTypeValueCardInserted:
|
||||||
|
return createState(StateType.LoginKeycardReadingKeycard, self.flowType, nil)
|
||||||
|
return nil
|
|
@ -0,0 +1,22 @@
|
||||||
|
type
|
||||||
|
LoginKeycardMaxPinRetriesReachedState* = ref object of State
|
||||||
|
|
||||||
|
proc newLoginKeycardMaxPinRetriesReachedState*(flowType: FlowType, backState: State): LoginKeycardMaxPinRetriesReachedState =
|
||||||
|
result = LoginKeycardMaxPinRetriesReachedState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardMaxPinRetriesReached, backState)
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardMaxPinRetriesReachedState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executeBackCommand*(self: LoginKeycardMaxPinRetriesReachedState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.AppLogin and controller.isKeycardCreatedAccountSelectedOne():
|
||||||
|
controller.runLoginFlow()
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: LoginKeycardMaxPinRetriesReachedState, controller: Controller): State =
|
||||||
|
return createState(StateType.KeycardRecover, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: LoginKeycardMaxPinRetriesReachedState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: LoginKeycardMaxPinRetriesReachedState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
|
@ -0,0 +1,12 @@
|
||||||
|
type
|
||||||
|
LoginKeycardMaxPukRetriesReachedState* = ref object of State
|
||||||
|
|
||||||
|
proc newLoginKeycardMaxPukRetriesReachedState*(flowType: FlowType, backState: State): LoginKeycardMaxPukRetriesReachedState =
|
||||||
|
result = LoginKeycardMaxPukRetriesReachedState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardMaxPukRetriesReached, backState)
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardMaxPukRetriesReachedState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: LoginKeycardMaxPukRetriesReachedState, controller: Controller): State =
|
||||||
|
return createState(StateType.UserProfileEnterSeedPhrase, self.flowType, nil)
|
|
@ -0,0 +1,55 @@
|
||||||
|
import ../../../global/global_singleton
|
||||||
|
|
||||||
|
type
|
||||||
|
LoginKeycardReadingKeycardState* = ref object of State
|
||||||
|
|
||||||
|
proc newLoginKeycardReadingKeycardState*(flowType: FlowType, backState: State): LoginKeycardReadingKeycardState =
|
||||||
|
result = LoginKeycardReadingKeycardState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardReadingKeycard, backState)
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardReadingKeycardState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: LoginKeycardReadingKeycardState, controller: Controller) =
|
||||||
|
if not controller.keychainErrorOccurred():
|
||||||
|
controller.enterKeycardPin(controller.getPin())
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: LoginKeycardReadingKeycardState, controller: Controller): State =
|
||||||
|
if controller.keychainErrorOccurred():
|
||||||
|
return createState(StateType.LoginKeycardEnterPin, self.flowType, nil)
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: LoginKeycardReadingKeycardState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: LoginKeycardReadingKeycardState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: LoginKeycardReadingKeycardState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
controller.loginAccountKeycard()
|
||||||
|
return nil
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
if not controller.keyUidMatch(keycardEvent.keyUid):
|
||||||
|
return createState(StateType.LoginKeycardWrongKeycard, self.flowType, nil)
|
||||||
|
let value = singletonInstance.localAccountSettings.getStoreToKeychainValue()
|
||||||
|
if value == LS_VALUE_STORE:
|
||||||
|
controller.tryToObtainDataFromKeychain()
|
||||||
|
return nil
|
||||||
|
return createState(StateType.LoginKeycardEnterPin, self.flowType, nil)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.LoginKeycardMaxPinRetriesReached, self.flowType, nil)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorNoKeys:
|
||||||
|
return createState(StateType.LoginKeycardEmpty, self.flowType, nil)
|
||||||
|
if keycardFlowType == ResponseTypeValueSwapCard and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPUKRetries:
|
||||||
|
return createState(StateType.LoginKeycardMaxPukRetriesReached, self.flowType, nil)
|
|
@ -0,0 +1,15 @@
|
||||||
|
type
|
||||||
|
LoginKeycardWrongKeycardState* = ref object of State
|
||||||
|
|
||||||
|
proc newLoginKeycardWrongKeycardState*(flowType: FlowType, backState: State): LoginKeycardWrongKeycardState =
|
||||||
|
result = LoginKeycardWrongKeycardState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardWrongKeycard, backState)
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardWrongKeycardState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: LoginKeycardWrongKeycardState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: LoginKeycardWrongKeycardState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
|
@ -0,0 +1,43 @@
|
||||||
|
type
|
||||||
|
LoginKeycardWrongPinState* = ref object of State
|
||||||
|
pinValid: bool
|
||||||
|
|
||||||
|
proc newLoginKeycardWrongPinState*(flowType: FlowType, backState: State): LoginKeycardWrongPinState =
|
||||||
|
result = LoginKeycardWrongPinState()
|
||||||
|
result.setup(flowType, StateType.LoginKeycardWrongPin, backState)
|
||||||
|
result.pinValid = false
|
||||||
|
|
||||||
|
proc delete*(self: LoginKeycardWrongPinState) =
|
||||||
|
self.State.delete
|
||||||
|
|
||||||
|
method executePrimaryCommand*(self: LoginKeycardWrongPinState, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if controller.getPin().len == PINLengthForStatusApp:
|
||||||
|
controller.enterKeycardPin(controller.getPin())
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: LoginKeycardWrongPinState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: LoginKeycardWrongPinState, controller: Controller): State =
|
||||||
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: LoginKeycardWrongPinState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == RequestParamPIN:
|
||||||
|
controller.setKeycardData($keycardEvent.pinRetries)
|
||||||
|
if keycardEvent.pinRetries > 0:
|
||||||
|
return nil
|
||||||
|
return createState(StateType.LoginKeycardMaxPinRetriesReached, self.flowType, nil)
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterPUK and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
if keycardEvent.pinRetries == 0 and keycardEvent.pukRetries > 0:
|
||||||
|
return createState(StateType.LoginKeycardMaxPinRetriesReached, self.flowType, nil)
|
||||||
|
return nil
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len == 0:
|
||||||
|
controller.setKeycardEvent(keycardEvent)
|
||||||
|
controller.loginAccountKeycard()
|
||||||
|
return nil
|
|
@ -1,7 +1,3 @@
|
||||||
import state
|
|
||||||
import ../controller
|
|
||||||
import welcome_state_new_user, welcome_state_old_user
|
|
||||||
|
|
||||||
type
|
type
|
||||||
LoginState* = ref object of State
|
LoginState* = ref object of State
|
||||||
|
|
||||||
|
@ -12,14 +8,22 @@ proc newLoginState*(flowType: FlowType, backState: State): LoginState =
|
||||||
proc delete*(self: LoginState) =
|
proc delete*(self: LoginState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method moveToNextPrimaryState*(self: LoginState): bool =
|
|
||||||
return false
|
|
||||||
|
|
||||||
method executePrimaryCommand*(self: LoginState, controller: Controller) =
|
method executePrimaryCommand*(self: LoginState, controller: Controller) =
|
||||||
controller.login()
|
controller.login()
|
||||||
|
|
||||||
method getNextSecondaryState*(self: LoginState): State =
|
method getNextSecondaryState*(self: LoginState, controller: Controller): State =
|
||||||
return newWelcomeStateNewUser(FlowType.General, self)
|
return createState(StateType.WelcomeNewStatusUser, self.flowType, self)
|
||||||
|
|
||||||
method getNextTertiaryState*(self: LoginState): State =
|
method getNextTertiaryState*(self: LoginState, controller: Controller): State =
|
||||||
return newWelcomeStateOldUser(FlowType.General, self)
|
return createState(StateType.WelcomeOldStatusUser, self.flowType, self)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: LoginState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return nil
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.LoginKeycardInsertKeycard, self.flowType, nil)
|
|
@ -1,5 +1,3 @@
|
||||||
import state, welcome_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
NotificationState* = ref object of State
|
NotificationState* = ref object of State
|
||||||
|
|
||||||
|
@ -10,5 +8,5 @@ proc newNotificationState*(flowType: FlowType, backState: State): NotificationSt
|
||||||
proc delete*(self: NotificationState) =
|
proc delete*(self: NotificationState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: NotificationState): State =
|
method getNextPrimaryState*(self: NotificationState, controller: Controller): State =
|
||||||
return newWelcomeState(FlowType.General, nil)
|
return createState(StateType.Welcome, FlowType.General, nil)
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
import ../controller
|
import ../controller
|
||||||
|
from ../../../../app_service/service/keycard/service import KeycardEvent, KeyDetails
|
||||||
|
|
||||||
|
export KeycardEvent, KeyDetails
|
||||||
|
|
||||||
type FlowType* {.pure.} = enum
|
type FlowType* {.pure.} = enum
|
||||||
General = "General"
|
General = "General"
|
||||||
FirstRunNewUserNewKeys = "FirstRunNewUserNewKeys"
|
FirstRunNewUserNewKeys = "FirstRunNewUserNewKeys"
|
||||||
FirstRunNewUserNewKeycardKeys = "FirstRunNewUserNewKeycardKeys"
|
FirstRunNewUserNewKeycardKeys = "FirstRunNewUserNewKeycardKeys"
|
||||||
FirstRunNewUserImportSeedPhrase = "FirstRunNewUserImportSeedPhrase"
|
FirstRunNewUserImportSeedPhrase = "FirstRunNewUserImportSeedPhrase"
|
||||||
|
FirstRunNewUserImportSeedPhraseIntoKeycard = "FirstRunNewUserImportSeedPhraseIntoKeycard"
|
||||||
FirstRunOldUserSyncCode = "FirstRunOldUserSyncCode"
|
FirstRunOldUserSyncCode = "FirstRunOldUserSyncCode"
|
||||||
FirstRunOldUserKeycardImport = "FirstRunOldUserKeycardImport"
|
FirstRunOldUserKeycardImport = "FirstRunOldUserKeycardImport"
|
||||||
FirstRunOldUserImportSeedPhrase = "FirstRunOldUserImportSeedPhrase"
|
FirstRunOldUserImportSeedPhrase = "FirstRunOldUserImportSeedPhrase"
|
||||||
|
@ -23,7 +27,35 @@ type StateType* {.pure.} = enum
|
||||||
UserProfileImportSeedPhrase = "UserProfileImportSeedPhrase"
|
UserProfileImportSeedPhrase = "UserProfileImportSeedPhrase"
|
||||||
UserProfileEnterSeedPhrase = "UserProfileEnterSeedPhrase"
|
UserProfileEnterSeedPhrase = "UserProfileEnterSeedPhrase"
|
||||||
Biometrics = "Biometrics"
|
Biometrics = "Biometrics"
|
||||||
|
KeycardPluginReader = "KeycardPluginReader"
|
||||||
|
KeycardInsertKeycard = "KeycardInsertKeycard"
|
||||||
|
KeycardReadingKeycard = "KeycardReadingKeycard"
|
||||||
|
KeycardCreatePin = "KeycardCreatePin"
|
||||||
|
KeycardRepeatPin = "KeycardRepeatPin"
|
||||||
|
KeycardPinSet = "KeycardPinSet"
|
||||||
|
KeycardEnterPin = "KeycardEnterPin"
|
||||||
|
KeycardWrongPin = "KeycardWrongPin"
|
||||||
|
KeycardEnterPuk = "KeycardEnterPuk"
|
||||||
|
KeycardWrongPuk = "KeycardWrongPuk"
|
||||||
|
KeycardDisplaySeedPhrase = "KeycardDisplaySeedPhrase"
|
||||||
|
KeycardEnterSeedPhraseWords = "KeycardEnterSeedPhraseWords"
|
||||||
|
KeycardNotEmpty = "KeycardNotEmpty"
|
||||||
|
KeycardEmpty = "KeycardEmpty"
|
||||||
|
KeycardLocked = "KeycardLocked"
|
||||||
|
KeycardRecover = "KeycardRecover"
|
||||||
|
KeycardMaxPairingSlotsReached = "KeycardMaxPairingSlotsReached"
|
||||||
|
KeycardMaxPinRetriesReached = "KeycardMaxPinRetriesReached"
|
||||||
|
KeycardMaxPukRetriesReached = "KeycardMaxPukRetriesReached"
|
||||||
Login = "Login"
|
Login = "Login"
|
||||||
|
LoginKeycardInsertKeycard = "LoginKeycardInsertKeycard"
|
||||||
|
LoginKeycardReadingKeycard = "LoginKeycardReadingKeycard"
|
||||||
|
LoginKeycardEnterPin = "LoginKeycardEnterPin"
|
||||||
|
LoginKeycardWrongKeycard = "LoginKeycardWrongKeycard"
|
||||||
|
LoginKeycardWrongPin = "LoginKeycardWrongPin"
|
||||||
|
LoginKeycardMaxPinRetriesReached = "LoginKeycardMaxPinRetriesReached"
|
||||||
|
LoginKeycardMaxPukRetriesReached = "LoginKeycardMaxPukRetriesReached"
|
||||||
|
LoginKeycardEmpty = "LoginKeycardEmpty"
|
||||||
|
|
||||||
|
|
||||||
## This is the base class for all state we may have in onboarding/login flow.
|
## This is the base class for all state we may have in onboarding/login flow.
|
||||||
## We should not instance of this class (in c++ this will be an abstract class).
|
## We should not instance of this class (in c++ this will be an abstract class).
|
||||||
|
@ -67,15 +99,15 @@ method displayBackButton*(self: State): bool {.inline base.} =
|
||||||
return not self.backState.isNil
|
return not self.backState.isNil
|
||||||
|
|
||||||
## Returns next state instance in case the "primary" action is triggered
|
## Returns next state instance in case the "primary" action is triggered
|
||||||
method getNextPrimaryState*(self: State): State {.inline base.} =
|
method getNextPrimaryState*(self: State, controller: Controller): State {.inline base.} =
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
## Returns next state instance in case the "secondary" action is triggered
|
## Returns next state instance in case the "secondary" action is triggered
|
||||||
method getNextSecondaryState*(self: State): State {.inline base.} =
|
method getNextSecondaryState*(self: State, controller: Controller): State {.inline base.} =
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
## Returns next state instance in case the "tertiary" action is triggered
|
## Returns next state instance in case the "tertiary" action is triggered
|
||||||
method getNextTertiaryState*(self: State): State {.inline base.} =
|
method getNextTertiaryState*(self: State, controller: Controller): State {.inline base.} =
|
||||||
return nil
|
return nil
|
||||||
|
|
||||||
## This method is executed in case "back" button is clicked
|
## This method is executed in case "back" button is clicked
|
||||||
|
@ -94,18 +126,7 @@ method executeSecondaryCommand*(self: State, controller: Controller) {.inline ba
|
||||||
method executeTertiaryCommand*(self: State, controller: Controller) {.inline base.} =
|
method executeTertiaryCommand*(self: State, controller: Controller) {.inline base.} =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
## Returns true if we should move from this state immediatelly when the "primary" action is triggered,
|
## This method is used for handling aync responses for keycard related states
|
||||||
## in case we need to wait for some other action, or some aync event, this should return false
|
method resolveKeycardNextState*(self: State, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
method moveToNextPrimaryState*(self: State): bool {.inline base.} =
|
controller: Controller): State {.inline base.} =
|
||||||
return true
|
return nil
|
||||||
|
|
||||||
## Returns true if we should move from this state immediatelly when the "secondary" action is triggered,
|
|
||||||
## in case we need to wait for some other action, or some aync event, this should return false
|
|
||||||
method moveToNextSecondaryState*(self: State): bool {.inline base.} =
|
|
||||||
return true
|
|
||||||
|
|
||||||
## Returns true if we should move from this state immediatelly when the "tertiary" action is triggered,
|
|
||||||
## in case we need to wait for some other action, or some aync event, this should return false
|
|
||||||
method moveToNextTertiaryState*(self: State): bool {.inline base.} =
|
|
||||||
return true
|
|
||||||
|
|
|
@ -0,0 +1,134 @@
|
||||||
|
import chronicles
|
||||||
|
import ../../../../app_service/service/keycard/constants
|
||||||
|
import ../controller
|
||||||
|
from ../../../../app_service/service/keycard/service import PINLengthForStatusApp
|
||||||
|
from ../../../../app_service/service/keycard/service import PUKLengthForStatusApp
|
||||||
|
import state
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "startup-module-state-factory"
|
||||||
|
|
||||||
|
# Forward declaration
|
||||||
|
proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: State): State
|
||||||
|
|
||||||
|
include biometrics_state
|
||||||
|
include keycard_create_pin_state
|
||||||
|
include keycard_display_seed_phrase_state
|
||||||
|
include keycard_empty_state
|
||||||
|
include keycard_enter_pin_state
|
||||||
|
include keycard_enter_puk_state
|
||||||
|
include keycard_enter_seed_phrase_words_state
|
||||||
|
include keycard_insert_keycard_state
|
||||||
|
include keycard_locked_state
|
||||||
|
include keycard_max_pairing_slots_reached_state
|
||||||
|
include keycard_max_pin_retries_reached_state
|
||||||
|
include keycard_max_puk_retries_reached_state
|
||||||
|
include keycard_not_empty_state
|
||||||
|
include keycard_pin_set_state
|
||||||
|
include keycard_plugin_reader_state
|
||||||
|
include keycard_reading_keycard_state
|
||||||
|
include keycard_recover_state
|
||||||
|
include keycard_repeat_pin_state
|
||||||
|
include keycard_wrong_pin_state
|
||||||
|
include keycard_wrong_puk_state
|
||||||
|
include notification_state
|
||||||
|
include user_profile_chat_key_state
|
||||||
|
include user_profile_confirm_password_state
|
||||||
|
include user_profile_create_password_state
|
||||||
|
include user_profile_create_state
|
||||||
|
include user_profile_enter_seed_phrase_state
|
||||||
|
include user_profile_import_seed_phrase_state
|
||||||
|
include welcome_state_new_user
|
||||||
|
include welcome_state_old_user
|
||||||
|
include welcome_state
|
||||||
|
include login_state
|
||||||
|
include login_keycard_insert_keycard_state
|
||||||
|
include login_keycard_reading_keycard_state
|
||||||
|
include login_keycard_enter_pin_state
|
||||||
|
include login_keycard_wrong_keycard
|
||||||
|
include login_keycard_wrong_pin_state
|
||||||
|
include login_keycard_max_pin_retries_reached_state
|
||||||
|
include login_keycard_max_puk_retries_reached_state
|
||||||
|
include login_keycard_empty_state
|
||||||
|
|
||||||
|
proc createState*(stateToBeCreated: StateType, flowType: FlowType, backState: State): State =
|
||||||
|
if stateToBeCreated == StateType.AllowNotifications:
|
||||||
|
return newNotificationState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.Welcome:
|
||||||
|
return newWelcomeState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.WelcomeNewStatusUser:
|
||||||
|
return newWelcomeStateNewUser(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.WelcomeOldStatusUser:
|
||||||
|
return newWelcomeStateOldUser(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.UserProfileCreate:
|
||||||
|
return newUserProfileCreateState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.UserProfileChatKey:
|
||||||
|
return newUserProfileChatKeyState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.UserProfileCreatePassword:
|
||||||
|
return newUserProfileCreatePasswordState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.UserProfileConfirmPassword:
|
||||||
|
return newUserProfileConfirmPasswordState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.UserProfileImportSeedPhrase:
|
||||||
|
return newUserProfileImportSeedPhraseState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.UserProfileEnterSeedPhrase:
|
||||||
|
return newUserProfileEnterSeedPhraseState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.Biometrics:
|
||||||
|
return newBiometricsState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardPluginReader:
|
||||||
|
return newKeycardPluginReaderState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardInsertKeycard:
|
||||||
|
return newKeycardInsertKeycardState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardReadingKeycard:
|
||||||
|
return newKeycardReadingKeycardState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardCreatePin:
|
||||||
|
return newKeycardCreatePinState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardRepeatPin:
|
||||||
|
return newKeycardRepeatPinState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardPinSet:
|
||||||
|
return newKeycardPinSetState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardEnterPin:
|
||||||
|
return newKeycardEnterPinState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardWrongPin:
|
||||||
|
return newKeycardWrongPinState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardEnterPuk:
|
||||||
|
return newKeycardEnterPukState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardWrongPuk:
|
||||||
|
return newKeycardWrongPukState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardDisplaySeedPhrase:
|
||||||
|
return newKeycardDisplaySeedPhraseState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardEnterSeedPhraseWords:
|
||||||
|
return newKeycardEnterSeedPhraseWordsState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardNotEmpty:
|
||||||
|
return newKeycardNotEmptyState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardEmpty:
|
||||||
|
return newKeycardEmptyState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardLocked:
|
||||||
|
return newKeycardLockedState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardRecover:
|
||||||
|
return newKeycardRecoverState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardMaxPairingSlotsReached:
|
||||||
|
return newKeycardMaxPairingSlotsReachedState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardMaxPinRetriesReached:
|
||||||
|
return newKeycardMaxPinRetriesReachedState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.KeycardMaxPukRetriesReached:
|
||||||
|
return newKeycardMaxPukRetriesReachedState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.Login:
|
||||||
|
return newLoginState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardInsertKeycard:
|
||||||
|
return newLoginKeycardInsertKeycardState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardReadingKeycard:
|
||||||
|
return newLoginKeycardReadingKeycardState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardEnterPin:
|
||||||
|
return newLoginKeycardEnterPinState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardWrongKeycard:
|
||||||
|
return newLoginKeycardWrongKeycardState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardWrongPin:
|
||||||
|
return newLoginKeycardWrongPinState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardMaxPinRetriesReached:
|
||||||
|
return newLoginKeycardMaxPinRetriesReachedState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardMaxPukRetriesReached:
|
||||||
|
return newLoginKeycardMaxPukRetriesReachedState(flowType, backState)
|
||||||
|
if stateToBeCreated == StateType.LoginKeycardEmpty:
|
||||||
|
return newLoginKeycardEmptyState(flowType, backState)
|
||||||
|
|
||||||
|
error "No implementation available for state ", state=stateToBeCreated
|
|
@ -1,6 +1,3 @@
|
||||||
import state
|
|
||||||
import user_profile_create_password_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
UserProfileChatKeyState* = ref object of State
|
UserProfileChatKeyState* = ref object of State
|
||||||
|
|
||||||
|
@ -11,5 +8,12 @@ proc newUserProfileChatKeyState*(flowType: FlowType, backState: State): UserProf
|
||||||
proc delete*(self: UserProfileChatKeyState) =
|
proc delete*(self: UserProfileChatKeyState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: UserProfileChatKeyState): State =
|
method getNextPrimaryState*(self: UserProfileChatKeyState, controller: Controller): State =
|
||||||
return newUserProfileCreatePasswordState(self.State.flowType, self)
|
if self.flowType == FlowType.FirstRunNewUserNewKeys or
|
||||||
|
self.flowType == FlowType.FirstRunNewUserImportSeedPhrase or
|
||||||
|
self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||||
|
return createState(StateType.UserProfileCreatePassword, self.flowType, self)
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserNewKeycardKeys or
|
||||||
|
self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard or
|
||||||
|
self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
return createState(StateType.Biometrics, self.flowType, self)
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
import state
|
|
||||||
import biometrics_state
|
|
||||||
import ../controller
|
|
||||||
|
|
||||||
type
|
type
|
||||||
UserProfileConfirmPasswordState* = ref object of State
|
UserProfileConfirmPasswordState* = ref object of State
|
||||||
|
|
||||||
|
@ -12,23 +8,23 @@ proc newUserProfileConfirmPasswordState*(flowType: FlowType, backState: State):
|
||||||
proc delete*(self: UserProfileConfirmPasswordState) =
|
proc delete*(self: UserProfileConfirmPasswordState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method moveToNextPrimaryState*(self: UserProfileConfirmPasswordState): bool =
|
method getNextPrimaryState*(self: UserProfileConfirmPasswordState, controller: Controller): State =
|
||||||
return defined(macosx)
|
if not defined(macosx):
|
||||||
|
|
||||||
method getNextPrimaryState*(self: UserProfileConfirmPasswordState): State =
|
|
||||||
if not self.moveToNextPrimaryState():
|
|
||||||
return nil
|
return nil
|
||||||
return newBiometricsState(self.State.flowType, nil)
|
return createState(StateType.Biometrics, self.flowType, self)
|
||||||
|
|
||||||
method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller: Controller) =
|
method executePrimaryCommand*(self: UserProfileConfirmPasswordState, controller: Controller) =
|
||||||
if self.moveToNextPrimaryState():
|
if defined(macosx):
|
||||||
return
|
return
|
||||||
|
let storeToKeychain = false # false, cause we don't have keychain support for other than mac os
|
||||||
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
if self.flowType == FlowType.FirstRunNewUserNewKeys:
|
||||||
controller.storeGeneratedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
controller.storeGeneratedAccountAndLogin(storeToKeychain)
|
||||||
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
elif self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||||
controller.storeImportedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||||
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
elif self.flowType == FlowType.FirstRunOldUserImportSeedPhrase:
|
||||||
## This should not be the correct call for this flow, this is an issue, but since current implementation is like that
|
## This should not be the correct call for this flow, this is an issue,
|
||||||
## and this is not a bug fixing issue, left as it is.
|
## but since current implementation is like that and this is not a bug fixing issue, left as it is.
|
||||||
controller.storeImportedAccountAndLogin(storeToKeychain = false) # false, cause we don't have keychain support for other than mac os
|
controller.storeImportedAccountAndLogin(storeToKeychain)
|
||||||
|
elif self.flowType == FlowType.FirstRunNewUserNewKeycardKeys:
|
||||||
|
controller.storeKeycardAccountAndLogin(storeToKeychain)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
import state
|
|
||||||
import ../controller
|
|
||||||
import user_profile_confirm_password_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
UserProfileCreatePasswordState* = ref object of State
|
UserProfileCreatePasswordState* = ref object of State
|
||||||
|
|
||||||
|
@ -12,8 +8,8 @@ proc newUserProfileCreatePasswordState*(flowType: FlowType, backState: State): U
|
||||||
proc delete*(self: UserProfileCreatePasswordState) =
|
proc delete*(self: UserProfileCreatePasswordState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: UserProfileCreatePasswordState): State =
|
method getNextPrimaryState*(self: UserProfileCreatePasswordState, controller: Controller): State =
|
||||||
return newUserProfileConfirmPasswordState(self.State.flowType, self)
|
return createState(StateType.UserProfileConfirmPassword, self.flowType, self)
|
||||||
|
|
||||||
method executeBackCommand*(self: UserProfileCreatePasswordState, controller: Controller) =
|
method executeBackCommand*(self: UserProfileCreatePasswordState, controller: Controller) =
|
||||||
controller.setPassword("")
|
controller.setPassword("")
|
|
@ -1,7 +1,3 @@
|
||||||
import state
|
|
||||||
import ../controller
|
|
||||||
import user_profile_chat_key_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
UserProfileCreateState* = ref object of State
|
UserProfileCreateState* = ref object of State
|
||||||
|
|
||||||
|
@ -12,8 +8,8 @@ proc newUserProfileCreateState*(flowType: FlowType, backState: State): UserProfi
|
||||||
proc delete*(self: UserProfileCreateState) =
|
proc delete*(self: UserProfileCreateState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: UserProfileCreateState): State =
|
method getNextPrimaryState*(self: UserProfileCreateState, controller: Controller): State =
|
||||||
return newUserProfileChatKeyState(self.State.flowType, self)
|
return createState(StateType.UserProfileChatKey, self.flowType, self)
|
||||||
|
|
||||||
method executeBackCommand*(self: UserProfileCreateState, controller: Controller) =
|
method executeBackCommand*(self: UserProfileCreateState, controller: Controller) =
|
||||||
controller.setDisplayName("")
|
controller.setDisplayName("")
|
|
@ -1,7 +1,3 @@
|
||||||
import state
|
|
||||||
import ../controller
|
|
||||||
import user_profile_create_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
UserProfileEnterSeedPhraseState* = ref object of State
|
UserProfileEnterSeedPhraseState* = ref object of State
|
||||||
successfulImport: bool
|
successfulImport: bool
|
||||||
|
@ -14,13 +10,44 @@ proc newUserProfileEnterSeedPhraseState*(flowType: FlowType, backState: State):
|
||||||
proc delete*(self: UserProfileEnterSeedPhraseState) =
|
proc delete*(self: UserProfileEnterSeedPhraseState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method moveToNextPrimaryState*(self: UserProfileEnterSeedPhraseState): bool =
|
method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState, controller: Controller): State =
|
||||||
return self.successfulImport
|
if not self.successfulImport:
|
||||||
|
|
||||||
method getNextPrimaryState*(self: UserProfileEnterSeedPhraseState): State =
|
|
||||||
if not self.moveToNextPrimaryState():
|
|
||||||
return nil
|
return nil
|
||||||
return newUserProfileCreateState(self.State.flowType, self)
|
if self.flowType == FlowType.FirstRunOldUserImportSeedPhrase or
|
||||||
|
self.flowType == FlowType.FirstRunNewUserImportSeedPhrase:
|
||||||
|
return createState(StateType.UserProfileCreate, self.flowType, self)
|
||||||
|
|
||||||
method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) =
|
method executePrimaryCommand*(self: UserProfileEnterSeedPhraseState, controller: Controller) =
|
||||||
self.successfulImport = controller.importMnemonic()
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
controller.runLoadAccountFlowWithSeedPhrase(controller.getSeedPhraseLength(), controller.getSeedPhrase(), true)
|
||||||
|
else:
|
||||||
|
self.successfulImport = controller.importMnemonic()
|
||||||
|
if self.successfulImport:
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
controller.storeSeedPhraseToKeycard(controller.getSeedPhraseLength(), controller.getSeedPhrase())
|
||||||
|
elif self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
controller.runLoadAccountFlowWithSeedPhrase(controller.getSeedPhraseLength(), controller.getSeedPhrase(), true)
|
||||||
|
|
||||||
|
method resolveKeycardNextState*(self: UserProfileEnterSeedPhraseState, keycardFlowType: string, keycardEvent: KeycardEvent,
|
||||||
|
controller: Controller): State =
|
||||||
|
if self.flowType == FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard:
|
||||||
|
if keycardFlowType == ResponseTypeValueEnterNewPIN and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorRequireInit:
|
||||||
|
return createState(StateType.KeycardCreatePin, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.FirstRunOldUserKeycardImport:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return createState(StateType.KeycardPluginReader, self.flowType, self)
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, self.flowType, self.getBackState)
|
||||||
|
if self.flowType == FlowType.AppLogin:
|
||||||
|
if keycardFlowType == ResponseTypeValueKeycardFlowResult and
|
||||||
|
keycardEvent.error.len > 0 and
|
||||||
|
keycardEvent.error == ErrorConnection:
|
||||||
|
controller.resumeCurrentFlowLater()
|
||||||
|
return createState(StateType.KeycardPluginReader, self.flowType, self)
|
||||||
|
if keycardFlowType == ResponseTypeValueInsertCard:
|
||||||
|
return createState(StateType.KeycardInsertKeycard, self.flowType, self.getBackState)
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import state
|
|
||||||
import user_profile_enter_seed_phrase_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
UserProfileImportSeedPhraseState* = ref object of State
|
UserProfileImportSeedPhraseState* = ref object of State
|
||||||
|
|
||||||
|
@ -11,5 +8,8 @@ proc newUserProfileImportSeedPhraseState*(flowType: FlowType, backState: State):
|
||||||
proc delete*(self: UserProfileImportSeedPhraseState) =
|
proc delete*(self: UserProfileImportSeedPhraseState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: UserProfileImportSeedPhraseState): State =
|
method getNextPrimaryState*(self: UserProfileImportSeedPhraseState, controller: Controller): State =
|
||||||
return newUserProfileEnterSeedPhraseState(self.State.flowType, self)
|
return createState(StateType.UserProfileEnterSeedPhrase, FlowType.FirstRunNewUserImportSeedPhrase, self)
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: UserProfileImportSeedPhraseState, controller: Controller): State =
|
||||||
|
return createState(StateType.KeycardPluginReader, FlowType.FirstRunNewUserImportSeedPhraseIntoKeycard, self)
|
|
@ -1,6 +1,3 @@
|
||||||
import state
|
|
||||||
import welcome_state_new_user, welcome_state_old_user
|
|
||||||
|
|
||||||
type
|
type
|
||||||
WelcomeState* = ref object of State
|
WelcomeState* = ref object of State
|
||||||
|
|
||||||
|
@ -11,8 +8,8 @@ proc newWelcomeState*(flowType: FlowType, backState: State): WelcomeState =
|
||||||
proc delete*(self: WelcomeState) =
|
proc delete*(self: WelcomeState) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: WelcomeState): State =
|
method getNextPrimaryState*(self: WelcomeState, controller: Controller): State =
|
||||||
return newWelcomeStateNewUser(FlowType.General, self)
|
return createState(StateType.WelcomeNewStatusUser, FlowType.General, self)
|
||||||
|
|
||||||
method getNextSecondaryState*(self: WelcomeState): State =
|
method getNextSecondaryState*(self: WelcomeState, controller: Controller): State =
|
||||||
return newWelcomeStateOldUser(FlowType.General, self)
|
return createState(StateType.WelcomeOldStatusUser, FlowType.General, self)
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import state
|
|
||||||
import user_profile_create_state, user_profile_import_seed_phrase_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
WelcomeStateNewUser* = ref object of State
|
WelcomeStateNewUser* = ref object of State
|
||||||
|
|
||||||
|
@ -11,14 +8,15 @@ proc newWelcomeStateNewUser*(flowType: FlowType, backState: State): WelcomeState
|
||||||
proc delete*(self: WelcomeStateNewUser) =
|
proc delete*(self: WelcomeStateNewUser) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: WelcomeStateNewUser): State =
|
method executeBackCommand*(self: WelcomeStateNewUser, controller: Controller) =
|
||||||
return newUserProfileCreateState(FlowType.FirstRunNewUserNewKeys, self)
|
if self.flowType == FlowType.AppLogin and controller.isKeycardCreatedAccountSelectedOne():
|
||||||
|
controller.runLoginFlow()
|
||||||
|
|
||||||
method getNextSecondaryState*(self: WelcomeStateNewUser): State =
|
method getNextPrimaryState*(self: WelcomeStateNewUser, controller: Controller): State =
|
||||||
# We will handle here a click on `Generate keys for a new Keycard`
|
return createState(StateType.UserProfileCreate, FlowType.FirstRunNewUserNewKeys, self)
|
||||||
discard
|
|
||||||
|
|
||||||
method getNextTertiaryState*(self: WelcomeStateNewUser): State =
|
|
||||||
return newUserProfileImportSeedPhraseState(FlowType.FirstRunNewUserImportSeedPhrase, self)
|
|
||||||
|
|
||||||
|
method getNextSecondaryState*(self: WelcomeStateNewUser, controller: Controller): State =
|
||||||
|
return createState(StateType.KeycardPluginReader, FlowType.FirstRunNewUserNewKeycardKeys, self)
|
||||||
|
|
||||||
|
method getNextTertiaryState*(self: WelcomeStateNewUser, controller: Controller): State =
|
||||||
|
return createState(StateType.UserProfileImportSeedPhrase, FlowType.General, self)
|
|
@ -1,6 +1,3 @@
|
||||||
import state
|
|
||||||
import user_profile_enter_seed_phrase_state
|
|
||||||
|
|
||||||
type
|
type
|
||||||
WelcomeStateOldUser* = ref object of State
|
WelcomeStateOldUser* = ref object of State
|
||||||
|
|
||||||
|
@ -11,19 +8,17 @@ proc newWelcomeStateOldUser*(flowType: FlowType, backState: State): WelcomeState
|
||||||
proc delete*(self: WelcomeStateOldUser) =
|
proc delete*(self: WelcomeStateOldUser) =
|
||||||
self.State.delete
|
self.State.delete
|
||||||
|
|
||||||
method getNextPrimaryState*(self: WelcomeStateOldUser): State =
|
method executeBackCommand*(self: WelcomeStateOldUser, controller: Controller) =
|
||||||
|
if self.flowType == FlowType.AppLogin and controller.isKeycardCreatedAccountSelectedOne():
|
||||||
|
controller.runLoginFlow()
|
||||||
|
|
||||||
|
method getNextPrimaryState*(self: WelcomeStateOldUser, controller: Controller): State =
|
||||||
# We will handle here a click on `Scan sync code`
|
# We will handle here a click on `Scan sync code`
|
||||||
discard
|
discard
|
||||||
|
|
||||||
method getNextSecondaryState*(self: WelcomeStateOldUser): State =
|
method getNextSecondaryState*(self: WelcomeStateOldUser, controller: Controller): State =
|
||||||
# We will handle here a click on `Login with Keycard`
|
return createState(StateType.KeycardPluginReader, FlowType.FirstRunOldUserKeycardImport, self)
|
||||||
discard
|
|
||||||
|
|
||||||
method getNextTertiaryState*(self: WelcomeStateOldUser): State =
|
method getNextTertiaryState*(self: WelcomeStateOldUser, controller: Controller): State =
|
||||||
## This is added as next state in case of import seed for an old user, but this doesn't match the flow
|
return createState(StateType.UserProfileEnterSeedPhrase, FlowType.FirstRunOldUserImportSeedPhrase, self)
|
||||||
## in the design. Need to be fixed correctly.
|
|
||||||
## Why it's not fixed now???
|
|
||||||
## -> Cause this is just a improving and moving to a better form what we currently have, fixing will be done in another issue
|
|
||||||
## and need to be discussed as we haven't had that flow implemented ever before
|
|
||||||
return newUserProfileEnterSeedPhraseState(FlowType.FirstRunOldUserImportSeedPhrase, self)
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import ../../../app_service/service/accounts/service
|
import ../../../app_service/service/accounts/service as accounts_service
|
||||||
import models/login_account_item as login_acc_item
|
import models/login_account_item as login_acc_item
|
||||||
|
from ../../../app_service/service/keycard/service import KeycardEvent, KeyDetails
|
||||||
|
|
||||||
|
|
||||||
type
|
type
|
||||||
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
AccessInterface* {.pure inheritable.} = ref object of RootObj
|
||||||
|
@ -37,6 +39,9 @@ method getImportedAccount*(self: AccessInterface): GeneratedAccountDto {.base.}
|
||||||
method generateImage*(self: AccessInterface, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.base.} =
|
method generateImage*(self: AccessInterface, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getCroppedProfileImage*(self: AccessInterface): string {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method setDisplayName*(self: AccessInterface, value: string) {.base.} =
|
method setDisplayName*(self: AccessInterface, value: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
@ -49,6 +54,15 @@ method setPassword*(self: AccessInterface, value: string) {.base.} =
|
||||||
method getPassword*(self: AccessInterface): string {.base.} =
|
method getPassword*(self: AccessInterface): string {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method setPin*(self: AccessInterface, value: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method setPuk*(self: AccessInterface, value: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getPin*(self: AccessInterface): string {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} =
|
method getPasswordStrengthScore*(self: AccessInterface, password: string, userName: string): int {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
@ -73,12 +87,24 @@ method onNodeLogin*(self: AccessInterface, error: string) {.base.} =
|
||||||
method emitAccountLoginError*(self: AccessInterface, error: string) {.base.} =
|
method emitAccountLoginError*(self: AccessInterface, error: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method emitObtainingPasswordError*(self: AccessInterface, errorDescription: string) {.base.} =
|
method emitObtainingPasswordError*(self: AccessInterface, errorDescription: string, errorType: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method emitObtainingPasswordSuccess*(self: AccessInterface, password: string) {.base.} =
|
method emitObtainingPasswordSuccess*(self: AccessInterface, password: string) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onKeycardResponse*(self: AccessInterface, keycardFlowType: string, keycardEvent: KeycardEvent) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method checkRepeatedKeycardPinWhileTyping*(self: AccessInterface, pin: string): bool {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method getSeedPhrase*(self: AccessInterface): string {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method setKeycardData*(self: AccessInterface, value: string) {.base.} =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
# This way (using concepts) is used only for the modules managed by AppController
|
# This way (using concepts) is used only for the modules managed by AppController
|
||||||
type
|
type
|
||||||
DelegateInterface* = concept c
|
DelegateInterface* = concept c
|
||||||
|
|
|
@ -12,8 +12,10 @@ type
|
||||||
colorHash: color_hash_model.Model
|
colorHash: color_hash_model.Model
|
||||||
colorHashVariant: QVariant
|
colorHashVariant: QVariant
|
||||||
colorId: int
|
colorId: int
|
||||||
|
keycardPairing: string
|
||||||
|
|
||||||
proc initItem*(name, thumbnailImage, largeImage, keyUid: string, colorHash: seq[ColorHashSegment], colorId: int):
|
proc initItem*(name, thumbnailImage, largeImage, keyUid: string, colorHash: seq[ColorHashSegment], colorId: int,
|
||||||
|
keycardPairing: string):
|
||||||
Item =
|
Item =
|
||||||
result.name = name
|
result.name = name
|
||||||
result.thumbnailImage = thumbnailImage
|
result.thumbnailImage = thumbnailImage
|
||||||
|
@ -23,6 +25,7 @@ proc initItem*(name, thumbnailImage, largeImage, keyUid: string, colorHash: seq[
|
||||||
result.colorHash.setItems(map(colorHash, x => color_hash_item.initItem(x.len, x.colorIdx)))
|
result.colorHash.setItems(map(colorHash, x => color_hash_item.initItem(x.len, x.colorIdx)))
|
||||||
result.colorHashVariant = newQVariant(result.colorHash)
|
result.colorHashVariant = newQVariant(result.colorHash)
|
||||||
result.colorId = colorId
|
result.colorId = colorId
|
||||||
|
result.keycardPairing = keycardPairing
|
||||||
|
|
||||||
proc getName*(self: Item): string =
|
proc getName*(self: Item): string =
|
||||||
return self.name
|
return self.name
|
||||||
|
@ -44,3 +47,9 @@ proc getColorHashVariant*(self: Item): QVariant =
|
||||||
|
|
||||||
proc getColorId*(self: Item): int =
|
proc getColorId*(self: Item): int =
|
||||||
return self.colorId
|
return self.colorId
|
||||||
|
|
||||||
|
proc getKeycardPairing*(self: Item): string =
|
||||||
|
return self.keycardPairing
|
||||||
|
|
||||||
|
proc getKeycardCreatedAccount*(self: Item): bool =
|
||||||
|
return self.keycardPairing.len > 0
|
|
@ -10,6 +10,8 @@ type
|
||||||
KeyUid
|
KeyUid
|
||||||
ColorHash
|
ColorHash
|
||||||
ColorId
|
ColorId
|
||||||
|
KeycardPairing
|
||||||
|
KeycardCreatedAccount
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type
|
type
|
||||||
|
@ -37,7 +39,9 @@ QtObject:
|
||||||
ModelRole.LargeImage.int:"largeImage",
|
ModelRole.LargeImage.int:"largeImage",
|
||||||
ModelRole.KeyUid.int:"keyUid",
|
ModelRole.KeyUid.int:"keyUid",
|
||||||
ModelRole.ColorHash.int:"colorHash",
|
ModelRole.ColorHash.int:"colorHash",
|
||||||
ModelRole.ColorId.int:"colorId"
|
ModelRole.ColorId.int:"colorId",
|
||||||
|
ModelRole.KeycardPairing.int:"keycardPairing",
|
||||||
|
ModelRole.KeycardCreatedAccount.int:"keycardCreatedAccount"
|
||||||
}.toTable
|
}.toTable
|
||||||
|
|
||||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||||
|
@ -63,6 +67,10 @@ QtObject:
|
||||||
result = newQVariant(item.getColorHash())
|
result = newQVariant(item.getColorHash())
|
||||||
of ModelRole.ColorId:
|
of ModelRole.ColorId:
|
||||||
result = newQVariant(item.getColorId())
|
result = newQVariant(item.getColorId())
|
||||||
|
of ModelRole.KeycardPairing:
|
||||||
|
result = newQVariant(item.getKeycardPairing())
|
||||||
|
of ModelRole.KeycardCreatedAccount:
|
||||||
|
result = newQVariant(item.getKeycardCreatedAccount())
|
||||||
|
|
||||||
proc setItems*(self: Model, items: seq[Item]) =
|
proc setItems*(self: Model, items: seq[Item]) =
|
||||||
self.beginResetModel()
|
self.beginResetModel()
|
||||||
|
|
|
@ -2,7 +2,7 @@ import NimQml, chronicles
|
||||||
|
|
||||||
import io_interface
|
import io_interface
|
||||||
import view, controller
|
import view, controller
|
||||||
import internal/[state, notification_state, welcome_state, login_state]
|
import internal/[state, state_factory]
|
||||||
import models/generated_account_item as gen_acc_item
|
import models/generated_account_item as gen_acc_item
|
||||||
import models/login_account_item as login_acc_item
|
import models/login_account_item as login_acc_item
|
||||||
import ../../global/global_singleton
|
import ../../global/global_singleton
|
||||||
|
@ -12,6 +12,7 @@ import ../../../app_service/service/keychain/service as keychain_service
|
||||||
import ../../../app_service/service/accounts/service as accounts_service
|
import ../../../app_service/service/accounts/service as accounts_service
|
||||||
import ../../../app_service/service/general/service as general_service
|
import ../../../app_service/service/general/service as general_service
|
||||||
import ../../../app_service/service/profile/service as profile_service
|
import ../../../app_service/service/profile/service as profile_service
|
||||||
|
import ../../../app_service/service/keycard/service as keycard_service
|
||||||
|
|
||||||
export io_interface
|
export io_interface
|
||||||
|
|
||||||
|
@ -30,14 +31,15 @@ proc newModule*[T](delegate: T,
|
||||||
keychainService: keychain_service.Service,
|
keychainService: keychain_service.Service,
|
||||||
accountsService: accounts_service.Service,
|
accountsService: accounts_service.Service,
|
||||||
generalService: general_service.Service,
|
generalService: general_service.Service,
|
||||||
profileService: profile_service.Service):
|
profileService: profile_service.Service,
|
||||||
|
keycardService: keycard_service.Service):
|
||||||
Module[T] =
|
Module[T] =
|
||||||
result = Module[T]()
|
result = Module[T]()
|
||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.view = view.newView(result)
|
result.view = view.newView(result)
|
||||||
result.viewVariant = newQVariant(result.view)
|
result.viewVariant = newQVariant(result.view)
|
||||||
result.controller = controller.newController(result, events, generalService, accountsService, keychainService,
|
result.controller = controller.newController(result, events, generalService, accountsService, keychainService,
|
||||||
profileService)
|
profileService, keycardService)
|
||||||
|
|
||||||
method delete*[T](self: Module[T]) =
|
method delete*[T](self: Module[T]) =
|
||||||
self.view.delete
|
self.view.delete
|
||||||
|
@ -69,19 +71,21 @@ method load*[T](self: Module[T]) =
|
||||||
self.view.setCurrentStartupState(newWelcomeState(FlowType.General, nil))
|
self.view.setCurrentStartupState(newWelcomeState(FlowType.General, nil))
|
||||||
else:
|
else:
|
||||||
let openedAccounts = self.controller.getOpenedAccounts()
|
let openedAccounts = self.controller.getOpenedAccounts()
|
||||||
if(openedAccounts.len > 0):
|
var items: seq[login_acc_item.Item]
|
||||||
var items: seq[login_acc_item.Item]
|
for acc in openedAccounts:
|
||||||
for acc in openedAccounts:
|
var thumbnailImage: string
|
||||||
var thumbnailImage: string
|
var largeImage: string
|
||||||
var largeImage: string
|
self.extractImages(acc, thumbnailImage, largeImage)
|
||||||
self.extractImages(acc, thumbnailImage, largeImage)
|
items.add(login_acc_item.initItem(acc.name, thumbnailImage, largeImage, acc.keyUid, acc.colorHash, acc.colorId,
|
||||||
items.add(login_acc_item.initItem(acc.name, thumbnailImage, largeImage, acc.keyUid, acc.colorHash, acc.colorId))
|
acc.keycardPairing))
|
||||||
self.view.setLoginAccountsModelItems(items)
|
self.view.setLoginAccountsModelItems(items)
|
||||||
# set the first account as slected one
|
# set the first account as slected one
|
||||||
if items.len == 0:
|
if items.len == 0:
|
||||||
error "cannot run the app in login flow cause list of login accounts is empty"
|
# we should never be here, since else block of `if (shouldStartWithOnboardingScreen)`
|
||||||
quit() # quit the app
|
# ensures that `openedAccounts` is not empty array
|
||||||
self.setSelectedLoginAccount(items[0])
|
error "cannot run the app in login flow cause list of login accounts is empty"
|
||||||
|
quit() # quit the app
|
||||||
|
self.setSelectedLoginAccount(items[0])
|
||||||
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil))
|
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil))
|
||||||
self.delegate.startupDidLoad()
|
self.delegate.startupDidLoad()
|
||||||
|
|
||||||
|
@ -96,35 +100,54 @@ method emitLogOut*[T](self: Module[T]) =
|
||||||
|
|
||||||
method onBackActionClicked*[T](self: Module[T]) =
|
method onBackActionClicked*[T](self: Module[T]) =
|
||||||
let currStateObj = self.view.currentStartupStateObj()
|
let currStateObj = self.view.currentStartupStateObj()
|
||||||
if not currStateObj.isNil:
|
if currStateObj.isNil:
|
||||||
currStateObj.executeBackCommand(self.controller)
|
error "cannot resolve current state"
|
||||||
let backState = currStateObj.getBackState()
|
return
|
||||||
self.view.setCurrentStartupState(backState)
|
debug "back_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
|
||||||
currStateObj.delete()
|
currStateObj.executeBackCommand(self.controller)
|
||||||
|
let backState = currStateObj.getBackState()
|
||||||
|
self.view.setCurrentStartupState(backState)
|
||||||
|
debug "back_action - set state", setCurrFlow=backState.flowType(), newCurrState=backState.stateType()
|
||||||
|
currStateObj.delete()
|
||||||
|
|
||||||
method onPrimaryActionClicked*[T](self: Module[T]) =
|
method onPrimaryActionClicked*[T](self: Module[T]) =
|
||||||
let currStateObj = self.view.currentStartupStateObj()
|
let currStateObj = self.view.currentStartupStateObj()
|
||||||
if not currStateObj.isNil:
|
if currStateObj.isNil:
|
||||||
currStateObj.executePrimaryCommand(self.controller)
|
error "cannot resolve current state"
|
||||||
if currStateObj.moveToNextPrimaryState():
|
return
|
||||||
let nextState = currStateObj.getNextPrimaryState()
|
debug "primary_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
|
||||||
self.view.setCurrentStartupState(nextState)
|
currStateObj.executePrimaryCommand(self.controller)
|
||||||
|
let nextState = currStateObj.getNextPrimaryState(self.controller)
|
||||||
|
if nextState.isNil:
|
||||||
|
return
|
||||||
|
self.view.setCurrentStartupState(nextState)
|
||||||
|
debug "primary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
|
||||||
|
|
||||||
method onSecondaryActionClicked*[T](self: Module[T]) =
|
method onSecondaryActionClicked*[T](self: Module[T]) =
|
||||||
let currStateObj = self.view.currentStartupStateObj()
|
let currStateObj = self.view.currentStartupStateObj()
|
||||||
if not currStateObj.isNil:
|
if currStateObj.isNil:
|
||||||
currStateObj.executeSecondaryCommand(self.controller)
|
error "cannot resolve current state"
|
||||||
if currStateObj.moveToNextSecondaryState():
|
return
|
||||||
let nextState = currStateObj.getNextSecondaryState()
|
debug "secondary_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
|
||||||
self.view.setCurrentStartupState(nextState)
|
currStateObj.executeSecondaryCommand(self.controller)
|
||||||
|
let nextState = currStateObj.getNextSecondaryState(self.controller)
|
||||||
|
if nextState.isNil:
|
||||||
|
return
|
||||||
|
self.view.setCurrentStartupState(nextState)
|
||||||
|
debug "secondary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
|
||||||
|
|
||||||
method onTertiaryActionClicked*[T](self: Module[T]) =
|
method onTertiaryActionClicked*[T](self: Module[T]) =
|
||||||
let currStateObj = self.view.currentStartupStateObj()
|
let currStateObj = self.view.currentStartupStateObj()
|
||||||
if not currStateObj.isNil:
|
if currStateObj.isNil:
|
||||||
currStateObj.executeTertiaryCommand(self.controller)
|
error "cannot resolve current state"
|
||||||
if currStateObj.moveToNextTertiaryState():
|
return
|
||||||
let nextState = currStateObj.getNextTertiaryState()
|
debug "tertiary_action", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
|
||||||
self.view.setCurrentStartupState(nextState)
|
currStateObj.executeTertiaryCommand(self.controller)
|
||||||
|
let nextState = currStateObj.getNextTertiaryState(self.controller)
|
||||||
|
if nextState.isNil:
|
||||||
|
return
|
||||||
|
self.view.setCurrentStartupState(nextState)
|
||||||
|
debug "tertiary_action - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
|
||||||
|
|
||||||
method getImportedAccount*[T](self: Module[T]): GeneratedAccountDto =
|
method getImportedAccount*[T](self: Module[T]): GeneratedAccountDto =
|
||||||
return self.controller.getImportedAccount()
|
return self.controller.getImportedAccount()
|
||||||
|
@ -132,6 +155,9 @@ method getImportedAccount*[T](self: Module[T]): GeneratedAccountDto =
|
||||||
method generateImage*[T](self: Module[T], imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
method generateImage*[T](self: Module[T], imageUrl: string, aX: int, aY: int, bX: int, bY: int): string =
|
||||||
return self.controller.generateImage(imageUrl, aX, aY, bX, bY)
|
return self.controller.generateImage(imageUrl, aX, aY, bX, bY)
|
||||||
|
|
||||||
|
method getCroppedProfileImage*[T](self: Module[T]): string =
|
||||||
|
return self.controller.getCroppedProfileImage()
|
||||||
|
|
||||||
method setDisplayName*[T](self: Module[T], value: string) =
|
method setDisplayName*[T](self: Module[T], value: string) =
|
||||||
self.controller.setDisplayName(value)
|
self.controller.setDisplayName(value)
|
||||||
|
|
||||||
|
@ -144,6 +170,15 @@ method setPassword*[T](self: Module[T], value: string) =
|
||||||
method getPassword*[T](self: Module[T]): string =
|
method getPassword*[T](self: Module[T]): string =
|
||||||
return self.controller.getPassword()
|
return self.controller.getPassword()
|
||||||
|
|
||||||
|
method setPin*[T](self: Module[T], value: string) =
|
||||||
|
self.controller.setPin(value)
|
||||||
|
|
||||||
|
method setPuk*[T](self: Module[T], value: string) =
|
||||||
|
self.controller.setPuk(value)
|
||||||
|
|
||||||
|
method getPin*[T](self: Module[T]): string =
|
||||||
|
return self.controller.getPin()
|
||||||
|
|
||||||
method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int =
|
method getPasswordStrengthScore*[T](self: Module[T], password, userName: string): int =
|
||||||
return self.controller.getPasswordStrengthScore(password, userName)
|
return self.controller.getPasswordStrengthScore(password, userName)
|
||||||
|
|
||||||
|
@ -161,13 +196,18 @@ method importAccountSuccess*[T](self: Module[T]) =
|
||||||
|
|
||||||
method setSelectedLoginAccount*[T](self: Module[T], item: login_acc_item.Item) =
|
method setSelectedLoginAccount*[T](self: Module[T], item: login_acc_item.Item) =
|
||||||
self.controller.setSelectedLoginAccountKeyUid(item.getKeyUid())
|
self.controller.setSelectedLoginAccountKeyUid(item.getKeyUid())
|
||||||
|
if item.getKeycardCreatedAccount():
|
||||||
|
self.view.setCurrentStartupState(newLoginState(FlowType.AppLogin, nil)) # nim garbage collector will handle all abandoned state objects
|
||||||
|
self.controller.runLoginFlow()
|
||||||
|
else:
|
||||||
|
self.controller.tryToObtainDataFromKeychain()
|
||||||
self.view.setSelectedLoginAccount(item)
|
self.view.setSelectedLoginAccount(item)
|
||||||
|
|
||||||
method emitAccountLoginError*[T](self: Module[T], error: string) =
|
method emitAccountLoginError*[T](self: Module[T], error: string) =
|
||||||
self.view.emitAccountLoginError(error)
|
self.view.emitAccountLoginError(error)
|
||||||
|
|
||||||
method emitObtainingPasswordError*[T](self: Module[T], errorDescription: string) =
|
method emitObtainingPasswordError*[T](self: Module[T], errorDescription: string, errorType: string) =
|
||||||
self.view.emitObtainingPasswordError(errorDescription)
|
self.view.emitObtainingPasswordError(errorDescription, errorType)
|
||||||
|
|
||||||
method emitObtainingPasswordSuccess*[T](self: Module[T], password: string) =
|
method emitObtainingPasswordSuccess*[T](self: Module[T], password: string) =
|
||||||
self.view.emitObtainingPasswordSuccess(password)
|
self.view.emitObtainingPasswordSuccess(password)
|
||||||
|
@ -175,7 +215,7 @@ method emitObtainingPasswordSuccess*[T](self: Module[T], password: string) =
|
||||||
method onNodeLogin*[T](self: Module[T], error: string) =
|
method onNodeLogin*[T](self: Module[T], error: string) =
|
||||||
let currStateObj = self.view.currentStartupStateObj()
|
let currStateObj = self.view.currentStartupStateObj()
|
||||||
if currStateObj.isNil:
|
if currStateObj.isNil:
|
||||||
error "error: cannot determine current startup state"
|
error "cannot determine current startup state"
|
||||||
quit() # quit the app
|
quit() # quit the app
|
||||||
|
|
||||||
if error.len == 0:
|
if error.len == 0:
|
||||||
|
@ -187,4 +227,37 @@ method onNodeLogin*[T](self: Module[T], error: string) =
|
||||||
self.emitAccountLoginError(error)
|
self.emitAccountLoginError(error)
|
||||||
else:
|
else:
|
||||||
self.setupAccountError(error)
|
self.setupAccountError(error)
|
||||||
error "error: ", methodName="onNodeLogin", errDesription =error
|
error "login error", methodName="onNodeLogin", errDesription =error
|
||||||
|
|
||||||
|
method onKeycardResponse*[T](self: Module[T], keycardFlowType: string, keycardEvent: KeycardEvent) =
|
||||||
|
let currStateObj = self.view.currentStartupStateObj()
|
||||||
|
if currStateObj.isNil:
|
||||||
|
error "cannot resolve current state"
|
||||||
|
return
|
||||||
|
debug "on_keycard_response", currFlow=currStateObj.flowType(), currState=currStateObj.stateType()
|
||||||
|
let nextState = currStateObj.resolveKeycardNextState(keycardFlowType, keycardEvent, self.controller)
|
||||||
|
if nextState.isNil:
|
||||||
|
return
|
||||||
|
self.view.setCurrentStartupState(nextState)
|
||||||
|
debug "on_keycard_response - set state", setCurrFlow=nextState.flowType(), setCurrState=nextState.stateType()
|
||||||
|
|
||||||
|
method checkRepeatedKeycardPinWhileTyping*[T](self: Module[T], pin: string): bool =
|
||||||
|
self.controller.setPinMatch(false)
|
||||||
|
let storedPin = self.controller.getPin()
|
||||||
|
if pin.len > storedPin.len:
|
||||||
|
return false
|
||||||
|
elif pin.len < storedPin.len:
|
||||||
|
for i in 0 ..< pin.len:
|
||||||
|
if pin[i] != storedPin[i]:
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
else:
|
||||||
|
let match = pin == storedPin
|
||||||
|
self.controller.setPinMatch(match)
|
||||||
|
return match
|
||||||
|
|
||||||
|
method getSeedPhrase*[T](self: Module[T]): string =
|
||||||
|
return self.controller.getSeedPhrase()
|
||||||
|
|
||||||
|
method setKeycardData*[T](self: Module[T], value: string) =
|
||||||
|
self.view.setKeycardData(value)
|
|
@ -26,30 +26,35 @@ QtObject:
|
||||||
|
|
||||||
proc getKeyUid(self: SelectedLoginAccount): string {.slot.} =
|
proc getKeyUid(self: SelectedLoginAccount): string {.slot.} =
|
||||||
return self.item.getKeyUid()
|
return self.item.getKeyUid()
|
||||||
|
|
||||||
QtProperty[string] keyUid:
|
QtProperty[string] keyUid:
|
||||||
read = getKeyUid
|
read = getKeyUid
|
||||||
|
|
||||||
|
proc getKeycardPairing(self: SelectedLoginAccount): string {.slot.} =
|
||||||
|
return self.item.getKeycardPairing()
|
||||||
|
QtProperty[string] keycardPairing:
|
||||||
|
read = getKeycardPairing
|
||||||
|
|
||||||
|
proc getKeycardCreatedAccount(self: SelectedLoginAccount): bool {.slot.} =
|
||||||
|
return self.item.getKeycardPairing().len > 0
|
||||||
|
QtProperty[bool] keycardCreatedAccount:
|
||||||
|
read = getKeycardCreatedAccount
|
||||||
|
|
||||||
proc getColorHash(self: SelectedLoginAccount): QVariant {.slot.} =
|
proc getColorHash(self: SelectedLoginAccount): QVariant {.slot.} =
|
||||||
return self.item.getColorHashVariant()
|
return self.item.getColorHashVariant()
|
||||||
|
|
||||||
QtProperty[QVariant] colorHash:
|
QtProperty[QVariant] colorHash:
|
||||||
read = getColorHash
|
read = getColorHash
|
||||||
|
|
||||||
proc getColorId(self: SelectedLoginAccount): int {.slot.} =
|
proc getColorId(self: SelectedLoginAccount): int {.slot.} =
|
||||||
return self.item.getColorId()
|
return self.item.getColorId()
|
||||||
|
|
||||||
QtProperty[int] colorId:
|
QtProperty[int] colorId:
|
||||||
read = getColorId
|
read = getColorId
|
||||||
|
|
||||||
proc getThumbnailImage(self: SelectedLoginAccount): string {.slot.} =
|
proc getThumbnailImage(self: SelectedLoginAccount): string {.slot.} =
|
||||||
return self.item.getThumbnailImage()
|
return self.item.getThumbnailImage()
|
||||||
|
|
||||||
QtProperty[string] thumbnailImage:
|
QtProperty[string] thumbnailImage:
|
||||||
read = getThumbnailImage
|
read = getThumbnailImage
|
||||||
|
|
||||||
proc getLargeImage(self: SelectedLoginAccount): string {.slot.} =
|
proc getLargeImage(self: SelectedLoginAccount): string {.slot.} =
|
||||||
return self.item.getLargeImage()
|
return self.item.getLargeImage()
|
||||||
|
|
||||||
QtProperty[string] largeImage:
|
QtProperty[string] largeImage:
|
||||||
read = getLargeImage
|
read = getLargeImage
|
|
@ -26,6 +26,7 @@ QtObject:
|
||||||
loginAccountsModel: login_acc_model.Model
|
loginAccountsModel: login_acc_model.Model
|
||||||
loginAccountsModelVariant: QVariant
|
loginAccountsModelVariant: QVariant
|
||||||
appState: AppState
|
appState: AppState
|
||||||
|
keycardData: string # used to temporary store the data coming from keycard, depends on current state different data may be stored
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.currentStartupStateVariant.delete
|
self.currentStartupStateVariant.delete
|
||||||
|
@ -133,7 +134,10 @@ QtObject:
|
||||||
notify = importedAccountChanged
|
notify = importedAccountChanged
|
||||||
|
|
||||||
proc generateImage*(self: View, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.slot.} =
|
proc generateImage*(self: View, imageUrl: string, aX: int, aY: int, bX: int, bY: int): string {.slot.} =
|
||||||
self.delegate.generateImage(imageUrl, aX, aY, bX, bY)
|
return self.delegate.generateImage(imageUrl, aX, aY, bX, bY)
|
||||||
|
|
||||||
|
proc getCroppedProfileImage*(self: View): string {.slot.} =
|
||||||
|
return self.delegate.getCroppedProfileImage()
|
||||||
|
|
||||||
proc setDisplayName*(self: View, value: string) {.slot.} =
|
proc setDisplayName*(self: View, value: string) {.slot.} =
|
||||||
self.delegate.setDisplayName(value)
|
self.delegate.setDisplayName(value)
|
||||||
|
@ -147,6 +151,15 @@ QtObject:
|
||||||
proc getPassword*(self: View): string {.slot.} =
|
proc getPassword*(self: View): string {.slot.} =
|
||||||
return self.delegate.getPassword()
|
return self.delegate.getPassword()
|
||||||
|
|
||||||
|
proc setPin*(self: View, value: string) {.slot.} =
|
||||||
|
self.delegate.setPin(value)
|
||||||
|
|
||||||
|
proc getPin*(self: View): string {.slot.} =
|
||||||
|
return self.delegate.getPin()
|
||||||
|
|
||||||
|
proc setPuk*(self: View, value: string) {.slot.} =
|
||||||
|
self.delegate.setPuk(value)
|
||||||
|
|
||||||
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
|
proc getPasswordStrengthScore*(self: View, password: string, userName: string): int {.slot.} =
|
||||||
return self.delegate.getPasswordStrengthScore(password, userName)
|
return self.delegate.getPasswordStrengthScore(password, userName)
|
||||||
|
|
||||||
|
@ -194,10 +207,28 @@ QtObject:
|
||||||
proc emitAccountLoginError*(self: View, error: string) =
|
proc emitAccountLoginError*(self: View, error: string) =
|
||||||
self.accountLoginError(error)
|
self.accountLoginError(error)
|
||||||
|
|
||||||
proc obtainingPasswordError*(self:View, errorDescription: string) {.signal.}
|
proc obtainingPasswordError*(self:View, errorDescription: string, errorType: string) {.signal.}
|
||||||
proc emitObtainingPasswordError*(self: View, errorDescription: string) =
|
proc emitObtainingPasswordError*(self: View, errorDescription: string, errorType: string) =
|
||||||
self.obtainingPasswordError(errorDescription)
|
self.obtainingPasswordError(errorDescription, errorType)
|
||||||
|
|
||||||
proc obtainingPasswordSuccess*(self:View, password: string) {.signal.}
|
proc obtainingPasswordSuccess*(self:View, password: string) {.signal.}
|
||||||
proc emitObtainingPasswordSuccess*(self: View, password: string) =
|
proc emitObtainingPasswordSuccess*(self: View, password: string) =
|
||||||
self.obtainingPasswordSuccess(password)
|
self.obtainingPasswordSuccess(password)
|
||||||
|
|
||||||
|
proc checkRepeatedKeycardPinWhileTyping*(self: View, pin: string): bool {.slot.} =
|
||||||
|
return self.delegate.checkRepeatedKeycardPinWhileTyping(pin)
|
||||||
|
|
||||||
|
proc getSeedPhrase*(self: View): string {.slot.} =
|
||||||
|
return self.delegate.getSeedPhrase()
|
||||||
|
|
||||||
|
proc keycardDataChanged*(self: View) {.signal.}
|
||||||
|
proc setKeycardData*(self: View, value: string) =
|
||||||
|
if self.keycardData == value:
|
||||||
|
return
|
||||||
|
self.keycardData = value
|
||||||
|
self.keycardDataChanged()
|
||||||
|
proc getKeycardData*(self: View): string {.slot.} =
|
||||||
|
return self.keycardData
|
||||||
|
QtProperty[string] keycardData:
|
||||||
|
read = getKeycardData
|
||||||
|
notify = keycardDataChanged
|
|
@ -1,9 +1,10 @@
|
||||||
import os, json, sequtils, strutils, uuids
|
import os, json, sequtils, strutils, uuids, times
|
||||||
import json_serialization, chronicles
|
import json_serialization, chronicles
|
||||||
|
|
||||||
import ../../../app/global/global_singleton
|
import ../../../app/global/global_singleton
|
||||||
import ./dto/accounts as dto_accounts
|
import ./dto/accounts as dto_accounts
|
||||||
import ./dto/generated_accounts as dto_generated_accounts
|
import ./dto/generated_accounts as dto_generated_accounts
|
||||||
|
from ../keycard/service import KeycardEvent, KeyDetails
|
||||||
import ../../../backend/general as status_general
|
import ../../../backend/general as status_general
|
||||||
import ../../../backend/core as status_core
|
import ../../../backend/core as status_core
|
||||||
|
|
||||||
|
@ -143,6 +144,25 @@ proc saveAccountAndLogin(self: Service, hashedPassword: string, account,
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "error: ", procName="saveAccountAndLogin", errName = e.name, errDesription = e.msg
|
error "error: ", procName="saveAccountAndLogin", errName = e.name, errDesription = e.msg
|
||||||
|
|
||||||
|
proc saveKeycardAccountAndLogin(self: Service, chatKey, password: string, account, subaccounts, settings,
|
||||||
|
config: JsonNode): AccountDto =
|
||||||
|
try:
|
||||||
|
let response = status_account.saveAccountAndLoginWithKeycard(chatKey, password, account, subaccounts, settings, config)
|
||||||
|
|
||||||
|
var error = "response doesn't contain \"error\""
|
||||||
|
if(response.result.contains("error")):
|
||||||
|
error = response.result["error"].getStr
|
||||||
|
if error == "":
|
||||||
|
debug "Account saved succesfully"
|
||||||
|
result = toAccountDto(account)
|
||||||
|
return
|
||||||
|
|
||||||
|
let err = "Error saving account and logging in via keycard : " & error
|
||||||
|
error "error: ", procName="saveKeycardAccountAndLogin", errDesription = err
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error "error: ", procName="saveKeycardAccountAndLogin", errName = e.name, errDesription = e.msg
|
||||||
|
|
||||||
proc prepareAccountJsonObject(self: Service, account: GeneratedAccountDto, displayName: string): JsonNode =
|
proc prepareAccountJsonObject(self: Service, account: GeneratedAccountDto, displayName: string): JsonNode =
|
||||||
result = %* {
|
result = %* {
|
||||||
"name": if displayName == "": account.alias else: displayName,
|
"name": if displayName == "": account.alias else: displayName,
|
||||||
|
@ -259,15 +279,32 @@ proc setLocalAccountSettingsFile(self: Service) =
|
||||||
if(defined(macosx) and self.getLoggedInAccount.isValid()):
|
if(defined(macosx) and self.getLoggedInAccount.isValid()):
|
||||||
singletonInstance.localAccountSettings.setFileName(self.getLoggedInAccount.name)
|
singletonInstance.localAccountSettings.setFileName(self.getLoggedInAccount.name)
|
||||||
|
|
||||||
proc setupAccount*(self: Service, accountId, password, displayName: string): string =
|
proc addKeycardDetails(self: Service, settingsJson: var JsonNode, accountData: var JsonNode) =
|
||||||
|
let keycardPairingJsonString = readFile(main_constants.KEYCARDPAIRINGDATAFILE)
|
||||||
|
let keycardPairingJsonObj = keycardPairingJsonString.parseJSON
|
||||||
|
let now = now().toTime().toUnix()
|
||||||
|
for instanceUid, kcDataObj in keycardPairingJsonObj:
|
||||||
|
if not settingsJson.isNil:
|
||||||
|
settingsJson["keycard-instance-uid"] = %* instanceUid
|
||||||
|
settingsJson["keycard-paired-on"] = %* now
|
||||||
|
settingsJson["keycard-pairing"] = kcDataObj{"key"}
|
||||||
|
if not accountData.isNil:
|
||||||
|
accountData["keycard-pairing"] = kcDataObj{"key"}
|
||||||
|
|
||||||
|
proc setupAccount*(self: Service, accountId, password, displayName: string, keycardUsage: bool): string =
|
||||||
try:
|
try:
|
||||||
let installationId = $genUUID()
|
let installationId = $genUUID()
|
||||||
let accountDataJson = self.getAccountDataForAccountId(accountId, displayName)
|
var accountDataJson = self.getAccountDataForAccountId(accountId, displayName)
|
||||||
|
|
||||||
|
var usedPassword = password
|
||||||
|
if password.len == 0:
|
||||||
|
# this means we're setting up an account using keycard
|
||||||
|
usedPassword = accountDataJson{"key-uid"}.getStr
|
||||||
|
|
||||||
self.setKeyStoreDir(accountDataJson{"key-uid"}.getStr)
|
self.setKeyStoreDir(accountDataJson{"key-uid"}.getStr)
|
||||||
|
|
||||||
let subaccountDataJson = self.getSubaccountDataForAccountId(accountId, displayName)
|
let subaccountDataJson = self.getSubaccountDataForAccountId(accountId, displayName)
|
||||||
let settingsJson = self.getAccountSettings(accountId, installationId, displayName)
|
var settingsJson = self.getAccountSettings(accountId, installationId, displayName)
|
||||||
let nodeConfigJson = self.getDefaultNodeConfig(installationId)
|
let nodeConfigJson = self.getDefaultNodeConfig(installationId)
|
||||||
|
|
||||||
if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
|
if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
|
||||||
|
@ -276,10 +313,13 @@ proc setupAccount*(self: Service, accountId, password, displayName: string): str
|
||||||
error "error: ", procName="setupAccount", errDesription = description
|
error "error: ", procName="setupAccount", errDesription = description
|
||||||
return description
|
return description
|
||||||
|
|
||||||
let hashedPassword = hashString(password)
|
let hashedPassword = hashString(usedPassword)
|
||||||
discard self.storeAccount(accountId, hashedPassword)
|
discard self.storeAccount(accountId, hashedPassword)
|
||||||
discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS)
|
discard self.storeDerivedAccounts(accountId, hashedPassword, PATHS)
|
||||||
|
|
||||||
|
if keycardUsage:
|
||||||
|
self.addKeycardDetails(settingsJson, accountDataJson)
|
||||||
|
|
||||||
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson,
|
self.loggedInAccount = self.saveAccountAndLogin(hashedPassword, accountDataJson,
|
||||||
subaccountDataJson, settingsJson, nodeConfigJson)
|
subaccountDataJson, settingsJson, nodeConfigJson)
|
||||||
self.setLocalAccountSettingsFile()
|
self.setLocalAccountSettingsFile()
|
||||||
|
@ -292,7 +332,83 @@ proc setupAccount*(self: Service, accountId, password, displayName: string): str
|
||||||
error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg
|
error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg
|
||||||
return e.msg
|
return e.msg
|
||||||
|
|
||||||
|
proc setupAccountKeycard*(self: Service, keycardData: KeycardEvent) =
|
||||||
|
try:
|
||||||
|
let installationId = $genUUID()
|
||||||
|
|
||||||
|
let alias = generateAliasFromPk(keycardData.whisperKey.publicKey)
|
||||||
|
var accountDataJson = %* {
|
||||||
|
"name": alias,
|
||||||
|
"address": keycardData.masterKey.address,
|
||||||
|
"key-uid": keycardData.keyUid
|
||||||
|
}
|
||||||
|
|
||||||
|
self.setKeyStoreDir(keycardData.keyUid)
|
||||||
|
let nodeConfigJson = self.getDefaultNodeConfig(installationId)
|
||||||
|
let subaccountDataJson = %* [
|
||||||
|
{
|
||||||
|
"public-key": keycardData.walletKey.publicKey,
|
||||||
|
"address": keycardData.walletKey.address,
|
||||||
|
"color": "#4360df",
|
||||||
|
"wallet": true,
|
||||||
|
"path": PATH_DEFAULT_WALLET,
|
||||||
|
"name": "Status account",
|
||||||
|
"derived-from": keycardData.masterKey.address,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"public-key": keycardData.whisperKey.publicKey,
|
||||||
|
"address": keycardData.whisperKey.address,
|
||||||
|
"name": alias,
|
||||||
|
"path": PATH_WHISPER,
|
||||||
|
"chat": true,
|
||||||
|
"derived-from": ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
var settingsJson = %* {
|
||||||
|
"key-uid": keycardData.keyUid,
|
||||||
|
"public-key": keycardData.whisperKey.publicKey,
|
||||||
|
"name": alias,
|
||||||
|
"display-name": "",
|
||||||
|
"address": keycardData.whisperKey.address,
|
||||||
|
"eip1581-address": keycardData.eip1581Key.address,
|
||||||
|
"dapps-address": keycardData.walletKey.address,
|
||||||
|
"wallet-root-address": keycardData.walletRootKey.address,
|
||||||
|
"preview-privacy?": true,
|
||||||
|
"signing-phrase": generateSigningPhrase(3),
|
||||||
|
"log-level": $LogLevel.INFO,
|
||||||
|
"latest-derived-path": 0,
|
||||||
|
"currency": "usd",
|
||||||
|
"networks/networks": @[],
|
||||||
|
"networks/current-network": "",
|
||||||
|
"wallet/visible-tokens": {},
|
||||||
|
"waku-enabled": true,
|
||||||
|
"appearance": 0,
|
||||||
|
"installation-id": installationId
|
||||||
|
}
|
||||||
|
|
||||||
|
self.addKeycardDetails(settingsJson, accountDataJson)
|
||||||
|
|
||||||
|
if(accountDataJson.isNil or subaccountDataJson.isNil or settingsJson.isNil or
|
||||||
|
nodeConfigJson.isNil):
|
||||||
|
let description = "at least one json object is not prepared well"
|
||||||
|
error "error: ", procName="setupAccountKeycard", errDesription = description
|
||||||
|
return
|
||||||
|
|
||||||
|
let hashedPassword = hashString(keycardData.keyUid) # using hashed keyUid as password
|
||||||
|
|
||||||
|
self.loggedInAccount = self.saveKeycardAccountAndLogin(keycardData.whisperKey.privateKey,
|
||||||
|
hashedPassword,
|
||||||
|
accountDataJson,
|
||||||
|
subaccountDataJson,
|
||||||
|
settingsJson,
|
||||||
|
nodeConfigJson)
|
||||||
|
except Exception as e:
|
||||||
|
error "error: ", procName="setupAccount", errName = e.name, errDesription = e.msg
|
||||||
|
|
||||||
proc importMnemonic*(self: Service, mnemonic: string): string =
|
proc importMnemonic*(self: Service, mnemonic: string): string =
|
||||||
|
if mnemonic.len == 0:
|
||||||
|
return "empty mnemonic"
|
||||||
try:
|
try:
|
||||||
let response = status_account.multiAccountImportMnemonic(mnemonic)
|
let response = status_account.multiAccountImportMnemonic(mnemonic)
|
||||||
self.importedAccount = toGeneratedAccountDto(response.result)
|
self.importedAccount = toGeneratedAccountDto(response.result)
|
||||||
|
@ -376,6 +492,35 @@ proc login*(self: Service, account: AccountDto, password: string): string =
|
||||||
error "error: ", procName="login", errName = e.name, errDesription = e.msg
|
error "error: ", procName="login", errName = e.name, errDesription = e.msg
|
||||||
return e.msg
|
return e.msg
|
||||||
|
|
||||||
|
proc loginAccountKeycard*(self: Service, keycardData: KeycardEvent): string =
|
||||||
|
try:
|
||||||
|
self.setKeyStoreDir(keycardData.keyUid)
|
||||||
|
|
||||||
|
let alias = generateAliasFromPk(keycardData.whisperKey.publicKey)
|
||||||
|
var accountDataJson = %* {
|
||||||
|
"name": alias,
|
||||||
|
"address": keycardData.masterKey.address,
|
||||||
|
"key-uid": keycardData.keyUid
|
||||||
|
}
|
||||||
|
var settingsJson: JsonNode
|
||||||
|
self.addKeycardDetails(settingsJson, accountDataJson)
|
||||||
|
|
||||||
|
let hashedPassword = hashString(keycardData.keyUid) # using hashed keyUid as password
|
||||||
|
|
||||||
|
let response = status_account.loginWithKeycard(keycardData.whisperKey.privateKey,
|
||||||
|
hashedPassword,
|
||||||
|
accountDataJson)
|
||||||
|
|
||||||
|
var error = "response doesn't contain \"error\""
|
||||||
|
if(response.result.contains("error")):
|
||||||
|
error = response.result["error"].getStr
|
||||||
|
if error == "":
|
||||||
|
debug "Account logged in succesfully"
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
error "error: ", procName="loginAccountKeycard", errName = e.name, errDesription = e.msg
|
||||||
|
return e.msg
|
||||||
|
|
||||||
proc verifyAccountPassword*(self: Service, account: string, password: string): bool =
|
proc verifyAccountPassword*(self: Service, account: string, password: string): bool =
|
||||||
try:
|
try:
|
||||||
let response = status_account.verifyAccountPassword(account, password, self.keyStoreDir)
|
let response = status_account.verifyAccountPassword(account, password, self.keyStoreDir)
|
||||||
|
|
|
@ -81,16 +81,6 @@ proc keys*(obj: JsonNode): seq[string] =
|
||||||
for k, _ in obj:
|
for k, _ in obj:
|
||||||
result.add k
|
result.add k
|
||||||
|
|
||||||
proc generateSigningPhrase*(count: int): string =
|
|
||||||
let now = getTime()
|
|
||||||
var rng = initRand(now.toUnix * 1000000000 + now.nanosecond)
|
|
||||||
var phrases: seq[string] = @[]
|
|
||||||
|
|
||||||
for i in 1..count:
|
|
||||||
phrases.add(rng.sample(signing_phrases.phrases))
|
|
||||||
|
|
||||||
result = phrases.join(" ")
|
|
||||||
|
|
||||||
proc handleRPCErrors*(response: string) =
|
proc handleRPCErrors*(response: string) =
|
||||||
let parsedReponse = parseJson(response)
|
let parsedReponse = parseJson(response)
|
||||||
if (parsedReponse.hasKey("error")):
|
if (parsedReponse.hasKey("error")):
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
#################################################
|
||||||
|
# Async timer
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
type
|
||||||
|
TimerTaskArg = ref object of QObjectTaskArg
|
||||||
|
timeoutInMilliseconds: int
|
||||||
|
|
||||||
|
const timerTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
|
let arg = decode[TimerTaskArg](argEncoded)
|
||||||
|
sleep(arg.timeoutInMilliseconds)
|
||||||
|
arg.finish("")
|
|
@ -0,0 +1,66 @@
|
||||||
|
const ResponseKeyType* = "type"
|
||||||
|
const ResponseKeyEvent* = "event"
|
||||||
|
|
||||||
|
const ResponseTypeValueKeycardFlowResult* = "keycard.flow-result"
|
||||||
|
const ResponseTypeValueInsertCard* = "keycard.action.insert-card"
|
||||||
|
const ResponseTypeValueCardInserted* = "keycard.action.card-inserted"
|
||||||
|
const ResponseTypeValueSwapCard* = "keycard.action.swap-card"
|
||||||
|
const ResponseTypeValueEnterPairing* = "keycard.action.enter-pairing"
|
||||||
|
const ResponseTypeValueEnterPIN* = "keycard.action.enter-pin"
|
||||||
|
const ResponseTypeValueEnterPUK* = "keycard.action.enter-puk"
|
||||||
|
const ResponseTypeValueEnterNewPair* = "keycard.action.enter-new-pairing"
|
||||||
|
const ResponseTypeValueEnterNewPIN* = "keycard.action.enter-new-pin"
|
||||||
|
const ResponseTypeValueEnterNewPUK* = "keycard.action.enter-new-puk"
|
||||||
|
const ResponseTypeValueEnterTXHash* = "keycard.action.enter-tx-hash"
|
||||||
|
const ResponseTypeValueEnterPath* = "keycard.action.enter-bip44-path"
|
||||||
|
const ResponseTypeValueEnterMnemonic* = "keycard.action.enter-mnemonic"
|
||||||
|
|
||||||
|
const ErrorKey* = "error"
|
||||||
|
const ErrorOK* = "ok"
|
||||||
|
const ErrorCancel* = "cancel"
|
||||||
|
const ErrorConnection* = "connection-error"
|
||||||
|
const ErrorUnknownFlow* = "unknown-flow"
|
||||||
|
const ErrorNotAKeycard* = "not-a-keycard"
|
||||||
|
const ErrorNoKeys* = "no-keys"
|
||||||
|
const ErrorHasKeys* = "has-keys"
|
||||||
|
const ErrorRequireInit* = "require-init"
|
||||||
|
const ErrorPairing* = "pairing"
|
||||||
|
const ErrorUnblocking* = "unblocking"
|
||||||
|
const ErrorSigning* = "signing"
|
||||||
|
const ErrorExporting* = "exporting"
|
||||||
|
const ErrorChanging* = "changing-credentials"
|
||||||
|
const ErrorLoadingKeys* = "loading-keys"
|
||||||
|
|
||||||
|
const RequestParamAppInfo* = "application-info"
|
||||||
|
const RequestParamInstanceUID* = "instance-uid"
|
||||||
|
const RequestParamFactoryReset* = "factory reset"
|
||||||
|
const RequestParamKeyUID* = "key-uid"
|
||||||
|
const RequestParamFreeSlots* = "free-pairing-slots"
|
||||||
|
const RequestParamPINRetries* = "pin-retries"
|
||||||
|
const RequestParamPUKRetries* = "puk-retries"
|
||||||
|
const RequestParamPairingPass* = "pairing-pass"
|
||||||
|
const RequestParamPaired* = "paired"
|
||||||
|
const RequestParamNewPairing* = "new-pairing-pass"
|
||||||
|
const RequestParamDefPairing* = "KeycardDefaultPairing"
|
||||||
|
const RequestParamPIN* = "pin"
|
||||||
|
const RequestParamNewPIN* = "new-pin"
|
||||||
|
const RequestParamPUK* = "puk"
|
||||||
|
const RequestParamNewPUK* = "new-puk"
|
||||||
|
const RequestParamMasterKey* = "master-key"
|
||||||
|
const RequestParamWalleRootKey* = "wallet-root-key"
|
||||||
|
const RequestParamWalletKey* = "wallet-key"
|
||||||
|
const RequestParamEIP1581Key* = "eip1581-key"
|
||||||
|
const RequestParamWhisperKey* = "whisper-key"
|
||||||
|
const RequestParamEncKey* = "encryption-key"
|
||||||
|
const RequestParamExportedKey* = "exported-key"
|
||||||
|
const RequestParamMnemonic* = "mnemonic"
|
||||||
|
const RequestParamMnemonicLen* = "mnemonic-length"
|
||||||
|
const RequestParamMnemonicIdxs* = "mnemonic-indexes"
|
||||||
|
const RequestParamTXHash* = "tx-hash"
|
||||||
|
const RequestParamBIP44Path* = "bip44-path"
|
||||||
|
const RequestParamTXSignature* = "tx-signature"
|
||||||
|
const RequestParamOverwrite* = "overwrite"
|
||||||
|
|
||||||
|
const RequestParamAddress* = "address"
|
||||||
|
const RequestParamPublicKey* = "publicKey"
|
||||||
|
const RequestParamPrivateKey* = "privateKey"
|
|
@ -0,0 +1,57 @@
|
||||||
|
type
|
||||||
|
KeyDetails* = object
|
||||||
|
address*: string
|
||||||
|
publicKey*: string
|
||||||
|
privateKey*: string
|
||||||
|
|
||||||
|
KeycardEvent* = object
|
||||||
|
error*: string
|
||||||
|
seedPhraseIndexes*: seq[int]
|
||||||
|
freePairingSlots*: int
|
||||||
|
keyUid*: string
|
||||||
|
pinRetries*: int
|
||||||
|
pukRetries*: int
|
||||||
|
eip1581Key*: KeyDetails
|
||||||
|
encryptionKey*: KeyDetails
|
||||||
|
masterKey*: KeyDetails
|
||||||
|
walletKey*: KeyDetails
|
||||||
|
walletRootKey*: KeyDetails
|
||||||
|
whisperKey*: KeyDetails
|
||||||
|
|
||||||
|
proc toKeyDetails(jsonObj: JsonNode): KeyDetails =
|
||||||
|
discard jsonObj.getProp(RequestParamAddress, result.address)
|
||||||
|
discard jsonObj.getProp(RequestParamPrivateKey, result.privateKey)
|
||||||
|
if jsonObj.getProp(RequestParamPublicKey, result.publicKey):
|
||||||
|
result.publicKey = "0x" & result.publicKey
|
||||||
|
|
||||||
|
proc toKeycardEvent(jsonObj: JsonNode): KeycardEvent =
|
||||||
|
discard jsonObj.getProp(ErrorKey, result.error)
|
||||||
|
discard jsonObj.getProp(RequestParamFreeSlots, result.freePairingSlots)
|
||||||
|
discard jsonObj.getProp(RequestParamPINRetries, result.pinRetries)
|
||||||
|
discard jsonObj.getProp(RequestParamPUKRetries, result.pukRetries)
|
||||||
|
if jsonObj.getProp(RequestParamKeyUID, result.keyUid):
|
||||||
|
result.keyUid = "0x" & result.keyUid
|
||||||
|
|
||||||
|
var obj: JsonNode
|
||||||
|
if(jsonObj.getProp(RequestParamEIP1581Key, obj)):
|
||||||
|
result.eip1581Key = toKeyDetails(obj)
|
||||||
|
|
||||||
|
if(jsonObj.getProp(RequestParamEncKey, obj)):
|
||||||
|
result.encryptionKey = toKeyDetails(obj)
|
||||||
|
|
||||||
|
if(jsonObj.getProp(RequestParamMasterKey, obj)):
|
||||||
|
result.masterKey = toKeyDetails(obj)
|
||||||
|
|
||||||
|
if(jsonObj.getProp(RequestParamWalletKey, obj)):
|
||||||
|
result.walletKey = toKeyDetails(obj)
|
||||||
|
|
||||||
|
if(jsonObj.getProp(RequestParamWalleRootKey, obj)):
|
||||||
|
result.walletRootKey = toKeyDetails(obj)
|
||||||
|
|
||||||
|
if(jsonObj.getProp(RequestParamWhisperKey, obj)):
|
||||||
|
result.whisperKey = toKeyDetails(obj)
|
||||||
|
|
||||||
|
var indexesArr: JsonNode
|
||||||
|
if jsonObj.getProp(RequestParamMnemonicIdxs, indexesArr) and indexesArr.kind == JArray:
|
||||||
|
for ind in indexesArr:
|
||||||
|
result.seedPhraseIndexes.add(ind.getInt)
|
|
@ -0,0 +1,240 @@
|
||||||
|
import NimQml, json, os, chronicles, random
|
||||||
|
import keycard_go
|
||||||
|
import ../../../app/core/eventemitter
|
||||||
|
import ../../../app/core/tasks/[qt, threadpool]
|
||||||
|
import ../../../constants as status_const
|
||||||
|
|
||||||
|
import constants
|
||||||
|
|
||||||
|
type FlowType {.pure.} = enum
|
||||||
|
NoFlow = -1 # this type is added only for the desktop app purpose
|
||||||
|
GetAppInfo = 0 # enumeration of these flows should follow enumeration in the `status-keycard-go`
|
||||||
|
RecoverAccount
|
||||||
|
LoadAccount
|
||||||
|
Login
|
||||||
|
ExportPublic
|
||||||
|
Sign
|
||||||
|
ChangePIN
|
||||||
|
ChangePUK
|
||||||
|
ChangePairing
|
||||||
|
UnpairThis
|
||||||
|
UnpairOthers
|
||||||
|
DeleteAccountAndUnpair
|
||||||
|
|
||||||
|
const PINLengthForStatusApp* = 6
|
||||||
|
const PUKLengthForStatusApp* = 12
|
||||||
|
|
||||||
|
const SupportedMnemonicLength12* = 12
|
||||||
|
const SupportedMnemonicLength18* = 18
|
||||||
|
const SupportedMnemonicLength24* = 24
|
||||||
|
|
||||||
|
const MnemonicLengthForStatusApp = SupportedMnemonicLength12
|
||||||
|
const TimerIntervalInMilliseconds = 3 * 1000 # 3 seconds
|
||||||
|
|
||||||
|
const SignalKeycardResponse* = "keycardResponse"
|
||||||
|
|
||||||
|
logScope:
|
||||||
|
topics = "keycard-service"
|
||||||
|
|
||||||
|
include ../../common/json_utils
|
||||||
|
include ../../common/mnemonics
|
||||||
|
include internal
|
||||||
|
include async_tasks
|
||||||
|
|
||||||
|
type
|
||||||
|
KeycardArgs* = ref object of Args
|
||||||
|
flowType*: string
|
||||||
|
flowEvent*: KeycardEvent
|
||||||
|
|
||||||
|
QtObject:
|
||||||
|
type Service* = ref object of QObject
|
||||||
|
events: EventEmitter
|
||||||
|
threadpool: ThreadPool
|
||||||
|
closingApp: bool
|
||||||
|
currentFlow: FlowType
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Forward declaration section
|
||||||
|
proc runTimer(self: Service)
|
||||||
|
proc factoryReset*(self: Service)
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
|
||||||
|
proc setup(self: Service) =
|
||||||
|
self.QObject.setup
|
||||||
|
|
||||||
|
proc delete*(self: Service) =
|
||||||
|
self.closingApp = true
|
||||||
|
self.QObject.delete
|
||||||
|
|
||||||
|
proc newService*(events: EventEmitter, threadpool: ThreadPool): Service =
|
||||||
|
new(result)
|
||||||
|
result.setup()
|
||||||
|
result.events = events
|
||||||
|
result.threadpool = threadpool
|
||||||
|
result.closingApp = false
|
||||||
|
result.currentFlow = FlowType.NoFlow
|
||||||
|
|
||||||
|
proc init*(self: Service) =
|
||||||
|
debug "init keycard using ", pairingsJson=status_const.KEYCARDPAIRINGDATAFILE
|
||||||
|
let initResp = keycard_go.keycardInitFlow(status_const.KEYCARDPAIRINGDATAFILE)
|
||||||
|
debug "initialization response: ", initResp
|
||||||
|
|
||||||
|
proc processSignal(self: Service, signal: string) =
|
||||||
|
var jsonSignal: JsonNode
|
||||||
|
try:
|
||||||
|
jsonSignal = signal.parseJson
|
||||||
|
except:
|
||||||
|
error "Invalid signal received", data = signal
|
||||||
|
return
|
||||||
|
|
||||||
|
debug "keycard_signal", response=signal
|
||||||
|
|
||||||
|
var typeObj, eventObj: JsonNode
|
||||||
|
if(not jsonSignal.getProp(ResponseKeyType, typeObj) or
|
||||||
|
not jsonSignal.getProp(ResponseKeyEvent, eventObj)):
|
||||||
|
return
|
||||||
|
|
||||||
|
let flowType = typeObj.getStr
|
||||||
|
let flowEvent = toKeycardEvent(eventObj)
|
||||||
|
self.events.emit(SignalKeycardResponse, KeycardArgs(flowType: flowType, flowEvent: flowEvent))
|
||||||
|
|
||||||
|
proc receiveKeycardSignal(self: Service, signal: string) {.slot.} =
|
||||||
|
self.processSignal(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 startFlow(self: Service, payload: JsonNode) =
|
||||||
|
let response = keycard_go.keycardStartFlow(self.currentFlow.int, $payload)
|
||||||
|
debug "keycardStartFlow", flowType=self.currentFlow.int, payload=payload, response=response
|
||||||
|
|
||||||
|
proc resumeFlow(self: Service, payload: JsonNode) =
|
||||||
|
let response = keycard_go.keycardResumeFlow($payload)
|
||||||
|
debug "keycardResumeFlow", flowType=self.currentFlow.int, payload=payload, response=response
|
||||||
|
|
||||||
|
proc cancelCurrentFlow*(self: Service) =
|
||||||
|
let response = keycard_go.keycardCancelFlow()
|
||||||
|
self.currentFlow = FlowType.NoFlow
|
||||||
|
debug "keycardCancelFlow", flowType=self.currentFlow.int, response=response
|
||||||
|
|
||||||
|
proc generateRandomPUK*(self: Service): string =
|
||||||
|
for i in 0 ..< PUKLengthForStatusApp:
|
||||||
|
result = result & $rand(0 .. 9)
|
||||||
|
|
||||||
|
proc onTimeout(self: Service, response: string) {.slot.} =
|
||||||
|
if(self.closingApp or self.currentFlow == FlowType.NoFlow):
|
||||||
|
return
|
||||||
|
debug "onTimeout, about to start flow: ", flowType=self.currentFlow
|
||||||
|
self.startFlow(%* { })
|
||||||
|
|
||||||
|
proc runTimer(self: Service) =
|
||||||
|
if(self.closingApp or self.currentFlow == FlowType.NoFlow):
|
||||||
|
return
|
||||||
|
|
||||||
|
let arg = TimerTaskArg(
|
||||||
|
tptr: cast[ByteAddress](timerTask),
|
||||||
|
vptr: cast[ByteAddress](self.vptr),
|
||||||
|
slot: "onTimeout",
|
||||||
|
timeoutInMilliseconds: TimerIntervalInMilliseconds
|
||||||
|
)
|
||||||
|
self.threadpool.start(arg)
|
||||||
|
|
||||||
|
proc startLoadAccountFlow*(self: Service, factoryReset: bool) =
|
||||||
|
var payload = %* { }
|
||||||
|
if factoryReset:
|
||||||
|
payload[RequestParamFactoryReset] = %* factoryReset
|
||||||
|
self.currentFlow = FlowType.LoadAccount
|
||||||
|
self.startFlow(payload)
|
||||||
|
|
||||||
|
proc startLoadAccountFlowWithSeedPhrase*(self: Service, seedPhraseLength: int, seedPhrase: string, factoryReset: bool) =
|
||||||
|
if seedPhrase.len == 0:
|
||||||
|
info "empty seed phrase provided"
|
||||||
|
return
|
||||||
|
var payload = %* {
|
||||||
|
RequestParamOverwrite: true,
|
||||||
|
RequestParamMnemonicLen: seedPhraseLength,
|
||||||
|
RequestParamNewPUK: self.generateRandomPUK(),
|
||||||
|
RequestParamMnemonic: seedPhrase
|
||||||
|
}
|
||||||
|
if factoryReset:
|
||||||
|
payload[RequestParamFactoryReset] = %* factoryReset
|
||||||
|
self.currentFlow = FlowType.LoadAccount
|
||||||
|
self.startFlow(payload)
|
||||||
|
|
||||||
|
proc startLoginFlow*(self: Service) =
|
||||||
|
let payload = %* { }
|
||||||
|
self.currentFlow = FlowType.Login
|
||||||
|
self.startFlow(payload)
|
||||||
|
|
||||||
|
proc startLoginFlowAutomatically*(self: Service, pin: string) =
|
||||||
|
let payload = %* {
|
||||||
|
RequestParamPIN: pin
|
||||||
|
}
|
||||||
|
self.currentFlow = FlowType.Login
|
||||||
|
self.startFlow(payload)
|
||||||
|
|
||||||
|
proc startRecoverAccountFlow*(self: Service) =
|
||||||
|
let payload = %* { }
|
||||||
|
self.currentFlow = FlowType.RecoverAccount
|
||||||
|
self.startFlow(payload)
|
||||||
|
|
||||||
|
proc storePin*(self: Service, pin: string, puk: string) =
|
||||||
|
if pin.len == 0:
|
||||||
|
info "empty pin provided"
|
||||||
|
return
|
||||||
|
var payload = %* {
|
||||||
|
RequestParamOverwrite: true,
|
||||||
|
RequestParamMnemonicLen: MnemonicLengthForStatusApp,
|
||||||
|
RequestParamPIN: pin,
|
||||||
|
RequestParamNewPIN: pin
|
||||||
|
}
|
||||||
|
if puk.len > 0:
|
||||||
|
payload[RequestParamNewPUK] = %* puk
|
||||||
|
self.resumeFlow(payload)
|
||||||
|
|
||||||
|
proc enterPin*(self: Service, pin: string) =
|
||||||
|
if pin.len == 0:
|
||||||
|
info "empty pin provided"
|
||||||
|
return
|
||||||
|
var payload = %* {
|
||||||
|
RequestParamPIN: pin
|
||||||
|
}
|
||||||
|
self.resumeFlow(payload)
|
||||||
|
|
||||||
|
proc enterPuk*(self: Service, puk: string) =
|
||||||
|
if puk.len == 0:
|
||||||
|
info "empty puk provided"
|
||||||
|
return
|
||||||
|
var payload = %* {
|
||||||
|
RequestParamPUK: puk
|
||||||
|
}
|
||||||
|
self.resumeFlow(payload)
|
||||||
|
|
||||||
|
proc storeSeedPhrase*(self: Service, seedPhraseLength: int, seedPhrase: string) =
|
||||||
|
if seedPhrase.len == 0:
|
||||||
|
info "empty seed phrase provided"
|
||||||
|
return
|
||||||
|
var payload = %* {
|
||||||
|
RequestParamOverwrite: true,
|
||||||
|
RequestParamMnemonicLen: seedPhraseLength,
|
||||||
|
RequestParamNewPUK: self.generateRandomPUK(),
|
||||||
|
RequestParamMnemonic: seedPhrase
|
||||||
|
}
|
||||||
|
self.resumeFlow(payload)
|
||||||
|
|
||||||
|
proc resumeCurrentFlow*(self: Service) =
|
||||||
|
var payload = %* { }
|
||||||
|
self.resumeFlow(payload)
|
||||||
|
|
||||||
|
proc resumeCurrentFlowLater*(self: Service) =
|
||||||
|
self.runTimer()
|
||||||
|
|
||||||
|
proc factoryReset*(self: Service) =
|
||||||
|
var payload = %* {
|
||||||
|
RequestParamFactoryReset: true
|
||||||
|
}
|
||||||
|
self.resumeFlow(payload)
|
|
@ -42,21 +42,21 @@ QtObject:
|
||||||
signalConnect(self.keychainManager, "error(QString, int, QString)", self,
|
signalConnect(self.keychainManager, "error(QString, int, QString)", self,
|
||||||
"onKeychainManagerError(QString, int, QString)", 2)
|
"onKeychainManagerError(QString, int, QString)", 2)
|
||||||
|
|
||||||
proc storePassword*(self: Service, username: string, password: string) =
|
proc storeData*(self: Service, key: string, data: string) =
|
||||||
self.keychainManager.storeDataAsync(username, password)
|
self.keychainManager.storeDataAsync(key, data)
|
||||||
|
|
||||||
proc tryToObtainPassword*(self: Service, username: string) =
|
proc tryToObtainData*(self: Service, key: string) =
|
||||||
self.keychainManager.readDataAsync(username)
|
self.keychainManager.readDataAsync(key)
|
||||||
|
|
||||||
proc onKeychainManagerError*(self: Service, errorType: string, errorCode: int,
|
proc onKeychainManagerError*(self: Service, errorType: string, errorCode: int,
|
||||||
errorDescription: string) {.slot.} =
|
errorDescription: string) {.slot.} =
|
||||||
## This slot is called in case an error occured while we're dealing with
|
## This slot is called in case an error occured while we're dealing with
|
||||||
## KeychainManager. So far we're just logging the error.
|
## KeychainManager. So far we're just logging the error.
|
||||||
info "KeychainManager stopped: ", msg = errorCode, errorDescription
|
info "KeychainManager stopped: ", errCode=errorCode, errType=errorType, errDesc=errorDescription
|
||||||
|
|
||||||
let arg = KeyChainServiceArg(errCode: errorCode, errType: errorType,
|
let arg = KeyChainServiceArg(errCode: errorCode, errType: errorType,
|
||||||
errDescription: errorDescription)
|
errDescription: errorDescription)
|
||||||
self.events.emit("", arg)
|
self.events.emit(SIGNAL_KEYCHAIN_SERVICE_ERROR, arg)
|
||||||
|
|
||||||
proc onKeychainManagerSuccess*(self: Service, data: string) {.slot.} =
|
proc onKeychainManagerSuccess*(self: Service, data: string) {.slot.} =
|
||||||
## This slot is called in case a password is successfully retrieved from the
|
## This slot is called in case a password is successfully retrieved from the
|
||||||
|
|
|
@ -218,6 +218,26 @@ proc saveAccountAndLogin*(hashedPassword: string, account, subaccounts, settings
|
||||||
error "error doing rpc request", methodName = "saveAccountAndLogin", exception=e.msg
|
error "error doing rpc request", methodName = "saveAccountAndLogin", exception=e.msg
|
||||||
raise newException(RpcException, e.msg)
|
raise newException(RpcException, e.msg)
|
||||||
|
|
||||||
|
proc saveAccountAndLoginWithKeycard*(chatKey, password: string, account, subaccounts, settings, config: JsonNode):
|
||||||
|
RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
try:
|
||||||
|
let response = status_go.saveAccountAndLoginWithKeycard($account, password, $settings, $config, $subaccounts, chatKey)
|
||||||
|
result.result = Json.decode(response, JsonNode)
|
||||||
|
|
||||||
|
except RpcException as e:
|
||||||
|
error "error doing rpc request", methodName = "saveAccountAndLogin", exception=e.msg
|
||||||
|
raise newException(RpcException, e.msg)
|
||||||
|
|
||||||
|
proc convertToKeycardAccount*(keyStoreDir: string, account: JsonNode, settings: JsonNode, password: string, newPassword: string):
|
||||||
|
RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
try:
|
||||||
|
let response = status_go.convertToKeycardAccount(keyStoreDir, $account, $settings, password, newPassword)
|
||||||
|
result.result = Json.decode(response, JsonNode)
|
||||||
|
|
||||||
|
except RpcException as e:
|
||||||
|
error "error doing rpc request", methodName = "convertToKeycardAccount", exception=e.msg
|
||||||
|
raise newException(RpcException, e.msg)
|
||||||
|
|
||||||
proc login*(name, keyUid, hashedPassword, thumbnail, large: string, nodeCfgObj: string):
|
proc login*(name, keyUid, hashedPassword, thumbnail, large: string, nodeCfgObj: string):
|
||||||
RpcResponse[JsonNode]
|
RpcResponse[JsonNode]
|
||||||
{.raises: [Exception].} =
|
{.raises: [Exception].} =
|
||||||
|
@ -238,6 +258,14 @@ proc login*(name, keyUid, hashedPassword, thumbnail, large: string, nodeCfgObj:
|
||||||
error "error doing rpc request", methodName = "login", exception=e.msg
|
error "error doing rpc request", methodName = "login", exception=e.msg
|
||||||
raise newException(RpcException, e.msg)
|
raise newException(RpcException, e.msg)
|
||||||
|
|
||||||
|
proc loginWithKeycard*(chatKey, password: string, account: JsonNode): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
try:
|
||||||
|
let response = status_go.loginWithKeycard($account, password, chatKey)
|
||||||
|
result.result = Json.decode(response, JsonNode)
|
||||||
|
except RpcException as e:
|
||||||
|
error "error doing rpc request", methodName = "loginWithKeycard", exception=e.msg
|
||||||
|
raise newException(RpcException, e.msg)
|
||||||
|
|
||||||
proc verifyAccountPassword*(address: string, password: string, keystoreDir: string):
|
proc verifyAccountPassword*(address: string, password: string, keystoreDir: string):
|
||||||
RpcResponse[JsonNode] {.raises: [Exception].} =
|
RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -59,6 +59,7 @@ let
|
||||||
ROOTKEYSTOREDIR* = joinPath(baseDir, "data", "keystore")
|
ROOTKEYSTOREDIR* = joinPath(baseDir, "data", "keystore")
|
||||||
TMPDIR* = joinPath(baseDir, "tmp") & sep
|
TMPDIR* = joinPath(baseDir, "tmp") & sep
|
||||||
LOGDIR* = joinPath(baseDir, "logs") & sep
|
LOGDIR* = joinPath(baseDir, "logs") & sep
|
||||||
|
KEYCARDPAIRINGDATAFILE* = joinPath(baseDir, "data", "keycard/pairings.json")
|
||||||
|
|
||||||
proc ensureDirectories*(dataDir, tmpDir, logDir: string) =
|
proc ensureDirectories*(dataDir, tmpDir, logDir: string) =
|
||||||
createDir(dataDir)
|
createDir(dataDir)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import NimQml, chronicles, os, strformat, strutils, times, md5, json
|
import NimQml, chronicles, os, strformat, strutils, times, md5, json
|
||||||
|
|
||||||
import status_go
|
import status_go
|
||||||
|
import keycard_go
|
||||||
import app/core/main
|
import app/core/main
|
||||||
import constants
|
import constants
|
||||||
|
|
||||||
|
@ -11,6 +12,7 @@ logScope:
|
||||||
topics = "status-app"
|
topics = "status-app"
|
||||||
|
|
||||||
var signalsManagerQObjPointer: pointer
|
var signalsManagerQObjPointer: pointer
|
||||||
|
var keycardServiceQObjPointer: pointer
|
||||||
|
|
||||||
proc isExperimental(): string =
|
proc isExperimental(): string =
|
||||||
result = if getEnv("EXPERIMENTAL") == "1": "1" else: "0" # value explicity passed to avoid trusting input
|
result = if getEnv("EXPERIMENTAL") == "1": "1" else: "0" # value explicity passed to avoid trusting input
|
||||||
|
@ -66,11 +68,15 @@ proc setupRemoteSignalsHandling() =
|
||||||
# Please note that this must use the `cdecl` calling convention because
|
# Please note that this must use the `cdecl` calling convention because
|
||||||
# it will be passed as a regular C function to statusgo_backend. This means that
|
# it will be passed as a regular C function to statusgo_backend. This means that
|
||||||
# we cannot capture any local variables here (we must rely on globals)
|
# we cannot capture any local variables here (we must rely on globals)
|
||||||
var callback: SignalCallback = proc(p0: cstring) {.cdecl.} =
|
var callbackStatusGo: status_go.SignalCallback = proc(p0: cstring) {.cdecl.} =
|
||||||
if signalsManagerQObjPointer != nil:
|
if signalsManagerQObjPointer != nil:
|
||||||
signal_handler(signalsManagerQObjPointer, p0, "receiveSignal")
|
signal_handler(signalsManagerQObjPointer, p0, "receiveSignal")
|
||||||
|
status_go.setSignalEventCallback(callbackStatusGo)
|
||||||
|
|
||||||
status_go.setSignalEventCallback(callback)
|
var callbackKeycardGo: keycard_go.KeycardSignalCallback = proc(p0: cstring) {.cdecl.} =
|
||||||
|
if keycardServiceQObjPointer != nil:
|
||||||
|
signal_handler(keycardServiceQObjPointer, p0, "receiveKeycardSignal")
|
||||||
|
keycard_go.setSignalEventCallback(callbackKeycardGo)
|
||||||
|
|
||||||
proc mainProc() =
|
proc mainProc() =
|
||||||
if defined(macosx) and defined(production):
|
if defined(macosx) and defined(production):
|
||||||
|
@ -133,6 +139,7 @@ proc mainProc() =
|
||||||
defer:
|
defer:
|
||||||
info "shutting down..."
|
info "shutting down..."
|
||||||
signalsManagerQObjPointer = nil
|
signalsManagerQObjPointer = nil
|
||||||
|
keycardServiceQObjPointer = nil
|
||||||
isProductionQVariant.delete()
|
isProductionQVariant.delete()
|
||||||
isExperimentalQVariant.delete()
|
isExperimentalQVariant.delete()
|
||||||
signalsManagerQVariant.delete()
|
signalsManagerQVariant.delete()
|
||||||
|
@ -149,6 +156,12 @@ proc mainProc() =
|
||||||
info "Terminating the app as the second instance"
|
info "Terminating the app as the second instance"
|
||||||
quit()
|
quit()
|
||||||
|
|
||||||
|
# We need these global variables in order to be able to access the application
|
||||||
|
# from the non-closure callback passed to `statusgo_backend.setSignalEventCallback`
|
||||||
|
signalsManagerQObjPointer = cast[pointer](statusFoundation.signalsManager.vptr)
|
||||||
|
keycardServiceQObjPointer = cast[pointer](appController.keycardService.vptr)
|
||||||
|
setupRemoteSignalsHandling()
|
||||||
|
|
||||||
info fmt("Version: {DESKTOP_VERSION}")
|
info fmt("Version: {DESKTOP_VERSION}")
|
||||||
info fmt("Commit: {GIT_COMMIT}")
|
info fmt("Commit: {GIT_COMMIT}")
|
||||||
info "Current date:", currentDateTime=now()
|
info "Current date:", currentDateTime=now()
|
||||||
|
@ -156,11 +169,6 @@ proc mainProc() =
|
||||||
info "starting application controller..."
|
info "starting application controller..."
|
||||||
appController.start()
|
appController.start()
|
||||||
|
|
||||||
# We need this global variable in order to be able to access the application
|
|
||||||
# from the non-closure callback passed to `statusgo_backend.setSignalEventCallback`
|
|
||||||
signalsManagerQObjPointer = cast[pointer](statusFoundation.signalsManager.vptr)
|
|
||||||
setupRemoteSignalsHandling()
|
|
||||||
|
|
||||||
info "starting application..."
|
info "starting application..."
|
||||||
app.exec()
|
app.exec()
|
||||||
|
|
||||||
|
|
|
@ -31,41 +31,86 @@ OnboardingBasePage {
|
||||||
{
|
{
|
||||||
return allowNotificationsViewComponent
|
return allowNotificationsViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.welcome)
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.welcome)
|
||||||
{
|
{
|
||||||
return welcomeViewComponent
|
return welcomeViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeNewStatusUser ||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeNewStatusUser ||
|
||||||
root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeOldStatusUser ||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.welcomeOldStatusUser ||
|
||||||
root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileImportSeedPhrase)
|
root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileImportSeedPhrase)
|
||||||
{
|
{
|
||||||
return keysMainViewComponent
|
return keysMainViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreate ||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreate ||
|
||||||
root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileChatKey)
|
root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileChatKey)
|
||||||
{
|
{
|
||||||
return insertDetailsViewComponent
|
return insertDetailsViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreatePassword)
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileCreatePassword)
|
||||||
{
|
{
|
||||||
return createPasswordViewComponent
|
return createPasswordViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileConfirmPassword)
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileConfirmPassword)
|
||||||
{
|
{
|
||||||
return confirmPasswordViewComponent
|
return confirmPasswordViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.biometrics)
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.biometrics)
|
||||||
{
|
{
|
||||||
return touchIdAuthViewComponent
|
return touchIdAuthViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileEnterSeedPhrase)
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.userProfileEnterSeedPhrase)
|
||||||
{
|
{
|
||||||
return seedPhraseInputViewComponent
|
return seedPhraseInputViewComponent
|
||||||
}
|
}
|
||||||
else if (root.startupStore.currentStartupState.stateType === Constants.startupState.login)
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.login ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardInsertKeycard ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardReadingKeycard ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardEnterPin ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardWrongKeycard ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardWrongPin ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardMaxPinRetriesReached ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardMaxPukRetriesReached ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.loginKeycardEmpty)
|
||||||
{
|
{
|
||||||
return loginViewComponent
|
return loginViewComponent
|
||||||
}
|
}
|
||||||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPluginReader ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardInsertKeycard ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardReadingKeycard)
|
||||||
|
{
|
||||||
|
return keycardInitViewComponent
|
||||||
|
}
|
||||||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardCreatePin ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRepeatPin ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPinSet ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEnterPin ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardWrongPin)
|
||||||
|
{
|
||||||
|
return keycardPinViewComponent
|
||||||
|
}
|
||||||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardDisplaySeedPhrase)
|
||||||
|
{
|
||||||
|
return seedphraseViewComponent
|
||||||
|
}
|
||||||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEnterSeedPhraseWords)
|
||||||
|
{
|
||||||
|
return seedphraseWordsInputViewComponent
|
||||||
|
}
|
||||||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardNotEmpty ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEmpty ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardLocked ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecover ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPairingSlotsReached ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPinRetriesReached ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPukRetriesReached)
|
||||||
|
{
|
||||||
|
return keycardStateViewComponent
|
||||||
|
}
|
||||||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEnterPuk ||
|
||||||
|
root.startupStore.currentStartupState.stateType === Constants.startupState.keycardWrongPuk)
|
||||||
|
{
|
||||||
|
return keycardPukViewComponent
|
||||||
|
}
|
||||||
|
|
||||||
return undefined
|
return undefined
|
||||||
}
|
}
|
||||||
|
@ -87,7 +132,9 @@ OnboardingBasePage {
|
||||||
onAccountImportError: {
|
onAccountImportError: {
|
||||||
if (error === Constants.existingAccountError) {
|
if (error === Constants.existingAccountError) {
|
||||||
msgDialog.title = qsTr("Keys for this account already exist")
|
msgDialog.title = qsTr("Keys for this account already exist")
|
||||||
msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase")
|
msgDialog.text = qsTr("Keys for this account already exist and can't be added again. If you've lost \
|
||||||
|
your password, passcode or Keycard, uninstall the app, reinstall and access your keys by entering your seed phrase. In \
|
||||||
|
case of Keycard try recovering using PUK or reinstall the app and try login with the Keycard option.")
|
||||||
} else {
|
} else {
|
||||||
msgDialog.title = qsTr("Error importing seed")
|
msgDialog.title = qsTr("Error importing seed")
|
||||||
msgDialog.text = error
|
msgDialog.text = error
|
||||||
|
@ -169,4 +216,46 @@ OnboardingBasePage {
|
||||||
startupStore: root.startupStore
|
startupStore: root.startupStore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keycardInitViewComponent
|
||||||
|
KeycardInitView {
|
||||||
|
startupStore: root.startupStore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keycardPinViewComponent
|
||||||
|
KeycardPinView {
|
||||||
|
startupStore: root.startupStore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keycardPukViewComponent
|
||||||
|
KeycardPukView {
|
||||||
|
startupStore: root.startupStore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: seedphraseViewComponent
|
||||||
|
SeedPhraseView {
|
||||||
|
startupStore: root.startupStore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: seedphraseWordsInputViewComponent
|
||||||
|
SeedPhraseWordsInputView {
|
||||||
|
startupStore: root.startupStore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keycardStateViewComponent
|
||||||
|
KeycardStateView {
|
||||||
|
startupStore: root.startupStore
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ MenuItem {
|
||||||
property string colorId: ""
|
property string colorId: ""
|
||||||
property var colorHash
|
property var colorHash
|
||||||
property url image: ""
|
property url image: ""
|
||||||
|
property bool keycardCreatedAccount: false
|
||||||
property StatusIconSettings iconSettings: StatusIconSettings {
|
property StatusIconSettings iconSettings: StatusIconSettings {
|
||||||
name: "add"
|
name: "add"
|
||||||
}
|
}
|
||||||
|
@ -65,8 +66,28 @@ MenuItem {
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.left: userImageOrIcon.right
|
anchors.left: userImageOrIcon.right
|
||||||
anchors.leftMargin: 16
|
anchors.right: root.keycardCreatedAccount? keycardIcon.left : parent.right
|
||||||
|
anchors.leftMargin: Style.current.padding
|
||||||
color: !!root.colorId ? Theme.palette.directColor1 : Theme.palette.primaryColor1
|
color: !!root.colorId ? Theme.palette.directColor1 : Theme.palette.primaryColor1
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: keycardIcon
|
||||||
|
active: root.keycardCreatedAccount
|
||||||
|
sourceComponent: keycardIconComponent
|
||||||
|
anchors.rightMargin: Style.current.padding
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: keycardIconComponent
|
||||||
|
StatusIcon {
|
||||||
|
icon: "keycard"
|
||||||
|
height: Style.current.padding
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ import utils 1.0 as Imports
|
||||||
|
|
||||||
import shared 1.0
|
import shared 1.0
|
||||||
import shared.controls 1.0
|
import shared.controls 1.0
|
||||||
import shared.keycard 1.0
|
|
||||||
|
|
||||||
StatusModal {
|
StatusModal {
|
||||||
property bool firstPINFieldValid: false
|
property bool firstPINFieldValid: false
|
||||||
|
|
|
@ -35,6 +35,10 @@ QtObject {
|
||||||
return root.startupModuleInst.generateImage(source, aX, aY, bX, bY)
|
return root.startupModuleInst.generateImage(source, aX, aY, bX, bY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCroppedProfileImage() {
|
||||||
|
return root.startupModuleInst.getCroppedProfileImage()
|
||||||
|
}
|
||||||
|
|
||||||
function setDisplayName(value) {
|
function setDisplayName(value) {
|
||||||
root.startupModuleInst.setDisplayName(value)
|
root.startupModuleInst.setDisplayName(value)
|
||||||
}
|
}
|
||||||
|
@ -51,6 +55,18 @@ QtObject {
|
||||||
return root.startupModuleInst.getPassword()
|
return root.startupModuleInst.getPassword()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setPin(value) {
|
||||||
|
root.startupModuleInst.setPin(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPin() {
|
||||||
|
return root.startupModuleInst.getPin()
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPuk(value) {
|
||||||
|
root.startupModuleInst.setPuk(value)
|
||||||
|
}
|
||||||
|
|
||||||
function getPasswordStrengthScore(password) {
|
function getPasswordStrengthScore(password) {
|
||||||
let userName = root.startupModuleInst.importedAccountAlias
|
let userName = root.startupModuleInst.importedAccountAlias
|
||||||
return root.startupModuleInst.getPasswordStrengthScore(password, userName)
|
return root.startupModuleInst.getPasswordStrengthScore(password, userName)
|
||||||
|
@ -63,4 +79,12 @@ QtObject {
|
||||||
function setSelectedLoginAccountByIndex(index) {
|
function setSelectedLoginAccountByIndex(index) {
|
||||||
root.startupModuleInst.setSelectedLoginAccountByIndex(index)
|
root.startupModuleInst.setSelectedLoginAccountByIndex(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checkRepeatedKeycardPinWhileTyping(pin) {
|
||||||
|
return root.startupModuleInst.checkRepeatedKeycardPinWhileTyping(pin)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSeedPhrase() {
|
||||||
|
return root.startupModuleInst.getSeedPhrase()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ Item {
|
||||||
}
|
}
|
||||||
nameInput.text = root.startupStore.getDisplayName();
|
nameInput.text = root.startupStore.getDisplayName();
|
||||||
nameInput.input.edit.forceActiveFocus();
|
nameInput.input.edit.forceActiveFocus();
|
||||||
|
userImage.image.source = root.startupStore.getCroppedProfileImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
|
|
|
@ -1,115 +0,0 @@
|
||||||
import QtQuick 2.13
|
|
||||||
|
|
||||||
import shared.keycard 1.0
|
|
||||||
import "../popups"
|
|
||||||
import "../stores"
|
|
||||||
|
|
||||||
Item {
|
|
||||||
enum OnboardingFlow {
|
|
||||||
Recover,
|
|
||||||
Generate,
|
|
||||||
ImportMnemonic
|
|
||||||
}
|
|
||||||
|
|
||||||
property var onClosed: function () {}
|
|
||||||
property bool connected: false
|
|
||||||
property int flow: KeycardFlowSelectionView.OnboardingFlow.Recover
|
|
||||||
|
|
||||||
id: keycardView
|
|
||||||
Component.onCompleted: {
|
|
||||||
insertCard.open()
|
|
||||||
KeycardStore.startConnection()
|
|
||||||
}
|
|
||||||
|
|
||||||
KeycardCreatePINModal {
|
|
||||||
id: createPinModal
|
|
||||||
onSubmitBtnClicked: KeycardStore.init(pin)
|
|
||||||
onClosed: function () {
|
|
||||||
if (!createPinModal.submitted) {
|
|
||||||
keycardView.onClosed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PairingModal {
|
|
||||||
id: pairingModal
|
|
||||||
onClosed: function () {
|
|
||||||
if (!pairingModal.submitted) {
|
|
||||||
keycardView.onClosed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PINModal {
|
|
||||||
id: pinModal
|
|
||||||
onClosed: function () {
|
|
||||||
if (!pinModal.submitted) {
|
|
||||||
keycardView.onClosed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InsertCard {
|
|
||||||
id: insertCard
|
|
||||||
onCancel: function() {
|
|
||||||
keycardView.onClosed()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not Refactored Yet
|
|
||||||
// Connections {
|
|
||||||
// id: connection
|
|
||||||
// target: OnboardingStore.keycardModelInst
|
|
||||||
// ignoreUnknownSignals: true
|
|
||||||
|
|
||||||
// onCardUnpaired: {
|
|
||||||
// pairingModal.open()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onCardPaired: {
|
|
||||||
// pinModal.open()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onCardAuthenticated: {
|
|
||||||
// switch (flow) {
|
|
||||||
// case OnboardingFlow.Recover: {
|
|
||||||
// KeycardStore.recoverAccount();
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case OnboardingFlow.Generate: {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case OnboardingFlow.ImportMnemonic: {
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //TODO: support the states below
|
|
||||||
|
|
||||||
// onCardPreInit: {
|
|
||||||
// createPinModal.open()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onCardFrozen: {
|
|
||||||
// keycardView.onClosed()
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onCardBlocked: {
|
|
||||||
// keycardView.onClosed()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // TODO: handle these by showing an error an prompting for another card
|
|
||||||
// // later add factory reset option for the NoFreeSlots case
|
|
||||||
|
|
||||||
// onCardNoFreeSlots: {
|
|
||||||
// keycardView.onClosed()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onCardNotKeycard: {
|
|
||||||
// keycardView.onClosed()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StartupStore startupStore
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if(root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPluginReader) {
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property int index: 0
|
||||||
|
property variant images : [
|
||||||
|
Style.svg("keycard/card0@2x"),
|
||||||
|
Style.svg("keycard/card1@2x"),
|
||||||
|
Style.svg("keycard/card2@2x"),
|
||||||
|
Style.svg("keycard/card3@2x")
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
interval: 400
|
||||||
|
running: true
|
||||||
|
repeat: true
|
||||||
|
onTriggered: {
|
||||||
|
d.index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredHeight: sourceSize.height
|
||||||
|
Layout.preferredWidth: sourceSize.width
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
antialiasing: true
|
||||||
|
source: d.images[d.index % d.images.length]
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: title
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardPluginReader
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPluginReader
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTrId("Plug in Keycard reader...")
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize1
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardInsertKeycard
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardInsertKeycard
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTrId("Insert your Keycard...")
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize1
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardReadingKeycard
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardReadingKeycard
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Reading Keycard...")
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize2
|
||||||
|
color: Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,200 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StartupStore startupStore
|
||||||
|
|
||||||
|
property int remainingAttempts: parseInt(root.startupStore.startupModuleInst.keycardData, 10)
|
||||||
|
|
||||||
|
onRemainingAttemptsChanged: {
|
||||||
|
if (root.startupStore.currentStartupState.stateType === Constants.startupState.keycardWrongPin) {
|
||||||
|
pinInputField.statesInitialization()
|
||||||
|
pinInputField.forceFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onStateChanged: {
|
||||||
|
if(state === Constants.startupState.keycardPinSet) {
|
||||||
|
pinInputField.setPin("123456") // we are free to set fake pin in this case
|
||||||
|
} else {
|
||||||
|
pinInputField.statesInitialization()
|
||||||
|
pinInputField.forceFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: timer
|
||||||
|
interval: 1000
|
||||||
|
running: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPinSet
|
||||||
|
onTriggered: {
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
Image {
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredHeight: sourceSize.height
|
||||||
|
Layout.preferredWidth: sourceSize.width
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
antialiasing: true
|
||||||
|
source: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPinSet?
|
||||||
|
Style.svg("keycard/card-success3@2x") :
|
||||||
|
Style.svg("keycard/card3@2x")
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: title
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.weight: Font.Bold
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize1
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusPinInput {
|
||||||
|
id: pinInputField
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
validator: StatusIntValidator{bottom: 0; top: 999999;}
|
||||||
|
pinLen: Constants.keycard.general.keycardPinLength
|
||||||
|
enabled: root.startupStore.currentStartupState.stateType !== Constants.startupState.keycardPinSet
|
||||||
|
|
||||||
|
onPinInputChanged: {
|
||||||
|
if(pinInput.length == 0)
|
||||||
|
return
|
||||||
|
if(root.state === Constants.startupState.keycardCreatePin ||
|
||||||
|
root.state === Constants.startupState.keycardEnterPin ||
|
||||||
|
root.state === Constants.startupState.keycardWrongPin) {
|
||||||
|
root.startupStore.setPin(pinInput)
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
else if(root.state === Constants.startupState.keycardRepeatPin) {
|
||||||
|
let pinsMatch = root.startupStore.checkRepeatedKeycardPinWhileTyping(pinInput)
|
||||||
|
if (pinsMatch) {
|
||||||
|
info.text = qsTr("It is very important that you do not loose this PIN")
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
} else {
|
||||||
|
info.text = qsTr("PINs don't match")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: info
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize3
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: message
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize3
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardCreatePin
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardCreatePin
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Create new Keycard PIN")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("It is very important that you do not loose this PIN")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: message
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardRepeatPin
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRepeatPin
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Repeat Keycard PIN")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("It is very important that you do not loose this PIN")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: message
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardPinSet
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardPinSet
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Keycard PIN set")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: message
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardEnterPin
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEnterPin
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Enter Keycard PIN")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: message
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardWrongPin
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardWrongPin
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Enter Keycard PIN")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("PIN incorrect")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: message
|
||||||
|
text: qsTr("%n attempt(s) remaining", "", d.remainingAttempts)
|
||||||
|
color: root.remainingAttempts === 1?
|
||||||
|
Theme.palette.dangerColor1 :
|
||||||
|
Theme.palette.baseColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,195 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Controls.Validators 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StartupStore startupStore
|
||||||
|
|
||||||
|
property int remainingAttempts: parseInt(root.startupStore.startupModuleInst.keycardData, 10)
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
d.allEntriesValid = false
|
||||||
|
d.pukArray = Array(d.pukLength)
|
||||||
|
d.pukArray.fill("")
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
readonly property int pukLength: 12
|
||||||
|
property var pukArray: []
|
||||||
|
property bool allEntriesValid: false
|
||||||
|
readonly property int rowSpacing: Style.current.padding
|
||||||
|
|
||||||
|
function updateValidity() {
|
||||||
|
for(let i = 0; i < pukLength; ++i) {
|
||||||
|
if(pukArray[i].length !== 1) {
|
||||||
|
allEntriesValid = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
allEntriesValid = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitPuk() {
|
||||||
|
let puk = d.pukArray.join("")
|
||||||
|
root.startupStore.setPuk(puk)
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: footerWrapper.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: title
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize1
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
text: qsTr("Enter PUK code to recover Keycard")
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: rowLayout
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
spacing: d.rowSpacing
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
for (var i = 0; i < children.length - 1; ++i) {
|
||||||
|
if(children[i] && children[i].input && children[i+1] && children[i+1].input){
|
||||||
|
children[i].input.tabNavItem = children[i+1].input.edit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(children.length > 0){
|
||||||
|
children[0].input.edit.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: d.pukLength
|
||||||
|
delegate: StatusInput {
|
||||||
|
Layout.preferredWidth: Constants.keycard.general.pukCellWidth
|
||||||
|
Layout.preferredHeight: Constants.keycard.general.pukCellHeight
|
||||||
|
input.acceptReturn: true
|
||||||
|
validators: [
|
||||||
|
StatusRegularExpressionValidator {
|
||||||
|
regularExpression: /[0-9]/
|
||||||
|
errorMessage: ""
|
||||||
|
},
|
||||||
|
StatusMinLengthValidator {
|
||||||
|
minLength: 1
|
||||||
|
errorMessage: ""
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
text = text.trim()
|
||||||
|
if(text.length >= 1) {
|
||||||
|
text = text.charAt(0);
|
||||||
|
}
|
||||||
|
if(Utils.isDigit(text)) {
|
||||||
|
let nextInd = index+1
|
||||||
|
if(nextInd <= rowLayout.children.length - 1 &&
|
||||||
|
rowLayout.children[nextInd] &&
|
||||||
|
rowLayout.children[nextInd].input){
|
||||||
|
rowLayout.children[nextInd].input.edit.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
text = ""
|
||||||
|
}
|
||||||
|
d.pukArray[index] = text
|
||||||
|
d.updateValidity()
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPressed: {
|
||||||
|
if(input.edit.keyEvent === Qt.Key_Backspace){
|
||||||
|
if (text == ""){
|
||||||
|
let prevInd = index-1
|
||||||
|
if(prevInd >= 0){
|
||||||
|
rowLayout.children[prevInd].input.edit.forceActiveFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (input.edit.keyEvent === Qt.Key_Return ||
|
||||||
|
input.edit.keyEvent === Qt.Key_Enter) {
|
||||||
|
if(d.allEntriesValid) {
|
||||||
|
event.accepted = true
|
||||||
|
d.submitPuk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: info
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize3
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: footerWrapper
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: Constants.keycard.general.footerWrapperHeight
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
enabled: d.allEntriesValid
|
||||||
|
text: qsTr("Recover Keycard")
|
||||||
|
onClicked: {
|
||||||
|
d.submitPuk()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardEnterPuk
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEnterPuk
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardWrongPuk
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardWrongPuk
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("Invalid PUK code, %n attempt(s) remaining", "", root.remainingAttempts)
|
||||||
|
}
|
||||||
|
StateChangeScript {
|
||||||
|
script: d.allEntriesValid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StartupStore startupStore
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: footerWrapper.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
Image {
|
||||||
|
id: image
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
Layout.preferredHeight: sourceSize.height
|
||||||
|
Layout.preferredWidth: sourceSize.width
|
||||||
|
fillMode: Image.PreserveAspectFit
|
||||||
|
antialiasing: true
|
||||||
|
mipmap: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: title
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize1
|
||||||
|
font.weight: Font.Bold
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: info
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize3
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: footerWrapper
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: Constants.keycard.general.footerWrapperHeight
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
spacing: Style.current.bigPadding
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
id: button
|
||||||
|
visible: text.length > 0
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
focus: true
|
||||||
|
onClicked: {
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: link
|
||||||
|
visible: text.length > 0
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.buttonFontSize
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
hoverEnabled: true
|
||||||
|
onEntered: {
|
||||||
|
parent.font.underline = true
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
parent.font.underline = false
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
root.startupStore.doSecondaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
states: [
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardNotEmpty
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardNotEmpty
|
||||||
|
PropertyChanges {
|
||||||
|
target: image
|
||||||
|
source: Style.svg("keycard/card3@2x")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("This Keycard already stores keys")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("To generate new keys, you will need to perform a factory reset first")
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: button
|
||||||
|
text: qsTr("Factory reset")
|
||||||
|
type: StatusBaseButton.Type.Normal
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: link
|
||||||
|
text: qsTr("Insert another Keycard")
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardEmpty
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardEmpty
|
||||||
|
PropertyChanges {
|
||||||
|
target: image
|
||||||
|
source: Style.svg("keycard/card-error3@2x")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("The keycard is empty")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: button
|
||||||
|
text: qsTr("Generate new keys for this Keycard")
|
||||||
|
type: StatusBaseButton.Type.Normal
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: link
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardLocked
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardLocked
|
||||||
|
PropertyChanges {
|
||||||
|
target: image
|
||||||
|
source: Style.svg("keycard/card-error3@2x")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Keycard locked and already stores keys")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("The Keycard you have inserted is locked, you will need to factory reset it before proceeding")
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: button
|
||||||
|
text: qsTr("Factory reset")
|
||||||
|
type: StatusBaseButton.Type.Normal
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: link
|
||||||
|
text: qsTr("Insert another Keycard")
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardMaxPairingSlotsReached
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPairingSlotsReached
|
||||||
|
PropertyChanges {
|
||||||
|
target: image
|
||||||
|
source: Style.svg("keycard/card-error3@2x")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Keycard locked")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("Max pairing slots reached for this keycard")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: button
|
||||||
|
text: qsTr("Factory reset")
|
||||||
|
type: StatusBaseButton.Type.Normal
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: link
|
||||||
|
text: qsTr("Insert another Keycard")
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardMaxPukRetriesReached
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPukRetriesReached
|
||||||
|
PropertyChanges {
|
||||||
|
target: image
|
||||||
|
source: Style.svg("keycard/card-error3@2x")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Keycard locked")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("Max PUK retries reached for this keycard")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: button
|
||||||
|
text: qsTr("Factory reset")
|
||||||
|
type: StatusBaseButton.Type.Normal
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: link
|
||||||
|
text: qsTr("Insert another Keycard")
|
||||||
|
color: Theme.palette.primaryColor1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardMaxPinRetriesReached
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardMaxPinRetriesReached
|
||||||
|
PropertyChanges {
|
||||||
|
target: image
|
||||||
|
source: Style.svg("keycard/card-error3@2x")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: qsTr("Keycard locked")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: button
|
||||||
|
text: qsTr("Recover your Keycard")
|
||||||
|
type: StatusBaseButton.Type.Danger
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: link
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
State {
|
||||||
|
name: Constants.startupState.keycardRecover
|
||||||
|
when: root.startupStore.currentStartupState.stateType === Constants.startupState.keycardRecover
|
||||||
|
PropertyChanges {
|
||||||
|
target: image
|
||||||
|
source: Style.svg("keycard/card-error3@2x")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: title
|
||||||
|
text: qsTr("Recover your Keycard")
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: info
|
||||||
|
text: ""
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: button
|
||||||
|
text: qsTr("Recover with seed phrase")
|
||||||
|
type: StatusBaseButton.Type.Danger
|
||||||
|
}
|
||||||
|
PropertyChanges {
|
||||||
|
target: link
|
||||||
|
text: qsTr("Recover with PUK")
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -86,6 +86,7 @@ Item {
|
||||||
id: keycardLink
|
id: keycardLink
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
color: Theme.palette.primaryColor1
|
color: Theme.palette.primaryColor1
|
||||||
|
font.pixelSize: 15
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
@ -148,10 +149,10 @@ Item {
|
||||||
//TODO remove when sync code is implemented
|
//TODO remove when sync code is implemented
|
||||||
opacity: 0.0
|
opacity: 0.0
|
||||||
}
|
}
|
||||||
// PropertyChanges {
|
PropertyChanges {
|
||||||
// target: keycardLink
|
target: keycardLink
|
||||||
// text: qsTr("Login with Keycard")
|
text: qsTr("Login with Keycard")
|
||||||
// }
|
}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: seedLink
|
target: seedLink
|
||||||
text: qsTr("Enter a seed phrase")
|
text: qsTr("Enter a seed phrase")
|
||||||
|
@ -179,10 +180,10 @@ Item {
|
||||||
//TODO remove when sync code is implemented
|
//TODO remove when sync code is implemented
|
||||||
opacity: 1.0
|
opacity: 1.0
|
||||||
}
|
}
|
||||||
// PropertyChanges {
|
PropertyChanges {
|
||||||
// target: keycardLink
|
target: keycardLink
|
||||||
// text: qsTr("Generate keys for a new Keycard")
|
text: qsTr("Generate keys for a new Keycard")
|
||||||
// }
|
}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: seedLink
|
target: seedLink
|
||||||
text: qsTr("Import a seed phrase")
|
text: qsTr("Import a seed phrase")
|
||||||
|
@ -217,10 +218,10 @@ Item {
|
||||||
//TODO remove when sync code is implemented
|
//TODO remove when sync code is implemented
|
||||||
opacity: 1.0
|
opacity: 1.0
|
||||||
}
|
}
|
||||||
// PropertyChanges {
|
PropertyChanges {
|
||||||
// target: keycardLink
|
target: keycardLink
|
||||||
// text: qsTr("Import a seed phrase into a new Keycard")
|
text: qsTr("Import a seed phrase into a new Keycard")
|
||||||
// }
|
}
|
||||||
PropertyChanges {
|
PropertyChanges {
|
||||||
target: seedLink
|
target: seedLink
|
||||||
text: ""
|
text: ""
|
||||||
|
|
|
@ -277,9 +277,22 @@ Item {
|
||||||
function checkMnemonicLength() {
|
function checkMnemonicLength() {
|
||||||
submitButton.enabled = (root.mnemonicInput.length === root.tabs[switchTabBar.currentIndex])
|
submitButton.enabled = (root.mnemonicInput.length === root.tabs[switchTabBar.currentIndex])
|
||||||
}
|
}
|
||||||
text: root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhrase?
|
text: {
|
||||||
qsTr("Import") :
|
if (root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhrase) {
|
||||||
qsTr("Restore Status Profile")
|
return qsTr("Import")
|
||||||
|
}
|
||||||
|
else if (root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunOldUserImportSeedPhrase) {
|
||||||
|
return qsTr("Restore Status Profile")
|
||||||
|
}
|
||||||
|
else if (root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunOldUserKeycardImport ||
|
||||||
|
root.startupStore.currentStartupState.flowType === Constants.startupFlow.appLogin) {
|
||||||
|
return qsTr("Recover Keycard")
|
||||||
|
}
|
||||||
|
else if (root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhraseIntoKeycard) {
|
||||||
|
return qsTr("Next")
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let mnemonicString = "";
|
let mnemonicString = "";
|
||||||
var sortTable = mnemonicInput.sort(function (a, b) {
|
var sortTable = mnemonicInput.sort(function (a, b) {
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StartupStore startupStore
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
let seedPhrase = root.startupStore.getSeedPhrase()
|
||||||
|
if(seedPhrase.length === 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
d.seedPhraseModel = seedPhrase.split(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property var seedPhraseModel: []
|
||||||
|
readonly property int numOfColumns: 4
|
||||||
|
readonly property int rowSpacing: Style.current.bigPadding
|
||||||
|
readonly property int columnSpacing: Style.current.bigPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
Keys.onPressed: {
|
||||||
|
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
|
||||||
|
event.accepted = true
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: footerWrapper.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: title
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize1
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
text: qsTr("Write down your seed phrase")
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: info
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize3
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
text: qsTr("You will need this to recover your Keycard if you loose\nyour PIN of if the wrong PIN is entered five times in a row.")
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: grid
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
columns: d.numOfColumns
|
||||||
|
rowSpacing: d.rowSpacing
|
||||||
|
columnSpacing: d.columnSpacing
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: d.seedPhraseModel
|
||||||
|
delegate: Item {
|
||||||
|
Layout.preferredWidth: Constants.keycard.general.seedPhraseCellWidth
|
||||||
|
Layout.preferredHeight: Constants.keycard.general.seedPhraseCellHeight
|
||||||
|
StatusBaseText {
|
||||||
|
id: wordNumber
|
||||||
|
width: Constants.keycard.general.seedPhraseCellNumberWidth
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Qt.AlignRight
|
||||||
|
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
text: "%1.".arg(model.index + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: word
|
||||||
|
anchors.left: wordNumber.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: Style.current.xlPadding
|
||||||
|
horizontalAlignment: Qt.AlignLeft
|
||||||
|
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
text: model.modelData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: footerWrapper
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: Constants.keycard.general.footerWrapperHeight
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
text: qsTr("Next")
|
||||||
|
focus: true
|
||||||
|
onClicked: {
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,187 @@
|
||||||
|
import QtQuick 2.13
|
||||||
|
import QtQuick.Layouts 1.12
|
||||||
|
import QtQuick.Controls 2.13
|
||||||
|
|
||||||
|
import StatusQ.Core 0.1
|
||||||
|
import StatusQ.Core.Theme 0.1
|
||||||
|
import StatusQ.Controls 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
import "../stores"
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property StartupStore startupStore
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
d.dirtyWordAtIndex = [false, false, false, false]
|
||||||
|
d.validWordAtIndex = [false, false, false, false]
|
||||||
|
d.anyInputDirty = false
|
||||||
|
d.allEntriesValid = false
|
||||||
|
|
||||||
|
let seedPhrase = root.startupStore.getSeedPhrase()
|
||||||
|
if(seedPhrase.length === 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
d.seedPhrases = seedPhrase.split(" ")
|
||||||
|
|
||||||
|
let numbers = []
|
||||||
|
while (numbers.length < 4) {
|
||||||
|
let randomNo = Math.floor(Math.random() * 12)
|
||||||
|
if(numbers.indexOf(randomNo) == -1)
|
||||||
|
numbers.push(randomNo)
|
||||||
|
}
|
||||||
|
numbers.sort((a, b) => { return a < b? -1 : a > b? 1 : 0 })
|
||||||
|
d.wordNumbers = numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
property var seedPhrases: []
|
||||||
|
property var wordNumbers: []
|
||||||
|
property var dirtyWordAtIndex: [false, false, false, false]
|
||||||
|
property var validWordAtIndex: [false, false, false, false]
|
||||||
|
property bool anyInputDirty: false
|
||||||
|
property bool allEntriesValid: false
|
||||||
|
readonly property int numOfColumns: 4
|
||||||
|
readonly property int rowSpacing: Style.current.bigPadding
|
||||||
|
readonly property int columnSpacing: Style.current.bigPadding
|
||||||
|
|
||||||
|
function updateValidity(index, valid, dirty) {
|
||||||
|
dirtyWordAtIndex[index] = dirty
|
||||||
|
validWordAtIndex[index] = valid
|
||||||
|
let anyDirty = false
|
||||||
|
let allValid = true
|
||||||
|
for(let i = 0; i < validWordAtIndex.length; ++i) {
|
||||||
|
allValid = allValid && validWordAtIndex[i]
|
||||||
|
anyDirty = anyDirty || (dirtyWordAtIndex[index] && !validWordAtIndex[index])
|
||||||
|
}
|
||||||
|
allEntriesValid = allValid
|
||||||
|
anyInputDirty = anyDirty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: footerWrapper.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Style.current.padding
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: title
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize1
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
text: qsTr("Enter seed phrase words")
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
id: grid
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
columns: d.numOfColumns
|
||||||
|
rowSpacing: d.rowSpacing
|
||||||
|
columnSpacing: d.columnSpacing
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
for (var i = 0; i < children.length - 1; ++i) {
|
||||||
|
if(children[i].inputField && children[i+1].inputField){
|
||||||
|
children[i].inputField.input.tabNavItem = children[i+1].inputField.input.edit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: d.wordNumbers
|
||||||
|
delegate: Item {
|
||||||
|
Layout.preferredWidth: Constants.keycard.general.seedPhraseCellWidth
|
||||||
|
Layout.preferredHeight: Constants.keycard.general.seedPhraseCellHeight
|
||||||
|
|
||||||
|
property alias inputField: word
|
||||||
|
property alias wN: wordNumber
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: wordNumber
|
||||||
|
width: Constants.keycard.general.seedPhraseCellNumberWidth
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
horizontalAlignment: Qt.AlignRight
|
||||||
|
font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
|
||||||
|
color: Theme.palette.directColor1
|
||||||
|
text: "%1.".arg(model.modelData + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusInput {
|
||||||
|
id: word
|
||||||
|
anchors.left: wordNumber.right
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.leftMargin: Style.current.xlPadding
|
||||||
|
input.edit.font.pixelSize: Constants.keycard.general.seedPhraseCellFontSize
|
||||||
|
input.acceptReturn: true
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
if(text.length == 0)
|
||||||
|
return
|
||||||
|
if(/(^\s|^\r|^\n)|(\s$|^\r$|^\n$)/.test(text)) {
|
||||||
|
text = text.trim()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
else if(/\s|\r|\n/.test(text)) {
|
||||||
|
text = ""
|
||||||
|
return
|
||||||
|
}
|
||||||
|
valid = d.seedPhrases[model.modelData] === text
|
||||||
|
d.updateValidity(index, valid, text !== "")
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyPressed: {
|
||||||
|
if (d.allEntriesValid &&
|
||||||
|
(input.edit.keyEvent === Qt.Key_Return ||
|
||||||
|
input.edit.keyEvent === Qt.Key_Enter)) {
|
||||||
|
event.accepted = true
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusBaseText {
|
||||||
|
id: info
|
||||||
|
Layout.alignment: Qt.AlignHCenter
|
||||||
|
font.pixelSize: Constants.keycard.general.fontSize3
|
||||||
|
color: Theme.palette.dangerColor1
|
||||||
|
horizontalAlignment: Qt.AlignHCenter
|
||||||
|
text: d.anyInputDirty && !d.allEntriesValid? qsTr("Invalid word") : ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: footerWrapper
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
height: Constants.keycard.general.footerWrapperHeight
|
||||||
|
|
||||||
|
StatusButton {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.topMargin: Style.current.padding
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
enabled: d.allEntriesValid
|
||||||
|
text: qsTr("Next")
|
||||||
|
onClicked: {
|
||||||
|
root.startupStore.doPrimaryAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,7 +62,11 @@ Item {
|
||||||
anchors.top: txtTitle.bottom
|
anchors.top: txtTitle.bottom
|
||||||
anchors.topMargin: Style.current.padding
|
anchors.topMargin: Style.current.padding
|
||||||
color: Style.current.secondaryText
|
color: Style.current.secondaryText
|
||||||
text: qsTr("Would you like to use Touch ID\nto login to Status?")
|
text: root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserNewKeycardKeys ||
|
||||||
|
root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhraseIntoKeycard ||
|
||||||
|
root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunOldUserKeycardImport?
|
||||||
|
qsTr("Would you like to use TouchID instead of a PIN code\nto login to Status using your Keycard?") :
|
||||||
|
qsTr("Would you like to use Touch ID\nto login to Status?")
|
||||||
horizontalAlignment: Text.AlignHCenter
|
horizontalAlignment: Text.AlignHCenter
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
|
@ -86,7 +90,11 @@ Item {
|
||||||
objectName: "touchIdIPreferToUseMyPasswordText"
|
objectName: "touchIdIPreferToUseMyPasswordText"
|
||||||
Layout.alignment: Qt.AlignHCenter
|
Layout.alignment: Qt.AlignHCenter
|
||||||
color: Theme.palette.primaryColor1
|
color: Theme.palette.primaryColor1
|
||||||
text: qsTr("I prefer to use my password")
|
text: root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserNewKeycardKeys ||
|
||||||
|
root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunNewUserImportSeedPhraseIntoKeycard ||
|
||||||
|
root.startupStore.currentStartupState.flowType === Constants.startupFlow.firstRunOldUserKeycardImport?
|
||||||
|
qsTr("I prefer to use my PIN") :
|
||||||
|
qsTr("I prefer to use my password")
|
||||||
font.pixelSize: 15
|
font.pixelSize: 15
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
|
@ -25,7 +25,7 @@ StatusModal {
|
||||||
function onChangePasswordResponse(success, errorMsg) {
|
function onChangePasswordResponse(success, errorMsg) {
|
||||||
if (success) {
|
if (success) {
|
||||||
if (Qt.platform.os === "osx") {
|
if (Qt.platform.os === "osx") {
|
||||||
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore;
|
localAccountSettings.storeToKeychainValue = Constants.keychain.storedValue.store;
|
||||||
root.privacyStore.storeToKeyChain(d.passwordProcessing);
|
root.privacyStore.storeToKeyChain(d.passwordProcessing);
|
||||||
}
|
}
|
||||||
passwordChanged()
|
passwordChanged()
|
||||||
|
|
|
@ -31,7 +31,6 @@ QtObject {
|
||||||
readonly property string nodeManagement: "nodeManagement"
|
readonly property string nodeManagement: "nodeManagement"
|
||||||
readonly property string onlineUsers: "onlineUsers"
|
readonly property string onlineUsers: "onlineUsers"
|
||||||
readonly property string gifWidget: "gifWidget"
|
readonly property string gifWidget: "gifWidget"
|
||||||
readonly property string keycard: "keycard"
|
|
||||||
readonly property string communityHistoryArchiveSupport: "communityHistoryArchiveSupport"
|
readonly property string communityHistoryArchiveSupport: "communityHistoryArchiveSupport"
|
||||||
readonly property string communitiesPortal: "communitiesPortal"
|
readonly property string communitiesPortal: "communitiesPortal"
|
||||||
}
|
}
|
||||||
|
@ -131,9 +130,5 @@ QtObject {
|
||||||
else if (feature === experimentalFeatures.gifWidget) {
|
else if (feature === experimentalFeatures.gifWidget) {
|
||||||
localAccountSensitiveSettings.isGifWidgetEnabled = !localAccountSensitiveSettings.isGifWidgetEnabled
|
localAccountSensitiveSettings.isGifWidgetEnabled = !localAccountSensitiveSettings.isGifWidgetEnabled
|
||||||
}
|
}
|
||||||
else if (feature === experimentalFeatures.keycard) {
|
|
||||||
localAccountSettings.isKeycardEnabled = !localAccountSettings.isKeycardEnabled
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -163,18 +163,6 @@ SettingsContentBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: replace with StatusQ component
|
|
||||||
StatusSettingsLineButton {
|
|
||||||
anchors.leftMargin: 0
|
|
||||||
anchors.rightMargin: 0
|
|
||||||
text: qsTr("Keycard")
|
|
||||||
isSwitch: true
|
|
||||||
switchChecked: localAccountSettings.isKeycardEnabled
|
|
||||||
onClicked: {
|
|
||||||
root.advancedStore.toggleExperimentalFeature(root.advancedStore.experimentalFeatures.keycard)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusSectionHeadline {
|
StatusSectionHeadline {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
|
|
|
@ -41,7 +41,7 @@ ColumnLayout {
|
||||||
if (biometricsSwitch.checked)
|
if (biometricsSwitch.checked)
|
||||||
Global.openPopup(storePasswordModal)
|
Global.openPopup(storePasswordModal)
|
||||||
else
|
else
|
||||||
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueNever;
|
localAccountSettings.storeToKeychainValue = Constants.keychain.storedValue.never;
|
||||||
}
|
}
|
||||||
|
|
||||||
function offerToStorePassword(password, runStoreToKeyChainPopup)
|
function offerToStorePassword(password, runStoreToKeyChainPopup)
|
||||||
|
@ -49,7 +49,7 @@ ColumnLayout {
|
||||||
if (Qt.platform.os !== "osx")
|
if (Qt.platform.os !== "osx")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
localAccountSettings.storeToKeychainValue = Constants.storeToKeychainValueStore;
|
localAccountSettings.storeToKeychainValue = Constants.keychain.storedValue.store;
|
||||||
root.privacyStore.storeToKeyChain(password);
|
root.privacyStore.storeToKeyChain(password);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ ColumnLayout {
|
||||||
components: [ StatusSwitch {
|
components: [ StatusSwitch {
|
||||||
id: biometricsSwitch
|
id: biometricsSwitch
|
||||||
horizontalPadding: 0
|
horizontalPadding: 0
|
||||||
readonly property bool currentStoredValue: localAccountSettings.storeToKeychainValue === Constants.storeToKeychainValueStore
|
readonly property bool currentStoredValue: localAccountSettings.storeToKeychainValue === Constants.keychain.storedValue.store
|
||||||
checked: currentStoredValue
|
checked: currentStoredValue
|
||||||
} ]
|
} ]
|
||||||
sensor.onClicked: biometricsSwitch.toggle()
|
sensor.onClicked: biometricsSwitch.toggle()
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="90" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#FF2D55"/>
|
||||||
|
</g>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<path d="M108.499 88.0308C108.225 87.9967 108 87.7761 108 87.5L108 84C108 81.7909 106.209 80 104 80C101.791 80 100 81.7909 100 84L100 87.5C100 87.7761 99.775 87.9967 99.501 88.0308C97.5274 88.2764 96 89.9598 96 92L96 96C96 98.2091 97.7909 100 100 100L108 100C110.209 100 112 98.2091 112 96L112 92C112 89.9598 110.473 88.2764 108.499 88.0308ZM110.5 92C110.5 90.6193 109.381 89.5 108 89.5L100 89.5C98.6193 89.5 97.5 90.6193 97.5 92L97.5 96C97.5 97.3807 98.6193 98.5 100 98.5L108 98.5C109.381 98.5 110.5 97.3807 110.5 96L110.5 92ZM106 88C106.276 88 106.5 87.7761 106.5 87.5L106.5 84C106.5 82.6193 105.381 81.5 104 81.5C102.619 81.5 101.5 82.6193 101.5 84L101.5 87.5C101.5 87.7761 101.724 88 102 88L106 88ZM104.984 94.1319C104.85 94.2491 104.75 94.4092 104.75 94.5877L104.75 96C104.75 96.4142 104.414 96.75 104 96.75C103.586 96.75 103.25 96.4142 103.25 96L103.25 94.5877C103.25 94.4092 103.15 94.2491 103.016 94.1319C102.7 93.8569 102.5 93.4518 102.5 93C102.5 92.1716 103.172 91.5 104 91.5C104.828 91.5 105.5 92.1716 105.5 93C105.5 93.4518 105.3 93.8569 104.984 94.1319Z" fill="black"/>
|
||||||
|
<path d="M108.499 88.0308C108.225 87.9967 108 87.7761 108 87.5L108 84C108 81.7909 106.209 80 104 80C101.791 80 100 81.7909 100 84L100 87.5C100 87.7761 99.775 87.9967 99.501 88.0308C97.5274 88.2764 96 89.9598 96 92L96 96C96 98.2091 97.7909 100 100 100L108 100C110.209 100 112 98.2091 112 96L112 92C112 89.9598 110.473 88.2764 108.499 88.0308ZM110.5 92C110.5 90.6193 109.381 89.5 108 89.5L100 89.5C98.6193 89.5 97.5 90.6193 97.5 92L97.5 96C97.5 97.3807 98.6193 98.5 100 98.5L108 98.5C109.381 98.5 110.5 97.3807 110.5 96L110.5 92ZM106 88C106.276 88 106.5 87.7761 106.5 87.5L106.5 84C106.5 82.6193 105.381 81.5 104 81.5C102.619 81.5 101.5 82.6193 101.5 84L101.5 87.5C101.5 87.7761 101.724 88 102 88L106 88ZM104.984 94.1319C104.85 94.2491 104.75 94.4092 104.75 94.5877L104.75 96C104.75 96.4142 104.414 96.75 104 96.75C103.586 96.75 103.25 96.4142 103.25 96L103.25 94.5877C103.25 94.4092 103.15 94.2491 103.016 94.1319C102.7 93.8569 102.5 93.4518 102.5 93C102.5 92.1716 103.172 91.5 104 91.5C104.828 91.5 105.5 92.1716 105.5 93C105.5 93.4518 105.3 93.8569 104.984 94.1319Z" stroke="black"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1,24 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="90" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#FF2D55"/>
|
||||||
|
</g>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<path d="M108.499 88.0308C108.225 87.9967 108 87.7761 108 87.5L108 84C108 81.7909 106.209 80 104 80C101.791 80 100 81.7909 100 84L100 87.5C100 87.7761 99.775 87.9967 99.501 88.0308C97.5274 88.2764 96 89.9598 96 92L96 96C96 98.2091 97.7909 100 100 100L108 100C110.209 100 112 98.2091 112 96L112 92C112 89.9598 110.473 88.2764 108.499 88.0308ZM110.5 92C110.5 90.6193 109.381 89.5 108 89.5L100 89.5C98.6193 89.5 97.5 90.6193 97.5 92L97.5 96C97.5 97.3807 98.6193 98.5 100 98.5L108 98.5C109.381 98.5 110.5 97.3807 110.5 96L110.5 92ZM106 88C106.276 88 106.5 87.7761 106.5 87.5L106.5 84C106.5 82.6193 105.381 81.5 104 81.5C102.619 81.5 101.5 82.6193 101.5 84L101.5 87.5C101.5 87.7761 101.724 88 102 88L106 88ZM104.984 94.1319C104.85 94.2491 104.75 94.4092 104.75 94.5877L104.75 96C104.75 96.4142 104.414 96.75 104 96.75C103.586 96.75 103.25 96.4142 103.25 96L103.25 94.5877C103.25 94.4092 103.15 94.2491 103.016 94.1319C102.7 93.8569 102.5 93.4518 102.5 93C102.5 92.1716 103.172 91.5 104 91.5C104.828 91.5 105.5 92.1716 105.5 93C105.5 93.4518 105.3 93.8569 104.984 94.1319Z" fill="black"/>
|
||||||
|
<path d="M108.499 88.0308C108.225 87.9967 108 87.7761 108 87.5L108 84C108 81.7909 106.209 80 104 80C101.791 80 100 81.7909 100 84L100 87.5C100 87.7761 99.775 87.9967 99.501 88.0308C97.5274 88.2764 96 89.9598 96 92L96 96C96 98.2091 97.7909 100 100 100L108 100C110.209 100 112 98.2091 112 96L112 92C112 89.9598 110.473 88.2764 108.499 88.0308ZM110.5 92C110.5 90.6193 109.381 89.5 108 89.5L100 89.5C98.6193 89.5 97.5 90.6193 97.5 92L97.5 96C97.5 97.3807 98.6193 98.5 100 98.5L108 98.5C109.381 98.5 110.5 97.3807 110.5 96L110.5 92ZM106 88C106.276 88 106.5 87.7761 106.5 87.5L106.5 84C106.5 82.6193 105.381 81.5 104 81.5C102.619 81.5 101.5 82.6193 101.5 84L101.5 87.5C101.5 87.7761 101.724 88 102 88L106 88ZM104.984 94.1319C104.85 94.2491 104.75 94.4092 104.75 94.5877L104.75 96C104.75 96.4142 104.414 96.75 104 96.75C103.586 96.75 103.25 96.4142 103.25 96L103.25 94.5877C103.25 94.4092 103.15 94.2491 103.016 94.1319C102.7 93.8569 102.5 93.4518 102.5 93C102.5 92.1716 103.172 91.5 104 91.5C104.828 91.5 105.5 92.1716 105.5 93C105.5 93.4518 105.3 93.8569 104.984 94.1319Z" stroke="black"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.7 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="90" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#27D8B9"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="white"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="90" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#27D8B9"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="white"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="100" cy="100" r="90" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="100" cy="100" r="63" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="100" cy="100" r="36" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_11634_22687)">
|
||||||
|
<path d="M60 81.8889C60 76.9797 63.9797 73 68.8889 73H131.111C136.02 73 140 76.9797 140 81.8889V117.444C140 122.354 136.02 126.333 131.111 126.333H68.8889C63.9797 126.333 60 122.354 60 117.444V81.8889Z" fill="black"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M111.274 99.4963C108 98.8057 105.555 95.8626 105.555 92.393C105.555 88.4189 108.761 85.2227 112.748 85.2227C116.734 85.2227 119.941 88.4189 119.941 92.393C120.015 95.8031 117.6 98.7046 114.394 99.4588V106.647H114.567L118.38 102.155H122.02L117.514 107.338L122.28 114.335H118.9L115.434 109.324L114.394 110.534V114.335H111.274V99.4963ZM108.415 92.393C108.415 90.0606 110.321 88.0733 112.748 88.0733C115.174 88.0733 117.168 90.0601 117.081 92.393C117.081 94.7248 115.174 96.712 112.747 96.712C110.408 96.712 108.415 94.8114 108.415 92.393Z" fill="#FF2D55"/>
|
||||||
|
<path d="M73.334 95.2222C73.334 93.9949 74.3289 93 75.5562 93H82.2229C83.4502 93 84.4451 93.9949 84.4451 95.2222V98.5556C84.4451 99.7829 83.4502 100.778 82.2229 100.778H75.5562C74.3289 100.778 73.334 99.7829 73.334 98.5556V95.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_11634_22687" x="44" y="59" width="112" height="85.332" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_11634_22687"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_11634_22687" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
<svg width="200" height="200" viewBox="0 0 200 200" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<circle cx="100" cy="100" r="90" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="100" cy="100" r="63" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<circle cx="100" cy="100" r="36" fill="#FF2D55" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_11634_22687)">
|
||||||
|
<path d="M60 81.8889C60 76.9797 63.9797 73 68.8889 73H131.111C136.02 73 140 76.9797 140 81.8889V117.444C140 122.354 136.02 126.333 131.111 126.333H68.8889C63.9797 126.333 60 122.354 60 117.444V81.8889Z" fill="black"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M111.274 99.4963C108 98.8057 105.555 95.8626 105.555 92.393C105.555 88.4189 108.761 85.2227 112.748 85.2227C116.734 85.2227 119.941 88.4189 119.941 92.393C120.015 95.8031 117.6 98.7046 114.394 99.4588V106.647H114.567L118.38 102.155H122.02L117.514 107.338L122.28 114.335H118.9L115.434 109.324L114.394 110.534V114.335H111.274V99.4963ZM108.415 92.393C108.415 90.0606 110.321 88.0733 112.748 88.0733C115.174 88.0733 117.168 90.0601 117.081 92.393C117.081 94.7248 115.174 96.712 112.747 96.712C110.408 96.712 108.415 94.8114 108.415 92.393Z" fill="#FF2D55"/>
|
||||||
|
<path d="M73.334 95.2222C73.334 93.9949 74.3289 93 75.5562 93H82.2229C83.4502 93 84.4451 93.9949 84.4451 95.2222V98.5556C84.4451 99.7829 83.4502 100.778 82.2229 100.778H75.5562C74.3289 100.778 73.334 99.7829 73.334 98.5556V95.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_11634_22687" x="44" y="59" width="112" height="85.332" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_11634_22687"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_11634_22687" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,20 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,20 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,21 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.0 KiB |
|
@ -0,0 +1,22 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,22 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="90" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,23 @@
|
||||||
|
<svg width="180" height="180" viewBox="0 0 180 180" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="180" height="180" fill="#E5E5E5"/>
|
||||||
|
<rect x="-526" y="-222" width="1232" height="770" rx="5" fill="white"/>
|
||||||
|
<circle cx="90" cy="90" r="90" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="63" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<circle cx="90" cy="90" r="36" fill="#4360DF" fill-opacity="0.1"/>
|
||||||
|
<g filter="url(#filter0_d_1023_298171)">
|
||||||
|
<path d="M50 71.8889C50 66.9797 53.9797 63 58.8889 63H121.111C126.02 63 130 66.9797 130 71.8889V107.444C130 112.354 126.02 116.333 121.111 116.333H58.8889C53.9797 116.333 50 112.354 50 107.444V71.8889Z" fill="#2D2D2D"/>
|
||||||
|
</g>
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M101.274 89.4963C97.9997 88.8057 95.5547 85.8626 95.5547 82.393C95.5547 78.4189 98.7613 75.2227 102.748 75.2227C106.734 75.2227 109.941 78.4189 109.941 82.393C110.015 85.8031 107.6 88.7046 104.394 89.4588V96.6465H104.567L108.38 92.1547H112.02L107.514 97.3382L112.28 104.335H108.9L105.434 99.3244L104.394 100.534V104.335H101.274V89.4963ZM98.4147 82.393C98.4147 80.0606 100.321 78.0733 102.748 78.0733C105.174 78.0733 107.168 80.0601 107.081 82.393C107.081 84.7248 105.174 86.712 102.747 86.712C100.408 86.712 98.4147 84.8114 98.4147 82.393Z" fill="#27D8B9"/>
|
||||||
|
<path d="M63.334 85.2222C63.334 83.9949 64.3289 83 65.5562 83H72.2229C73.4502 83 74.4451 83.9949 74.4451 85.2222V88.5556C74.4451 89.7829 73.4502 90.7778 72.2229 90.7778H65.5562C64.3289 90.7778 63.334 89.7829 63.334 88.5556V85.2222Z" fill="#F0CC73"/>
|
||||||
|
<defs>
|
||||||
|
<filter id="filter0_d_1023_298171" x="34" y="49" width="112" height="85.334" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||||
|
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||||
|
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||||
|
<feOffset dy="2"/>
|
||||||
|
<feGaussianBlur stdDeviation="8"/>
|
||||||
|
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0.0333333 0 0 0 0 0.1 0 0 0 0.12 0"/>
|
||||||
|
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1023_298171"/>
|
||||||
|
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1023_298171" result="shape"/>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
|
@ -1,72 +0,0 @@
|
||||||
import QtQuick 2.13
|
|
||||||
import QtQuick.Controls 2.13
|
|
||||||
import QtQuick.Layouts 1.13
|
|
||||||
|
|
||||||
import StatusQ.Core 0.1
|
|
||||||
import StatusQ.Core.Theme 0.1
|
|
||||||
import StatusQ.Controls 0.1
|
|
||||||
import StatusQ.Popups 0.1
|
|
||||||
|
|
||||||
StatusModal {
|
|
||||||
property var onCancel: function() {}
|
|
||||||
|
|
||||||
id: insertCard
|
|
||||||
anchors.centerIn: parent
|
|
||||||
|
|
||||||
showHeader: false
|
|
||||||
focus: visible
|
|
||||||
|
|
||||||
contentItem: Item {
|
|
||||||
width: insertCard.width
|
|
||||||
implicitHeight: childrenRect.height
|
|
||||||
Column {
|
|
||||||
width: parent.width - 32
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 16
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusBaseText {
|
|
||||||
text: qsTr("Please insert your Keycard to proceed or press the cancel button to cancel the operation")
|
|
||||||
font.pixelSize: 15
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
wrapMode: Text.WordWrap
|
|
||||||
color: Theme.palette.directColor1
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width
|
|
||||||
height: 16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
rightButtons: [
|
|
||||||
StatusButton {
|
|
||||||
id: cancelButton
|
|
||||||
text: qsTr("Cancel")
|
|
||||||
onClicked: {
|
|
||||||
insertCard.close()
|
|
||||||
onCancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
// Not Refactored Yet
|
|
||||||
// Connections {
|
|
||||||
// id: connection
|
|
||||||
// target: keycardModel
|
|
||||||
// ignoreUnknownSignals: true
|
|
||||||
|
|
||||||
// onCardConnected: {
|
|
||||||
// insertCard.close()
|
|
||||||
// }
|
|
||||||
|
|
||||||
// onCardDisconnected: {
|
|
||||||
// insertCard.open()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
|