Skip to content

Commit 024ac2a

Browse files
committed
fix(efp): refactor following addresses UI and add storybook pages
Refactors FollowingAddresses components to use injected rootStore and activeNetworksModel, improving testability and modularity. Adds storybook pages for FollowingAddressMenu, FollowingAddressesDelegate, and FollowingAddressesView for interactive testing and development. Updates RootStore stubs and related stores to support new signals and mock data. Improves pagination, event handling, and UI consistency in the following addresses workflow.
1 parent 06a861e commit 024ac2a

File tree

13 files changed

+798
-116
lines changed

13 files changed

+798
-116
lines changed

src/app_service/service/following_address/service.nim

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,17 +84,20 @@ QtObject:
8484

8585
if not errorString.isEmptyOrWhitespace:
8686
error "onFollowingAddressesFetched got error from backend", errorString = errorString
87-
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, Args())
88-
raise newException(Exception, "Error fetching following addresses: " & errorString)
87+
let args = FollowingAddressesArgs(userAddress: userAddress, addresses: @[])
88+
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, args)
89+
return
8990
if followingAddressesJson.isNil or followingAddressesJson.kind == JNull:
9091
warn "onFollowingAddressesFetched: followingAddressesJson is nil or null"
91-
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, Args())
92+
let args = FollowingAddressesArgs(userAddress: userAddress, addresses: @[])
93+
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, args)
9294
return
9395

9496
discard followingAddressesJson.getProp("result", followingResult)
9597
if followingResult.isNil or followingResult.kind == JNull:
9698
warn "onFollowingAddressesFetched: followingResult is nil or null"
97-
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, Args())
99+
let args = FollowingAddressesArgs(userAddress: userAddress, addresses: @[])
100+
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, args)
98101
return
99102

100103
let addresses = followingResult.getElems().map(proc(x: JsonNode): FollowingAddressDto = x.toFollowingAddressDto())
@@ -107,8 +110,9 @@ QtObject:
107110
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, args)
108111

109112
except Exception as e:
110-
error "onFollowingAddressesFetched exception", msg = e.msg, stack = e.getStackTrace()
111-
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, Args())
113+
error "onFollowingAddressesFetched exception", msg = e.msg
114+
let args = FollowingAddressesArgs(userAddress: "", addresses: @[])
115+
self.events.emit(SIGNAL_FOLLOWING_ADDRESSES_UPDATED, args)
112116

