Skip to content

Commit a168286

Browse files
committed
User credentials not collapsing when a request is run
Request success shown in alert / response collapsed by default
1 parent 5ab9ed3 commit a168286

File tree

3 files changed

+137
-115
lines changed

3 files changed

+137
-115
lines changed

native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/SessionDetailViewController.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ struct SessionDetailView: View {
6060
})
6161

6262
// User Credentials Section
63-
UserCredentialsView(isExpanded: $isUserCredentialsExpanded)
64-
.id(refreshTrigger)
63+
UserCredentialsView(isExpanded: $isUserCredentialsExpanded, refreshTrigger: refreshTrigger)
6564

6665
// JWT Access Token Details Section (if applicable)
6766
if let credentials = UserAccountManager.shared.currentUserAccount?.credentials,

native/SampleApps/AuthFlowTester/AuthFlowTester/Views/RestApiTestView.swift

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@ import SwiftUI
2929
import SalesforceSDKCore
3030

3131
struct RestApiTestView: View {
32+
enum AlertType {
33+
case success
34+
case error(String)
35+
}
36+
3237
@State private var isLoading = false
3338
@State private var lastRequestResult: String = ""
3439
@State private var isResultExpanded = false
40+
@State private var alertType: AlertType?
3541

3642
let onRequestCompleted: () -> Void
3743

@@ -59,57 +65,73 @@ struct RestApiTestView: View {
5965
}
6066
.disabled(isLoading)
6167

62-
// Result section - always visible
63-
VStack(alignment: .leading, spacing: 8) {
64-
Button(action: {
65-
if !lastRequestResult.isEmpty {
68+
// Response details section - collapsible
69+
if !lastRequestResult.isEmpty {
70+
VStack(alignment: .leading, spacing: 8) {
71+
Button(action: {
6672
withAnimation {
6773
isResultExpanded.toggle()
6874
}
69-
}
70-
}) {
71-
HStack {
72-
Text("Last Request Result:")
73-
.font(.subheadline)
74-
.fontWeight(.semibold)
75-
.foregroundColor(.primary)
76-
Spacer()
77-
if !lastRequestResult.isEmpty {
75+
}) {
76+
HStack {
77+
Text("Response Details")
78+
.font(.subheadline)
79+
.fontWeight(.semibold)
80+
.foregroundColor(.primary)
81+
Spacer()
7882
Image(systemName: isResultExpanded ? "chevron.up" : "chevron.down")
7983
.font(.caption)
8084
.foregroundColor(.secondary)
8185
}
82-
}
83-
}
84-
.disabled(lastRequestResult.isEmpty)
85-
86-
if lastRequestResult.isEmpty {
87-
Text("No request made yet")
88-
.font(.system(.caption, design: .monospaced))
89-
.foregroundColor(.secondary)
9086
.padding(8)
91-
.frame(maxWidth: .infinity, alignment: .leading)
87+
.background(Color(.tertiarySystemBackground))
88+
.cornerRadius(6)
89+
}
90+
91+
if isResultExpanded {
92+
ScrollView([.vertical, .horizontal], showsIndicators: true) {
93+
Text(lastRequestResult)
94+
.font(.system(.caption, design: .monospaced))
95+
.foregroundColor(.primary)
96+
.padding(8)
97+
.frame(maxWidth: .infinity, alignment: .leading)
98+
.textSelection(.enabled)
99+
}
100+
.frame(minHeight: 200, maxHeight: 400)
92101
.background(Color(.systemGray6))
93102
.cornerRadius(4)
94-
} else if isResultExpanded {
95-
ScrollView([.vertical, .horizontal], showsIndicators: true) {
96-
Text(lastRequestResult)
97-
.font(.system(.caption, design: .monospaced))
98-
.foregroundColor(lastRequestResult.hasPrefix("") ? .green : .red)
99-
.padding(8)
100-
.frame(maxWidth: .infinity, alignment: .leading)
101-
.textSelection(.enabled)
102103
}
103-
.frame(minHeight: 200, maxHeight: 400)
104-
.background(Color(.systemGray6))
105-
.cornerRadius(4)
106104
}
105+
.padding(.vertical, 4)
107106
}
108-
.padding(.vertical, 4)
109107
}
110108
.padding()
111109
.background(Color(.secondarySystemBackground))
112110
.cornerRadius(8)
111+
.alert(item: Binding(
112+
get: { alertType.map { AlertItem(type: $0) } },
113+
set: { alertType = $0?.type }
114+
)) { alertItem in
115+
switch alertItem.type {
116+
case .success:
117+
return Alert(
118+
title: Text("Request Successful"),
119+
message: Text("The REST API request completed successfully. Expand 'Response Details' below to see the full response."),
120+
dismissButton: .default(Text("OK"))
121+
)
122+
case .error(let message):
123+
return Alert(
124+
title: Text("Request Failed"),
125+
message: Text(message),
126+
dismissButton: .default(Text("OK"))
127+
)
128+
}
129+
}
130+
}
131+
132+
struct AlertItem: Identifiable {
133+
let id = UUID()
134+
let type: AlertType
113135
}
114136

115137
// MARK: - REST API Request
@@ -118,22 +140,25 @@ struct RestApiTestView: View {
118140
private func makeRestRequest() async {
119141
isLoading = true
120142
lastRequestResult = ""
143+
isResultExpanded = false // Start collapsed
121144

122145
do {
123146
let request = RestClient.shared.cheapRequest("v63.0")
124147
let response = try await RestClient.shared.send(request: request)
125148

126149
// Request succeeded - pretty print the JSON
127150
let prettyJSON = prettyPrintJSON(response.asString())
128-
lastRequestResult = "✓ Success:\n\n\(prettyJSON)"
129-
isResultExpanded = true // Auto-expand on new result
151+
lastRequestResult = prettyJSON
152+
alertType = .success
153+
// Response starts collapsed - user can expand to see details
130154

131155
// Notify parent to refresh fields
132156
onRequestCompleted()
133157
} catch {
134158
// Request failed
135-
lastRequestResult = "✗ Error: \(error.localizedDescription)"
136-
isResultExpanded = true // Auto-expand on error
159+
lastRequestResult = error.localizedDescription
160+
alertType = .error(error.localizedDescription)
161+
// Error details start collapsed - user can expand to see details
137162
}
138163

139164
isLoading = false

native/SampleApps/AuthFlowTester/AuthFlowTester/Views/UserCredentialsView.swift

Lines changed: 73 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ import SalesforceSDKCore
3030

3131
struct UserCredentialsView: View {
3232
@Binding var isExpanded: Bool
33+
let refreshTrigger: UUID
3334

34-
// Section expansion states - all start expanded
35+
// Section expansion states - all start collapsed
3536
@State private var userIdentityExpanded = false
3637
@State private var oauthConfigExpanded = false
3738
@State private var tokensExpanded = false
@@ -62,69 +63,71 @@ struct UserCredentialsView: View {
6263
}
6364

6465
if isExpanded {
65-
InfoSectionView(title: "User Identity", isExpanded: $userIdentityExpanded) {
66-
InfoRowView(label: "Username:", value: username)
67-
InfoRowView(label: "User ID:", value: userId)
68-
InfoRowView(label: "Organization ID:", value: organizationId)
69-
}
70-
71-
InfoSectionView(title: "OAuth Client Configuration", isExpanded: $oauthConfigExpanded) {
72-
InfoRowView(label: "Client ID:", value: clientId, isSensitive: true)
73-
InfoRowView(label: "Redirect URI:", value: redirectUri)
74-
InfoRowView(label: "Protocol:", value: authProtocol)
75-
InfoRowView(label: "Domain:", value: domain)
76-
InfoRowView(label: "Identifier:", value: identifier)
77-
}
78-
79-
InfoSectionView(title: "Tokens", isExpanded: $tokensExpanded) {
80-
InfoRowView(label: "Access Token:", value: accessToken, isSensitive: true)
81-
InfoRowView(label: "Refresh Token:", value: refreshToken, isSensitive: true)
82-
InfoRowView(label: "Token Format:", value: tokenFormat)
83-
InfoRowView(label: "JWT:", value: jwt, isSensitive: true)
84-
InfoRowView(label: "Auth Code:", value: authCode, isSensitive: true)
85-
InfoRowView(label: "Challenge String:", value: challengeString, isSensitive: true)
86-
InfoRowView(label: "Issued At:", value: issuedAt)
87-
}
88-
89-
InfoSectionView(title: "URLs", isExpanded: $urlsExpanded) {
90-
InfoRowView(label: "Instance URL:", value: instanceUrl)
91-
InfoRowView(label: "API Instance URL:", value: apiInstanceUrl)
92-
InfoRowView(label: "API URL:", value: apiUrl)
93-
InfoRowView(label: "Identity URL:", value: identityUrl)
94-
}
95-
96-
InfoSectionView(title: "Community", isExpanded: $communityExpanded) {
97-
InfoRowView(label: "Community ID:", value: communityId)
98-
InfoRowView(label: "Community URL:", value: communityUrl)
99-
}
100-
101-
InfoSectionView(title: "Domains and SIDs", isExpanded: $domainsAndSidsExpanded) {
102-
InfoRowView(label: "Lightning Domain:", value: lightningDomain)
103-
InfoRowView(label: "Lightning SID:", value: lightningSid, isSensitive: true)
104-
InfoRowView(label: "VF Domain:", value: vfDomain)
105-
InfoRowView(label: "VF SID:", value: vfSid, isSensitive: true)
106-
InfoRowView(label: "Content Domain:", value: contentDomain)
107-
InfoRowView(label: "Content SID:", value: contentSid, isSensitive: true)
108-
InfoRowView(label: "Parent SID:", value: parentSid, isSensitive: true)
109-
InfoRowView(label: "SID Cookie Name:", value: sidCookieName)
110-
}
111-
112-
InfoSectionView(title: "Cookies and Security", isExpanded: $cookiesAndSecurityExpanded) {
113-
InfoRowView(label: "CSRF Token:", value: csrfToken, isSensitive: true)
114-
InfoRowView(label: "Cookie Client Src:", value: cookieClientSrc)
115-
InfoRowView(label: "Cookie SID Client:", value: cookieSidClient, isSensitive: true)
116-
}
117-
118-
InfoSectionView(title: "Beacon", isExpanded: $beaconExpanded) {
119-
InfoRowView(label: "Beacon Child Consumer Key:", value: beaconChildConsumerKey)
120-
InfoRowView(label: "Beacon Child Consumer Secret:", value: beaconChildConsumerSecret, isSensitive: true)
121-
}
122-
123-
InfoSectionView(title: "Other", isExpanded: $otherExpanded) {
124-
InfoRowView(label: "Scopes:", value: credentialsScopes)
125-
InfoRowView(label: "Encrypted:", value: encrypted)
126-
InfoRowView(label: "Additional OAuth Fields:", value: additionalOAuthFields)
66+
VStack(spacing: 8) {
67+
InfoSectionView(title: "User Identity", isExpanded: $userIdentityExpanded) {
68+
InfoRowView(label: "Username:", value: username)
69+
InfoRowView(label: "User ID:", value: userId)
70+
InfoRowView(label: "Organization ID:", value: organizationId)
71+
}
72+
73+
InfoSectionView(title: "OAuth Client Configuration", isExpanded: $oauthConfigExpanded) {
74+
InfoRowView(label: "Client ID:", value: clientId, isSensitive: true)
75+
InfoRowView(label: "Redirect URI:", value: redirectUri)
76+
InfoRowView(label: "Protocol:", value: authProtocol)
77+
InfoRowView(label: "Domain:", value: domain)
78+
InfoRowView(label: "Identifier:", value: identifier)
79+
}
80+
81+
InfoSectionView(title: "Tokens", isExpanded: $tokensExpanded) {
82+
InfoRowView(label: "Access Token:", value: accessToken, isSensitive: true)
83+
InfoRowView(label: "Refresh Token:", value: refreshToken, isSensitive: true)
84+
InfoRowView(label: "Token Format:", value: tokenFormat)
85+
InfoRowView(label: "JWT:", value: jwt, isSensitive: true)
86+
InfoRowView(label: "Auth Code:", value: authCode, isSensitive: true)
87+
InfoRowView(label: "Challenge String:", value: challengeString, isSensitive: true)
88+
InfoRowView(label: "Issued At:", value: issuedAt)
89+
InfoRowView(label: "Scopes:", value: credentialsScopes)
90+
}
91+
92+
InfoSectionView(title: "URLs", isExpanded: $urlsExpanded) {
93+
InfoRowView(label: "Instance URL:", value: instanceUrl)
94+
InfoRowView(label: "API Instance URL:", value: apiInstanceUrl)
95+
InfoRowView(label: "API URL:", value: apiUrl)
96+
InfoRowView(label: "Identity URL:", value: identityUrl)
97+
}
98+
99+
InfoSectionView(title: "Community", isExpanded: $communityExpanded) {
100+
InfoRowView(label: "Community ID:", value: communityId)
101+
InfoRowView(label: "Community URL:", value: communityUrl)
102+
}
103+
104+
InfoSectionView(title: "Domains and SIDs", isExpanded: $domainsAndSidsExpanded) {
105+
InfoRowView(label: "Lightning Domain:", value: lightningDomain)
106+
InfoRowView(label: "Lightning SID:", value: lightningSid, isSensitive: true)
107+
InfoRowView(label: "VF Domain:", value: vfDomain)
108+
InfoRowView(label: "VF SID:", value: vfSid, isSensitive: true)
109+
InfoRowView(label: "Content Domain:", value: contentDomain)
110+
InfoRowView(label: "Content SID:", value: contentSid, isSensitive: true)
111+
InfoRowView(label: "Parent SID:", value: parentSid, isSensitive: true)
112+
InfoRowView(label: "SID Cookie Name:", value: sidCookieName)
113+
}
114+
115+
InfoSectionView(title: "Cookies and Security", isExpanded: $cookiesAndSecurityExpanded) {
116+
InfoRowView(label: "CSRF Token:", value: csrfToken, isSensitive: true)
117+
InfoRowView(label: "Cookie Client Src:", value: cookieClientSrc)
118+
InfoRowView(label: "Cookie SID Client:", value: cookieSidClient, isSensitive: true)
119+
}
120+
121+
InfoSectionView(title: "Beacon", isExpanded: $beaconExpanded) {
122+
InfoRowView(label: "Beacon Child Consumer Key:", value: beaconChildConsumerKey)
123+
InfoRowView(label: "Beacon Child Consumer Secret:", value: beaconChildConsumerSecret, isSensitive: true)
124+
}
125+
126+
InfoSectionView(title: "Other", isExpanded: $otherExpanded) {
127+
InfoRowView(label: "Additional OAuth Fields:", value: additionalOAuthFields)
128+
}
127129
}
130+
.id(refreshTrigger)
128131
}
129132
}
130133
.padding()
@@ -204,6 +207,13 @@ struct UserCredentialsView: View {
204207
formatter.timeStyle = .medium
205208
return formatter.string(from: date)
206209
}
210+
211+
private var credentialsScopes: String {
212+
guard let scopes = credentials?.scopes else {
213+
return ""
214+
}
215+
return scopes.joined(separator: " ")
216+
}
207217

208218
// URLs
209219
private var instanceUrl: String {
@@ -287,18 +297,6 @@ struct UserCredentialsView: View {
287297
}
288298

289299
// Other
290-
private var credentialsScopes: String {
291-
guard let scopes = credentials?.scopes else {
292-
return ""
293-
}
294-
return scopes.joined(separator: " ")
295-
}
296-
297-
private var encrypted: String {
298-
guard let creds = credentials else { return "" }
299-
return creds.isEncrypted ? "Yes" : "No"
300-
}
301-
302300
private var additionalOAuthFields: String {
303301
guard let fields = credentials?.additionalOAuthFields,
304302
let jsonData = try? JSONSerialization.data(withJSONObject: fields, options: .prettyPrinted),

0 commit comments

Comments
 (0)