forked from zelo72/mastodon-ios
feat: show discard alert when user cancel toot composing
This commit is contained in:
parent
1746c1fc77
commit
36604d150f
|
@ -14,6 +14,10 @@
|
|||
"vote_failure": {
|
||||
"title": "Vote Failure",
|
||||
"poll_expired": "The poll has expired"
|
||||
},
|
||||
"discard_compose_content": {
|
||||
"title": "Discard Toot",
|
||||
"message": "Confirm discard composed toot content."
|
||||
}
|
||||
},
|
||||
"controls": {
|
||||
|
@ -27,6 +31,7 @@
|
|||
"confirm": "Confirm",
|
||||
"continue": "Continue",
|
||||
"cancel": "Cancel",
|
||||
"discard": "Discard",
|
||||
"take_photo": "Take photo",
|
||||
"save_photo": "Save photo",
|
||||
"sign_in": "Sign In",
|
||||
|
|
|
@ -46,7 +46,7 @@ extension ComposeStatusSection {
|
|||
cell.statusView.headerContainerStackView.isHidden = false
|
||||
cell.statusView.headerInfoLabel.text = "[TODO] \(replyTo.author.displayName)"
|
||||
}
|
||||
ComposeStatusSection.configureComposeTootContent(cell: cell, attribute: attribute)
|
||||
ComposeStatusSection.configure(cell: cell, attribute: attribute)
|
||||
// self size input cell
|
||||
cell.composeContent
|
||||
.receive(on: DispatchQueue.main)
|
||||
|
@ -62,16 +62,18 @@ extension ComposeStatusSection {
|
|||
}
|
||||
|
||||
extension ComposeStatusSection {
|
||||
static func configureComposeTootContent(
|
||||
static func configure(
|
||||
cell: ComposeTootContentTableViewCell,
|
||||
attribute: ComposeStatusItem.ComposeTootAttribute
|
||||
) {
|
||||
// set avatar
|
||||
attribute.avatarURL
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { avatarURL in
|
||||
cell.statusView.configure(with: AvatarConfigurableViewConfiguration(avatarImageURL: avatarURL))
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
// set display name and username
|
||||
Publishers.CombineLatest(
|
||||
attribute.displayName.eraseToAnyPublisher(),
|
||||
attribute.username.eraseToAnyPublisher()
|
||||
|
@ -82,5 +84,11 @@ extension ComposeStatusSection {
|
|||
cell.statusView.usernameLabel.text = username
|
||||
}
|
||||
.store(in: &cell.disposeBag)
|
||||
|
||||
// bind compose content
|
||||
cell.composeContent
|
||||
.map { $0 as String? }
|
||||
.assign(to: \.value, on: attribute.composeContent)
|
||||
.store(in: &cell.disposeBag)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,12 @@ internal enum L10n {
|
|||
/// Please try again later.
|
||||
internal static let pleaseTryAgainLater = L10n.tr("Localizable", "Common.Alerts.Common.PleaseTryAgainLater")
|
||||
}
|
||||
internal enum DiscardComposeContent {
|
||||
/// Confirm discard composed toot content.
|
||||
internal static let message = L10n.tr("Localizable", "Common.Alerts.DiscardComposeContent.Message")
|
||||
/// Discard Toot
|
||||
internal static let title = L10n.tr("Localizable", "Common.Alerts.DiscardComposeContent.Title")
|
||||
}
|
||||
internal enum ServerError {
|
||||
/// Server Error
|
||||
internal static let title = L10n.tr("Localizable", "Common.Alerts.ServerError.Title")
|
||||
|
@ -46,6 +52,8 @@ internal enum L10n {
|
|||
internal static let confirm = L10n.tr("Localizable", "Common.Controls.Actions.Confirm")
|
||||
/// Continue
|
||||
internal static let `continue` = L10n.tr("Localizable", "Common.Controls.Actions.Continue")
|
||||
/// Discard
|
||||
internal static let discard = L10n.tr("Localizable", "Common.Controls.Actions.Discard")
|
||||
/// Edit
|
||||
internal static let edit = L10n.tr("Localizable", "Common.Controls.Actions.Edit")
|
||||
/// OK
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
"Common.Alerts.Common.PleaseTryAgain" = "Please try again.";
|
||||
"Common.Alerts.Common.PleaseTryAgainLater" = "Please try again later.";
|
||||
"Common.Alerts.DiscardComposeContent.Message" = "Confirm discard composed toot content.";
|
||||
"Common.Alerts.DiscardComposeContent.Title" = "Discard Toot";
|
||||
"Common.Alerts.ServerError.Title" = "Server Error";
|
||||
"Common.Alerts.SignUpFailure.Title" = "Sign Up Failure";
|
||||
"Common.Alerts.VoteFailure.PollExpired" = "The poll has expired";
|
||||
|
@ -9,6 +11,7 @@
|
|||
"Common.Controls.Actions.Cancel" = "Cancel";
|
||||
"Common.Controls.Actions.Confirm" = "Confirm";
|
||||
"Common.Controls.Actions.Continue" = "Continue";
|
||||
"Common.Controls.Actions.Discard" = "Discard";
|
||||
"Common.Controls.Actions.Edit" = "Edit";
|
||||
"Common.Controls.Actions.Ok" = "OK";
|
||||
"Common.Controls.Actions.OpenInSafari" = "Open in Safari";
|
||||
|
|
|
@ -70,7 +70,6 @@ extension ComposeViewController {
|
|||
navigationItem.leftBarButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ComposeViewController.cancelBarButtonItemPressed(_:)))
|
||||
navigationItem.rightBarButtonItem = composeTootBarButtonItem
|
||||
|
||||
|
||||
tableView.translatesAutoresizingMaskIntoConstraints = false
|
||||
view.addSubview(tableView)
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -87,13 +86,17 @@ extension ComposeViewController {
|
|||
composeToolbarView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
||||
composeToolbarView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||
composeToolbarViewBottomLayoutConstraint,
|
||||
composeToolbarView.heightAnchor.constraint(equalToConstant: 44),
|
||||
composeToolbarView.heightAnchor.constraint(equalToConstant: ComposeToolbarView.toolbarHeight),
|
||||
])
|
||||
composeToolbarView.preservesSuperviewLayoutMargins = true
|
||||
composeToolbarView.delegate = self
|
||||
|
||||
tableView.delegate = self
|
||||
viewModel.setupDiffableDataSource(for: tableView, dependency: self)
|
||||
|
||||
// respond scrollView overlap change
|
||||
view.layoutIfNeeded()
|
||||
// update layout when keyboard show/dismiss
|
||||
Publishers.CombineLatest3(
|
||||
KeyboardResponderService.shared.isShow.eraseToAnyPublisher(),
|
||||
KeyboardResponderService.shared.state.eraseToAnyPublisher(),
|
||||
|
@ -125,8 +128,9 @@ extension ComposeViewController {
|
|||
return
|
||||
}
|
||||
|
||||
self.tableView.contentInset.bottom = padding
|
||||
self.tableView.verticalScrollIndicatorInsets.bottom = padding
|
||||
// add 16pt margin
|
||||
self.tableView.contentInset.bottom = padding + 16
|
||||
self.tableView.verticalScrollIndicatorInsets.bottom = padding + 16
|
||||
UIView.animate(withDuration: 0.3) {
|
||||
self.composeToolbarViewBottomLayoutConstraint.constant = padding
|
||||
self.view.layoutIfNeeded()
|
||||
|
@ -134,8 +138,10 @@ extension ComposeViewController {
|
|||
})
|
||||
.store(in: &disposeBag)
|
||||
|
||||
tableView.delegate = self
|
||||
viewModel.setupDiffableDataSource(for: tableView, dependency: self)
|
||||
viewModel.isComposeTootBarButtonItemEnabled
|
||||
.receive(on: DispatchQueue.main)
|
||||
.assign(to: \.isEnabled, on: composeTootBarButtonItem)
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
|
@ -168,12 +174,32 @@ extension ComposeViewController {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func showDismissConfirmAlertController() {
|
||||
let alertController = UIAlertController(
|
||||
title: L10n.Common.Alerts.DiscardComposeContent.title,
|
||||
message: L10n.Common.Alerts.DiscardComposeContent.message,
|
||||
preferredStyle: .alert
|
||||
)
|
||||
let discardAction = UIAlertAction(title: L10n.Common.Controls.Actions.discard, style: .destructive) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.dismiss(animated: true, completion: nil)
|
||||
}
|
||||
alertController.addAction(discardAction)
|
||||
let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel)
|
||||
alertController.addAction(cancelAction)
|
||||
present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
}
|
||||
|
||||
extension ComposeViewController {
|
||||
|
||||
@objc private func cancelBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
guard viewModel.shouldDismiss.value else {
|
||||
showDismissConfirmAlertController()
|
||||
return
|
||||
}
|
||||
dismiss(animated: true, completion: nil)
|
||||
}
|
||||
|
||||
|
@ -222,14 +248,6 @@ extension ComposeViewController: UITableViewDelegate {
|
|||
|
||||
// MARK: - ComposeViewController
|
||||
extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
|
||||
// func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
|
||||
// switch traitCollection.userInterfaceIdiom {
|
||||
// case .phone:
|
||||
// return .fullScreen
|
||||
// default:
|
||||
// return .pageSheet
|
||||
// }
|
||||
// }
|
||||
|
||||
func presentationControllerShouldDismiss(_ presentationController: UIPresentationController) -> Bool {
|
||||
return viewModel.shouldDismiss.value
|
||||
|
@ -237,6 +255,8 @@ extension ComposeViewController: UIAdaptivePresentationControllerDelegate {
|
|||
|
||||
func presentationControllerDidAttemptToDismiss(_ presentationController: UIPresentationController) {
|
||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||
showDismissConfirmAlertController()
|
||||
|
||||
}
|
||||
|
||||
func presentationControllerDidDismiss(_ presentationController: UIPresentationController) {
|
||||
|
|
|
@ -27,6 +27,7 @@ final class ComposeViewModel {
|
|||
// UI & UX
|
||||
let title: CurrentValueSubject<String, Never>
|
||||
let shouldDismiss = CurrentValueSubject<Bool, Never>(true)
|
||||
let isComposeTootBarButtonItemEnabled = CurrentValueSubject<Bool, Never>(false)
|
||||
|
||||
init(
|
||||
context: AppContext,
|
||||
|
@ -62,6 +63,24 @@ final class ComposeViewModel {
|
|||
self.composeTootAttribute.username.value = username
|
||||
}
|
||||
.store(in: &disposeBag)
|
||||
|
||||
composeTootAttribute.composeContent
|
||||
.receive(on: DispatchQueue.main)
|
||||
.map { content in
|
||||
let content = content?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
|
||||
return !content.isEmpty
|
||||
}
|
||||
.assign(to: \.value, on: isComposeTootBarButtonItemEnabled)
|
||||
.store(in: &disposeBag)
|
||||
|
||||
composeTootAttribute.composeContent
|
||||
.receive(on: DispatchQueue.main)
|
||||
.map { content in
|
||||
let content = content ?? ""
|
||||
return content.isEmpty
|
||||
}
|
||||
.assign(to: \.value, on: shouldDismiss)
|
||||
.store(in: &disposeBag)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ protocol ComposeToolbarViewDelegate: class {
|
|||
|
||||
final class ComposeToolbarView: UIView {
|
||||
|
||||
static let toolbarHeight: CGFloat = 44
|
||||
|
||||
weak var delegate: ComposeToolbarViewDelegate?
|
||||
|
||||
let mediaButton: UIButton = {
|
||||
|
|
Loading…
Reference in New Issue