113117
proc getTotalFollowingCount*(self: Service): int =
114118
return self.totalFollowingCount
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import QtQuick
2+
import QtQuick.Controls
3+
import QtQuick.Layouts
4+
5+
import StatusQ.Core.Theme
6+
7+
import AppLayouts.Wallet.controls
8+
import AppLayouts.Wallet.stores as WalletStores
9+
10+
import Models
11+
import Storybook
12+
import utils
13+
14+
SplitView {
15+
id: root
16+
17+
Logs { id: logs }
18+
19+
Rectangle {
20+
SplitView.fillWidth: true
21+
SplitView.fillHeight: true
22+
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
23+
24+
Button {
25+
anchors.centerIn: parent
26+
text: "Show Following Address Menu"
27+
onClicked: menu.popup()
28+
}
29+
30+
FollowingAddressMenu {
31+
id: menu
32+
anchors.centerIn: parent
33+
34+
// Pass mock rootStore (using singleton stub)
35+
rootStore: WalletStores.RootStore
36+
37+
name: nameField.text
38+
address: addressField.text
39+
ensName: ensField.text
40+
tags: tagsField.text.split(",").map(t => t.trim()).filter(t => t.length > 0)
41+
42+
activeNetworksModel: NetworksModel.flatNetworks
43+
}
44+
}
45+
46+
Pane {
47+
SplitView.minimumWidth: 350
48+
SplitView.preferredWidth: 350
49+
50+
ScrollView {
51+
anchors.fill: parent
52+
53+
ColumnLayout {
54+
spacing: 12
55+
width: parent.width
56+
57+
Label {
58+
text: "Name:"
59+
font.bold: true
60+
}
61+
TextField {
62+
id: nameField
63+
Layout.fillWidth: true
64+
text: "vitalik.eth"
65+
placeholderText: "Name or ENS"
66+
}
67+
68+
Label {
69+
text: "Address:"
70+
font.bold: true
71+
}
72+
TextField {
73+
id: addressField
74+
Layout.fillWidth: true
75+
text: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
76+
placeholderText: "0x..."
77+
}
78+
79+
Label {
80+
text: "ENS Name:"
81+
font.bold: true
82+
}
83+
TextField {
84+
id: ensField
85+
Layout.fillWidth: true
86+
text: "vitalik.eth"
87+
placeholderText: "name.eth"
88+
}
89+
90+
Label {
91+
text: "Tags (comma separated):"
92+
font.bold: true
93+
}
94+
TextField {
95+
id: tagsField
96+
Layout.fillWidth: true
97+
text: "friend, developer"
98+
placeholderText: "tag1, tag2"
99+
}
100+
101+
Rectangle {
102+
Layout.fillWidth: true
103+
height: 1
104+
color: Theme.palette.baseColor2
105+
}
106+
107+
Label {
108+
text: "Event Log:"
109+
font.bold: true
110+
}
111+
112+
LogsView {
113+
Layout.fillWidth: true
114+
Layout.preferredHeight: 150
115+
logText: logs.logText
116+
}
117+
}
118+
}
119+
}
120+
}
121+
122+
// category: Controls
123+
// status: good
124+
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import QtQuick
2+
import QtQuick.Controls
3+
import QtQuick.Layouts
4+
5+
import StatusQ.Core.Theme
6+
7+
import AppLayouts.Wallet.controls
8+
import AppLayouts.Wallet.stores as WalletStores
9+
import shared.stores as SharedStores
10+
11+
import Models
12+
import Storybook
13+
import utils
14+
15+
SplitView {
16+
id: root
17+
18+
Logs { id: logs }
19+
20+
SplitView {
21+
orientation: Qt.Vertical
22+
SplitView.fillWidth: true
23+
SplitView.fillHeight: true
24+
25+
Rectangle {
26+
SplitView.fillWidth: true
27+
SplitView.fillHeight: true
28+
color: Theme.palette.baseColor3
29+
30+
FollowingAddressesDelegate {
31+
id: delegate
32+
33+
anchors.centerIn: parent
34+
width: 600
35+
36+
// Properties
37+
title: titleField.text
38+
address: addressField.text
39+
ensName: ensField.text
40+
tags: tagsField.text.split(",").map(t => t.trim()).filter(t => t.length > 0)
41+
avatar: avatarField.text
42+
43+
// Stores (mock)
44+
rootStore: WalletStores.RootStore
45+
networkConnectionStore: SharedStores.NetworkConnectionStore {}
46+
activeNetworksModel: NetworksModel.flatNetworks
47+
48+
// Signals
49+
onClicked: logs.logEvent("delegate clicked")
50+
onMenuRequested: (name, address, ensName, tags) => {
51+
logs.logEvent("menuRequested: name=%1, address=%2, ens=%3, tags=%4"
52+
.arg(name).arg(address).arg(ensName).arg(tags.join(",")))
53+
}
54+
}
55+
}
56+
57+
LogsView {
58+
SplitView.preferredHeight: 150
59+
SplitView.fillWidth: true
60+
logText: logs.logText
61+
}
62+
}
63+
64+
Pane {
65+
SplitView.minimumWidth: 350
66+
SplitView.preferredWidth: 350
67+
68+
ScrollView {
69+
anchors.fill: parent
70+
clip: true
71+
72+
ColumnLayout {
73+
spacing: 12
74+
width: parent.width - 20
75+
76+
Label {
77+
text: "Delegate Properties"
78+
font.pixelSize: 18
79+
font.bold: true
80+
}
81+
82+
Rectangle {
83+
Layout.fillWidth: true
84+
height: 1
85+
color: Theme.palette.baseColor2
86+
}
87+
88+
Label {
89+
text: "Title:"
90+
font.bold: true
91+
}
92+
TextField {
93+
id: titleField
94+
Layout.fillWidth: true
95+
text: "vitalik.eth"
96+
placeholderText: "Display name"
97+
}
98+
99+
Label {
100+
text: "Address:"
101+
font.bold: true
102+
}
103+
TextField {
104+
id: addressField
105+
Layout.fillWidth: true
106+
text: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
107+
placeholderText: "0x..."
108+
}
109+
110+
Label {
111+
text: "ENS Name:"
112+
font.bold: true
113+
}
114+
TextField {
115+
id: ensField
116+
Layout.fillWidth: true
117+
text: "vitalik.eth"
118+
placeholderText: "name.eth"
119+
}
120+
121+
Label {
122+
text: "Tags (comma separated):"
123+
font.bold: true
124+
}
125+
TextField {
126+
id: tagsField
127+
Layout.fillWidth: true
128+
text: "friend, developer, ethereum"
129+
placeholderText: "tag1, tag2, tag3"
130+
}
131+
132+
Label {
133+
text: "Avatar (icon name):"
134+
font.bold: true
135+
}
136+
TextField {
137+
id: avatarField
138+
Layout.fillWidth: true
139+
text: ""
140+
placeholderText: "Leave empty for default"
141+
}
142+
143+
Rectangle {
144+
Layout.fillWidth: true
145+
height: 1
146+
color: Theme.palette.baseColor2
147+
}
148+
149+
Label {
150+
text: "Test Scenarios:"
151+
font.pixelSize: 16
152+
font.bold: true
153+
}
154+
155+
Button {
156+
Layout.fillWidth: true
157+
text: "Load: Saved Address (ends with 5c)"
158+
onClicked: {
159+
addressField.text = "0x929d0D5Cbc5228543Fa9b7df766CFf42C8c8975c"
160+
titleField.text = "Mock Saved Name"
161+
ensField.text = ""
162+
}
163+
}
164+
165+
Button {
166+
Layout.fillWidth: true
167+
text: "Load: ENS with No Save"
168+
onClicked: {
169+
addressField.text = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045"
170+
titleField.text = "vitalik.eth"
171+
ensField.text = "vitalik.eth"
172+
}
173+
}
174+
175+
Button {
176+
Layout.fillWidth: true
177+
text: "Load: Address Only"
178+
onClicked: {
179+
addressField.text = "0x1234567890123456789012345678901234567890"
180+
titleField.text = "0x1234567890123456789012345678901234567890"
181+
ensField.text = ""
182+
}
183+
}
184+
185+
Rectangle {
186+
Layout.fillWidth: true
187+
height: 1
188+
color: Theme.palette.baseColor2
189+
}
190+
191+
Label {
192+
text: "Tips:"
193+
font.bold: true
194+
}
195+
196+
Label {
197+
Layout.fillWidth: true
198+
text: "• Addresses ending in '5c' or '42' are mocked as 'saved'\n" +
199+
"• Click delegate to open activity popup\n" +
200+
"• Click menu button to test menu signal\n" +
201+
"• Click star to test save/unsave\n" +
202+
"• Avatar defaults to letter identicon if empty"
203+
wrapMode: Text.WordWrap
204+
font.pixelSize: 12
205+
color: Theme.palette.baseColor1
206+
}
207+
}
208+
}
209+
}
210+
}
211+
212+
// category: Controls
213+
// status: good
214+

0 commit comments

Comments
 (0)