Skip to content

Commit da13f2a

Browse files
committed
feat: add account management UI for seedphrase, auth methods, and network name
- Add 6 new SDK service methods: generateSeedphrase, regenerateSeedphrase, addAuth, removeAuth, changeNetworkName, claimNetworkName - Remove upgradeGuest and all guest mode references - Add Seedphrase Management section to Settings (generate/regenerate) - Add Sign-In Methods section to Settings (list, add, remove auth methods) - Add AddAuthSheet for adding email/seedphrase sign-in methods - Add editable network name field to ProfileView with claim support - Update MockUrApiService with stub implementations - Pass NetworkUserViewModel into Settings for auth method display - Update AccountNavStack to pass authType to ProfileView
1 parent b3e1fd7 commit da13f2a

12 files changed

Lines changed: 874 additions & 117 deletions

app/network/Authenticate/CreateNetwork/CreateNetworkView.swift

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -188,16 +188,7 @@ struct CreateNetworkView: View {
188188
#endif
189189

190190
Task {
191-
let result = deviceManager.device != nil
192-
// device exists - upgrade guest network
193-
? await viewModel.upgradeGuestNetwork(
194-
userAuth: userAuth,
195-
authJwt: authLoginArgs.authJwt,
196-
authType: authLoginArgs.authJwtType,
197-
walletAuth: authLoginArgs.walletAuth
198-
)
199-
// no device exists - create a new network
200-
: await viewModel.createNetwork(
191+
let result = await viewModel.createNetwork(
201192
userAuth: userAuth,
202193
authJwt: authLoginArgs.authJwt,
203194
authType: authLoginArgs.authJwtType,

app/network/Authenticate/CreateNetwork/CreateNetworkViewModel.swift

Lines changed: 0 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -247,58 +247,6 @@ extension CreateNetworkView {
247247

248248
}
249249

250-
func upgradeGuestNetwork(
251-
userAuth: String?,
252-
authJwt: String?,
253-
authType: String?,
254-
walletAuth: SdkWalletAuthArgs?
255-
) async -> LoginNetworkResult {
256-
257-
if !formIsValid {
258-
return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Create network form is invalid"]))
259-
}
260-
261-
if isCreatingNetwork {
262-
return .failure(NSError(domain: domain, code: 0, userInfo: [NSLocalizedDescriptionKey: "Network creation already in progress"]))
263-
}
264-
265-
266-
self.createNetworkErrorMessage = nil
267-
self.isCreatingNetwork = true
268-
269-
do {
270-
271-
let args = SdkUpgradeGuestArgs()
272-
args.networkName = networkName.trimmingCharacters(in: .whitespacesAndNewlines)
273-
274-
if let userAuth = userAuth {
275-
args.userAuth = userAuth
276-
args.password = password
277-
}
278-
279-
if let authJwt, let authType {
280-
args.authJwt = authJwt
281-
args.authJwtType = authType
282-
}
283-
284-
if let walletAuth {
285-
args.walletAuth = walletAuth
286-
}
287-
288-
let result = try await urApiService.upgradeGuest(args)
289-
290-
self.isCreatingNetwork = false
291-
292-
return result
293-
294-
} catch {
295-
self.isCreatingNetwork = false
296-
return .failure(error)
297-
}
298-
299-
300-
}
301-
302250
func createNetwork(
303251
userAuth: String?,
304252
authJwt: String?,

app/network/Main/Account/AccountNavStackView.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ struct AccountNavStackView: View {
105105
api: api,
106106
back: viewModel.back,
107107
networkName: networkName,
108-
userAuth: networkUserViewModel.networkUser?.userAuth
108+
userAuth: networkUserViewModel.networkUser?.userAuth,
109+
authType: networkUserViewModel.networkUser?.authType
109110
)
110111
.background(themeManager.currentTheme.backgroundColor)
111112
.navigationTitle("Profile")
@@ -119,6 +120,7 @@ struct AccountNavStackView: View {
119120
accountWalletsViewModel: accountWalletsViewModel,
120121
navigate: viewModel.navigate,
121122
providerCountries: providerCountries,
123+
networkUserViewModel: networkUserViewModel
122124
)
123125
.background(themeManager.currentTheme.backgroundColor.ignoresSafeArea())
124126
.navigationTitle("Settings")

app/network/Main/Account/Profile/ProfileView.swift

Lines changed: 88 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,42 +18,103 @@ struct ProfileView: View {
1818
var back: () -> Void
1919
var networkName: String?
2020
var userAuth: String?
21+
var authType: String?
2122

22-
init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?) {
23+
init(api: SdkApi, back: @escaping () -> Void, networkName: String?, userAuth: String?, authType: String? = nil) {
2324
_viewModel = StateObject.init(wrappedValue: ViewModel(
2425
api: api
2526
))
2627
self.back = back
2728
self.userAuth = userAuth
2829
self.networkName = networkName
30+
self.authType = authType
2931
}
3032

3133
var body: some View {
3234

3335
VStack {
34-
35-
// HStack {
36-
// Text("Profile")
37-
// .font(themeManager.currentTheme.titleFont)
38-
// .foregroundColor(themeManager.currentTheme.textColor)
39-
//
40-
// Spacer()
41-
// }
42-
//
43-
// Spacer().frame(height: 64)
44-
36+
4537
HStack {
4638
UrLabel(text: "Network name")
4739

4840
Spacer()
4941
}
5042

51-
HStack {
52-
Text(networkName ?? "")
53-
.font(themeManager.currentTheme.bodyFont)
54-
.foregroundColor(themeManager.currentTheme.textColor)
43+
if viewModel.isEditingNetworkName {
44+
// Editable view
45+
HStack {
46+
TextField("Enter network name", text: $viewModel.editedNetworkName)
47+
.textFieldStyle(.plain)
48+
.font(themeManager.currentTheme.bodyFont)
49+
.foregroundColor(themeManager.currentTheme.textColor)
50+
.autocorrectionDisabled()
51+
#if os(iOS)
52+
.autocapitalization(.none)
53+
#endif
54+
}
55+
.padding()
56+
.background(themeManager.currentTheme.tintedBackgroundBase)
57+
.cornerRadius(8)
5558

56-
Spacer()
59+
if let error = viewModel.networkNameError {
60+
Text(error)
61+
.font(themeManager.currentTheme.secondaryBodyFont)
62+
.foregroundColor(.urRed)
63+
}
64+
65+
HStack(spacing: 12) {
66+
UrButton(
67+
text: "Save",
68+
action: {
69+
Task {
70+
if authType == "seedphrase" {
71+
let result = await viewModel.claimNetworkName()
72+
handleNetworkNameResult(result)
73+
} else {
74+
let result = await viewModel.saveNetworkName()
75+
handleNetworkNameResult(result)
76+
}
77+
}
78+
},
79+
enabled: !viewModel.isSavingNetworkName && !viewModel.editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty,
80+
isProcessing: viewModel.isSavingNetworkName
81+
)
82+
83+
Button(action: {
84+
viewModel.cancelEditingNetworkName()
85+
}) {
86+
Text("Cancel")
87+
}
88+
}
89+
} else {
90+
// Read-only view
91+
HStack {
92+
Text(networkName ?? "")
93+
.font(themeManager.currentTheme.bodyFont)
94+
.foregroundColor(themeManager.currentTheme.textColor)
95+
96+
Spacer()
97+
98+
Button(action: {
99+
viewModel.startEditingNetworkName(currentName: networkName ?? "")
100+
}) {
101+
Image(systemName: "pencil")
102+
.foregroundColor(themeManager.currentTheme.textMutedColor)
103+
}
104+
}
105+
106+
HStack {
107+
if authType == "seedphrase" {
108+
Text("Claim a custom network name to replace your auto-generated one")
109+
.font(themeManager.currentTheme.secondaryBodyFont)
110+
.foregroundColor(themeManager.currentTheme.textMutedColor)
111+
} else {
112+
Text("Tap the edit icon to change your network name")
113+
.font(themeManager.currentTheme.secondaryBodyFont)
114+
.foregroundColor(themeManager.currentTheme.textMutedColor)
115+
}
116+
Spacer()
117+
}
57118
}
58119

59120
Spacer().frame(height: 32)
@@ -97,6 +158,16 @@ struct ProfileView: View {
97158
snackbarManager.showSnackbar(message: String(localized: "Error sending password reset link"))
98159
}
99160
}
161+
162+
private func handleNetworkNameResult(_ result: Result<String, Error>) {
163+
switch result {
164+
case .success(let newName):
165+
snackbarManager.showSnackbar(message: String(localized: "Network name changed to \(newName)"))
166+
case .failure(let error):
167+
print("Error changing network name: \(error)")
168+
snackbarManager.showSnackbar(message: String(localized: "Error changing network name"))
169+
}
170+
}
100171
}
101172

102173
#Preview {

app/network/Main/Account/Profile/ProfileViewModel.swift

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ extension ProfileView {
1818
@Published private(set) var isSendingPasswordResetLink: Bool = false
1919
@Published private(set) var sendPasswordResetLinkError: String?
2020

21+
@Published var isEditingNetworkName: Bool = false
22+
@Published var editedNetworkName: String = ""
23+
@Published private(set) var isSavingNetworkName: Bool = false
24+
@Published private(set) var networkNameError: String?
25+
2126
init(api: SdkApi) {
2227
self.api = api
2328
}
@@ -70,6 +75,116 @@ extension ProfileView {
7075

7176
}
7277

78+
func startEditingNetworkName(currentName: String) {
79+
editedNetworkName = currentName
80+
isEditingNetworkName = true
81+
networkNameError = nil
82+
}
83+
84+
func saveNetworkName() async -> Result<String, Error> {
85+
86+
let name = editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines)
87+
guard !name.isEmpty else {
88+
return .failure(NetworkNameError.empty)
89+
}
90+
91+
isSavingNetworkName = true
92+
networkNameError = nil
93+
94+
do {
95+
let result: SdkChangeNetworkNameResult = try await withCheckedThrowingContinuation { continuation in
96+
97+
let callback = ChangeNetworkNameCallback { result, err in
98+
99+
if let err = err {
100+
continuation.resume(throwing: err)
101+
return
102+
}
103+
104+
guard let result = result else {
105+
continuation.resume(throwing: NetworkNameError.resultNil)
106+
return
107+
}
108+
109+
if let errMsg = result.error?.message {
110+
continuation.resume(throwing: NSError(domain: "ProfileViewModel", code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg]))
111+
return
112+
}
113+
114+
continuation.resume(returning: result)
115+
}
116+
117+
let args = SdkChangeNetworkNameArgs()
118+
args.newName = name
119+
api.changeNetworkName(args, callback: callback)
120+
}
121+
122+
self.isSavingNetworkName = false
123+
self.isEditingNetworkName = false
124+
return .success(result.networkName)
125+
126+
} catch(let error) {
127+
self.isSavingNetworkName = false
128+
self.networkNameError = error.localizedDescription
129+
return .failure(error)
130+
}
131+
}
132+
133+
func claimNetworkName() async -> Result<String, Error> {
134+
135+
let name = editedNetworkName.trimmingCharacters(in: .whitespacesAndNewlines)
136+
guard !name.isEmpty else {
137+
return .failure(NetworkNameError.empty)
138+
}
139+
140+
isSavingNetworkName = true
141+
networkNameError = nil
142+
143+
do {
144+
let result: SdkClaimNetworkNameResult = try await withCheckedThrowingContinuation { continuation in
145+
146+
let callback = ClaimNetworkNameCallback { result, err in
147+
148+
if let err = err {
149+
continuation.resume(throwing: err)
150+
return
151+
}
152+
153+
guard let result = result else {
154+
continuation.resume(throwing: NetworkNameError.resultNil)
155+
return
156+
}
157+
158+
if let errMsg = result.error?.message {
159+
continuation.resume(throwing: NSError(domain: "ProfileViewModel", code: -1, userInfo: [NSLocalizedDescriptionKey: errMsg]))
160+
return
161+
}
162+
163+
continuation.resume(returning: result)
164+
}
165+
166+
let args = SdkClaimNetworkNameArgs()
167+
args.newName = name
168+
api.claimNetworkName(args, callback: callback)
169+
}
170+
171+
self.isSavingNetworkName = false
172+
self.isEditingNetworkName = false
173+
return .success(result.networkName)
174+
175+
} catch(let error) {
176+
self.isSavingNetworkName = false
177+
self.networkNameError = error.localizedDescription
178+
return .failure(error)
179+
}
180+
}
181+
182+
func cancelEditingNetworkName() {
183+
isEditingNetworkName = false
184+
editedNetworkName = ""
185+
networkNameError = nil
186+
}
187+
73188
}
74189

75190
}
@@ -79,8 +194,25 @@ enum SendPasswordResetLinkError: Error {
79194
case resultInvalid
80195
}
81196

197+
enum NetworkNameError: Error {
198+
case empty
199+
case resultNil
200+
}
201+
82202
private class AuthPasswordResetCallback: SdkCallback<SdkAuthPasswordResetResult, SdkAuthPasswordResetCallbackProtocol>, SdkAuthPasswordResetCallbackProtocol {
83203
func result(_ result: SdkAuthPasswordResetResult?, err: Error?) {
84204
handleResult(result, err: err)
85205
}
86206
}
207+
208+
private class ChangeNetworkNameCallback: SdkCallback<SdkChangeNetworkNameResult, SdkChangeNetworkNameCallbackProtocol>, SdkChangeNetworkNameCallbackProtocol {
209+
func result(_ result: SdkChangeNetworkNameResult?, err: Error?) {
210+
handleResult(result, err: err)
211+
}
212+
}
213+
214+
private class ClaimNetworkNameCallback: SdkCallback<SdkClaimNetworkNameResult, SdkClaimNetworkNameCallbackProtocol>, SdkClaimNetworkNameCallbackProtocol {
215+
func result(_ result: SdkClaimNetworkNameResult?, err: Error?) {
216+
handleResult(result, err: err)
217+
}
218+
}

0 commit comments

Comments
 (0)