* 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:
parent
83fd4a89fa
commit
ab39c6ef87
@ -49,6 +49,15 @@ extension ProfileAboutViewController {
|
|||||||
view.addSubview(collectionView)
|
view.addSubview(collectionView)
|
||||||
collectionView.pinToParent()
|
collectionView.pinToParent()
|
||||||
|
|
||||||
|
let longPressReorderGesture = UILongPressGestureRecognizer(
|
||||||
|
target: self,
|
||||||
|
action: #selector(ProfileAboutViewController.longPressReorderGestureHandler(_:))
|
||||||
|
)
|
||||||
|
collectionView.addGestureRecognizer(longPressReorderGesture)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
collectionView.delegate = self
|
collectionView.delegate = self
|
||||||
viewModel.setupDiffableDataSource(
|
viewModel.setupDiffableDataSource(
|
||||||
collectionView: collectionView,
|
collectionView: collectionView,
|
||||||
@ -56,11 +65,6 @@ extension ProfileAboutViewController {
|
|||||||
profileFieldEditCollectionViewCellDelegate: self
|
profileFieldEditCollectionViewCellDelegate: self
|
||||||
)
|
)
|
||||||
|
|
||||||
let longPressReorderGesture = UILongPressGestureRecognizer(
|
|
||||||
target: self,
|
|
||||||
action: #selector(ProfileAboutViewController.longPressReorderGestureHandler(_:))
|
|
||||||
)
|
|
||||||
collectionView.addGestureRecognizer(longPressReorderGesture)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -51,7 +51,7 @@ extension ProfileAboutViewModel {
|
|||||||
diffableDataSource.apply(snapshot)
|
diffableDataSource.apply(snapshot)
|
||||||
|
|
||||||
let fields = Publishers.CombineLatest3(
|
let fields = Publishers.CombineLatest3(
|
||||||
$isEditing.removeDuplicates(),
|
$isEditing,
|
||||||
profileInfo.$fields.removeDuplicates(),
|
profileInfo.$fields.removeDuplicates(),
|
||||||
profileInfoEditing.$fields.removeDuplicates()
|
profileInfoEditing.$fields.removeDuplicates()
|
||||||
).map { isEditing, displayFields, editingFields in
|
).map { isEditing, displayFields, editingFields in
|
||||||
@ -60,7 +60,7 @@ extension ProfileAboutViewModel {
|
|||||||
|
|
||||||
|
|
||||||
Publishers.CombineLatest4(
|
Publishers.CombineLatest4(
|
||||||
$isEditing.removeDuplicates(),
|
$isEditing,
|
||||||
$createdAt.removeDuplicates(),
|
$createdAt.removeDuplicates(),
|
||||||
fields,
|
fields,
|
||||||
$emojiMeta.removeDuplicates()
|
$emojiMeta.removeDuplicates()
|
||||||
@ -68,7 +68,7 @@ extension ProfileAboutViewModel {
|
|||||||
.throttle(for: 0.3, scheduler: DispatchQueue.main, latest: true)
|
.throttle(for: 0.3, scheduler: DispatchQueue.main, latest: true)
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] isEditing, createdAt, fields, emojiMeta in
|
.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 }
|
guard let diffableDataSource = self.diffableDataSource else { return }
|
||||||
|
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<ProfileFieldSection, ProfileFieldItem>()
|
var snapshot = NSDiffableDataSourceSnapshot<ProfileFieldSection, ProfileFieldItem>()
|
||||||
|
@ -32,7 +32,18 @@ final class ProfileViewController: UIViewController, NeedsDependency, MediaPrevi
|
|||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
//TODO: Replace with something better than !
|
//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()
|
let mediaPreviewTransitionController = MediaPreviewTransitionController()
|
||||||
|
|
||||||
@ -182,6 +193,98 @@ extension ProfileViewController {
|
|||||||
|
|
||||||
navigationItem.titleView = titleView
|
navigationItem.titleView = titleView
|
||||||
|
|
||||||
|
addChild(tabBarPagerController)
|
||||||
|
tabBarPagerController.view.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
view.addSubview(tabBarPagerController.view)
|
||||||
|
tabBarPagerController.didMove(toParent: self)
|
||||||
|
tabBarPagerController.view.pinToParent()
|
||||||
|
|
||||||
|
tabBarPagerController.delegate = self
|
||||||
|
tabBarPagerController.dataSource = self
|
||||||
|
|
||||||
|
tabBarPagerController.relayScrollView.refreshControl = refreshControl
|
||||||
|
refreshControl.addTarget(self, action: #selector(ProfileViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
||||||
|
|
||||||
|
// setup delegate
|
||||||
|
profileHeaderViewController.delegate = self
|
||||||
|
profilePagingViewController.viewModel.profileAboutViewController.delegate = self
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
navigationController?.navigationBar.prefersLargeTitles = false
|
||||||
|
|
||||||
|
bindViewModel()
|
||||||
|
bindTitleView()
|
||||||
|
bindMoreBarButtonItem()
|
||||||
|
bindPager()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
|
super.viewDidAppear(animated)
|
||||||
|
viewModel.viewDidAppear.send()
|
||||||
|
|
||||||
|
setNeedsStatusBarAppearanceUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ProfileViewController {
|
||||||
|
|
||||||
|
private func bindViewModel() {
|
||||||
|
// header
|
||||||
|
let headerViewModel = profileHeaderViewController.viewModel
|
||||||
|
viewModel.$account
|
||||||
|
.assign(to: \.account, on: headerViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
viewModel.$isEditing
|
||||||
|
.assign(to: \.isEditing, on: headerViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
viewModel.$isUpdating
|
||||||
|
.assign(to: \.isUpdating, on: headerViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
viewModel.$relationship
|
||||||
|
.assign(to: \.relationship, on: headerViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
viewModel.$accountForEdit
|
||||||
|
.assign(to: \.accountForEdit, on: headerViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
[
|
||||||
|
viewModel.postsUserTimelineViewModel,
|
||||||
|
viewModel.repliesUserTimelineViewModel,
|
||||||
|
viewModel.mediaUserTimelineViewModel,
|
||||||
|
].forEach { userTimelineViewModel in
|
||||||
|
|
||||||
|
viewModel.relationship.publisher
|
||||||
|
.map { $0.blocking }
|
||||||
|
.assign(to: \UserTimelineViewModel.isBlocking, on: userTimelineViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
viewModel.relationship.publisher
|
||||||
|
.compactMap { $0.blockedBy }
|
||||||
|
.assign(to: \UserTimelineViewModel.isBlockedBy, on: userTimelineViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
viewModel.$account
|
||||||
|
.compactMap { $0.suspended }
|
||||||
|
.assign(to: \UserTimelineViewModel.isSuspended, on: userTimelineViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// about
|
||||||
|
let aboutViewModel = viewModel.profileAboutViewModel
|
||||||
|
viewModel.$account
|
||||||
|
.assign(to: \.account, on: aboutViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
viewModel.$isEditing
|
||||||
|
.assign(to: \.isEditing, on: aboutViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
viewModel.$accountForEdit
|
||||||
|
.assign(to: \.accountForEdit, on: aboutViewModel)
|
||||||
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
let editingAndUpdatingPublisher = Publishers.CombineLatest(
|
let editingAndUpdatingPublisher = Publishers.CombineLatest(
|
||||||
viewModel.$isEditing,
|
viewModel.$isEditing,
|
||||||
viewModel.$isUpdating
|
viewModel.$isUpdating
|
||||||
@ -259,103 +362,25 @@ extension ProfileViewController {
|
|||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.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
|
context.publisherService.statusPublishResult.sink { [weak self] result in
|
||||||
if case .success(.edit(let status)) = result {
|
if case .success(.edit(let status)) = result {
|
||||||
self?.updateViewModelsWithDataControllers(status: .fromEntity(status.value), intent: .edit)
|
self?.updateViewModelsWithDataControllers(status: .fromEntity(status.value), intent: .edit)
|
||||||
}
|
}
|
||||||
}.store(in: &disposeBag)
|
}.store(in: &disposeBag)
|
||||||
|
|
||||||
addChild(tabBarPagerController)
|
|
||||||
tabBarPagerController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
view.addSubview(tabBarPagerController.view)
|
|
||||||
tabBarPagerController.didMove(toParent: self)
|
|
||||||
tabBarPagerController.view.pinToParent()
|
|
||||||
|
|
||||||
tabBarPagerController.delegate = self
|
|
||||||
tabBarPagerController.dataSource = self
|
|
||||||
|
|
||||||
tabBarPagerController.relayScrollView.refreshControl = refreshControl
|
|
||||||
refreshControl.addTarget(self, action: #selector(ProfileViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
|
||||||
super.viewDidAppear(animated)
|
|
||||||
viewModel.viewDidAppear.send()
|
|
||||||
|
|
||||||
setNeedsStatusBarAppearanceUpdate()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ProfileViewController {
|
|
||||||
|
|
||||||
private func bindViewModel() {
|
|
||||||
// header
|
|
||||||
let headerViewModel = profileHeaderViewController.viewModel
|
|
||||||
viewModel.$account
|
|
||||||
.assign(to: \.account, on: headerViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
viewModel.$isEditing
|
|
||||||
.assign(to: \.isEditing, on: headerViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
viewModel.$isUpdating
|
|
||||||
.assign(to: \.isUpdating, on: headerViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
viewModel.$relationship
|
|
||||||
.assign(to: \.relationship, on: headerViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
viewModel.$accountForEdit
|
|
||||||
.assign(to: \.accountForEdit, on: headerViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
[
|
|
||||||
viewModel.postsUserTimelineViewModel,
|
|
||||||
viewModel.repliesUserTimelineViewModel,
|
|
||||||
viewModel.mediaUserTimelineViewModel,
|
|
||||||
].forEach { userTimelineViewModel in
|
|
||||||
|
|
||||||
viewModel.relationship.publisher
|
|
||||||
.map { $0.blocking }
|
|
||||||
.assign(to: \UserTimelineViewModel.isBlocking, on: userTimelineViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
viewModel.relationship.publisher
|
|
||||||
.compactMap { $0.blockedBy }
|
|
||||||
.assign(to: \UserTimelineViewModel.isBlockedBy, on: userTimelineViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
viewModel.$account
|
|
||||||
.compactMap { $0.suspended }
|
|
||||||
.assign(to: \UserTimelineViewModel.isSuspended, on: userTimelineViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
|
|
||||||
// about
|
|
||||||
let aboutViewModel = viewModel.profileAboutViewModel
|
|
||||||
viewModel.$account
|
|
||||||
.assign(to: \.account, on: aboutViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
viewModel.$isEditing
|
|
||||||
.assign(to: \.isEditing, on: aboutViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
viewModel.$accountForEdit
|
|
||||||
.assign(to: \.accountForEdit, on: aboutViewModel)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func bindTitleView() {
|
private func bindTitleView() {
|
||||||
@ -443,7 +468,7 @@ extension ProfileViewController {
|
|||||||
viewModel.$isPagingEnabled
|
viewModel.$isPagingEnabled
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] isPagingEnabled in
|
.sink { [weak self] isPagingEnabled in
|
||||||
guard let self = self else { return }
|
guard let self else { return }
|
||||||
self.profilePagingViewController.containerView.isScrollEnabled = isPagingEnabled
|
self.profilePagingViewController.containerView.isScrollEnabled = isPagingEnabled
|
||||||
self.profilePagingViewController.buttonBarView.isUserInteractionEnabled = isPagingEnabled
|
self.profilePagingViewController.buttonBarView.isUserInteractionEnabled = isPagingEnabled
|
||||||
}
|
}
|
||||||
@ -458,11 +483,10 @@ extension ProfileViewController {
|
|||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
||||||
self.profilePagingViewController.becomeFirstResponder()
|
self.profilePagingViewController.becomeFirstResponder()
|
||||||
}
|
}
|
||||||
|
// dismiss keyboard if needs
|
||||||
|
self.view.endEditing(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// dismiss keyboard if needs
|
|
||||||
if !isEditing { self.view.endEditing(true) }
|
|
||||||
|
|
||||||
if isEditing,
|
if isEditing,
|
||||||
let index = self.profilePagingViewController.viewControllers.firstIndex(where: { type(of: $0) is ProfileAboutViewController.Type }),
|
let index = self.profilePagingViewController.viewControllers.firstIndex(where: { type(of: $0) is ProfileAboutViewController.Type }),
|
||||||
self.profilePagingViewController.canMoveTo(index: index)
|
self.profilePagingViewController.canMoveTo(index: index)
|
||||||
@ -495,8 +519,7 @@ extension ProfileViewController {
|
|||||||
extension ProfileViewController {
|
extension ProfileViewController {
|
||||||
|
|
||||||
@objc private func cancelEditingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
@objc private func cancelEditingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||||
viewModel.isEditing = false
|
cancelEditing()
|
||||||
profileHeaderViewController.viewModel.isEditing = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
||||||
@ -714,6 +737,8 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
|||||||
aboutProfileInfo: profileAboutViewModel.profileInfoEditing
|
aboutProfileInfo: profileAboutViewModel.profileInfoEditing
|
||||||
).value
|
).value
|
||||||
self.viewModel.isEditing = false
|
self.viewModel.isEditing = false
|
||||||
|
self.profileHeaderViewController.viewModel.isEditing = false
|
||||||
|
profileAboutViewModel.isEditing = false
|
||||||
self.viewModel.account = updatedAccount
|
self.viewModel.account = updatedAccount
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
@ -757,15 +782,25 @@ extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
|||||||
// enter editing mode
|
// enter editing mode
|
||||||
self.viewModel.isEditing = true
|
self.viewModel.isEditing = true
|
||||||
self.profileHeaderViewController.viewModel.isEditing = true
|
self.profileHeaderViewController.viewModel.isEditing = true
|
||||||
|
profileAboutViewModel.isEditing = true
|
||||||
}
|
}
|
||||||
} receiveValue: { [weak self] response in
|
} receiveValue: { [weak self] response in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
self.viewModel.accountForEdit = response.value
|
self.viewModel.accountForEdit = response.value
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.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() {
|
private func editRelationship() {
|
||||||
guard let relationship = viewModel.relationship, viewModel.isUpdating == false else {
|
guard let relationship = viewModel.relationship, viewModel.isUpdating == false else {
|
||||||
return
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user