Skip to content

Commit ff70aed

Browse files
committed
feat(Keycard): Adding a keycard info drawer for Android to guide the keycard interactions
Adding a `KeycardChannelDrawer` that's guiding the user whenever the keycard is needed. On IOS the system drawer is used
1 parent cd46a72 commit ff70aed

File tree

15 files changed

+1010
-66
lines changed

15 files changed

+1010
-66
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import QtQuick
2+
import QtQuick.Controls
3+
import QtQuick.Layouts
4+
5+
import Storybook
6+
7+
import StatusQ.Core
8+
import StatusQ.Core.Theme
9+
import StatusQ.Controls
10+
import StatusQ.Components
11+
12+
import shared.popups
13+
14+
SplitView {
15+
id: root
16+
17+
orientation: Qt.Horizontal
18+
19+
Logs { id: logs }
20+
21+
// Helper timers for test scenarios
22+
Timer {
23+
id: timer1
24+
interval: 1500
25+
onTriggered: {
26+
if (root.currentScenario === "success") {
27+
logs.logEvent("Changing to reading state")
28+
stateCombo.currentIndex = 2 // reading
29+
timer2.start()
30+
} else if (root.currentScenario === "error") {
31+
logs.logEvent("Changing to reading state")
32+
stateCombo.currentIndex = 2 // reading
33+
timer2.start()
34+
} else if (root.currentScenario === "quick") {
35+
logs.logEvent("Quick change to reading")
36+
stateCombo.currentIndex = 2 // reading
37+
timer2.start()
38+
}
39+
}
40+
}
41+
42+
Timer {
43+
id: timer2
44+
interval: root.currentScenario === "quick" ? 300 : 1500
45+
onTriggered: {
46+
if (root.currentScenario === "success") {
47+
logs.logEvent("Changing to idle state (success)")
48+
stateCombo.currentIndex = 0 // idle (will trigger success)
49+
} else if (root.currentScenario === "error") {
50+
logs.logEvent("Changing to error state")
51+
stateCombo.currentIndex = 3 // error
52+
} else if (root.currentScenario === "quick") {
53+
logs.logEvent("Quick change to idle (success)")
54+
stateCombo.currentIndex = 0 // idle
55+
}
56+
root.currentScenario = ""
57+
}
58+
}
59+
60+
property string currentScenario: ""
61+
62+
Item {
63+
SplitView.fillWidth: true
64+
SplitView.fillHeight: true
65+
66+
KeycardChannelDrawer {
67+
id: drawer
68+
69+
currentState: stateCombo.currentValue
70+
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
71+
72+
onDismissed: {
73+
logs.logEvent("KeycardChannelDrawer::dismissed()")
74+
}
75+
}
76+
}
77+
78+
LogsAndControlsPanel {
79+
id: logsAndControlsPanel
80+
81+
SplitView.preferredWidth: 350
82+
SplitView.fillHeight: true
83+
84+
logsView.logText: logs.logText
85+
86+
ColumnLayout {
87+
Layout.fillWidth: true
88+
spacing: Theme.padding
89+
90+
// State control section
91+
RowLayout {
92+
Layout.fillWidth: true
93+
spacing: Theme.halfPadding
94+
95+
Label {
96+
Layout.preferredWidth: 120
97+
text: "Current state:"
98+
}
99+
100+
ComboBox {
101+
id: stateCombo
102+
Layout.fillWidth: true
103+
104+
textRole: "text"
105+
valueRole: "value"
106+
107+
model: ListModel {
108+
ListElement { text: "Idle"; value: "idle" }
109+
ListElement { text: "Waiting for Keycard"; value: "waiting-for-keycard" }
110+
ListElement { text: "Reading"; value: "reading" }
111+
ListElement { text: "Error"; value: "error" }
112+
}
113+
114+
currentIndex: 0
115+
}
116+
}
117+
118+
// State info display
119+
Rectangle {
120+
Layout.fillWidth: true
121+
Layout.preferredHeight: infoColumn.implicitHeight + Theme.padding * 2
122+
color: Theme.palette.baseColor5
123+
radius: Theme.radius
124+
border.width: 1
125+
border.color: Theme.palette.baseColor2
126+
127+
ColumnLayout {
128+
id: infoColumn
129+
anchors.fill: parent
130+
anchors.margins: Theme.padding
131+
spacing: Theme.halfPadding
132+
133+
StatusBaseText {
134+
Layout.fillWidth: true
135+
text: "State Information"
136+
font.bold: true
137+
font.pixelSize: Theme.primaryTextFontSize
138+
}
139+
140+
StatusBaseText {
141+
Layout.fillWidth: true
142+
text: "Current: %1".arg(stateCombo.currentText)
143+
font.pixelSize: Theme.tertiaryTextFontSize
144+
color: Theme.palette.baseColor1
145+
}
146+
147+
StatusBaseText {
148+
Layout.fillWidth: true
149+
text: "Opened: %1".arg(drawer.opened ? "Yes" : "No")
150+
font.pixelSize: Theme.tertiaryTextFontSize
151+
color: Theme.palette.baseColor1
152+
}
153+
}
154+
}
155+
156+
// Scenario buttons section
157+
Label {
158+
Layout.fillWidth: true
159+
Layout.topMargin: Theme.padding
160+
text: "Test Scenarios:"
161+
font.bold: true
162+
}
163+
164+
Button {
165+
Layout.fillWidth: true
166+
text: "Simulate Success Flow"
167+
onClicked: {
168+
logs.logEvent("Starting success flow simulation")
169+
root.currentScenario = "success"
170+
stateCombo.currentIndex = 1 // waiting-for-keycard
171+
timer1.start()
172+
}
173+
}
174+
175+
Button {
176+
Layout.fillWidth: true
177+
text: "Simulate Error Flow"
178+
onClicked: {
179+
logs.logEvent("Starting error flow simulation")
180+
root.currentScenario = "error"
181+
stateCombo.currentIndex = 1 // waiting-for-keycard
182+
timer1.start()
183+
}
184+
}
185+
186+
Button {
187+
Layout.fillWidth: true
188+
text: "Simulate Quick State Changes"
189+
onClicked: {
190+
logs.logEvent("Testing state queue with rapid changes")
191+
root.currentScenario = "quick"
192+
stateCombo.currentIndex = 1 // waiting-for-keycard
193+
timer1.interval = 300
194+
timer1.start()
195+
}
196+
}
197+
198+
Button {
199+
Layout.fillWidth: true
200+
text: "Open Drawer Manually"
201+
onClicked: {
202+
logs.logEvent("Manually opening drawer")
203+
drawer.open()
204+
}
205+
}
206+
207+
Button {
208+
Layout.fillWidth: true
209+
text: "Clear Logs"
210+
onClicked: logs.clear()
211+
}
212+
213+
Item {
214+
Layout.fillHeight: true
215+
}
216+
}
217+
}
218+
}
219+
220+
// category: Popups
221+
// status: good
222+

