Skip to content

Commit

Permalink
MBL-1039: Refactor LoadingBarButtonItem to take a closure instead of …
Browse files Browse the repository at this point in the history
…a binding for save actions (#1881)
  • Loading branch information
amy-at-kickstarter authored and ifosli committed Nov 13, 2023
1 parent ba910f8 commit 197bf69
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ struct ChangeEmailView: View {
@State private var newEmailText = ""
@State private var newPasswordText = ""
@State private var saveEnabled = false
@State private var saveTriggered = false
@State private var showLoading = false
@State private var showBannerMessage = false
@State private var bannerMessage: MessageBannerViewViewModel?
Expand Down Expand Up @@ -132,20 +131,18 @@ struct ChangeEmailView: View {
ToolbarItem(placement: .navigationBarTrailing) {
LoadingBarButtonItem(
saveEnabled: $saveEnabled,
saveTriggered: $saveTriggered,
showLoading: $showLoading,
titleText: Strings.Save()
)
) {
focusField = nil
reactiveViewModel.didTapSaveButton()
}
.onReceive(reactiveViewModel.saveButtonEnabled) { newValue in
saveEnabled = newValue
}
.onReceive(reactiveViewModel.resetEditableText) { newValue in
showLoading = !newValue
}
.onChange(of: saveTriggered) { newValue in
focusField = nil
reactiveViewModel.saveTriggered.send(newValue)
}
}
}
.overlay(alignment: .bottom) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ struct ReportProjectFormView: View {
@SwiftUI.Environment(\.dismiss) private var dismiss
@StateObject private var viewModel = ReportProjectFormViewModel()

@State private var showLoading: Bool = false
@FocusState private var focusField: ReportFormFocusField?

var body: some View {
Expand Down Expand Up @@ -59,10 +58,11 @@ struct ReportProjectFormView: View {
ToolbarItem(placement: .navigationBarTrailing) {
LoadingBarButtonItem(
saveEnabled: $viewModel.saveButtonEnabled,
saveTriggered: $viewModel.saveTriggered,
showLoading: $showLoading,
showLoading: $viewModel.saveButtonLoading,
titleText: Strings.Send()
)
) {
viewModel.didTapSave()
}
}
}
.onAppear {
Expand All @@ -74,17 +74,12 @@ struct ReportProjectFormView: View {
viewModel.inputs.viewDidLoad()
}
.onReceive(viewModel.$bannerMessage) { newValue in
showLoading = false

/// bannerMessage is set to nil when its done presenting. When it is done, and submit was successful, dismiss this view.
if newValue == nil, viewModel.submitSuccess {
dismiss()
popToRoot = true
}
}
.onReceive(viewModel.$saveTriggered) { triggered in
showLoading = triggered
}
.overlay(alignment: .bottom) {
MessageBannerView(viewModel: $viewModel.bannerMessage)
.frame(
Expand Down
9 changes: 3 additions & 6 deletions Kickstarter-iOS/SharedViews/LoadingBarButtonItem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import SwiftUI
// TODO(MBL-1039) - Refactor this so that saveTriggered takes a closure, not a binding
struct LoadingBarButtonItem: View {
@Binding var saveEnabled: Bool
@Binding var saveTriggered: Bool
@Binding var showLoading: Bool
@State var titleText: String
let titleText: String
let action: () -> Void

var body: some View {
let buttonColor = $saveEnabled.wrappedValue ? Color(.ksr_create_700) : Color(.ksr_create_300)
Expand All @@ -15,17 +15,14 @@ struct LoadingBarButtonItem: View {
if !showLoading {
Button(titleText) {
showLoading = true
saveTriggered = true
action()
}
.font(Font(UIFont.systemFont(ofSize: 17)))
.foregroundColor(buttonColor)
.disabled(!$saveEnabled.wrappedValue)
} else {
ProgressView()
.foregroundColor(Color(.ksr_support_700))
.onDisappear {
saveTriggered = false
}
}
}
.accessibilityElement(children: .combine)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public protocol ChangeEmailViewModelInputsSwiftUIIntegrationTest {
func resendVerificationEmailButtonTapped()
func viewDidLoad()
func updateEmail(newEmail: String, currentPassword: String)
func didTapSaveButton()
}

public protocol ChangeEmailViewModelOutputsSwiftUIIntegrationTest {
Expand Down Expand Up @@ -243,6 +244,10 @@ public final class ChangeEmailViewModelSwiftUIIntegrationTest: ChangeEmailViewMo
self.viewDidLoadProperty.value = ()
}

public func didTapSaveButton() {
self.saveTriggered.send(true)
}

public let didFailToSendVerificationEmail: Signal<String, Never>
public let didSendVerificationEmail: Signal<Void, Never>
public let emailText: Signal<String, Never>
Expand Down
46 changes: 29 additions & 17 deletions Library/ViewModels/ReportProjectFormViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@ public final class ReportProjectFormViewModel: ReportProjectFormViewModelType,
ReportProjectFormViewModelInputs, ReportProjectFormViewModelOutputs, ObservableObject {
@Published public var retrievedEmail: String? = nil
@Published public var saveButtonEnabled: Bool = false
@Published public var saveButtonLoading: Bool = false
@Published public var detailsText: String = ""
@Published public var saveTriggered: Bool = false
@Published public var bannerMessage: MessageBannerViewViewModel? = nil

@Published public var submitSuccess: Bool = false

private let viewDidLoadSubject = PassthroughSubject<Bool, Never>()
private let saveTriggeredSubject = PassthroughSubject<(), Never>()

private var cancellables = Set<AnyCancellable>()

Expand Down Expand Up @@ -53,33 +54,40 @@ public final class ReportProjectFormViewModel: ReportProjectFormViewModelType,
.assign(to: &$retrievedEmail)

/// Submits report on saveTriggered
self.$saveTriggered
.filter { $0 } // only save if saveTriggered is true
self.saveTriggeredSubject
.compactMap { [weak self] _ in
self?.createFlaggingInput()
}
.flatMap { $0 }
.sink { [weak self] _ in
// An error happens

self?.saveTriggered = false
self?.bannerMessage = MessageBannerViewViewModel((
type: .error,
message: Strings.Something_went_wrong_please_try_again()
))
self?.saveButtonEnabled = true

} receiveValue: { [weak self] _ in
.flatMap { [weak self] createFlaggingInput in
createFlaggingInput.catch { _ in
// An API error happens.
// We need to catch this up here in flatMap, instead of in sink,
// because we don't want an API failure to cancel this pipeline.
// If the pipeline gets canceled, you can't re-submit after a failure.

self?.bannerMessage = MessageBannerViewViewModel((
type: .error,
message: Strings.Something_went_wrong_please_try_again()
))
self?.saveButtonEnabled = true
self?.saveButtonLoading = false

return Empty<EmptyResponseEnvelope, Never>()
}
}
.sink(receiveValue: { [weak self] _ in
// Submitted successfully

self?.saveTriggered = false
self?.saveButtonEnabled = false
self?.saveButtonLoading = false

self?.bannerMessage = MessageBannerViewViewModel((
type: .success,
message: Strings.Your_message_has_been_sent()
))
self?.submitSuccess = true
}

})
.store(in: &self.cancellables)
}

Expand All @@ -101,6 +109,10 @@ public final class ReportProjectFormViewModel: ReportProjectFormViewModelType,
self.viewDidLoadSubject.send(true)
}

public func didTapSave() {
self.saveTriggeredSubject.send(())
}

public var inputs: ReportProjectFormViewModelInputs {
return self
}
Expand Down

0 comments on commit 197bf69

Please sign in to comment.