Fix Profile Editing again (IOS-239, #1244) (#1259)

* Fix Profile Editing again (IOS-239, #1244)

This needs a bit of explanation, I guess, so please don't squash if possible? I didn't take into consideration, that the `ProfileViewController.viewModel` changes. And when it changes, all the combine-connections just ... disappear. This PR changes that (but probably I oversaw something again).

* Disable pull to refresh for editing mode (IOS-239)

* In case of nothing change, cancel editing (IOS-239)
This commit is contained in:
Nathan Mattes 2024-03-21 08:35:18 +01:00 committed by GitHub
parent 83fd4a89fa
commit ab39c6ef87
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 147 additions and 108 deletions

View File

@ -48,21 +48,25 @@ extension ProfileAboutViewController {
collectionView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(collectionView)
collectionView.pinToParent()
collectionView.delegate = self
viewModel.setupDiffableDataSource(
collectionView: collectionView,
profileFieldCollectionViewCellDelegate: self,
profileFieldEditCollectionViewCellDelegate: self
)
let longPressReorderGesture = UILongPressGestureRecognizer(
target: self,
action: #selector(ProfileAboutViewController.longPressReorderGestureHandler(_:))
)
collectionView.addGestureRecognizer(longPressReorderGesture)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
collectionView.delegate = self
viewModel.setupDiffableDataSource(
collectionView: collectionView,
profileFieldCollectionViewCellDelegate: self,
profileFieldEditCollectionViewCellDelegate: self
)
}
}
extension ProfileAboutViewController {

View File

@ -51,7 +51,7 @@ extension ProfileAboutViewModel {
diffableDataSource.apply(snapshot)
let fields = Publishers.CombineLatest3(
$isEditing.removeDuplicates(),
$isEditing,
profileInfo.$fields.removeDuplicates(),
profileInfoEditing.$fields.removeDuplicates()
).map { isEditing, displayFields, editingFields in
@ -60,7 +60,7 @@ extension ProfileAboutViewModel {
Publishers.CombineLatest4(
$isEditing.removeDuplicates(),
$isEditing,
$createdAt.removeDuplicates(),
fields,
$emojiMeta.removeDuplicates()
@ -68,7 +68,7 @@ extension ProfileAboutViewModel {
.throttle(for: 0.3, scheduler: DispatchQueue.main, latest: true)
.receive(on: DispatchQueue.main)
.sink { [weak self] isEditing, createdAt, fields, emojiMeta in
guard let self = self else { return }
guard let self else { return }
guard let diffableDataSource = self.diffableDataSource else { return }
var snapshot = NSDiffableDataSourceSnapshot<ProfileFieldSection, ProfileFieldItem>()

View File

@ -32,8 +32,19 @@ final class ProfileViewController: UIViewController, NeedsDependency, MediaPrevi
var disposeBag = Set<AnyCancellable>()
//TODO: Replace with something better than !
var viewModel: ProfileViewModel!
var viewModel: ProfileViewModel! {
didSet {
if isViewLoaded {
bindViewModel()
viewModel.isEditing = false
profileHeaderViewController.viewModel.isEditing = false
profilePagingViewController.viewModel.profileAboutViewController.viewModel.isEditing = false
viewModel.profileAboutViewModel.isEditing = false
}
}
}
let mediaPreviewTransitionController = MediaPreviewTransitionController()
private(set) lazy var cancelEditingBarButtonItem: UIBarButtonItem = {
@ -182,89 +193,6 @@ extension ProfileViewController {
navigationItem.titleView = titleView
let editingAndUpdatingPublisher = Publishers.CombineLatest(
viewModel.$isEditing,
viewModel.$isUpdating
)
// note: not add .share() here
let barButtonItemHiddenPublisher = Publishers.CombineLatest3(
viewModel.$isMeBarButtonItemsHidden,
viewModel.$isReplyBarButtonItemHidden,
viewModel.$isMoreMenuBarButtonItemHidden
)
editingAndUpdatingPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] isEditing, isUpdating in
guard let self = self else { return }
self.cancelEditingBarButtonItem.isEnabled = !isUpdating
}
.store(in: &disposeBag)
// build items
Publishers.CombineLatest4(
viewModel.$relationship,
profileHeaderViewController.viewModel.$isTitleViewDisplaying,
editingAndUpdatingPublisher,
barButtonItemHiddenPublisher
)
.receive(on: DispatchQueue.main)
.sink { [weak self] account, isTitleViewDisplaying, tuple1, tuple2 in
guard let self else { return }
let (isEditing, _) = tuple1
let (isMeBarButtonItemsHidden, isReplyBarButtonItemHidden, isMoreMenuBarButtonItemHidden) = tuple2
var items: [UIBarButtonItem] = []
defer {
if items.isNotEmpty {
self.navigationItem.rightBarButtonItems = items
} else {
self.navigationItem.rightBarButtonItems = nil
}
}
if let suspended = self.viewModel.account.suspended, suspended == true {
return
}
guard isEditing == false else {
items.append(self.cancelEditingBarButtonItem)
return
}
guard isTitleViewDisplaying == false else {
return
}
guard isMeBarButtonItemsHidden else {
items.append(self.settingBarButtonItem)
items.append(self.shareBarButtonItem)
items.append(self.favoriteBarButtonItem)
items.append(self.bookmarkBarButtonItem)
if self.currentInstance?.canFollowTags == true {
items.append(self.followedTagsBarButtonItem)
}
return
}
if !isMoreMenuBarButtonItemHidden {
items.append(self.moreMenuBarButtonItem)
}
if !isReplyBarButtonItemHidden {
items.append(self.replyBarButtonItem)
}
}
.store(in: &disposeBag)
context.publisherService.statusPublishResult.sink { [weak self] result in
if case .success(.edit(let status)) = result {
self?.updateViewModelsWithDataControllers(status: .fromEntity(status.value), intent: .edit)
}
}.store(in: &disposeBag)
addChild(tabBarPagerController)
tabBarPagerController.view.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tabBarPagerController.view)
@ -280,17 +208,17 @@ extension ProfileViewController {
// setup delegate
profileHeaderViewController.delegate = self
profilePagingViewController.viewModel.profileAboutViewController.delegate = self
bindViewModel()
bindTitleView()
bindMoreBarButtonItem()
bindPager()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.navigationBar.prefersLargeTitles = false
bindViewModel()
bindTitleView()
bindMoreBarButtonItem()
bindPager()
}
override func viewDidAppear(_ animated: Bool) {
@ -356,6 +284,103 @@ extension ProfileViewController {
viewModel.$accountForEdit
.assign(to: \.accountForEdit, on: aboutViewModel)
.store(in: &disposeBag)
let editingAndUpdatingPublisher = Publishers.CombineLatest(
viewModel.$isEditing,
viewModel.$isUpdating
)
// note: not add .share() here
let barButtonItemHiddenPublisher = Publishers.CombineLatest3(
viewModel.$isMeBarButtonItemsHidden,
viewModel.$isReplyBarButtonItemHidden,
viewModel.$isMoreMenuBarButtonItemHidden
)
editingAndUpdatingPublisher
.receive(on: DispatchQueue.main)
.sink { [weak self] isEditing, isUpdating in
guard let self = self else { return }
self.cancelEditingBarButtonItem.isEnabled = !isUpdating
}
.store(in: &disposeBag)
// build items
Publishers.CombineLatest4(
viewModel.$relationship,
profileHeaderViewController.viewModel.$isTitleViewDisplaying,
editingAndUpdatingPublisher,
barButtonItemHiddenPublisher
)
.receive(on: DispatchQueue.main)
.sink { [weak self] account, isTitleViewDisplaying, tuple1, tuple2 in
guard let self else { return }
let (isEditing, _) = tuple1
let (isMeBarButtonItemsHidden, isReplyBarButtonItemHidden, isMoreMenuBarButtonItemHidden) = tuple2
var items: [UIBarButtonItem] = []
defer {
if items.isNotEmpty {
self.navigationItem.rightBarButtonItems = items
} else {
self.navigationItem.rightBarButtonItems = nil
}
}
if let suspended = self.viewModel.account.suspended, suspended == true {
return
}
guard isEditing == false else {
items.append(self.cancelEditingBarButtonItem)
return
}
guard isTitleViewDisplaying == false else {
return
}
guard isMeBarButtonItemsHidden else {
items.append(self.settingBarButtonItem)
items.append(self.shareBarButtonItem)
items.append(self.favoriteBarButtonItem)
items.append(self.bookmarkBarButtonItem)
if self.currentInstance?.canFollowTags == true {
items.append(self.followedTagsBarButtonItem)
}
return
}
if !isMoreMenuBarButtonItemHidden {
items.append(self.moreMenuBarButtonItem)
}
if !isReplyBarButtonItemHidden {
items.append(self.replyBarButtonItem)
}
}
.store(in: &disposeBag)
viewModel.$isEditing
.receive(on: DispatchQueue.main)
.sink { [weak self] isEditing in
guard let self else { return }
if isEditing {
tabBarPagerController.relayScrollView.refreshControl = nil
} else {
tabBarPagerController.relayScrollView.refreshControl = refreshControl
}
}
.store(in: &disposeBag)
context.publisherService.statusPublishResult.sink { [weak self] result in
if case .success(.edit(let status)) = result {
self?.updateViewModelsWithDataControllers(status: .fromEntity(status.value), intent: .edit)
}
}.store(in: &disposeBag)
}
private func bindTitleView() {
@ -443,7 +468,7 @@ extension ProfileViewController {
viewModel.$isPagingEnabled
.receive(on: DispatchQueue.main)
.sink { [weak self] isPagingEnabled in
guard let self = self else { return }
guard let self else { return }
self.profilePagingViewController.containerView.isScrollEnabled = isPagingEnabled
self.profilePagingViewController.buttonBarView.isUserInteractionEnabled = isPagingEnabled
}
@ -458,11 +483,10 @@ extension ProfileViewController {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.profilePagingViewController.becomeFirstResponder()
}
// dismiss keyboard if needs
self.view.endEditing(true)
}
// dismiss keyboard if needs
if !isEditing { self.view.endEditing(true) }
if isEditing,
let index = self.profilePagingViewController.viewControllers.firstIndex(where: { type(of: $0) is ProfileAboutViewController.Type }),
self.profilePagingViewController.canMoveTo(index: index)
@ -495,8 +519,7 @@ extension ProfileViewController {
extension ProfileViewController {
@objc private func cancelEditingBarButtonItemPressed(_ sender: UIBarButtonItem) {
viewModel.isEditing = false
profileHeaderViewController.viewModel.isEditing = false
cancelEditing()
}
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
@ -714,6 +737,8 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
aboutProfileInfo: profileAboutViewModel.profileInfoEditing
).value
self.viewModel.isEditing = false
self.profileHeaderViewController.viewModel.isEditing = false
profileAboutViewModel.isEditing = false
self.viewModel.account = updatedAccount
} catch {
@ -757,15 +782,25 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
// enter editing mode
self.viewModel.isEditing = true
self.profileHeaderViewController.viewModel.isEditing = true
profileAboutViewModel.isEditing = true
}
} receiveValue: { [weak self] response in
guard let self = self else { return }
self.viewModel.accountForEdit = response.value
}
.store(in: &disposeBag)
} else if isEdited == false {
cancelEditing()
}
}
private func cancelEditing() {
viewModel.isEditing = false
profileHeaderViewController.viewModel.isEditing = false
profilePagingViewController.viewModel.profileAboutViewController.viewModel.isEditing = false
viewModel.profileAboutViewModel.isEditing = false
}
private func editRelationship() {
guard let relationship = viewModel.relationship, viewModel.isUpdating == false else {
return