ui/StatusQ/src/StatusQ/Controls/StatusPinInput.qml

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,13 @@ Item {
102102
*/
103103
property int additionalSpacing: 0
104104

105+
/*!
106+
\qmlproperty flags StatusPinInput::inputMethodHints
107+
This property allows you to customize the input method hints for the virtual keyboard.
108+
The default value is Qt.ImhNone which allows any input based on the validator.
109+
*/
110+
property int inputMethodHints: Qt.ImhNone
111+
105112
signal pinEditedManually()
106113

107114
QtObject {
@@ -158,9 +165,10 @@ Item {
158165
Convenient method to force active focus in case it gets stolen by any other component.
159166
*/
160167
function forceFocus() {
161-
if (Utils.isMobile)
162-
return
163168
inputText.forceActiveFocus()
169+
if (Qt.inputMethod.visible == false) {
170+
Qt.inputMethod.show()
171+
}
164172
d.activateBlink()
165173
}
166174

@@ -208,10 +216,14 @@ Item {
208216
TextInput {
209217
id: inputText
210218
objectName: "pinInputTextInput"
211-
visible: false
212-
focus: !Utils.isMobile
219+
visible: true
220+
// Set explicit dimensions for Android keyboard input to work
221+
width: 1
222+
height: 1
223+
opacity: 0
213224
maximumLength: root.pinLen
214-
validator: d.statusValidator.validatorObj
225+
inputMethodHints: root.inputMethodHints
226+
// validator: d.statusValidator.validatorObj
215227
onTextChanged: {
216228
// Modify state of current introduced character position:
217229
if(text.length >= (d.currentPinIndex + 1)) {

ui/app/AppLayouts/Onboarding/components/LoginKeycardBox.qml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ Control {
113113
objectName: "pinInput"
114114
validator: StatusIntValidator { bottom: 0; top: 999999 }
115115
visible: false
116+
inputMethodHints: Qt.ImhDigitsOnly
116117

117118
onPinInputChanged: {
118119
if (pinInput.length === 6) {
@@ -235,6 +236,7 @@ Control {
235236
PropertyChanges {
236237
target: pinInputField
237238
visible: true
239+
focus: true
238240
}
239241
PropertyChanges {
240242
target: background
@@ -251,4 +253,9 @@ Control {
251253
}
252254
}
253255
]
256+
257+
TapHandler {
258+
enabled: pinInputField.visible
259+
onTapped: pinInputField.forceFocus()
260+
}
254261
}

ui/app/AppLayouts/Onboarding/pages/KeycardEnterPinPage.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ KeycardBasePage {
161161
anchors.horizontalCenter: parent.horizontalCenter
162162
pinLen: Constants.keycard.general.keycardPinLength
163163
validator: StatusIntValidator { bottom: 0; top: 999999 }
164+
inputMethodHints: Qt.ImhDigitsOnly
164165
onPinInputChanged: {
165166
if (pinInput.pinInput.length === pinInput.pinLen) {
166167
root.authorizationRequested(pinInput.pinInput)

ui/i18n/qml_base_en.ts

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2693,6 +2693,26 @@ Do you wish to override the security check and continue?</source>
26932693
<source>Zoom</source>
26942694
<translation type="unfinished"></translation>
26952695
</message>
2696+
<message>
2697+
<source>Clear site data</source>
2698+
<translation type="unfinished"></translation>
2699+
</message>
2700+
<message>
2701+
<source>Use it to reset the current site if it doesn&apos;t load or work properly.</source>
2702+
<translation type="unfinished"></translation>
2703+
</message>
2704+
<message>
2705+
<source>Clearing cache...</source>
2706+
<translation type="unfinished"></translation>
2707+
</message>
2708+
<message>
2709+
<source>Clear cache</source>
2710+
<translation type="unfinished"></translation>
2711+
</message>
2712+
<message>
2713+
<source>Clears cached files, cookies, and history for the entire browser. Browsing is paused until it is done.</source>
2714+
<translation type="unfinished"></translation>
2715+
</message>
26962716
</context>
26972717
<context>
26982718
<name>BrowserTabView</name>
@@ -7622,13 +7642,6 @@ Please add it and try again.</source>
76227642
<translation type="unfinished"></translation>
76237643
</message>
76247644
</context>
7625-
<context>
7626-
<name>FeeRow</name>
7627-
<message>
7628-
<source>Max.</source>
7629-
<translation type="unfinished"></translation>
7630-
</message>
7631-
</context>
76327645
<context>
76337646
<name>FeesBox</name>
76347647
<message>
@@ -8891,6 +8904,45 @@ L2 fee: %2</source>
88918904
<translation type="unfinished"></translation>
88928905
</message>
88938906
</context>
8907+
<context>
8908+
<name>KeycardChannelDrawer</name>
8909+
<message>
8910+
<source>Insert Keycard</source>
8911+
<translation type="unfinished"></translation>
8912+
</message>
8913+
<message>
8914+
<source>Please tap your Keycard to the back of your device</source>
8915+
<translation type="unfinished"></translation>
8916+
</message>
8917+
<message>
8918+
<source>Reading Keycard</source>
8919+
<translation type="unfinished"></translation>
8920+
</message>
8921+
<message>
8922+
<source>Please keep your Keycard in place</source>
8923+
<translation type="unfinished"></translation>
8924+
</message>
8925+
<message>
8926+
<source>Keycard Error</source>
8927+
<translation type="unfinished"></translation>
8928+
</message>
8929+
<message>
8930+
<source>An error occurred. Please try again.</source>
8931+
<translation type="unfinished"></translation>
8932+
</message>
8933+
<message>
8934+
<source>Success</source>
8935+
<translation type="unfinished"></translation>
8936+
</message>
8937+
<message>
8938+
<source>Keycard operation completed successfully</source>
8939+
<translation type="unfinished"></translation>
8940+
</message>
8941+
<message>
8942+
<source>Dismiss</source>
8943+
<translation type="unfinished"></translation>
8944+
</message>
8945+
</context>
88948946
<context>
88958947
<name>KeycardConfirmation</name>
88968948
<message>
@@ -8986,10 +9038,6 @@ Are you sure you want to do this?</source>
89869038
<source>PIN correct</source>
89879039
<translation type="unfinished"></translation>
89889040
</message>
8989-
<message>
8990-
<source>Keycard blocked</source>
8991-
<translation type="unfinished"></translation>
8992-
</message>
89939041
<message numerus="yes">
89949042
<source>%n attempt(s) remaining</source>
89959043
<translation type="unfinished">

0 commit comments

Comments
 (0)