Make the app work on iPad again (IOS-192)
Friends don't let friends use forced unwrapping.
This commit is contained in:
parent
6bcbc0ac07
commit
88c5cfa140
@ -16,7 +16,7 @@ extension HomeTimelineViewController: DataSourceProvider {
|
|||||||
}
|
}
|
||||||
guard let indexPath = _indexPath else { return nil }
|
guard let indexPath = _indexPath else { return nil }
|
||||||
|
|
||||||
guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else {
|
guard let item = viewModel?.diffableDataSource?.itemIdentifier(for: indexPath) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ extension HomeTimelineViewController: DataSourceProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) {
|
func update(status: MastodonStatus, intent: MastodonStatus.UpdateIntent) {
|
||||||
viewModel.dataController.update(status: status, intent: intent)
|
viewModel?.dataController.update(status: status, intent: intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
@ -25,7 +25,7 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency, Media
|
|||||||
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
weak var coordinator: SceneCoordinator! { willSet { precondition(!isViewLoaded) } }
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
var viewModel: HomeTimelineViewModel!
|
var viewModel: HomeTimelineViewModel?
|
||||||
|
|
||||||
let mediaPreviewTransitionController = MediaPreviewTransitionController()
|
let mediaPreviewTransitionController = MediaPreviewTransitionController()
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ extension HomeTimelineViewController {
|
|||||||
title = L10n.Scene.HomeTimeline.title
|
title = L10n.Scene.HomeTimeline.title
|
||||||
view.backgroundColor = .secondarySystemBackground
|
view.backgroundColor = .secondarySystemBackground
|
||||||
|
|
||||||
viewModel.$displaySettingBarButtonItem
|
viewModel?.$displaySettingBarButtonItem
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] displaySettingBarButtonItem in
|
.sink { [weak self] displaySettingBarButtonItem in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@ -97,7 +97,7 @@ extension HomeTimelineViewController {
|
|||||||
navigationItem.titleView = titleView
|
navigationItem.titleView = titleView
|
||||||
titleView.delegate = self
|
titleView.delegate = self
|
||||||
|
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.state
|
viewModel?.homeTimelineNavigationBarTitleViewModel.state
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] state in
|
.sink { [weak self] state in
|
||||||
@ -106,7 +106,7 @@ extension HomeTimelineViewController {
|
|||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.state
|
viewModel?.homeTimelineNavigationBarTitleViewModel.state
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.filter { $0 == .publishedButton }
|
.filter { $0 == .publishedButton }
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
@ -137,27 +137,27 @@ extension HomeTimelineViewController {
|
|||||||
publishProgressView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
publishProgressView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
|
||||||
])
|
])
|
||||||
|
|
||||||
viewModel.tableView = tableView
|
viewModel?.tableView = tableView
|
||||||
tableView.delegate = self
|
tableView.delegate = self
|
||||||
viewModel.setupDiffableDataSource(
|
viewModel?.setupDiffableDataSource(
|
||||||
tableView: tableView,
|
tableView: tableView,
|
||||||
statusTableViewCellDelegate: self,
|
statusTableViewCellDelegate: self,
|
||||||
timelineMiddleLoaderTableViewCellDelegate: self
|
timelineMiddleLoaderTableViewCellDelegate: self
|
||||||
)
|
)
|
||||||
|
|
||||||
// setup batch fetch
|
// setup batch fetch
|
||||||
viewModel.listBatchFetchViewModel.setup(scrollView: tableView)
|
viewModel?.listBatchFetchViewModel.setup(scrollView: tableView)
|
||||||
viewModel.listBatchFetchViewModel.shouldFetch
|
viewModel?.listBatchFetchViewModel.shouldFetch
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
guard self.view.window != nil else { return }
|
guard self.view.window != nil else { return }
|
||||||
self.viewModel.loadOldestStateMachine.enter(HomeTimelineViewModel.LoadOldestState.Loading.self)
|
self.viewModel?.loadOldestStateMachine.enter(HomeTimelineViewModel.LoadOldestState.Loading.self)
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
// bind refresh control
|
// bind refresh control
|
||||||
viewModel.didLoadLatest
|
viewModel?.didLoadLatest
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] _ in
|
.sink { [weak self] _ in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@ -170,8 +170,8 @@ extension HomeTimelineViewController {
|
|||||||
|
|
||||||
context.publisherService.statusPublishResult.receive(on: DispatchQueue.main).sink { result in
|
context.publisherService.statusPublishResult.receive(on: DispatchQueue.main).sink { result in
|
||||||
if case .success(.edit(let status)) = result {
|
if case .success(.edit(let status)) = result {
|
||||||
self.viewModel.hasPendingStatusEditReload = true
|
self.viewModel?.hasPendingStatusEditReload = true
|
||||||
self.viewModel.dataController.update(status: .fromEntity(status.value), intent: .edit)
|
self.viewModel?.dataController.update(status: .fromEntity(status.value), intent: .edit)
|
||||||
}
|
}
|
||||||
}.store(in: &disposeBag)
|
}.store(in: &disposeBag)
|
||||||
|
|
||||||
@ -204,7 +204,7 @@ extension HomeTimelineViewController {
|
|||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
viewModel.timelineIsEmpty
|
viewModel?.timelineIsEmpty
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] isEmpty in
|
.sink { [weak self] isEmpty in
|
||||||
if isEmpty {
|
if isEmpty {
|
||||||
@ -218,9 +218,9 @@ extension HomeTimelineViewController {
|
|||||||
userDoesntFollowPeople = true
|
userDoesntFollowPeople = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (self?.viewModel.presentedSuggestions == false) && userDoesntFollowPeople {
|
if (self?.viewModel?.presentedSuggestions == false) && userDoesntFollowPeople {
|
||||||
self?.findPeopleButtonPressed(self)
|
self?.findPeopleButtonPressed(self)
|
||||||
self?.viewModel.presentedSuggestions = true
|
self?.viewModel?.presentedSuggestions = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self?.emptyView.removeFromSuperview()
|
self?.emptyView.removeFromSuperview()
|
||||||
@ -264,16 +264,16 @@ extension HomeTimelineViewController {
|
|||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
if let timestamp = viewModel.lastAutomaticFetchTimestamp {
|
if let timestamp = viewModel?.lastAutomaticFetchTimestamp {
|
||||||
let now = Date()
|
let now = Date()
|
||||||
if now.timeIntervalSince(timestamp) > 60 {
|
if now.timeIntervalSince(timestamp) > 60 {
|
||||||
self.viewModel.lastAutomaticFetchTimestamp = now
|
self.viewModel?.lastAutomaticFetchTimestamp = now
|
||||||
self.viewModel.homeTimelineNeedRefresh.send()
|
self.viewModel?.homeTimelineNeedRefresh.send()
|
||||||
} else {
|
} else {
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.viewModel.homeTimelineNeedRefresh.send()
|
self.viewModel?.homeTimelineNeedRefresh.send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +284,7 @@ extension HomeTimelineViewController {
|
|||||||
// do nothing
|
// do nothing
|
||||||
} completion: { _ in
|
} completion: { _ in
|
||||||
// fix AutoLayout cell height not update after rotate issue
|
// fix AutoLayout cell height not update after rotate issue
|
||||||
self.viewModel.cellFrameCache.removeAllObjects()
|
self.viewModel?.cellFrameCache.removeAllObjects()
|
||||||
self.tableView.reloadData()
|
self.tableView.reloadData()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -356,7 +356,9 @@ extension HomeTimelineViewController {
|
|||||||
extension HomeTimelineViewController {
|
extension HomeTimelineViewController {
|
||||||
|
|
||||||
@objc private func findPeopleButtonPressed(_ sender: Any?) {
|
@objc private func findPeopleButtonPressed(_ sender: Any?) {
|
||||||
let suggestionAccountViewModel = SuggestionAccountViewModel(context: context, authContext: viewModel.authContext)
|
guard let authContext = viewModel?.authContext else { return }
|
||||||
|
|
||||||
|
let suggestionAccountViewModel = SuggestionAccountViewModel(context: context, authContext: authContext)
|
||||||
suggestionAccountViewModel.delegate = viewModel
|
suggestionAccountViewModel.delegate = viewModel
|
||||||
_ = coordinator.present(
|
_ = coordinator.present(
|
||||||
scene: .suggestionAccount(viewModel: suggestionAccountViewModel),
|
scene: .suggestionAccount(viewModel: suggestionAccountViewModel),
|
||||||
@ -366,7 +368,9 @@ extension HomeTimelineViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func manuallySearchButtonPressed(_ sender: UIButton) {
|
@objc private func manuallySearchButtonPressed(_ sender: UIButton) {
|
||||||
let searchDetailViewModel = SearchDetailViewModel(authContext: viewModel.authContext)
|
guard let authContext = viewModel?.authContext else { return }
|
||||||
|
|
||||||
|
let searchDetailViewModel = SearchDetailViewModel(authContext: authContext)
|
||||||
_ = coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .modal(animated: true, completion: nil))
|
_ = coordinator.present(scene: .searchDetail(viewModel: searchDetailViewModel), from: self, transition: .modal(animated: true, completion: nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -377,16 +381,18 @@ extension HomeTimelineViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func refreshControlValueChanged(_ sender: RefreshControl) {
|
@objc private func refreshControlValueChanged(_ sender: RefreshControl) {
|
||||||
guard viewModel.loadLatestStateMachine.enter(HomeTimelineViewModel.LoadLatestState.LoadingManually.self) else {
|
guard let viewModel, viewModel.loadLatestStateMachine.enter(HomeTimelineViewModel.LoadLatestState.LoadingManually.self) else {
|
||||||
sender.endRefreshing()
|
sender.endRefreshing()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func signOutAction(_ sender: UIAction) {
|
@objc func signOutAction(_ sender: UIAction) {
|
||||||
|
guard let authContext = viewModel?.authContext else { return }
|
||||||
|
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
try await context.authenticationService.signOutMastodonUser(authenticationBox: viewModel.authContext.mastodonAuthenticationBox)
|
try await context.authenticationService.signOutMastodonUser(authenticationBox: authContext.mastodonAuthenticationBox)
|
||||||
let userIdentifier = viewModel.authContext.mastodonAuthenticationBox
|
let userIdentifier = authContext.mastodonAuthenticationBox
|
||||||
FileManager.default.invalidateHomeTimelineCache(for: userIdentifier)
|
FileManager.default.invalidateHomeTimelineCache(for: userIdentifier)
|
||||||
FileManager.default.invalidateNotificationsAll(for: userIdentifier)
|
FileManager.default.invalidateNotificationsAll(for: userIdentifier)
|
||||||
FileManager.default.invalidateNotificationsMentions(for: userIdentifier)
|
FileManager.default.invalidateNotificationsMentions(for: userIdentifier)
|
||||||
@ -400,7 +406,7 @@ extension HomeTimelineViewController {
|
|||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
switch scrollView {
|
switch scrollView {
|
||||||
case tableView:
|
case tableView:
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.handleScrollViewDidScroll(scrollView)
|
viewModel?.homeTimelineNavigationBarTitleViewModel.handleScrollViewDidScroll(scrollView)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -411,7 +417,7 @@ extension HomeTimelineViewController {
|
|||||||
case tableView:
|
case tableView:
|
||||||
|
|
||||||
let indexPath = IndexPath(row: 0, section: 0)
|
let indexPath = IndexPath(row: 0, section: 0)
|
||||||
guard viewModel.diffableDataSource?.itemIdentifier(for: indexPath) != nil else {
|
guard viewModel?.diffableDataSource?.itemIdentifier(for: indexPath) != nil else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
// save position
|
// save position
|
||||||
@ -428,7 +434,7 @@ extension HomeTimelineViewController {
|
|||||||
private func savePositionBeforeScrollToTop() {
|
private func savePositionBeforeScrollToTop() {
|
||||||
// check save action interval
|
// check save action interval
|
||||||
// should not fast than 0.5s to prevent save when scrollToTop on-flying
|
// should not fast than 0.5s to prevent save when scrollToTop on-flying
|
||||||
if let record = viewModel.scrollPositionRecord {
|
if let record = viewModel?.scrollPositionRecord {
|
||||||
let now = Date()
|
let now = Date()
|
||||||
guard now.timeIntervalSince(record.timestamp) > 0.5 else {
|
guard now.timeIntervalSince(record.timestamp) > 0.5 else {
|
||||||
// skip this save action
|
// skip this save action
|
||||||
@ -436,7 +442,7 @@ extension HomeTimelineViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
guard let diffableDataSource = viewModel?.diffableDataSource else { return }
|
||||||
guard let anchorIndexPaths = tableView.indexPathsForVisibleRows?.sorted() else { return }
|
guard let anchorIndexPaths = tableView.indexPathsForVisibleRows?.sorted() else { return }
|
||||||
guard !anchorIndexPaths.isEmpty else { return }
|
guard !anchorIndexPaths.isEmpty else { return }
|
||||||
let anchorIndexPath = anchorIndexPaths[anchorIndexPaths.count / 2]
|
let anchorIndexPath = anchorIndexPaths[anchorIndexPaths.count / 2]
|
||||||
@ -447,7 +453,7 @@ extension HomeTimelineViewController {
|
|||||||
let cellFrameInView = tableView.convert(anchorCell.frame, to: view)
|
let cellFrameInView = tableView.convert(anchorCell.frame, to: view)
|
||||||
return cellFrameInView.origin.y
|
return cellFrameInView.origin.y
|
||||||
}()
|
}()
|
||||||
viewModel.scrollPositionRecord = HomeTimelineViewModel.ScrollPositionRecord(
|
viewModel?.scrollPositionRecord = HomeTimelineViewModel.ScrollPositionRecord(
|
||||||
item: anchorItem,
|
item: anchorItem,
|
||||||
offset: offset,
|
offset: offset,
|
||||||
timestamp: Date()
|
timestamp: Date()
|
||||||
@ -462,19 +468,19 @@ extension HomeTimelineViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func restorePositionWhenScrollToTop() {
|
private func restorePositionWhenScrollToTop() {
|
||||||
guard let diffableDataSource = self.viewModel.diffableDataSource else { return }
|
guard let diffableDataSource = viewModel?.diffableDataSource else { return }
|
||||||
guard let record = self.viewModel.scrollPositionRecord,
|
guard let record = viewModel?.scrollPositionRecord,
|
||||||
let indexPath = diffableDataSource.indexPath(for: record.item)
|
let indexPath = diffableDataSource.indexPath(for: record.item)
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
tableView.scrollToRow(at: indexPath, at: .middle, animated: true)
|
tableView.scrollToRow(at: indexPath, at: .middle, animated: true)
|
||||||
viewModel.scrollPositionRecord = nil
|
viewModel?.scrollPositionRecord = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - AuthContextProvider
|
// MARK: - AuthContextProvider
|
||||||
extension HomeTimelineViewController: AuthContextProvider {
|
extension HomeTimelineViewController: AuthContextProvider {
|
||||||
var authContext: AuthContext { viewModel.authContext }
|
var authContext: AuthContext { viewModel!.authContext }
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - UITableViewDelegate
|
// MARK: - UITableViewDelegate
|
||||||
@ -507,7 +513,7 @@ extension HomeTimelineViewController: UITableViewDelegate, AutoGenerateTableView
|
|||||||
|
|
||||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
|
if indexPath.row == tableView.numberOfRows(inSection: indexPath.section) - 1 {
|
||||||
viewModel.timelineDidReachEnd()
|
viewModel?.timelineDidReachEnd()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -515,12 +521,12 @@ extension HomeTimelineViewController: UITableViewDelegate, AutoGenerateTableView
|
|||||||
// MARK: - TimelineMiddleLoaderTableViewCellDelegate
|
// MARK: - TimelineMiddleLoaderTableViewCellDelegate
|
||||||
extension HomeTimelineViewController: TimelineMiddleLoaderTableViewCellDelegate {
|
extension HomeTimelineViewController: TimelineMiddleLoaderTableViewCellDelegate {
|
||||||
func timelineMiddleLoaderTableViewCell(_ cell: TimelineMiddleLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton) {
|
func timelineMiddleLoaderTableViewCell(_ cell: TimelineMiddleLoaderTableViewCell, loadMoreButtonDidPressed button: UIButton) {
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
guard let diffableDataSource = viewModel?.diffableDataSource else { return }
|
||||||
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
guard let indexPath = tableView.indexPath(for: cell) else { return }
|
||||||
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return }
|
||||||
|
|
||||||
Task {
|
Task {
|
||||||
await viewModel.loadMore(item: item)
|
await viewModel?.loadMore(item: item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -531,6 +537,8 @@ extension HomeTimelineViewController: ScrollViewContainer {
|
|||||||
var scrollView: UIScrollView { return tableView }
|
var scrollView: UIScrollView { return tableView }
|
||||||
|
|
||||||
func scrollToTop(animated: Bool) {
|
func scrollToTop(animated: Bool) {
|
||||||
|
guard let viewModel else { return }
|
||||||
|
|
||||||
if scrollView.contentOffset.y < scrollView.frame.height,
|
if scrollView.contentOffset.y < scrollView.frame.height,
|
||||||
viewModel.loadLatestStateMachine.canEnterState(HomeTimelineViewModel.LoadLatestState.Loading.self),
|
viewModel.loadLatestStateMachine.canEnterState(HomeTimelineViewModel.LoadLatestState.Loading.self),
|
||||||
(scrollView.contentOffset.y + scrollView.adjustedContentInset.top) == 0.0,
|
(scrollView.contentOffset.y + scrollView.adjustedContentInset.top) == 0.0,
|
||||||
@ -569,7 +577,7 @@ extension HomeTimelineViewController: HomeTimelineNavigationBarTitleViewDelegate
|
|||||||
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, buttonDidPressed sender: UIButton) {
|
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, buttonDidPressed sender: UIButton) {
|
||||||
switch titleView.state {
|
switch titleView.state {
|
||||||
case .newPostButton:
|
case .newPostButton:
|
||||||
guard let diffableDataSource = viewModel.diffableDataSource else { return }
|
guard let diffableDataSource = viewModel?.diffableDataSource else { return }
|
||||||
let indexPath = IndexPath(row: 0, section: 0)
|
let indexPath = IndexPath(row: 0, section: 0)
|
||||||
guard diffableDataSource.itemIdentifier(for: indexPath) != nil else { return }
|
guard diffableDataSource.itemIdentifier(for: indexPath) != nil else { return }
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ final class NotificationViewController: TabmanViewController, NeedsDependency {
|
|||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
var observations = Set<NSKeyValueObservation>()
|
var observations = Set<NSKeyValueObservation>()
|
||||||
|
|
||||||
var viewModel: NotificationViewModel!
|
var viewModel: NotificationViewModel?
|
||||||
|
|
||||||
let pageSegmentedControl = UISegmentedControl()
|
let pageSegmentedControl = UISegmentedControl()
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ final class NotificationViewController: TabmanViewController, NeedsDependency {
|
|||||||
animated: animated
|
animated: animated
|
||||||
)
|
)
|
||||||
|
|
||||||
viewModel.currentPageIndex = index
|
viewModel?.currentPageIndex = index
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ extension NotificationViewController {
|
|||||||
|
|
||||||
view.backgroundColor = .secondarySystemBackground
|
view.backgroundColor = .secondarySystemBackground
|
||||||
|
|
||||||
setupSegmentedControl(scopes: viewModel.scopes)
|
setupSegmentedControl(scopes: APIService.MastodonNotificationScope.allCases)
|
||||||
pageSegmentedControl.translatesAutoresizingMaskIntoConstraints = false
|
pageSegmentedControl.translatesAutoresizingMaskIntoConstraints = false
|
||||||
navigationItem.titleView = pageSegmentedControl
|
navigationItem.titleView = pageSegmentedControl
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
@ -58,7 +58,7 @@ extension NotificationViewController {
|
|||||||
pageSegmentedControl.addTarget(self, action: #selector(NotificationViewController.pageSegmentedControlValueChanged(_:)), for: .valueChanged)
|
pageSegmentedControl.addTarget(self, action: #selector(NotificationViewController.pageSegmentedControlValueChanged(_:)), for: .valueChanged)
|
||||||
|
|
||||||
dataSource = viewModel
|
dataSource = viewModel
|
||||||
viewModel.$viewControllers
|
viewModel?.$viewControllers
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] viewControllers in
|
.sink { [weak self] viewControllers in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@ -68,11 +68,11 @@ extension NotificationViewController {
|
|||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
viewModel.viewControllers = viewModel.scopes.map { scope in
|
viewModel?.viewControllers = APIService.MastodonNotificationScope.allCases.map { scope in
|
||||||
createViewController(for: scope)
|
createViewController(for: scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.$currentPageIndex
|
viewModel?.$currentPageIndex
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
.sink { [weak self] currentPageIndex in
|
.sink { [weak self] currentPageIndex in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@ -127,7 +127,7 @@ extension NotificationViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// set initial selection
|
// set initial selection
|
||||||
guard !pageSegmentedControl.isSelected else { return }
|
guard let viewModel, !pageSegmentedControl.isSelected else { return }
|
||||||
if viewModel.currentPageIndex < pageSegmentedControl.numberOfSegments {
|
if viewModel.currentPageIndex < pageSegmentedControl.numberOfSegments {
|
||||||
pageSegmentedControl.selectedSegmentIndex = viewModel.currentPageIndex
|
pageSegmentedControl.selectedSegmentIndex = viewModel.currentPageIndex
|
||||||
} else {
|
} else {
|
||||||
@ -136,12 +136,13 @@ extension NotificationViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func createViewController(for scope: NotificationTimelineViewModel.Scope) -> UIViewController {
|
private func createViewController(for scope: NotificationTimelineViewModel.Scope) -> UIViewController {
|
||||||
|
guard let authContext = viewModel?.authContext else { return UITableViewController() }
|
||||||
let viewController = NotificationTimelineViewController()
|
let viewController = NotificationTimelineViewController()
|
||||||
viewController.context = context
|
viewController.context = context
|
||||||
viewController.coordinator = coordinator
|
viewController.coordinator = coordinator
|
||||||
viewController.viewModel = NotificationTimelineViewModel(
|
viewController.viewModel = NotificationTimelineViewModel(
|
||||||
context: context,
|
context: context,
|
||||||
authContext: viewModel.authContext,
|
authContext: authContext,
|
||||||
scope: scope
|
scope: scope
|
||||||
)
|
)
|
||||||
return viewController
|
return viewController
|
||||||
|
@ -22,7 +22,6 @@ final class NotificationViewModel {
|
|||||||
let viewDidLoad = PassthroughSubject<Void, Never>()
|
let viewDidLoad = PassthroughSubject<Void, Never>()
|
||||||
|
|
||||||
// output
|
// output
|
||||||
let scopes = NotificationTimelineViewModel.Scope.allCases
|
|
||||||
@Published var viewControllers: [UIViewController] = []
|
@Published var viewControllers: [UIViewController] = []
|
||||||
@Published var currentPageIndex = 0 {
|
@Published var currentPageIndex = 0 {
|
||||||
didSet {
|
didSet {
|
||||||
|
@ -39,9 +39,9 @@ final class ContentSplitViewController: UIViewController, NeedsDependency {
|
|||||||
|
|
||||||
@Published var currentSupplementaryTab: Tab = .home
|
@Published var currentSupplementaryTab: Tab = .home
|
||||||
private(set) lazy var mainTabBarController: MainTabBarController = {
|
private(set) lazy var mainTabBarController: MainTabBarController = {
|
||||||
let mainTabBarController = MainTabBarController(context: context, coordinator: coordinator, authContext: authContext)
|
let mainTabBarController = MainTabBarController(context: self.context, coordinator: self.coordinator, authContext: self.authContext)
|
||||||
if let homeTimelineViewController = mainTabBarController.viewController(of: HomeTimelineViewController.self) {
|
if let homeTimelineViewController = mainTabBarController.viewController(of: HomeTimelineViewController.self) {
|
||||||
homeTimelineViewController.viewModel.displaySettingBarButtonItem = false
|
homeTimelineViewController.viewModel?.displaySettingBarButtonItem = false
|
||||||
}
|
}
|
||||||
return mainTabBarController
|
return mainTabBarController
|
||||||
}()
|
}()
|
||||||
|
@ -26,7 +26,7 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
|||||||
var searchTransitionController = SearchTransitionController()
|
var searchTransitionController = SearchTransitionController()
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
var disposeBag = Set<AnyCancellable>()
|
||||||
var viewModel: SearchViewModel!
|
var viewModel: SearchViewModel?
|
||||||
|
|
||||||
// use AutoLayout could set search bar margin automatically to
|
// use AutoLayout could set search bar margin automatically to
|
||||||
// layout alongside with split mode button (on iPad)
|
// layout alongside with split mode button (on iPad)
|
||||||
@ -37,7 +37,7 @@ final class SearchViewController: UIViewController, NeedsDependency {
|
|||||||
let searchBarTapPublisher = PassthroughSubject<String, Never>()
|
let searchBarTapPublisher = PassthroughSubject<String, Never>()
|
||||||
|
|
||||||
private(set) lazy var discoveryViewController: DiscoveryViewController? = {
|
private(set) lazy var discoveryViewController: DiscoveryViewController? = {
|
||||||
guard let authContext = viewModel.authContext else { return nil }
|
guard let authContext = viewModel?.authContext else { return nil }
|
||||||
let viewController = DiscoveryViewController()
|
let viewController = DiscoveryViewController()
|
||||||
viewController.context = context
|
viewController.context = context
|
||||||
viewController.coordinator = coordinator
|
viewController.coordinator = coordinator
|
||||||
@ -70,7 +70,7 @@ extension SearchViewController {
|
|||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
viewModel.viewDidAppeared.send()
|
viewModel?.viewDidAppeared.send()
|
||||||
|
|
||||||
// note:
|
// note:
|
||||||
// need set alpha because (maybe) SDK forget set alpha back
|
// need set alpha because (maybe) SDK forget set alpha back
|
||||||
@ -110,7 +110,7 @@ extension SearchViewController {
|
|||||||
.sink { [weak self] initialText in
|
.sink { [weak self] initialText in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
// push to search detail
|
// push to search detail
|
||||||
guard let authContext = self.viewModel.authContext else { return }
|
guard let authContext = self.viewModel?.authContext else { return }
|
||||||
let searchDetailViewModel = SearchDetailViewModel(authContext: authContext, initialSearchText: initialText)
|
let searchDetailViewModel = SearchDetailViewModel(authContext: authContext, initialSearchText: initialText)
|
||||||
searchDetailViewModel.needsBecomeFirstResponder = true
|
searchDetailViewModel.needsBecomeFirstResponder = true
|
||||||
self.navigationController?.delegate = self.searchTransitionController
|
self.navigationController?.delegate = self.searchTransitionController
|
||||||
|
Loading…
x
Reference in New Issue
Block a user