2021-02-23 09:45:00 +01:00
|
|
|
//
|
|
|
|
// ProfileViewController.swift
|
|
|
|
// Mastodon
|
|
|
|
//
|
|
|
|
// Created by MainasuK Cirno on 2021-2-23.
|
|
|
|
//
|
|
|
|
|
2021-04-01 08:39:15 +02:00
|
|
|
import os.log
|
2021-02-23 09:45:00 +01:00
|
|
|
import UIKit
|
2021-04-01 08:39:15 +02:00
|
|
|
import Combine
|
2021-07-23 13:10:27 +02:00
|
|
|
import MastodonMeta
|
|
|
|
import MetaTextKit
|
2022-01-27 14:23:39 +01:00
|
|
|
import MastodonAsset
|
|
|
|
import MastodonLocalization
|
|
|
|
import MastodonUI
|
|
|
|
import CoreDataStack
|
2022-05-13 11:23:35 +02:00
|
|
|
import TabBarPager
|
|
|
|
import XLPagerTabStrip
|
2022-01-27 14:23:39 +01:00
|
|
|
|
|
|
|
protocol ProfileViewModelEditable {
|
|
|
|
func isEdited() -> Bool
|
|
|
|
}
|
2021-02-23 09:45:00 +01:00
|
|
|
|
2021-04-28 14:10:17 +02:00
|
|
|
final class ProfileViewController: UIViewController, NeedsDependency, MediaPreviewableViewController {
|
2021-02-23 09:45:00 +01:00
|
|
|
|
2022-02-18 11:55:26 +01:00
|
|
|
public static let containerViewMarginForRegularHorizontalSizeClass: CGFloat = 64
|
|
|
|
public static let containerViewMarginForCompactHorizontalSizeClass: CGFloat = 16
|
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
let logger = Logger(subsystem: "ProfileViewController", category: "ViewController")
|
|
|
|
|
2021-02-23 09:45:00 +01:00
|
|
|
weak var context: AppContext! { willSet { precondition(!isViewLoaded) } }
|
|
|
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
|
|
|
|
2021-04-01 08:39:15 +02:00
|
|
|
var disposeBag = Set<AnyCancellable>()
|
|
|
|
var viewModel: ProfileViewModel!
|
|
|
|
|
2021-04-28 14:10:17 +02:00
|
|
|
let mediaPreviewTransitionController = MediaPreviewTransitionController()
|
|
|
|
|
2021-04-09 11:31:43 +02:00
|
|
|
private(set) lazy var cancelEditingBarButtonItem: UIBarButtonItem = {
|
|
|
|
let barButtonItem = UIBarButtonItem(title: L10n.Common.Controls.Actions.cancel, style: .plain, target: self, action: #selector(ProfileViewController.cancelEditingBarButtonItemPressed(_:)))
|
|
|
|
barButtonItem.tintColor = .white
|
|
|
|
return barButtonItem
|
|
|
|
}()
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-07 08:24:28 +02:00
|
|
|
private(set) lazy var settingBarButtonItem: UIBarButtonItem = {
|
2022-05-06 09:17:26 +02:00
|
|
|
let barButtonItem = UIBarButtonItem(
|
|
|
|
image: Asset.ObjectsAndTools.gear.image.withRenderingMode(.alwaysTemplate),
|
|
|
|
style: .plain,
|
|
|
|
target: self,
|
|
|
|
action: #selector(ProfileViewController.settingBarButtonItemPressed(_:))
|
|
|
|
)
|
2021-04-07 08:24:28 +02:00
|
|
|
barButtonItem.tintColor = .white
|
|
|
|
return barButtonItem
|
|
|
|
}()
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-07 08:24:28 +02:00
|
|
|
private(set) lazy var shareBarButtonItem: UIBarButtonItem = {
|
2022-05-06 09:17:26 +02:00
|
|
|
let barButtonItem = UIBarButtonItem(
|
|
|
|
image: Asset.Arrow.squareAndArrowUp.image.withRenderingMode(.alwaysTemplate),
|
|
|
|
style: .plain,
|
|
|
|
target: self,
|
|
|
|
action: #selector(ProfileViewController.shareBarButtonItemPressed(_:))
|
|
|
|
)
|
2021-04-07 08:24:28 +02:00
|
|
|
barButtonItem.tintColor = .white
|
|
|
|
return barButtonItem
|
|
|
|
}()
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-07 08:24:28 +02:00
|
|
|
private(set) lazy var favoriteBarButtonItem: UIBarButtonItem = {
|
2022-05-06 09:17:26 +02:00
|
|
|
let barButtonItem = UIBarButtonItem(
|
|
|
|
image: Asset.ObjectsAndTools.star.image.withRenderingMode(.alwaysTemplate),
|
|
|
|
style: .plain,
|
|
|
|
target: self,
|
|
|
|
action: #selector(ProfileViewController.favoriteBarButtonItemPressed(_:))
|
|
|
|
)
|
2021-04-07 08:24:28 +02:00
|
|
|
barButtonItem.tintColor = .white
|
|
|
|
return barButtonItem
|
|
|
|
}()
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-02 12:13:45 +02:00
|
|
|
private(set) lazy var replyBarButtonItem: UIBarButtonItem = {
|
|
|
|
let barButtonItem = UIBarButtonItem(image: UIImage(systemName: "arrowshape.turn.up.left"), style: .plain, target: self, action: #selector(ProfileViewController.replyBarButtonItemPressed(_:)))
|
|
|
|
barButtonItem.tintColor = .white
|
|
|
|
return barButtonItem
|
|
|
|
}()
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-02 12:13:45 +02:00
|
|
|
let moreMenuBarButtonItem: UIBarButtonItem = {
|
|
|
|
let barButtonItem = UIBarButtonItem(image: UIImage(systemName: "ellipsis.circle"), style: .plain, target: nil, action: nil)
|
|
|
|
barButtonItem.tintColor = .white
|
|
|
|
return barButtonItem
|
|
|
|
}()
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-01 08:39:15 +02:00
|
|
|
let refreshControl: UIRefreshControl = {
|
|
|
|
let refreshControl = UIRefreshControl()
|
2021-06-22 13:33:36 +02:00
|
|
|
refreshControl.tintColor = .white
|
2021-04-01 08:39:15 +02:00
|
|
|
return refreshControl
|
|
|
|
}()
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
private(set) lazy var tabBarPagerController = TabBarPagerController()
|
|
|
|
|
2021-04-09 11:31:43 +02:00
|
|
|
private(set) lazy var profileHeaderViewController: ProfileHeaderViewController = {
|
|
|
|
let viewController = ProfileHeaderViewController()
|
|
|
|
viewController.viewModel = ProfileHeaderViewModel(context: context)
|
|
|
|
return viewController
|
|
|
|
}()
|
2021-04-01 08:39:15 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
private(set) lazy var profilePagingViewController: ProfilePagingViewController = {
|
|
|
|
let profilePagingViewController = ProfilePagingViewController()
|
|
|
|
profilePagingViewController.viewModel = {
|
|
|
|
let profilePagingViewModel = ProfilePagingViewModel(
|
|
|
|
postsUserTimelineViewModel: viewModel.postsUserTimelineViewModel,
|
|
|
|
repliesUserTimelineViewModel: viewModel.repliesUserTimelineViewModel,
|
|
|
|
mediaUserTimelineViewModel: viewModel.mediaUserTimelineViewModel,
|
|
|
|
profileAboutViewModel: viewModel.profileAboutViewModel
|
|
|
|
)
|
|
|
|
profilePagingViewModel.viewControllers.forEach { viewController in
|
|
|
|
if let viewController = viewController as? NeedsDependency {
|
|
|
|
viewController.context = context
|
|
|
|
viewController.coordinator = coordinator
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return profilePagingViewModel
|
|
|
|
}()
|
|
|
|
return profilePagingViewController
|
|
|
|
}()
|
|
|
|
|
|
|
|
// let containerScrollView: UIScrollView = {
|
|
|
|
// let scrollView = UIScrollView()
|
|
|
|
// scrollView.scrollsToTop = false
|
|
|
|
// scrollView.showsVerticalScrollIndicator = false
|
|
|
|
// scrollView.preservesSuperviewLayoutMargins = true
|
|
|
|
// scrollView.delaysContentTouches = false
|
|
|
|
// return scrollView
|
|
|
|
// }()
|
|
|
|
//
|
|
|
|
// let overlayScrollView: UIScrollView = {
|
|
|
|
// let scrollView = UIScrollView()
|
|
|
|
// scrollView.showsVerticalScrollIndicator = false
|
|
|
|
// scrollView.backgroundColor = .clear
|
|
|
|
// scrollView.delaysContentTouches = false
|
|
|
|
// return scrollView
|
|
|
|
// }()
|
|
|
|
//
|
|
|
|
|
|
|
|
//// private var profileBannerImageViewLayoutConstraint: NSLayoutConstraint!
|
|
|
|
// private(set) lazy var profileSegmentedViewController = ProfilePagingViewController()
|
|
|
|
//
|
|
|
|
// private var contentOffsets: [Int: CGFloat] = [:]
|
|
|
|
// var currentPostTimelineTableViewContentSizeObservation: NSKeyValueObservation?
|
|
|
|
|
2021-04-09 13:44:48 +02:00
|
|
|
// title view nested in header
|
|
|
|
var titleView: DoubleTitleLabelNavigationBarTitleView {
|
|
|
|
profileHeaderViewController.titleView
|
|
|
|
}
|
2021-04-01 08:39:15 +02:00
|
|
|
|
|
|
|
deinit {
|
2021-09-30 08:19:15 +02:00
|
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
2021-04-01 08:39:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
//extension ProfileViewController {
|
|
|
|
//
|
|
|
|
// func observeTableViewContentSize(scrollView: UIScrollView) -> NSKeyValueObservation {
|
|
|
|
// updateOverlayScrollViewContentSize(scrollView: scrollView)
|
|
|
|
// return scrollView.observe(\.contentSize, options: .new) { scrollView, change in
|
|
|
|
// self.updateOverlayScrollViewContentSize(scrollView: scrollView)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func updateOverlayScrollViewContentSize(scrollView: UIScrollView) {
|
|
|
|
// let bottomPageHeight = max(scrollView.contentSize.height, self.containerScrollView.frame.height - ProfileHeaderViewController.headerMinHeight - self.containerScrollView.safeAreaInsets.bottom)
|
|
|
|
// let headerViewHeight: CGFloat = profileHeaderViewController.view.frame.height
|
|
|
|
// let contentSize = CGSize(
|
|
|
|
// width: self.containerScrollView.contentSize.width,
|
|
|
|
// height: bottomPageHeight + headerViewHeight
|
|
|
|
// )
|
|
|
|
// self.overlayScrollView.contentSize = contentSize
|
|
|
|
// // os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: contentSize: %s", ((#file as NSString).lastPathComponent), #line, #function, contentSize.debugDescription)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|
2021-02-23 09:45:00 +01:00
|
|
|
|
|
|
|
extension ProfileViewController {
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// override var preferredStatusBarStyle: UIStatusBarStyle {
|
|
|
|
// return .lightContent
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// override func viewSafeAreaInsetsDidChange() {
|
|
|
|
// super.viewSafeAreaInsetsDidChange()
|
|
|
|
//
|
|
|
|
// profileHeaderViewController.updateHeaderContainerSafeAreaInset(view.safeAreaInsets)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// override var isViewLoaded: Bool {
|
|
|
|
// return super.isViewLoaded
|
|
|
|
// }
|
2021-04-02 12:13:45 +02:00
|
|
|
|
2021-02-23 09:45:00 +01:00
|
|
|
override func viewDidLoad() {
|
|
|
|
super.viewDidLoad()
|
2021-07-06 12:00:39 +02:00
|
|
|
|
|
|
|
view.backgroundColor = ThemeService.shared.currentTheme.value.secondarySystemBackgroundColor
|
2021-07-05 10:07:17 +02:00
|
|
|
ThemeService.shared.currentTheme
|
2022-01-27 14:23:39 +01:00
|
|
|
.receive(on: DispatchQueue.main)
|
2021-07-05 10:07:17 +02:00
|
|
|
.sink { [weak self] theme in
|
|
|
|
guard let self = self else { return }
|
|
|
|
self.view.backgroundColor = theme.secondarySystemBackgroundColor
|
|
|
|
}
|
|
|
|
.store(in: &disposeBag)
|
2021-04-02 12:13:45 +02:00
|
|
|
|
2021-04-01 08:39:15 +02:00
|
|
|
let barAppearance = UINavigationBarAppearance()
|
2021-07-07 13:07:47 +02:00
|
|
|
if isModal {
|
|
|
|
barAppearance.configureWithDefaultBackground()
|
|
|
|
} else {
|
|
|
|
barAppearance.configureWithTransparentBackground()
|
|
|
|
}
|
2021-04-01 08:39:15 +02:00
|
|
|
navigationItem.standardAppearance = barAppearance
|
|
|
|
navigationItem.compactAppearance = barAppearance
|
|
|
|
navigationItem.scrollEdgeAppearance = barAppearance
|
2021-04-09 13:44:48 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
navigationItem.titleView = titleView
|
2021-04-07 08:24:28 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// let editingAndUpdatingPublisher = Publishers.CombineLatest(
|
|
|
|
// viewModel.$isEditing,
|
|
|
|
// viewModel.$isUpdating
|
|
|
|
// )
|
|
|
|
// // note: not add .share() here
|
|
|
|
//
|
|
|
|
// let barButtonItemHiddenPublisher = Publishers.CombineLatest3(
|
|
|
|
// viewModel.isMeBarButtonItemsHidden.eraseToAnyPublisher(),
|
|
|
|
// viewModel.isReplyBarButtonItemHidden.eraseToAnyPublisher(),
|
|
|
|
// viewModel.isMoreMenuBarButtonItemHidden.eraseToAnyPublisher()
|
|
|
|
// )
|
|
|
|
//
|
|
|
|
// editingAndUpdatingPublisher
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] isEditing, isUpdating in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// self.cancelEditingBarButtonItem.isEnabled = !isUpdating
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
2021-04-02 12:13:45 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// Publishers.CombineLatest4 (
|
|
|
|
// viewModel.suspended.eraseToAnyPublisher(),
|
|
|
|
// profileHeaderViewController.viewModel.isTitleViewDisplaying.eraseToAnyPublisher(),
|
|
|
|
// editingAndUpdatingPublisher.eraseToAnyPublisher(),
|
|
|
|
// barButtonItemHiddenPublisher.eraseToAnyPublisher()
|
|
|
|
// )
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] suspended, isTitleViewDisplaying, tuple1, tuple2 in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// let (isEditing, _) = tuple1
|
|
|
|
// let (isMeBarButtonItemsHidden, isReplyBarButtonItemHidden, isMoreMenuBarButtonItemHidden) = tuple2
|
|
|
|
//
|
|
|
|
// var items: [UIBarButtonItem] = []
|
|
|
|
// defer {
|
|
|
|
// self.navigationItem.rightBarButtonItems = !items.isEmpty ? items : nil
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// guard !suspended else {
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// guard !isEditing else {
|
|
|
|
// items.append(self.cancelEditingBarButtonItem)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// guard !isTitleViewDisplaying else {
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// guard isMeBarButtonItemsHidden else {
|
|
|
|
// items.append(self.settingBarButtonItem)
|
|
|
|
// items.append(self.shareBarButtonItem)
|
|
|
|
// items.append(self.favoriteBarButtonItem)
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// if !isMoreMenuBarButtonItemHidden {
|
|
|
|
// items.append(self.moreMenuBarButtonItem)
|
|
|
|
// }
|
|
|
|
// if !isReplyBarButtonItemHidden {
|
|
|
|
// items.append(self.replyBarButtonItem)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
2021-04-01 08:39:15 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
addChild(tabBarPagerController)
|
|
|
|
tabBarPagerController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
view.addSubview(tabBarPagerController.view)
|
|
|
|
tabBarPagerController.didMove(toParent: self)
|
2021-04-01 08:39:15 +02:00
|
|
|
NSLayoutConstraint.activate([
|
2022-05-13 11:23:35 +02:00
|
|
|
tabBarPagerController.view.topAnchor.constraint(equalTo: view.topAnchor),
|
|
|
|
tabBarPagerController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
|
|
tabBarPagerController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
|
|
|
tabBarPagerController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor),
|
2021-04-01 08:39:15 +02:00
|
|
|
])
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
tabBarPagerController.delegate = self
|
|
|
|
tabBarPagerController.dataSource = self
|
2022-01-27 14:23:39 +01:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
tabBarPagerController.relayScrollView.refreshControl = refreshControl
|
|
|
|
refreshControl.addTarget(self, action: #selector(ProfileViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
2022-01-27 14:23:39 +01:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// overlayScrollView.refreshControl = refreshControl
|
|
|
|
// refreshControl.addTarget(self, action: #selector(ProfileViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
|
|
|
//
|
|
|
|
// let postsUserTimelineViewModel = UserTimelineViewModel(context: context, domain: viewModel.domain.value, userID: viewModel.userID.value, queryFilter: UserTimelineViewModel.QueryFilter(excludeReplies: true))
|
|
|
|
// bind(userTimelineViewModel: postsUserTimelineViewModel)
|
|
|
|
//
|
|
|
|
// let repliesUserTimelineViewModel = UserTimelineViewModel(context: context, domain: viewModel.domain.value, userID: viewModel.userID.value, queryFilter: UserTimelineViewModel.QueryFilter(excludeReplies: false))
|
|
|
|
// bind(userTimelineViewModel: repliesUserTimelineViewModel)
|
|
|
|
//
|
|
|
|
// let mediaUserTimelineViewModel = UserTimelineViewModel(context: context, domain: viewModel.domain.value, userID: viewModel.userID.value, queryFilter: UserTimelineViewModel.QueryFilter(onlyMedia: true))
|
|
|
|
// bind(userTimelineViewModel: mediaUserTimelineViewModel)
|
|
|
|
//
|
|
|
|
// let profileAboutViewModel = ProfileAboutViewModel(context: context)
|
|
|
|
//
|
|
|
|
// profileSegmentedViewController.pagingViewController.viewModel = {
|
|
|
|
// let profilePagingViewModel = ProfilePagingViewModel(
|
|
|
|
// postsUserTimelineViewModel: postsUserTimelineViewModel,
|
|
|
|
// repliesUserTimelineViewModel: repliesUserTimelineViewModel,
|
|
|
|
// mediaUserTimelineViewModel: mediaUserTimelineViewModel,
|
|
|
|
// profileAboutViewModel: profileAboutViewModel
|
|
|
|
// )
|
|
|
|
// profilePagingViewModel.viewControllers.forEach { viewController in
|
|
|
|
// if let viewController = viewController as? NeedsDependency {
|
|
|
|
// viewController.context = context
|
|
|
|
// viewController.coordinator = coordinator
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// return profilePagingViewModel
|
|
|
|
// }()
|
|
|
|
//
|
|
|
|
// profileSegmentedViewController.pagingViewController.addBar(
|
|
|
|
// profileHeaderViewController.buttonBar,
|
|
|
|
// dataSource: profileSegmentedViewController.pagingViewController.viewModel,
|
|
|
|
// at: .custom(view: profileHeaderViewController.view, layout: { buttonBar in
|
|
|
|
// buttonBar.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
// self.profileHeaderViewController.view.addSubview(buttonBar)
|
|
|
|
// NSLayoutConstraint.activate([
|
|
|
|
// buttonBar.topAnchor.constraint(equalTo: self.profileHeaderViewController.profileHeaderView.bottomAnchor),
|
|
|
|
// buttonBar.leadingAnchor.constraint(equalTo: self.profileHeaderViewController.view.leadingAnchor),
|
|
|
|
// buttonBar.trailingAnchor.constraint(equalTo: self.profileHeaderViewController.view.trailingAnchor),
|
|
|
|
// buttonBar.bottomAnchor.constraint(equalTo: self.profileHeaderViewController.view.bottomAnchor),
|
|
|
|
// buttonBar.heightAnchor.constraint(equalToConstant: ProfileHeaderViewController.segmentedControlHeight).priority(.required - 1),
|
|
|
|
// ])
|
|
|
|
// })
|
|
|
|
// )
|
|
|
|
// updateBarButtonInsets()
|
|
|
|
//
|
|
|
|
// overlayScrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
// view.addSubview(overlayScrollView)
|
|
|
|
// NSLayoutConstraint.activate([
|
|
|
|
// overlayScrollView.frameLayoutGuide.topAnchor.constraint(equalTo: view.topAnchor),
|
|
|
|
// overlayScrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
|
|
// view.trailingAnchor.constraint(equalTo: overlayScrollView.frameLayoutGuide.trailingAnchor),
|
|
|
|
// view.bottomAnchor.constraint(equalTo: overlayScrollView.frameLayoutGuide.bottomAnchor),
|
|
|
|
// overlayScrollView.contentLayoutGuide.widthAnchor.constraint(equalTo: view.widthAnchor),
|
|
|
|
// ])
|
|
|
|
//
|
|
|
|
// containerScrollView.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
// view.addSubview(containerScrollView)
|
|
|
|
// NSLayoutConstraint.activate([
|
|
|
|
// containerScrollView.frameLayoutGuide.topAnchor.constraint(equalTo: view.topAnchor),
|
|
|
|
// containerScrollView.frameLayoutGuide.leadingAnchor.constraint(equalTo: view.leadingAnchor),
|
|
|
|
// view.trailingAnchor.constraint(equalTo: containerScrollView.frameLayoutGuide.trailingAnchor),
|
|
|
|
// view.bottomAnchor.constraint(equalTo: containerScrollView.frameLayoutGuide.bottomAnchor),
|
|
|
|
// containerScrollView.contentLayoutGuide.widthAnchor.constraint(equalTo: view.widthAnchor),
|
|
|
|
// ])
|
|
|
|
//
|
|
|
|
// // add segmented list
|
|
|
|
// addChild(profileSegmentedViewController)
|
|
|
|
// profileSegmentedViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
// containerScrollView.addSubview(profileSegmentedViewController.view)
|
|
|
|
// profileSegmentedViewController.didMove(toParent: self)
|
|
|
|
// NSLayoutConstraint.activate([
|
|
|
|
// profileSegmentedViewController.view.leadingAnchor.constraint(equalTo: containerScrollView.contentLayoutGuide.leadingAnchor),
|
|
|
|
// profileSegmentedViewController.view.trailingAnchor.constraint(equalTo: containerScrollView.contentLayoutGuide.trailingAnchor),
|
|
|
|
// profileSegmentedViewController.view.bottomAnchor.constraint(equalTo: containerScrollView.contentLayoutGuide.bottomAnchor),
|
|
|
|
// profileSegmentedViewController.view.heightAnchor.constraint(equalTo: containerScrollView.frameLayoutGuide.heightAnchor),
|
|
|
|
// ])
|
|
|
|
//
|
|
|
|
// // add header
|
|
|
|
// addChild(profileHeaderViewController)
|
|
|
|
// profileHeaderViewController.view.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
// containerScrollView.addSubview(profileHeaderViewController.view)
|
|
|
|
// profileHeaderViewController.didMove(toParent: self)
|
|
|
|
// NSLayoutConstraint.activate([
|
|
|
|
// profileHeaderViewController.view.topAnchor.constraint(equalTo: containerScrollView.topAnchor),
|
|
|
|
// profileHeaderViewController.view.leadingAnchor.constraint(equalTo: containerScrollView.contentLayoutGuide.leadingAnchor),
|
|
|
|
// containerScrollView.contentLayoutGuide.trailingAnchor.constraint(equalTo: profileHeaderViewController.view.trailingAnchor),
|
|
|
|
// profileSegmentedViewController.view.topAnchor.constraint(equalTo: profileHeaderViewController.view.bottomAnchor),
|
|
|
|
// ])
|
|
|
|
//
|
|
|
|
// containerScrollView.addGestureRecognizer(overlayScrollView.panGestureRecognizer)
|
|
|
|
// overlayScrollView.layer.zPosition = .greatestFiniteMagnitude // make vision top-most
|
|
|
|
// overlayScrollView.delegate = self
|
|
|
|
// profileHeaderViewController.delegate = self
|
|
|
|
// profileSegmentedViewController.pagingViewController.viewModel.profileAboutViewController.delegate = self
|
|
|
|
// profileSegmentedViewController.pagingViewController.pagingDelegate = self
|
|
|
|
//
|
|
|
|
// // bind view model
|
|
|
|
// bindProfile(
|
|
|
|
// headerViewModel: profileHeaderViewController.viewModel,
|
|
|
|
// aboutViewModel: profileAboutViewModel
|
|
|
|
// )
|
|
|
|
//
|
|
|
|
// bindTitleView()
|
|
|
|
// bindHeader()
|
|
|
|
// bindProfileRelationship()
|
|
|
|
// bindProfileDashboard()
|
|
|
|
//
|
|
|
|
// viewModel.needsPagingEnabled
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] needsPaingEnabled in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// self.profileSegmentedViewController.pagingViewController.isScrollEnabled = needsPaingEnabled
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// profileHeaderViewController.profileHeaderView.delegate = self
|
2022-01-27 14:23:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override func viewWillAppear(_ animated: Bool) {
|
|
|
|
super.viewWillAppear(animated)
|
|
|
|
|
|
|
|
// set back button tint color in SceneCoordinator.present(scene:from:transition:)
|
|
|
|
|
|
|
|
// force layout to make banner image tweak take effect
|
2022-05-13 11:23:35 +02:00
|
|
|
// view.layoutIfNeeded()
|
2022-01-27 14:23:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
|
super.viewDidAppear(animated)
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// viewModel.viewDidAppear.send()
|
|
|
|
//
|
|
|
|
// // set overlay scroll view initial content size
|
|
|
|
// guard let currentViewController = profileSegmentedViewController.pagingViewController.currentViewController as? ScrollViewContainer,
|
|
|
|
// let scrollView = currentViewController.scrollView
|
|
|
|
// else { return }
|
|
|
|
//
|
|
|
|
// currentPostTimelineTableViewContentSizeObservation = observeTableViewContentSize(scrollView: scrollView)
|
|
|
|
// scrollView.panGestureRecognizer.require(toFail: overlayScrollView.panGestureRecognizer)
|
2022-01-27 14:23:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
override func viewDidDisappear(_ animated: Bool) {
|
|
|
|
super.viewDidDisappear(animated)
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// currentPostTimelineTableViewContentSizeObservation = nil
|
2022-01-27 14:23:39 +01:00
|
|
|
}
|
|
|
|
|
2022-02-18 11:55:26 +01:00
|
|
|
override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
|
|
|
super.traitCollectionDidChange(previousTraitCollection)
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// updateBarButtonInsets()
|
2022-02-18 11:55:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
extension ProfileViewController {
|
2022-04-12 11:32:38 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// private func updateBarButtonInsets() {
|
|
|
|
// let margin: CGFloat = {
|
|
|
|
// switch traitCollection.userInterfaceIdiom {
|
|
|
|
// case .phone:
|
|
|
|
// return ProfileViewController.containerViewMarginForCompactHorizontalSizeClass
|
|
|
|
// default:
|
|
|
|
// return traitCollection.horizontalSizeClass == .regular ?
|
|
|
|
// ProfileViewController.containerViewMarginForRegularHorizontalSizeClass :
|
|
|
|
// ProfileViewController.containerViewMarginForCompactHorizontalSizeClass
|
|
|
|
// }
|
|
|
|
// }()
|
|
|
|
//
|
|
|
|
// profileHeaderViewController.buttonBar.layout.contentInset.left = margin
|
|
|
|
// profileHeaderViewController.buttonBar.layout.contentInset.right = margin
|
|
|
|
// }
|
2022-02-18 11:55:26 +01:00
|
|
|
|
2022-01-27 14:23:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
extension ProfileViewController {
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// private func bind(userTimelineViewModel: UserTimelineViewModel) {
|
|
|
|
// viewModel.domain
|
|
|
|
// viewModel.domain.assign(to: \.domain, on: userTimelineViewModel).store(in: &disposeBag)
|
|
|
|
// viewModel.userID.assign(to: \.userID, on: userTimelineViewModel).store(in: &disposeBag)
|
|
|
|
// viewModel.isBlocking.assign(to: \.value, on: userTimelineViewModel.isBlocking).store(in: &disposeBag)
|
|
|
|
// viewModel.isBlockedBy.assign(to: \.value, on: userTimelineViewModel.isBlockedBy).store(in: &disposeBag)
|
|
|
|
// viewModel.suspended.assign(to: \.value, on: userTimelineViewModel.isSuspended).store(in: &disposeBag)
|
|
|
|
// viewModel.name.assign(to: \.value, on: userTimelineViewModel.userDisplayName).store(in: &disposeBag)
|
|
|
|
// }
|
2021-07-23 13:10:27 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// private func bindProfile(
|
|
|
|
// headerViewModel: ProfileHeaderViewModel,
|
|
|
|
// aboutViewModel: ProfileAboutViewModel
|
|
|
|
// ) {
|
|
|
|
// // header
|
|
|
|
// viewModel.avatarImageURL
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .assign(to: \.avatarImageURL, on: headerViewModel.displayProfileInfo)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.name
|
|
|
|
// .map { $0 ?? "" }
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .assign(to: \.name, on: headerViewModel.displayProfileInfo)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.bioDescription
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .assign(to: \.note, on: headerViewModel.displayProfileInfo)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// // about
|
|
|
|
// Publishers.CombineLatest(
|
|
|
|
// viewModel.fields.removeDuplicates(),
|
|
|
|
// viewModel.emojiMeta.removeDuplicates()
|
|
|
|
// )
|
|
|
|
// .map { fields, emojiMeta -> [ProfileFieldItem.FieldValue] in
|
|
|
|
// fields.map { ProfileFieldItem.FieldValue(name: $0.name, value: $0.value, emojiMeta: emojiMeta) }
|
|
|
|
// }
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .assign(to: \.fields, on: aboutViewModel.displayProfileInfo)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// // common
|
|
|
|
// viewModel.accountForEdit
|
|
|
|
// .assign(to: \.accountForEdit, on: headerViewModel)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.accountForEdit
|
|
|
|
// .assign(to: \.accountForEdit, on: aboutViewModel)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.emojiMeta
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .assign(to: \.emojiMeta, on: headerViewModel)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.emojiMeta
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .assign(to: \.emojiMeta, on: aboutViewModel)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.isEditing
|
|
|
|
// .assign(to: \.isEditing, on: headerViewModel)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.isEditing
|
|
|
|
// .assign(to: \.isEditing, on: aboutViewModel)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// private func bindTitleView() {
|
|
|
|
// Publishers.CombineLatest3(
|
|
|
|
// viewModel.name,
|
|
|
|
// viewModel.emojiMeta,
|
|
|
|
// viewModel.statusesCount
|
|
|
|
// )
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] name, emojiMeta, statusesCount in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// guard let title = name, let statusesCount = statusesCount,
|
|
|
|
// let formattedStatusCount = MastodonMetricFormatter().string(from: statusesCount) else {
|
|
|
|
// self.titleView.isHidden = true
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// self.titleView.isHidden = false
|
|
|
|
// let subtitle = L10n.Plural.Count.MetricFormatted.post(formattedStatusCount, statusesCount)
|
|
|
|
// let mastodonContent = MastodonContent(content: title, emojis: emojiMeta)
|
|
|
|
// do {
|
|
|
|
// let metaContent = try MastodonMetaContent.convert(document: mastodonContent)
|
|
|
|
// self.titleView.update(titleMetaContent: metaContent, subtitle: subtitle)
|
|
|
|
// } catch {
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.name
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] name in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// self.navigationItem.title = name
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// private func bindHeader() {
|
|
|
|
// // heaer UI
|
|
|
|
// Publishers.CombineLatest(
|
|
|
|
// viewModel.bannerImageURL.eraseToAnyPublisher(),
|
|
|
|
// viewModel.viewDidAppear.eraseToAnyPublisher()
|
|
|
|
// )
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] bannerImageURL, _ in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.bannerImageView.af.cancelImageRequest()
|
|
|
|
// let placeholder = UIImage.placeholder(color: ProfileHeaderView.bannerImageViewPlaceholderColor)
|
|
|
|
// guard let bannerImageURL = bannerImageURL else {
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.bannerImageView.image = placeholder
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.bannerImageView.af.setImage(
|
|
|
|
// withURL: bannerImageURL,
|
|
|
|
// placeholderImage: placeholder,
|
|
|
|
// imageTransition: .crossDissolve(0.3),
|
|
|
|
// runImageTransitionIfCached: false,
|
|
|
|
// completion: { [weak self] response in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// guard let image = response.value else { return }
|
|
|
|
// guard image.size.width > 1 && image.size.height > 1 else {
|
|
|
|
// // restore to placeholder when image invalid
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.bannerImageView.image = placeholder
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// viewModel.username
|
|
|
|
// .map { username in username.flatMap { "@" + $0 } ?? " " }
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .assign(to: \.text, on: profileHeaderViewController.profileHeaderView.usernameLabel)
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// viewModel.isEditing
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] isEditing in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// // set first responder for key command
|
|
|
|
// if !isEditing {
|
|
|
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
|
|
|
|
// self.profileSegmentedViewController.pagingViewController.becomeFirstResponder()
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // dismiss keyboard if needs
|
|
|
|
// if !isEditing { self.view.endEditing(true) }
|
|
|
|
//
|
|
|
|
// self.profileHeaderViewController.buttonBar.isUserInteractionEnabled = !isEditing
|
|
|
|
// if isEditing {
|
|
|
|
// // scroll to About page
|
|
|
|
// self.profileSegmentedViewController.pagingViewController.scrollToPage(
|
|
|
|
// .last,
|
|
|
|
// animated: true,
|
|
|
|
// completion: nil
|
|
|
|
// )
|
|
|
|
// self.profileSegmentedViewController.pagingViewController.isScrollEnabled = false
|
|
|
|
// } else {
|
|
|
|
// self.profileSegmentedViewController.pagingViewController.isScrollEnabled = true
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// let animator = UIViewPropertyAnimator(duration: 0.33, curve: .easeInOut)
|
|
|
|
// animator.addAnimations {
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.alpha = isEditing ? 0.2 : 1.0
|
|
|
|
// }
|
|
|
|
// animator.startAnimation()
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// viewModel.needsImageOverlayBlurred
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] needsImageOverlayBlurred in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// UIView.animate(withDuration: 0.33) {
|
|
|
|
// let bannerEffect: UIVisualEffect? = needsImageOverlayBlurred ? ProfileHeaderView.bannerImageViewOverlayBlurEffect : nil
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.bannerImageViewOverlayVisualEffectView.effect = bannerEffect
|
|
|
|
// let avatarEffect: UIVisualEffect? = needsImageOverlayBlurred ? ProfileHeaderView.avatarImageViewOverlayBlurEffect : nil
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.avatarImageViewOverlayVisualEffectView.effect = avatarEffect
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// private func bindProfileRelationship() {
|
|
|
|
// Publishers.CombineLatest(
|
|
|
|
// viewModel.$user,
|
|
|
|
// viewModel.relationshipActionOptionSet
|
|
|
|
// )
|
|
|
|
// .asyncMap { [weak self] user, relationshipSet -> UIMenu? in
|
|
|
|
// guard let self = self else { return nil }
|
|
|
|
// guard let user = user else {
|
|
|
|
// return nil
|
|
|
|
// }
|
|
|
|
// let name = user.displayNameWithFallback
|
|
|
|
// let _ = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
|
|
|
// let menu = MastodonMenu.setupMenu(
|
|
|
|
// actions: [
|
|
|
|
// .muteUser(.init(name: name, isMuting: self.viewModel.isMuting.value)),
|
|
|
|
// .blockUser(.init(name: name, isBlocking: self.viewModel.isBlocking.value)),
|
|
|
|
// .reportUser(.init(name: name)),
|
|
|
|
// .shareUser(.init(name: name)),
|
|
|
|
// ],
|
|
|
|
// delegate: self
|
|
|
|
// )
|
|
|
|
// return menu
|
|
|
|
// }
|
|
|
|
// .sink { [weak self] completion in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// switch completion {
|
|
|
|
// case .failure:
|
|
|
|
// self.moreMenuBarButtonItem.menu = nil
|
|
|
|
// case .finished:
|
|
|
|
// break
|
|
|
|
// }
|
|
|
|
// } receiveValue: { [weak self] menu in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// self.moreMenuBarButtonItem.menu = menu
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// viewModel.isRelationshipActionButtonHidden
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] isHidden in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.relationshipActionButtonShadowContainer.isHidden = isHidden
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// Publishers.CombineLatest3(
|
|
|
|
// viewModel.relationshipActionOptionSet.eraseToAnyPublisher(),
|
|
|
|
// viewModel.isEditing.eraseToAnyPublisher(),
|
|
|
|
// viewModel.isUpdating.eraseToAnyPublisher()
|
|
|
|
// )
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] relationshipActionSet, isEditing, isUpdating in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// let friendshipButton = self.profileHeaderViewController.profileHeaderView.relationshipActionButton
|
|
|
|
// if relationshipActionSet.contains(.edit) {
|
|
|
|
// // check .edit state and set .editing when isEditing
|
|
|
|
// friendshipButton.configure(actionOptionSet: isUpdating ? .updating : (isEditing ? .editing : .edit))
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.configure(state: isEditing ? .editing : .normal)
|
|
|
|
// } else {
|
|
|
|
// friendshipButton.configure(actionOptionSet: relationshipActionSet)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
//
|
|
|
|
// Publishers.CombineLatest3(
|
|
|
|
// viewModel.isBlocking.eraseToAnyPublisher(),
|
|
|
|
// viewModel.isBlockedBy.eraseToAnyPublisher(),
|
|
|
|
// viewModel.suspended.eraseToAnyPublisher()
|
|
|
|
// )
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] isBlocking, isBlockedBy, suspended in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// let isNeedSetHidden = isBlocking || isBlockedBy || suspended
|
|
|
|
// self.profileHeaderViewController.viewModel.needsSetupBottomShadow.value = !isNeedSetHidden
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.bioContainerView.isHidden = isNeedSetHidden
|
|
|
|
// self.profileHeaderViewController.viewModel.needsFiledCollectionViewHidden.value = isNeedSetHidden
|
|
|
|
// self.profileHeaderViewController.buttonBar.isUserInteractionEnabled = !isNeedSetHidden
|
|
|
|
// self.viewModel.needsPagePinToTop.value = isNeedSetHidden
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// } // end func bindProfileRelationship
|
|
|
|
//
|
|
|
|
// private func bindProfileDashboard() {
|
|
|
|
// viewModel.statusesCount
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] count in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// let text = count.flatMap { MastodonMetricFormatter().string(from: $0) } ?? "-"
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.postDashboardMeterView.numberLabel.text = text
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.postDashboardMeterView.isAccessibilityElement = true
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.postDashboardMeterView.accessibilityLabel = L10n.Plural.Count.post(count ?? 0)
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.followingCount
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] count in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// let text = count.flatMap { MastodonMetricFormatter().string(from: $0) } ?? "-"
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.followingDashboardMeterView.numberLabel.text = text
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.followingDashboardMeterView.isAccessibilityElement = true
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.followingDashboardMeterView.accessibilityLabel = L10n.Plural.Count.following(count ?? 0)
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// viewModel.followersCount
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] count in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// let text = count.flatMap { MastodonMetricFormatter().string(from: $0) } ?? "-"
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.followersDashboardMeterView.numberLabel.text = text
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.followersDashboardMeterView.isAccessibilityElement = true
|
|
|
|
// self.profileHeaderViewController.profileHeaderView.statusDashboardView.followersDashboardMeterView.accessibilityLabel = L10n.Plural.Count.follower(count ?? 0)
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// private func handleMetaPress(_ meta: Meta) {
|
|
|
|
// switch meta {
|
|
|
|
// case .url(_, _, let url, _):
|
|
|
|
// guard let url = URL(string: url) else { return }
|
|
|
|
// coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
|
|
|
|
// case .mention(_, _, let userInfo):
|
|
|
|
// guard let href = userInfo?["href"] as? String,
|
|
|
|
// let url = URL(string: href) else { return }
|
|
|
|
// coordinator.present(scene: .safari(url: url), from: nil, transition: .safariPresent(animated: true, completion: nil))
|
|
|
|
// case .hashtag(_, let hashtag, _):
|
|
|
|
// let hashtagTimelineViewModel = HashtagTimelineViewModel(context: context, hashtag: hashtag)
|
|
|
|
// coordinator.present(scene: .hashtagTimeline(viewModel: hashtagTimelineViewModel), from: nil, transition: .show)
|
|
|
|
// case .email, .emoji:
|
|
|
|
// break
|
|
|
|
// }
|
|
|
|
// }
|
2021-04-01 08:39:15 +02:00
|
|
|
|
2021-04-06 10:43:08 +02:00
|
|
|
}
|
|
|
|
|
2021-04-01 08:39:15 +02:00
|
|
|
extension ProfileViewController {
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-09 11:31:43 +02:00
|
|
|
@objc private func cancelEditingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
|
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
2022-05-13 11:23:35 +02:00
|
|
|
// viewModel.isEditing.value = false
|
2021-04-09 11:31:43 +02:00
|
|
|
}
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-07 08:24:28 +02:00
|
|
|
@objc private func settingBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
|
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
2022-05-13 11:23:35 +02:00
|
|
|
// guard let setting = context.settingService.currentSetting.value else { return }
|
|
|
|
// let settingsViewModel = SettingsViewModel(context: context, setting: setting)
|
|
|
|
// coordinator.present(scene: .settings(viewModel: settingsViewModel), from: self, transition: .modal(animated: true, completion: nil))
|
2021-04-07 08:24:28 +02:00
|
|
|
}
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-07 08:24:28 +02:00
|
|
|
@objc private func shareBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
|
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
2022-05-13 11:23:35 +02:00
|
|
|
// guard let user = viewModel.user else { return }
|
|
|
|
// let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
|
|
|
// Task {
|
|
|
|
// let _activityViewController = try await DataSourceFacade.createActivityViewController(
|
|
|
|
// dependency: self,
|
|
|
|
// user: record
|
|
|
|
// )
|
|
|
|
// guard let activityViewController = _activityViewController else { return }
|
|
|
|
// self.coordinator.present(
|
|
|
|
// scene: .activityViewController(
|
|
|
|
// activityViewController: activityViewController,
|
|
|
|
// sourceView: nil,
|
|
|
|
// barButtonItem: sender
|
|
|
|
// ),
|
|
|
|
// from: self,
|
|
|
|
// transition: .activityViewControllerPresent(animated: true, completion: nil)
|
|
|
|
// )
|
|
|
|
// } // end Task
|
2021-04-07 08:24:28 +02:00
|
|
|
}
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-07 08:24:28 +02:00
|
|
|
@objc private func favoriteBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
|
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
2022-05-13 11:23:35 +02:00
|
|
|
// let favoriteViewModel = FavoriteViewModel(context: context)
|
|
|
|
// coordinator.present(scene: .favorite(viewModel: favoriteViewModel), from: self, transition: .show)
|
2021-04-07 08:24:28 +02:00
|
|
|
}
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-02 12:13:45 +02:00
|
|
|
@objc private func replyBarButtonItemPressed(_ sender: UIBarButtonItem) {
|
|
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
2022-05-13 11:23:35 +02:00
|
|
|
// guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
|
|
|
// guard let mastodonUser = viewModel.user else { return }
|
|
|
|
// let composeViewModel = ComposeViewModel(
|
|
|
|
// context: context,
|
|
|
|
// composeKind: .mention(user: .init(objectID: mastodonUser.objectID)),
|
|
|
|
// authenticationBox: authenticationBox
|
|
|
|
// )
|
|
|
|
// coordinator.present(scene: .compose(viewModel: composeViewModel), from: self, transition: .modal(animated: true, completion: nil))
|
2021-04-02 12:13:45 +02:00
|
|
|
}
|
2022-05-13 11:23:35 +02:00
|
|
|
|
2021-04-01 08:39:15 +02:00
|
|
|
@objc private func refreshControlValueChanged(_ sender: UIRefreshControl) {
|
|
|
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
let currentViewPage = profilePagingViewController.currentPage
|
|
|
|
// if let currentViewController = currentViewController as? UserTimelineViewController {
|
|
|
|
// currentViewController.viewModel.stateMachine.enter(UserTimelineViewModel.State.Reloading.self)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
|
|
|
|
// sender.endRefreshing()
|
2022-01-27 14:23:39 +01:00
|
|
|
// }
|
2021-04-01 08:39:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// MARK: - TabBarPagerDelegate
|
|
|
|
extension ProfileViewController: TabBarPagerDelegate {
|
2021-04-28 14:10:17 +02:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
func tabBarMinimalHeight() -> CGFloat {
|
|
|
|
return ProfileHeaderViewController.headerMinHeight
|
2021-04-28 14:36:10 +02:00
|
|
|
}
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
func resetPageContentOffset(_ tabBarPagerController: TabBarPagerController) {
|
|
|
|
for viewController in profilePagingViewController.viewModel.viewControllers {
|
|
|
|
viewController.pageScrollView.contentOffset = .zero
|
2021-11-01 12:54:07 +01:00
|
|
|
}
|
2021-04-01 08:39:15 +02:00
|
|
|
}
|
2022-05-13 11:23:35 +02:00
|
|
|
|
|
|
|
func tabBarPagerController(_ tabBarPagerController: TabBarPagerController, didScroll scrollView: UIScrollView) {
|
|
|
|
// elastically banner
|
|
|
|
if scrollView.contentOffset.y < -scrollView.safeAreaInsets.top {
|
|
|
|
let offset = scrollView.contentOffset.y - (-scrollView.safeAreaInsets.top)
|
|
|
|
profileHeaderViewController.profileHeaderView.bannerImageViewTopLayoutConstraint.constant = offset
|
|
|
|
} else {
|
|
|
|
profileHeaderViewController.profileHeaderView.bannerImageViewTopLayoutConstraint.constant = 0
|
2022-05-11 12:40:21 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
// MARK: - TabBarPagerDataSource
|
|
|
|
extension ProfileViewController: TabBarPagerDataSource {
|
|
|
|
func headerViewController() -> UIViewController & TabBarPagerHeader {
|
|
|
|
return profileHeaderViewController
|
2022-05-11 12:40:21 +02:00
|
|
|
}
|
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
func pageViewController() -> UIViewController & TabBarPageViewController {
|
|
|
|
return profilePagingViewController
|
2022-05-11 12:40:21 +02:00
|
|
|
}
|
|
|
|
}
|
2022-02-16 12:47:51 +01:00
|
|
|
|
2022-05-13 11:23:35 +02:00
|
|
|
//// MARK: - UIScrollViewDelegate
|
|
|
|
//extension ProfileViewController: UIScrollViewDelegate {
|
|
|
|
//
|
|
|
|
// func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
|
|
// contentOffsets[profileSegmentedViewController.pagingViewController.currentIndex!] = scrollView.contentOffset.y
|
|
|
|
// let topMaxContentOffsetY = profileSegmentedViewController.view.frame.minY - ProfileHeaderViewController.headerMinHeight - containerScrollView.safeAreaInsets.top
|
|
|
|
// if scrollView.contentOffset.y < topMaxContentOffsetY {
|
|
|
|
// self.containerScrollView.contentOffset.y = scrollView.contentOffset.y
|
|
|
|
// for postTimelineView in profileSegmentedViewController.pagingViewController.viewModel.viewControllers {
|
|
|
|
// postTimelineView.scrollView?.contentOffset.y = 0
|
|
|
|
// }
|
|
|
|
// contentOffsets.removeAll()
|
|
|
|
// } else {
|
|
|
|
// containerScrollView.contentOffset.y = topMaxContentOffsetY
|
|
|
|
// if viewModel.needsPagePinToTop.value {
|
|
|
|
// // do nothing
|
|
|
|
// } else {
|
|
|
|
// if let customScrollViewContainerController = profileSegmentedViewController.pagingViewController.currentViewController as? ScrollViewContainer {
|
|
|
|
// let contentOffsetY = scrollView.contentOffset.y - containerScrollView.contentOffset.y
|
|
|
|
// customScrollViewContainerController.scrollView?.contentOffset.y = contentOffsetY
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // elastically banner image
|
|
|
|
// let headerScrollProgress = (containerScrollView.contentOffset.y - containerScrollView.safeAreaInsets.top) / topMaxContentOffsetY
|
|
|
|
// let throttle = ProfileHeaderViewController.headerMinHeight / topMaxContentOffsetY
|
|
|
|
// profileHeaderViewController.updateHeaderScrollProgress(headerScrollProgress, throttle: throttle)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//// MARK: - ProfileHeaderViewControllerDelegate
|
|
|
|
//extension ProfileViewController: ProfileHeaderViewControllerDelegate {
|
|
|
|
//
|
|
|
|
// func profileHeaderViewController(_ viewController: ProfileHeaderViewController, viewLayoutDidUpdate view: UIView) {
|
|
|
|
// guard let scrollView = (profileSegmentedViewController.pagingViewController.currentViewController as? UserTimelineViewController)?.scrollView else {
|
|
|
|
// // assertionFailure()
|
|
|
|
// return
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// updateOverlayScrollViewContentSize(scrollView: scrollView)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//// MARK: - ProfilePagingViewControllerDelegate
|
|
|
|
//extension ProfileViewController: ProfilePagingViewControllerDelegate {
|
|
|
|
//
|
|
|
|
// func profilePagingViewController(_ viewController: ProfilePagingViewController, didScrollToPostCustomScrollViewContainerController postTimelineViewController: ScrollViewContainer, atIndex index: Int) {
|
|
|
|
// os_log("%{public}s[%{public}ld], %{public}s: select at index: %ld", ((#file as NSString).lastPathComponent), #line, #function, index)
|
|
|
|
//
|
|
|
|
//// // update segemented control
|
|
|
|
//// if index < profileHeaderViewController.pageSegmentedControl.numberOfSegments {
|
|
|
|
//// profileHeaderViewController.pageSegmentedControl.selectedSegmentIndex = index
|
|
|
|
//// }
|
|
|
|
//
|
|
|
|
// // save content offset
|
|
|
|
// overlayScrollView.contentOffset.y = contentOffsets[index] ?? containerScrollView.contentOffset.y
|
|
|
|
//
|
|
|
|
// // setup observer and gesture fallback
|
|
|
|
// if let scrollView = postTimelineViewController.scrollView {
|
|
|
|
// currentPostTimelineTableViewContentSizeObservation = observeTableViewContentSize(scrollView: scrollView)
|
|
|
|
// scrollView.panGestureRecognizer.require(toFail: overlayScrollView.panGestureRecognizer)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//// MARK: - ProfileHeaderViewDelegate
|
|
|
|
//extension ProfileViewController: ProfileHeaderViewDelegate {
|
|
|
|
// func profileHeaderView(_ profileHeaderView: ProfileHeaderView, avatarButtonDidPressed button: AvatarButton) {
|
|
|
|
// guard let user = viewModel.user else { return }
|
|
|
|
// let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
|
|
|
//
|
|
|
|
// Task {
|
|
|
|
// try await DataSourceFacade.coordinateToMediaPreviewScene(
|
|
|
|
// dependency: self,
|
|
|
|
// user: record,
|
|
|
|
// previewContext: DataSourceFacade.ImagePreviewContext(
|
|
|
|
// imageView: button.avatarImageView,
|
|
|
|
// containerView: .profileAvatar(profileHeaderView)
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// } // end Task
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func profileHeaderView(_ profileHeaderView: ProfileHeaderView, bannerImageViewDidPressed imageView: UIImageView) {
|
|
|
|
// guard let user = viewModel.user else { return }
|
|
|
|
// let record: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
|
|
|
//
|
|
|
|
// Task {
|
|
|
|
// try await DataSourceFacade.coordinateToMediaPreviewScene(
|
|
|
|
// dependency: self,
|
|
|
|
// user: record,
|
|
|
|
// previewContext: DataSourceFacade.ImagePreviewContext(
|
|
|
|
// imageView: imageView,
|
|
|
|
// containerView: .profileBanner(profileHeaderView)
|
|
|
|
// )
|
|
|
|
// )
|
|
|
|
// } // end Task
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func profileHeaderView(
|
|
|
|
// _ profileHeaderView: ProfileHeaderView,
|
|
|
|
// relationshipButtonDidPressed button: ProfileRelationshipActionButton
|
|
|
|
// ) {
|
|
|
|
// let relationshipActionSet = viewModel.relationshipActionOptionSet.value
|
|
|
|
//
|
|
|
|
// // handle edit logic for editable profile
|
|
|
|
// // handle relationship logic for non-editable profile
|
|
|
|
// if relationshipActionSet.contains(.edit) {
|
|
|
|
// // do nothing when updating
|
|
|
|
// guard !viewModel.isUpdating.value else { return }
|
|
|
|
//
|
|
|
|
// guard let profileHeaderViewModel = profileHeaderViewController.viewModel else { return }
|
|
|
|
// guard let profileAboutViewModel = profileSegmentedViewController.pagingViewController.viewModel.profileAboutViewController.viewModel else { return }
|
|
|
|
//
|
|
|
|
// let isEdited = profileHeaderViewModel.isEdited()
|
|
|
|
// || profileAboutViewModel.isEdited()
|
|
|
|
//
|
|
|
|
// if isEdited {
|
|
|
|
// // update profile if changed
|
|
|
|
// viewModel.isUpdating.value = true
|
|
|
|
// Task {
|
|
|
|
// do {
|
|
|
|
// // TODO: handle error
|
|
|
|
// _ = try await viewModel.updateProfileInfo(
|
|
|
|
// headerProfileInfo: profileHeaderViewModel.editProfileInfo,
|
|
|
|
// aboutProfileInfo: profileAboutViewModel.editProfileInfo
|
|
|
|
// )
|
|
|
|
// self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): update profile info success")
|
|
|
|
// self.viewModel.isEditing.value = false
|
|
|
|
//
|
|
|
|
// } catch {
|
|
|
|
// self.logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public): update profile info fail: \(error.localizedDescription)")
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// // finish updating
|
|
|
|
// self.viewModel.isUpdating.value = false
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// // set `updating` then toggle `edit` state
|
|
|
|
// viewModel.isUpdating.value = true
|
|
|
|
// viewModel.fetchEditProfileInfo()
|
|
|
|
// .receive(on: DispatchQueue.main)
|
|
|
|
// .sink { [weak self] completion in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// defer {
|
|
|
|
// // finish updating
|
|
|
|
// self.viewModel.isUpdating.value = false
|
|
|
|
// }
|
|
|
|
// switch completion {
|
|
|
|
// case .failure(let error):
|
|
|
|
// os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: fetch profile info for edit fail: %s", ((#file as NSString).lastPathComponent), #line, #function, error.localizedDescription)
|
|
|
|
// let alertController = UIAlertController(for: error, title: L10n.Common.Alerts.EditProfileFailure.title, preferredStyle: .alert)
|
|
|
|
// let okAction = UIAlertAction(title: L10n.Common.Controls.Actions.ok, style: .default, handler: nil)
|
|
|
|
// alertController.addAction(okAction)
|
|
|
|
// self.coordinator.present(
|
|
|
|
// scene: .alertController(alertController: alertController),
|
|
|
|
// from: nil,
|
|
|
|
// transition: .alertController(animated: true, completion: nil)
|
|
|
|
// )
|
|
|
|
// case .finished:
|
|
|
|
// os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s: fetch profile info for edit success", ((#file as NSString).lastPathComponent), #line, #function)
|
|
|
|
// // enter editing mode
|
|
|
|
// self.viewModel.isEditing.value.toggle()
|
|
|
|
// }
|
|
|
|
// } receiveValue: { [weak self] response in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// self.viewModel.accountForEdit.value = response.value
|
|
|
|
// }
|
|
|
|
// .store(in: &disposeBag)
|
|
|
|
// }
|
|
|
|
// } else {
|
|
|
|
// guard let relationshipAction = relationshipActionSet.highPriorityAction(except: .editOptions) else { return }
|
|
|
|
// switch relationshipAction {
|
|
|
|
// case .none:
|
|
|
|
// break
|
|
|
|
// case .follow, .request, .pending, .following:
|
|
|
|
// guard let user = viewModel.user else { return }
|
|
|
|
// let reocrd = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
|
|
|
// guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
|
|
|
// Task {
|
|
|
|
// try await DataSourceFacade.responseToUserFollowAction(
|
|
|
|
// dependency: self,
|
|
|
|
// user: reocrd,
|
|
|
|
// authenticationBox: authenticationBox
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
// case .muting:
|
|
|
|
// guard let authenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
|
|
|
// guard let user = viewModel.user else { return }
|
|
|
|
// let name = user.displayNameWithFallback
|
|
|
|
//
|
|
|
|
// let alertController = UIAlertController(
|
|
|
|
// title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.title,
|
|
|
|
// message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnmuteUser.message(name),
|
|
|
|
// preferredStyle: .alert
|
|
|
|
// )
|
|
|
|
// let record = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
|
|
|
// let unmuteAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unmute, style: .default) { [weak self] _ in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// Task {
|
|
|
|
// try await DataSourceFacade.responseToUserMuteAction(
|
|
|
|
// dependency: self,
|
|
|
|
// user: record,
|
|
|
|
// authenticationBox: authenticationBox
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// alertController.addAction(unmuteAction)
|
|
|
|
// let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
|
|
|
|
// alertController.addAction(cancelAction)
|
|
|
|
// present(alertController, animated: true, completion: nil)
|
|
|
|
// case .blocking:
|
|
|
|
// guard let authenticationBox = self.context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
|
|
|
// guard let user = viewModel.user else { return }
|
|
|
|
// let name = user.displayNameWithFallback
|
|
|
|
//
|
|
|
|
// let alertController = UIAlertController(
|
|
|
|
// title: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.title,
|
|
|
|
// message: L10n.Scene.Profile.RelationshipActionAlert.ConfirmUnblockUser.message(name),
|
|
|
|
// preferredStyle: .alert
|
|
|
|
// )
|
|
|
|
// let record = ManagedObjectRecord<MastodonUser>(objectID: user.objectID)
|
|
|
|
// let unblockAction = UIAlertAction(title: L10n.Common.Controls.Friendship.unblock, style: .default) { [weak self] _ in
|
|
|
|
// guard let self = self else { return }
|
|
|
|
// Task {
|
|
|
|
// try await DataSourceFacade.responseToUserBlockAction(
|
|
|
|
// dependency: self,
|
|
|
|
// user: record,
|
|
|
|
// authenticationBox: authenticationBox
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// alertController.addAction(unblockAction)
|
|
|
|
// let cancelAction = UIAlertAction(title: L10n.Common.Controls.Actions.cancel, style: .cancel, handler: nil)
|
|
|
|
// alertController.addAction(cancelAction)
|
|
|
|
// present(alertController, animated: true, completion: nil)
|
|
|
|
// case .blocked:
|
|
|
|
// break
|
|
|
|
// default:
|
|
|
|
// assertionFailure()
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func profileHeaderView(_ profileHeaderView: ProfileHeaderView, metaTextView: MetaTextView, metaDidPressed meta: Meta) {
|
|
|
|
// handleMetaPress(meta)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// func profileHeaderView(_ profileHeaderView: ProfileHeaderView, profileStatusDashboardView dashboardView: ProfileStatusDashboardView, dashboardMeterViewDidPressed dashboardMeterView: ProfileStatusDashboardMeterView, meter: ProfileStatusDashboardView.Meter) {
|
|
|
|
// switch meter {
|
|
|
|
// case .post:
|
|
|
|
// // do nothing
|
|
|
|
// break
|
|
|
|
// case .follower:
|
|
|
|
// guard let domain = viewModel.domain.value,
|
|
|
|
// let userID = viewModel.userID.value
|
|
|
|
// else { return }
|
|
|
|
// let followerListViewModel = FollowerListViewModel(
|
|
|
|
// context: context,
|
|
|
|
// domain: domain,
|
|
|
|
// userID: userID
|
|
|
|
// )
|
|
|
|
// coordinator.present(
|
|
|
|
// scene: .follower(viewModel: followerListViewModel),
|
|
|
|
// from: self,
|
|
|
|
// transition: .show
|
|
|
|
// )
|
|
|
|
// case .following:
|
|
|
|
// guard let domain = viewModel.domain.value,
|
|
|
|
// let userID = viewModel.userID.value
|
|
|
|
// else { return }
|
|
|
|
// let followingListViewModel = FollowingListViewModel(
|
|
|
|
// context: context,
|
|
|
|
// domain: domain,
|
|
|
|
// userID: userID
|
|
|
|
// )
|
|
|
|
// coordinator.present(
|
|
|
|
// scene: .following(viewModel: followingListViewModel),
|
|
|
|
// from: self,
|
|
|
|
// transition: .show
|
|
|
|
// )
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//// MARK: - ProfileAboutViewControllerDelegate
|
|
|
|
//extension ProfileViewController: ProfileAboutViewControllerDelegate {
|
|
|
|
// func profileAboutViewController(_ viewController: ProfileAboutViewController, profileFieldCollectionViewCell: ProfileFieldCollectionViewCell, metaLabel: MetaLabel, didSelectMeta meta: Meta) {
|
|
|
|
// handleMetaPress(meta)
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//// MARK: - MastodonMenuDelegate
|
|
|
|
//extension ProfileViewController: MastodonMenuDelegate {
|
|
|
|
// func menuAction(_ action: MastodonMenu.Action) {
|
|
|
|
// guard let authenticationBox = context.authenticationService.activeMastodonAuthenticationBox.value else { return }
|
|
|
|
// guard let user = viewModel.user else { return }
|
|
|
|
//
|
|
|
|
// let userRecord: ManagedObjectRecord<MastodonUser> = .init(objectID: user.objectID)
|
|
|
|
//
|
|
|
|
// Task {
|
|
|
|
// try await DataSourceFacade.responseToMenuAction(
|
|
|
|
// dependency: self,
|
|
|
|
// action: action,
|
|
|
|
// menuContext: DataSourceFacade.MenuContext(
|
|
|
|
// author: userRecord,
|
|
|
|
// status: nil,
|
|
|
|
// button: nil,
|
|
|
|
// barButtonItem: self.moreMenuBarButtonItem
|
|
|
|
// ),
|
|
|
|
// authenticationBox: authenticationBox
|
|
|
|
// )
|
|
|
|
// } // end Task
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//// MARK: - ScrollViewContainer
|
|
|
|
//extension ProfileViewController: ScrollViewContainer {
|
|
|
|
// var scrollView: UIScrollView? {
|
|
|
|
// return overlayScrollView
|
|
|
|
// }
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//extension ProfileViewController {
|
|
|
|
//
|
|
|
|
// override var keyCommands: [UIKeyCommand]? {
|
|
|
|
// if !viewModel.isEditing.value {
|
|
|
|
// return pageboyNavigateKeyCommands
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return nil
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
//
|
|
|
|
//// MARK: - PageboyNavigateable
|
|
|
|
//extension ProfileViewController: PageboyNavigateable {
|
|
|
|
//
|
|
|
|
// var navigateablePageViewController: PageboyViewController {
|
|
|
|
// return profileSegmentedViewController.pagingViewController
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// @objc func pageboyNavigateKeyCommandHandlerRelay(_ sender: UIKeyCommand) {
|
|
|
|
// pageboyNavigateKeyCommandHandler(sender)
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
//}
|
|
|
|
|