Skip to content

Commit 5ced7b3

Browse files
Merge branch 'main' into ui-tweaks
2 parents 47dbf7f + ef70a9d commit 5ced7b3

File tree

4 files changed

+143
-74
lines changed

4 files changed

+143
-74
lines changed

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Utils/StringUtils.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,4 +350,11 @@ public class StringUtils {
350350
public var privacyPolicyLabel: String {
351351
return localizedString(for: "PrivacyPolicy")
352352
}
353+
354+
/// Alert Error title
355+
/// found in:
356+
/// PasswordRecoveryView
357+
public var alertErrorTitle: String {
358+
return localizedString(for: "Error")
359+
}
353360
}

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/AuthPickerView.swift

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -18,56 +18,65 @@ public struct AuthPickerView {
1818
set: { authService.isShowingAuthModal = $0 }
1919
)
2020
}
21+
22+
@ViewBuilder
23+
private var authPickerTitleView: some View {
24+
if authService.authView == .authPicker {
25+
Text(authService.string.authPickerTitle)
26+
.font(.largeTitle)
27+
.fontWeight(.bold)
28+
.padding()
29+
}
30+
}
2131
}
2232

2333
extension AuthPickerView: View {
2434
public var body: some View {
2535
ScrollView {
2636
VStack {
27-
Text(authService.string.authPickerTitle)
28-
.font(.largeTitle)
29-
.fontWeight(.bold)
30-
.padding()
37+
authPickerTitleView
3138
if authService.authenticationState == .authenticated {
3239
SignedInView()
33-
} else if authService.authView == .passwordRecovery {
34-
PasswordRecoveryView()
35-
} else if authService.authView == .emailLink {
36-
Divider()
37-
EmailLinkView()
3840
} else {
39-
Divider()
40-
if authService.emailSignInEnabled {
41-
Text(authService.authenticationFlow == .login ? authService.string
42-
.emailLoginFlowLabel : authService.string.emailSignUpFlowLabel)
43-
Divider()
44-
45-
EmailAuthView()
46-
}
47-
VStack {
48-
authService.renderButtons()
49-
}.padding(.horizontal)
50-
51-
VStack { Divider() }
52-
if authService.emailSignInEnabled {
53-
HStack {
54-
Text(authService
55-
.authenticationFlow == .login ? authService.string.dontHaveAnAccountYetLabel :
56-
authService.string.alreadyHaveAnAccountLabel)
57-
Button(action: {
58-
withAnimation {
59-
switchFlow()
41+
switch authService.authView {
42+
case .passwordRecovery:
43+
PasswordRecoveryView()
44+
case .emailLink:
45+
EmailLinkView()
46+
case .authPicker:
47+
if authService.emailSignInEnabled {
48+
Text(authService.authenticationFlow == .login ? authService.string
49+
.emailLoginFlowLabel : authService.string.emailSignUpFlowLabel)
50+
Divider()
51+
EmailAuthView()
52+
}
53+
VStack {
54+
authService.renderButtons()
55+
}.padding(.horizontal)
56+
if authService.emailSignInEnabled {
57+
Divider()
58+
HStack {
59+
Text(authService
60+
.authenticationFlow == .login ? authService.string.dontHaveAnAccountYetLabel :
61+
authService.string.alreadyHaveAnAccountLabel)
62+
Button(action: {
63+
withAnimation {
64+
switchFlow()
65+
}
66+
}) {
67+
Text(authService.authenticationFlow == .signUp ? authService.string
68+
.emailLoginFlowLabel : authService.string.emailSignUpFlowLabel)
69+
.fontWeight(.semibold)
70+
.foregroundColor(.blue)
6071
}
61-
}) {
62-
Text(authService.authenticationFlow == .signUp ? authService.string
63-
.emailLoginFlowLabel : authService.string.emailSignUpFlowLabel)
64-
.fontWeight(.semibold)
65-
.foregroundColor(.blue)
6672
}
6773
}
74+
PrivacyTOCsView(displayMode: .footer)
75+
Text(authService.errorMessage).foregroundColor(.red)
76+
default:
77+
// TODO: - possibly refactor this, see: https://github.com/firebase/FirebaseUI-iOS/pull/1259#discussion_r2105473437
78+
EmptyView()
6879
}
69-
PrivacyTOCsView(displayMode: .footer)
70-
Text(authService.errorMessage).foregroundColor(.red)
7180
}
7281
}.sheet(isPresented: isAuthModalPresented) {
7382
VStack(spacing: 0) {
Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import FirebaseCore
12
import SwiftUI
23

34
struct PasswordPromptSheet {
@@ -9,26 +10,45 @@ struct PasswordPromptSheet {
910
extension PasswordPromptSheet: View {
1011
var body: some View {
1112
VStack(spacing: 20) {
12-
SecureField(authService.string.passwordInputLabel, text: $password)
13-
.textFieldStyle(.roundedBorder)
13+
Text(authService.string.confirmPasswordInputLabel)
14+
.font(.largeTitle)
15+
.fontWeight(.bold)
1416
.padding()
1517

16-
HStack {
17-
Button(authService.string.cancelButtonLabel) {
18-
coordinator.cancel()
19-
}
20-
Spacer()
21-
Button(authService.string.okButtonLabel) {
22-
coordinator.submit(password: password)
23-
}
24-
.disabled(password.isEmpty)
18+
Divider()
19+
20+
LabeledContent {
21+
TextField(authService.string.passwordInputLabel, text: $password)
22+
.textInputAutocapitalization(.never)
23+
.disableAutocorrection(true)
24+
.submitLabel(.next)
25+
} label: {
26+
Image(systemName: "lock")
27+
}.padding(.vertical, 10)
28+
.background(Divider(), alignment: .bottom)
29+
.padding(.bottom, 4)
30+
31+
Button(action: {
32+
coordinator.submit(password: password)
33+
}) {
34+
Text(authService.string.okButtonLabel)
35+
.padding(.vertical, 8)
36+
.frame(maxWidth: .infinity)
37+
}
38+
.disabled(password.isEmpty)
39+
.padding([.top, .bottom, .horizontal], 8)
40+
.frame(maxWidth: .infinity)
41+
.buttonStyle(.borderedProminent)
42+
43+
Button(authService.string.cancelButtonLabel) {
44+
coordinator.cancel()
2545
}
26-
.padding(.horizontal)
2746
}
2847
.padding()
2948
}
3049
}
3150

3251
#Preview {
33-
PasswordPromptSheet(coordinator: PasswordPromptCoordinator())
52+
FirebaseOptions.dummyConfigurationForPreview()
53+
return PasswordPromptSheet(coordinator: PasswordPromptCoordinator()).environment(AuthService())
3454
}

FirebaseSwiftUI/FirebaseAuthSwiftUI/Sources/Views/PasswordRecoveryView.swift

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
import FirebaseCore
22
import SwiftUI
33

4+
private struct ResultWrapper: Identifiable {
5+
let id = UUID()
6+
let value: Result<Void, Error>
7+
}
8+
49
public struct PasswordRecoveryView {
510
@Environment(AuthService.self) private var authService
611
@State private var email = ""
7-
@State private var showModal = false
12+
@State private var resultWrapper: ResultWrapper?
813

914
public init() {}
1015

1116
private func sendPasswordRecoveryEmail() async {
17+
let recoveryResult: Result<Void, Error>
18+
1219
do {
1320
try await authService.sendPasswordRecoveryEmail(to: email)
14-
showModal = true
15-
} catch {}
21+
resultWrapper = ResultWrapper(value: .success(()))
22+
} catch {
23+
resultWrapper = ResultWrapper(value: .failure(error))
24+
}
1625
}
1726
}
1827

@@ -33,9 +42,11 @@ extension PasswordRecoveryView: View {
3342
.submitLabel(.next)
3443
} label: {
3544
Image(systemName: "at")
36-
}.padding(.vertical, 6)
37-
.background(Divider(), alignment: .bottom)
38-
.padding(.bottom, 4)
45+
}
46+
.padding(.vertical, 6)
47+
.background(Divider(), alignment: .bottom)
48+
.padding(.bottom, 4)
49+
3950
Button(action: {
4051
Task {
4152
await sendPasswordRecoveryEmail()
@@ -46,11 +57,29 @@ extension PasswordRecoveryView: View {
4657
.frame(maxWidth: .infinity)
4758
}
4859
.disabled(!CommonUtils.isValidEmail(email))
49-
.padding([.top, .bottom], 8)
60+
.padding([.top, .bottom, .horizontal], 8)
5061
.frame(maxWidth: .infinity)
5162
.buttonStyle(.borderedProminent)
52-
}.sheet(isPresented: $showModal) {
53-
VStack {
63+
}
64+
.sheet(item: $resultWrapper) { wrapper in
65+
resultSheet(wrapper.value)
66+
}
67+
.navigationBarItems(leading: Button(action: {
68+
authService.authView = .authPicker
69+
}) {
70+
Image(systemName: "chevron.left")
71+
.foregroundColor(.blue)
72+
Text(authService.string.backButtonLabel)
73+
.foregroundColor(.blue)
74+
})
75+
}
76+
77+
@ViewBuilder
78+
@MainActor
79+
private func resultSheet(_ result: Result<Void, Error>) -> some View {
80+
VStack {
81+
switch result {
82+
case .success:
5483
Text(authService.string.passwordRecoveryEmailSentTitle)
5584
.font(.largeTitle)
5685
.fontWeight(.bold)
@@ -60,25 +89,29 @@ extension PasswordRecoveryView: View {
6089

6190
Divider()
6291

63-
Text(authService.string.passwordRecoveryEmailSentMessage)
92+
Text(String(format: authService.string.passwordRecoveryEmailSentMessage, email))
93+
.padding()
94+
95+
case .failure:
96+
Text(authService.string.alertErrorTitle)
97+
.font(.title)
98+
.fontWeight(.semibold)
99+
.padding()
100+
101+
Divider()
102+
103+
Text(authService.errorMessage)
64104
.padding()
65-
Button(authService.string.okButtonLabel) {
66-
showModal = false
67-
}
68-
.padding()
105+
}
106+
107+
Divider()
108+
109+
Button(authService.string.okButtonLabel) {
110+
self.resultWrapper = nil
69111
}
70112
.padding()
71-
}.onOpenURL { _ in
72-
// TODO: - move the user to email/password View
73113
}
74-
.navigationBarItems(leading: Button(action: {
75-
authService.authView = .authPicker
76-
}) {
77-
Image(systemName: "chevron.left")
78-
.foregroundColor(.blue)
79-
Text(authService.string.backButtonLabel)
80-
.foregroundColor(.blue)
81-
})
114+
.padding()
82115
}
83116
}
84117

0 commit comments

Comments
 (0)