Remove NavigationBarTitleView and add first draft of TimelineStatusPill (IOS-234)
This commit is contained in:
parent
cc9faf5aea
commit
6af94352e2
|
@ -91,8 +91,6 @@
|
||||||
2D7867192625B77500211898 /* NotificationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7867182625B77500211898 /* NotificationItem.swift */; };
|
2D7867192625B77500211898 /* NotificationItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D7867182625B77500211898 /* NotificationItem.swift */; };
|
||||||
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */; };
|
2D82B9FF25E7863200E36F0F /* OnboardingViewControllerAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */; };
|
||||||
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */; };
|
2D82BA0525E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */; };
|
||||||
2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8434F425FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift */; };
|
|
||||||
2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D8434FA25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift */; };
|
|
||||||
2D84350525FF858100EECE90 /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D84350425FF858100EECE90 /* UIScrollView.swift */; };
|
2D84350525FF858100EECE90 /* UIScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D84350425FF858100EECE90 /* UIScrollView.swift */; };
|
||||||
2D939AB525EDD8A90076FA61 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
2D939AB525EDD8A90076FA61 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D939AB425EDD8A90076FA61 /* String.swift */; };
|
||||||
2DAC9E38262FC2320062E1A6 /* SuggestionAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */; };
|
2DAC9E38262FC2320062E1A6 /* SuggestionAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */; };
|
||||||
|
@ -154,6 +152,7 @@
|
||||||
D8318A882A4468D300C0FB73 /* NotificationSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */; };
|
D8318A882A4468D300C0FB73 /* NotificationSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */; };
|
||||||
D8318A8A2A4468DC00C0FB73 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A892A4468DC00C0FB73 /* AboutViewController.swift */; };
|
D8318A8A2A4468DC00C0FB73 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8318A892A4468DC00C0FB73 /* AboutViewController.swift */; };
|
||||||
D8363B1629469CE200A74079 /* OnboardingNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8363B1529469CE200A74079 /* OnboardingNextView.swift */; };
|
D8363B1629469CE200A74079 /* OnboardingNextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D8363B1529469CE200A74079 /* OnboardingNextView.swift */; };
|
||||||
|
D84738D42BBD9ABE00ECD52B /* TimelineStatusPill.swift in Sources */ = {isa = PBXBuildFile; fileRef = D84738D32BBD9ABE00ECD52B /* TimelineStatusPill.swift */; };
|
||||||
D84FA0932AE6915800987F47 /* MBProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = D84FA0922AE6915800987F47 /* MBProgressHUD */; };
|
D84FA0932AE6915800987F47 /* MBProgressHUD in Frameworks */ = {isa = PBXBuildFile; productRef = D84FA0922AE6915800987F47 /* MBProgressHUD */; };
|
||||||
D852C23C2AC5D02C00309232 /* AboutInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */; };
|
D852C23C2AC5D02C00309232 /* AboutInstanceViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23B2AC5D02C00309232 /* AboutInstanceViewController.swift */; };
|
||||||
D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */; };
|
D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D852C23D2AC5D03300309232 /* InstanceRulesViewController.swift */; };
|
||||||
|
@ -715,8 +714,6 @@
|
||||||
2D7867182625B77500211898 /* NotificationItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItem.swift; sourceTree = "<group>"; };
|
2D7867182625B77500211898 /* NotificationItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItem.swift; sourceTree = "<group>"; };
|
||||||
2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewControllerAppearance.swift; sourceTree = "<group>"; };
|
2D82B9FE25E7863200E36F0F /* OnboardingViewControllerAppearance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewControllerAppearance.swift; sourceTree = "<group>"; };
|
||||||
2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
|
2D82BA0425E7897700E36F0F /* MastodonResendEmailViewModelNavigationDelegateShim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MastodonResendEmailViewModelNavigationDelegateShim.swift; sourceTree = "<group>"; };
|
||||||
2D8434F425FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineNavigationBarTitleViewModel.swift; sourceTree = "<group>"; };
|
|
||||||
2D8434FA25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeTimelineNavigationBarTitleView.swift; sourceTree = "<group>"; };
|
|
||||||
2D84350425FF858100EECE90 /* UIScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = "<group>"; };
|
2D84350425FF858100EECE90 /* UIScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollView.swift; sourceTree = "<group>"; };
|
||||||
2D939AB425EDD8A90076FA61 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
2D939AB425EDD8A90076FA61 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||||
2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewController.swift; sourceTree = "<group>"; };
|
2DAC9E37262FC2320062E1A6 /* SuggestionAccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuggestionAccountViewController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -779,6 +776,7 @@
|
||||||
D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsViewController.swift; sourceTree = "<group>"; };
|
D8318A872A4468D300C0FB73 /* NotificationSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsViewController.swift; sourceTree = "<group>"; };
|
||||||
D8318A892A4468DC00C0FB73 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
D8318A892A4468DC00C0FB73 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||||
D8363B1529469CE200A74079 /* OnboardingNextView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = OnboardingNextView.swift; sourceTree = "<group>"; tabWidth = 4; };
|
D8363B1529469CE200A74079 /* OnboardingNextView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = OnboardingNextView.swift; sourceTree = "<group>"; tabWidth = 4; };
|
||||||
|
D84738D32BBD9ABE00ECD52B /* TimelineStatusPill.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStatusPill.swift; sourceTree = "<group>"; };
|
||||||
D84C099D2B0F9E33009E685E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
D84C099D2B0F9E33009E685E /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
D84C099F2B0F9E41009E685E /* Setup.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Setup.md; sourceTree = "<group>"; };
|
D84C099F2B0F9E41009E685E /* Setup.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Setup.md; sourceTree = "<group>"; };
|
||||||
D84C09A02B0F9E41009E685E /* How-it-works.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "How-it-works.md"; sourceTree = "<group>"; };
|
D84C09A02B0F9E41009E685E /* How-it-works.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "How-it-works.md"; sourceTree = "<group>"; };
|
||||||
|
@ -1501,13 +1499,13 @@
|
||||||
2D38F1D325CD463600561493 /* HomeTimeline */ = {
|
2D38F1D325CD463600561493 /* HomeTimeline */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
DB1F239626117C360057430E /* View */,
|
|
||||||
2D38F1D425CD465300561493 /* HomeTimelineViewController.swift */,
|
2D38F1D425CD465300561493 /* HomeTimelineViewController.swift */,
|
||||||
DB697DD8278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift */,
|
DB697DD8278F4CED004EF2F7 /* HomeTimelineViewController+DataSourceProvider.swift */,
|
||||||
2D38F1E425CD46C100561493 /* HomeTimelineViewModel.swift */,
|
2D38F1E425CD46C100561493 /* HomeTimelineViewModel.swift */,
|
||||||
2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */,
|
2D5A3D2725CF8BC9002347D6 /* HomeTimelineViewModel+Diffable.swift */,
|
||||||
2D38F1EA25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift */,
|
2D38F1EA25CD477000561493 /* HomeTimelineViewModel+LoadLatestState.swift */,
|
||||||
2D38F1F625CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift */,
|
2D38F1F625CD47AC00561493 /* HomeTimelineViewModel+LoadOldestState.swift */,
|
||||||
|
D84738D32BBD9ABE00ECD52B /* TimelineStatusPill.swift */,
|
||||||
);
|
);
|
||||||
path = HomeTimeline;
|
path = HomeTimeline;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1983,15 +1981,6 @@
|
||||||
path = TableView;
|
path = TableView;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
DB1F239626117C360057430E /* View */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
2D8434F425FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift */,
|
|
||||||
2D8434FA25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift */,
|
|
||||||
);
|
|
||||||
path = View;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
DB3D0FF725BAA68500EAA174 /* Supporting Files */ = {
|
DB3D0FF725BAA68500EAA174 /* Supporting Files */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -3484,7 +3473,6 @@
|
||||||
DB0617FF27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift in Sources */,
|
DB0617FF27855D6C0030EE79 /* MastodonServerRulesViewModel+Diffable.swift in Sources */,
|
||||||
DBB5255E2611F07A002F1F29 /* ProfileViewModel.swift in Sources */,
|
DBB5255E2611F07A002F1F29 /* ProfileViewModel.swift in Sources */,
|
||||||
DB0FCB982797F6BF006C02E2 /* UserTableViewCell+ViewModel.swift in Sources */,
|
DB0FCB982797F6BF006C02E2 /* UserTableViewCell+ViewModel.swift in Sources */,
|
||||||
2D8434FB25FF46B300EECE90 /* HomeTimelineNavigationBarTitleView.swift in Sources */,
|
|
||||||
DB697DD6278F4C29004EF2F7 /* DataSourceProvider.swift in Sources */,
|
DB697DD6278F4C29004EF2F7 /* DataSourceProvider.swift in Sources */,
|
||||||
DB0FCB8E2796C0B7006C02E2 /* TrendCollectionViewCell.swift in Sources */,
|
DB0FCB8E2796C0B7006C02E2 /* TrendCollectionViewCell.swift in Sources */,
|
||||||
0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */,
|
0F1E2D0B2615C39400C38565 /* DoubleTitleLabelNavigationBarTitleView.swift in Sources */,
|
||||||
|
@ -3494,7 +3482,6 @@
|
||||||
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
|
DB45FAD725CA6C76005A8AC7 /* UIBarButtonItem.swift in Sources */,
|
||||||
D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */,
|
D8FAAE432AD047B200DC1832 /* AboutInstanceTableFooterView.swift in Sources */,
|
||||||
D808B94E296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift in Sources */,
|
D808B94E296EFBBA0031EB1E /* StatusEditHistoryTableViewCell.swift in Sources */,
|
||||||
2D8434F525FF465D00EECE90 /* HomeTimelineNavigationBarTitleViewModel.swift in Sources */,
|
|
||||||
D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */,
|
D852C23E2AC5D03300309232 /* InstanceRulesViewController.swift in Sources */,
|
||||||
DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */,
|
DB938F0F2624119800E5B6C1 /* ThreadViewModel+LoadThreadState.swift in Sources */,
|
||||||
DB6180F226391CF40018D199 /* MediaPreviewImageViewModel.swift in Sources */,
|
DB6180F226391CF40018D199 /* MediaPreviewImageViewModel.swift in Sources */,
|
||||||
|
@ -3763,6 +3750,7 @@
|
||||||
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
|
DB6B74FC272FF55800C70B6E /* UserSection.swift in Sources */,
|
||||||
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,
|
DB0FCB862796BDA1006C02E2 /* SearchSection.swift in Sources */,
|
||||||
DB1D61CF26F1B33600DA8662 /* WelcomeViewModel.swift in Sources */,
|
DB1D61CF26F1B33600DA8662 /* WelcomeViewModel.swift in Sources */,
|
||||||
|
D84738D42BBD9ABE00ECD52B /* TimelineStatusPill.swift in Sources */,
|
||||||
D8B5E4F42A4ED0240008970C /* NotificationSettingsViewModel.swift in Sources */,
|
D8B5E4F42A4ED0240008970C /* NotificationSettingsViewModel.swift in Sources */,
|
||||||
DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */,
|
DBD376B2269302A4007FEC24 /* UITableViewCell.swift in Sources */,
|
||||||
DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */,
|
DB4F0966269ED52200D62E92 /* SearchResultViewModel.swift in Sources */,
|
||||||
|
|
|
@ -44,8 +44,6 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency, Media
|
||||||
return emptyView
|
return emptyView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let titleView = HomeTimelineNavigationBarTitleView()
|
|
||||||
|
|
||||||
lazy var timelineSelectorButton = {
|
lazy var timelineSelectorButton = {
|
||||||
let button = UIButton(type: .custom)
|
let button = UIButton(type: .custom)
|
||||||
button.setAttributedTitle(
|
button.setAttributedTitle(
|
||||||
|
@ -101,6 +99,8 @@ final class HomeTimelineViewController: UIViewController, NeedsDependency, Media
|
||||||
}()
|
}()
|
||||||
|
|
||||||
let refreshControl = RefreshControl()
|
let refreshControl = RefreshControl()
|
||||||
|
let timelinePill = TimelineStatusPill()
|
||||||
|
|
||||||
|
|
||||||
private func generateTimeSelectorMenu() -> UIMenu {
|
private func generateTimeSelectorMenu() -> UIMenu {
|
||||||
let showFollowingAction = UIAction(title: L10n.Scene.HomeTimeline.TimelineMenu.following, image: .init(systemName: "house")) { [weak self] _ in
|
let showFollowingAction = UIAction(title: L10n.Scene.HomeTimeline.TimelineMenu.following, image: .init(systemName: "house")) { [weak self] _ in
|
||||||
|
@ -170,33 +170,6 @@ extension HomeTimelineViewController {
|
||||||
settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:))
|
settingBarButtonItem.action = #selector(HomeTimelineViewController.settingBarButtonItemPressed(_:))
|
||||||
|
|
||||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: timelineSelectorButton)
|
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: timelineSelectorButton)
|
||||||
|
|
||||||
// navigationItem.titleView = titleView
|
|
||||||
// titleView.delegate = self
|
|
||||||
|
|
||||||
viewModel?.homeTimelineNavigationBarTitleViewModel.state
|
|
||||||
.removeDuplicates()
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] state in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.titleView.configure(state: state)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
viewModel?.homeTimelineNavigationBarTitleViewModel.state
|
|
||||||
.removeDuplicates()
|
|
||||||
.filter { $0 == .publishedButton }
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
guard UserDefaults.shared.lastVersionPromptedForReview == nil else { return }
|
|
||||||
guard UserDefaults.shared.processCompletedCount > 3 else { return }
|
|
||||||
guard let windowScene = self.view.window?.windowScene else { return }
|
|
||||||
let version = UIApplication.appVersion()
|
|
||||||
UserDefaults.shared.lastVersionPromptedForReview = version
|
|
||||||
SKStoreReviewController.requestReview(in: windowScene)
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
tableView.refreshControl = refreshControl
|
tableView.refreshControl = refreshControl
|
||||||
refreshControl.addTarget(self, action: #selector(HomeTimelineViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
refreshControl.addTarget(self, action: #selector(HomeTimelineViewController.refreshControlValueChanged(_:)), for: .valueChanged)
|
||||||
|
@ -326,6 +299,15 @@ extension HomeTimelineViewController {
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
|
// timelinePill.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
// view.addSubview(timelinePill)
|
||||||
|
//
|
||||||
|
// // has to up updated and animated
|
||||||
|
// timelinePill.update(with: .postSent)
|
||||||
|
// NSLayoutConstraint.activate([
|
||||||
|
// timelinePill.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 8),
|
||||||
|
// timelinePill.centerXAnchor.constraint(equalTo: view.centerXAnchor),
|
||||||
|
// ])
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -480,15 +462,6 @@ extension HomeTimelineViewController {
|
||||||
}
|
}
|
||||||
// MARK: - UIScrollViewDelegate
|
// MARK: - UIScrollViewDelegate
|
||||||
extension HomeTimelineViewController {
|
extension HomeTimelineViewController {
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
||||||
switch scrollView {
|
|
||||||
case tableView:
|
|
||||||
viewModel?.homeTimelineNavigationBarTitleViewModel.handleScrollViewDidScroll(scrollView)
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
|
func scrollViewShouldScrollToTop(_ scrollView: UIScrollView) -> Bool {
|
||||||
switch scrollView {
|
switch scrollView {
|
||||||
case tableView:
|
case tableView:
|
||||||
|
@ -644,37 +617,6 @@ extension HomeTimelineViewController: ScrollViewContainer {
|
||||||
// MARK: - StatusTableViewCellDelegate
|
// MARK: - StatusTableViewCellDelegate
|
||||||
extension HomeTimelineViewController: StatusTableViewCellDelegate { }
|
extension HomeTimelineViewController: StatusTableViewCellDelegate { }
|
||||||
|
|
||||||
// MARK: - HomeTimelineNavigationBarTitleViewDelegate
|
|
||||||
extension HomeTimelineViewController: HomeTimelineNavigationBarTitleViewDelegate {
|
|
||||||
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, logoButtonDidPressed sender: UIButton) {
|
|
||||||
if shouldRestoreScrollPosition() {
|
|
||||||
restorePositionWhenScrollToTop()
|
|
||||||
} else {
|
|
||||||
savePositionBeforeScrollToTop()
|
|
||||||
scrollToTop(animated: true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, buttonDidPressed sender: UIButton) {
|
|
||||||
switch titleView.state {
|
|
||||||
case .newPostButton:
|
|
||||||
guard let diffableDataSource = viewModel?.diffableDataSource else { return }
|
|
||||||
let indexPath = IndexPath(row: 0, section: 0)
|
|
||||||
guard diffableDataSource.itemIdentifier(for: indexPath) != nil else { return }
|
|
||||||
|
|
||||||
savePositionBeforeScrollToTop()
|
|
||||||
tableView.scrollToRow(at: indexPath, at: .top, animated: true)
|
|
||||||
case .offlineButton:
|
|
||||||
// TODO: retry
|
|
||||||
break
|
|
||||||
case .publishedButton:
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HomeTimelineViewController {
|
extension HomeTimelineViewController {
|
||||||
override var keyCommands: [UIKeyCommand]? {
|
override var keyCommands: [UIKeyCommand]? {
|
||||||
return navigationKeyCommands + statusNavigationKeyCommands
|
return navigationKeyCommands + statusNavigationKeyCommands
|
||||||
|
|
|
@ -129,7 +129,6 @@ extension HomeTimelineViewModel.LoadLatestState {
|
||||||
}
|
}
|
||||||
|
|
||||||
await enter(state: Idle.self)
|
await enter(state: Idle.self)
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.receiveLoadingStateCompletion(.finished)
|
|
||||||
|
|
||||||
// stop refresher if no new statuses
|
// stop refresher if no new statuses
|
||||||
let statuses = response.value
|
let statuses = response.value
|
||||||
|
@ -137,11 +136,7 @@ extension HomeTimelineViewModel.LoadLatestState {
|
||||||
|
|
||||||
if newStatuses.isEmpty {
|
if newStatuses.isEmpty {
|
||||||
viewModel.didLoadLatest.send()
|
viewModel.didLoadLatest.send()
|
||||||
} else {
|
} else {
|
||||||
if !latestStatusIDs.isEmpty {
|
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.newPostsIncoming()
|
|
||||||
}
|
|
||||||
|
|
||||||
viewModel.dataController.records = {
|
viewModel.dataController.records = {
|
||||||
var newRecords: [MastodonFeed] = newStatuses.map {
|
var newRecords: [MastodonFeed] = newStatuses.map {
|
||||||
MastodonFeed.fromStatus(.fromEntity($0), kind: .home)
|
MastodonFeed.fromStatus(.fromEntity($0), kind: .home)
|
||||||
|
@ -168,7 +163,6 @@ extension HomeTimelineViewModel.LoadLatestState {
|
||||||
} catch {
|
} catch {
|
||||||
await enter(state: Idle.self)
|
await enter(state: Idle.self)
|
||||||
viewModel.didLoadLatest.send()
|
viewModel.didLoadLatest.send()
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.receiveLoadingStateCompletion(.failure(error))
|
|
||||||
}
|
}
|
||||||
} // end Task
|
} // end Task
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,12 +83,9 @@ extension HomeTimelineViewModel.LoadOldestState {
|
||||||
} else {
|
} else {
|
||||||
await self.enter(state: Idle.self)
|
await self.enter(state: Idle.self)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.receiveLoadingStateCompletion(.finished)
|
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
await self.enter(state: Fail.self)
|
await self.enter(state: Fail.self)
|
||||||
viewModel.homeTimelineNavigationBarTitleViewModel.receiveLoadingStateCompletion(.failure(error))
|
|
||||||
}
|
}
|
||||||
} // end Task
|
} // end Task
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ final class HomeTimelineViewModel: NSObject {
|
||||||
let context: AppContext
|
let context: AppContext
|
||||||
let authContext: AuthContext
|
let authContext: AuthContext
|
||||||
let dataController: FeedDataController
|
let dataController: FeedDataController
|
||||||
let homeTimelineNavigationBarTitleViewModel: HomeTimelineNavigationBarTitleViewModel
|
|
||||||
let listBatchFetchViewModel = ListBatchFetchViewModel()
|
let listBatchFetchViewModel = ListBatchFetchViewModel()
|
||||||
|
|
||||||
var presentedSuggestions = false
|
var presentedSuggestions = false
|
||||||
|
@ -81,7 +80,6 @@ final class HomeTimelineViewModel: NSObject {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.authContext = authContext
|
self.authContext = authContext
|
||||||
self.dataController = FeedDataController(context: context, authContext: authContext)
|
self.dataController = FeedDataController(context: context, authContext: authContext)
|
||||||
self.homeTimelineNavigationBarTitleViewModel = HomeTimelineNavigationBarTitleViewModel(context: context)
|
|
||||||
super.init()
|
super.init()
|
||||||
self.dataController.records = (try? FileManager.default.cachedHomeTimeline(for: authContext.mastodonAuthenticationBox).map {
|
self.dataController.records = (try? FileManager.default.cachedHomeTimeline(for: authContext.mastodonAuthenticationBox).map {
|
||||||
MastodonFeed.fromStatus($0, kind: .home)
|
MastodonFeed.fromStatus($0, kind: .home)
|
||||||
|
@ -92,16 +90,6 @@ final class HomeTimelineViewModel: NSObject {
|
||||||
self?.loadLatestStateMachine.enter(LoadLatestState.Loading.self)
|
self?.loadLatestStateMachine.enter(LoadLatestState.Loading.self)
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
||||||
// refresh after publish post
|
|
||||||
homeTimelineNavigationBarTitleViewModel.isPublished
|
|
||||||
.delay(for: 2, scheduler: DispatchQueue.main)
|
|
||||||
.sink { [weak self] isPublished in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.homeTimelineNeedRefresh.send()
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
self.dataController.$records
|
self.dataController.$records
|
||||||
.removeDuplicates()
|
.removeDuplicates()
|
||||||
.receive(on: DispatchQueue.main)
|
.receive(on: DispatchQueue.main)
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright © 2024 Mastodon gGmbH. All rights reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
import MastodonAsset
|
||||||
|
|
||||||
|
class TimelineStatusPill: UIButton {
|
||||||
|
|
||||||
|
func update(with state: State) {
|
||||||
|
var configuration = UIButton.Configuration.filled()
|
||||||
|
|
||||||
|
|
||||||
|
configuration.attributedTitle = AttributedString(
|
||||||
|
state.title, attributes: AttributeContainer(
|
||||||
|
[
|
||||||
|
.font: UIFontMetrics(forTextStyle: .subheadline).scaledFont(for: .systemFont(ofSize: 15, weight: .bold)),
|
||||||
|
.foregroundColor: UIColor.white
|
||||||
|
]
|
||||||
|
))
|
||||||
|
|
||||||
|
let image = state.image?
|
||||||
|
.withConfiguration(UIImage.SymbolConfiguration(paletteColors: [.white]))
|
||||||
|
.withConfiguration(UIImage.SymbolConfiguration(textStyle: .subheadline))
|
||||||
|
.withConfiguration(UIImage.SymbolConfiguration(pointSize: 12, weight: .bold, scale: .medium))
|
||||||
|
|
||||||
|
configuration.image = image
|
||||||
|
configuration.imagePadding = 8
|
||||||
|
configuration.baseBackgroundColor = state.backgroundColor
|
||||||
|
configuration.cornerStyle = .capsule
|
||||||
|
|
||||||
|
self.configuration = configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum State {
|
||||||
|
case newPosts
|
||||||
|
case postSent
|
||||||
|
case offline
|
||||||
|
|
||||||
|
var image: UIImage? {
|
||||||
|
switch self {
|
||||||
|
case .newPosts:
|
||||||
|
return UIImage(systemName: "chevron.up")
|
||||||
|
case .postSent:
|
||||||
|
return UIImage(systemName: "checkmark")
|
||||||
|
case .offline:
|
||||||
|
return UIImage(systemName: "bolt.horizontal.fill")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var backgroundColor: UIColor {
|
||||||
|
switch self {
|
||||||
|
case .newPosts:
|
||||||
|
return Asset.Colors.Brand.blurple.color
|
||||||
|
case .postSent:
|
||||||
|
return .systemGreen
|
||||||
|
case .offline:
|
||||||
|
return .systemGray
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var title: String {
|
||||||
|
//TODO: Localization
|
||||||
|
switch self {
|
||||||
|
case .newPosts:
|
||||||
|
return "New Posts"
|
||||||
|
case .postSent:
|
||||||
|
return "Post Sent"
|
||||||
|
case .offline:
|
||||||
|
return "Offline"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,221 +0,0 @@
|
||||||
//
|
|
||||||
// HomeTimelineNavigationBarTitleView.swift
|
|
||||||
// Mastodon
|
|
||||||
//
|
|
||||||
// Created by sxiaojian on 2021/3/15.
|
|
||||||
//
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
import MastodonUI
|
|
||||||
import MastodonAsset
|
|
||||||
import MastodonLocalization
|
|
||||||
|
|
||||||
protocol HomeTimelineNavigationBarTitleViewDelegate: AnyObject {
|
|
||||||
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, logoButtonDidPressed sender: UIButton)
|
|
||||||
func homeTimelineNavigationBarTitleView(_ titleView: HomeTimelineNavigationBarTitleView, buttonDidPressed sender: UIButton)
|
|
||||||
}
|
|
||||||
|
|
||||||
final class HomeTimelineNavigationBarTitleView: UIView {
|
|
||||||
|
|
||||||
let containerView = UIStackView()
|
|
||||||
|
|
||||||
let logoButton = HighlightDimmableButton()
|
|
||||||
let button = RoundedEdgesButton()
|
|
||||||
let label = UILabel()
|
|
||||||
|
|
||||||
// input
|
|
||||||
private var blockingState: HomeTimelineNavigationBarTitleViewModel.State?
|
|
||||||
weak var delegate: HomeTimelineNavigationBarTitleViewDelegate?
|
|
||||||
|
|
||||||
// output
|
|
||||||
private(set) var state: HomeTimelineNavigationBarTitleViewModel.State = .logo
|
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
|
||||||
super.init(frame: frame)
|
|
||||||
_init()
|
|
||||||
}
|
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
super.init(coder: coder)
|
|
||||||
_init()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HomeTimelineNavigationBarTitleView {
|
|
||||||
private func _init() {
|
|
||||||
containerView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
addSubview(containerView)
|
|
||||||
containerView.pinToParent()
|
|
||||||
|
|
||||||
containerView.addArrangedSubview(logoButton)
|
|
||||||
button.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
containerView.addArrangedSubview(button)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
button.heightAnchor.constraint(equalToConstant: 24).priority(.defaultHigh)
|
|
||||||
])
|
|
||||||
containerView.addArrangedSubview(label)
|
|
||||||
|
|
||||||
configure(state: .logo)
|
|
||||||
logoButton.addTarget(self, action: #selector(HomeTimelineNavigationBarTitleView.logoButtonDidPressed(_:)), for: .touchUpInside)
|
|
||||||
button.addTarget(self, action: #selector(HomeTimelineNavigationBarTitleView.buttonDidPressed(_:)), for: .touchUpInside)
|
|
||||||
|
|
||||||
logoButton.accessibilityIdentifier = "TitleButton"
|
|
||||||
logoButton.accessibilityTraits = [.header, .button]
|
|
||||||
button.accessibilityIdentifier = "TitleButton"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HomeTimelineNavigationBarTitleView {
|
|
||||||
@objc private func logoButtonDidPressed(_ sender: UIButton) {
|
|
||||||
delegate?.homeTimelineNavigationBarTitleView(self, logoButtonDidPressed: sender)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc private func buttonDidPressed(_ sender: UIButton) {
|
|
||||||
delegate?.homeTimelineNavigationBarTitleView(self, buttonDidPressed: sender)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HomeTimelineNavigationBarTitleView {
|
|
||||||
|
|
||||||
func resetContainer() {
|
|
||||||
logoButton.isHidden = true
|
|
||||||
button.isHidden = true
|
|
||||||
label.isHidden = true
|
|
||||||
}
|
|
||||||
|
|
||||||
func configure(state: HomeTimelineNavigationBarTitleViewModel.State) {
|
|
||||||
self.state = state
|
|
||||||
|
|
||||||
// check state block or not
|
|
||||||
guard blockingState == nil else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
resetContainer()
|
|
||||||
|
|
||||||
switch state {
|
|
||||||
case .logo:
|
|
||||||
logoButton.tintColor = Asset.Colors.Label.primary.color
|
|
||||||
logoButton.setImage(Asset.Asset.mastodonTextLogo.image.withRenderingMode(.alwaysTemplate), for: .normal)
|
|
||||||
logoButton.contentMode = .center
|
|
||||||
logoButton.isHidden = false
|
|
||||||
logoButton.accessibilityLabel = L10n.Scene.HomeTimeline.NavigationBarState.Accessibility.logoLabel // TODO :i18n
|
|
||||||
logoButton.accessibilityHint = L10n.Scene.HomeTimeline.NavigationBarState.Accessibility.logoHint
|
|
||||||
case .newPostButton:
|
|
||||||
configureButton(
|
|
||||||
title: L10n.Scene.HomeTimeline.NavigationBarState.newPosts,
|
|
||||||
textColor: .white,
|
|
||||||
backgroundColor: Asset.Colors.Brand.blurple.color
|
|
||||||
)
|
|
||||||
button.isHidden = false
|
|
||||||
button.accessibilityLabel = L10n.Scene.HomeTimeline.NavigationBarState.newPosts
|
|
||||||
case .offlineButton:
|
|
||||||
configureButton(
|
|
||||||
title: L10n.Scene.HomeTimeline.NavigationBarState.offline,
|
|
||||||
textColor: .white,
|
|
||||||
backgroundColor: Asset.Colors.danger.color
|
|
||||||
)
|
|
||||||
button.isHidden = false
|
|
||||||
button.accessibilityLabel = L10n.Scene.HomeTimeline.NavigationBarState.offline
|
|
||||||
case .publishingPostLabel:
|
|
||||||
label.font = .systemFont(ofSize: 17, weight: .semibold)
|
|
||||||
label.textColor = Asset.Colors.Label.primary.color
|
|
||||||
label.text = L10n.Scene.HomeTimeline.NavigationBarState.publishing
|
|
||||||
label.textAlignment = .center
|
|
||||||
label.isHidden = false
|
|
||||||
button.accessibilityLabel = L10n.Scene.HomeTimeline.NavigationBarState.publishing
|
|
||||||
case .publishedButton:
|
|
||||||
blockingState = state
|
|
||||||
configureButton(
|
|
||||||
title: L10n.Scene.HomeTimeline.NavigationBarState.published,
|
|
||||||
textColor: .white,
|
|
||||||
backgroundColor: Asset.Colors.successGreen.color
|
|
||||||
)
|
|
||||||
button.isHidden = false
|
|
||||||
button.accessibilityLabel = L10n.Scene.HomeTimeline.NavigationBarState.published
|
|
||||||
|
|
||||||
let presentDuration: TimeInterval = 0.33
|
|
||||||
let scaleAnimator = UIViewPropertyAnimator(duration: presentDuration, timingParameters: UISpringTimingParameters())
|
|
||||||
button.transform = CGAffineTransform(scaleX: 0.5, y: 0.5)
|
|
||||||
scaleAnimator.addAnimations {
|
|
||||||
self.button.transform = .identity
|
|
||||||
}
|
|
||||||
let alphaAnimator = UIViewPropertyAnimator(duration: presentDuration, curve: .easeInOut)
|
|
||||||
button.alpha = 0.3
|
|
||||||
alphaAnimator.addAnimations {
|
|
||||||
self.button.alpha = 1
|
|
||||||
}
|
|
||||||
scaleAnimator.startAnimation()
|
|
||||||
alphaAnimator.startAnimation()
|
|
||||||
|
|
||||||
let dismissDuration: TimeInterval = 3
|
|
||||||
let dissolveAnimator = UIViewPropertyAnimator(duration: dismissDuration, curve: .easeInOut)
|
|
||||||
dissolveAnimator.addAnimations({
|
|
||||||
self.button.alpha = 0
|
|
||||||
}, delayFactor: 0.9) // at 2.7s
|
|
||||||
dissolveAnimator.addCompletion { _ in
|
|
||||||
self.blockingState = nil
|
|
||||||
self.configure(state: self.state)
|
|
||||||
self.button.alpha = 1
|
|
||||||
}
|
|
||||||
dissolveAnimator.startAnimation()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func configureButton(title: String, textColor: UIColor, backgroundColor: UIColor) {
|
|
||||||
button.setBackgroundImage(.placeholder(color: backgroundColor), for: .normal)
|
|
||||||
button.setBackgroundImage(.placeholder(color: backgroundColor.withAlphaComponent(0.5)), for: .highlighted)
|
|
||||||
button.setTitleColor(textColor, for: .normal)
|
|
||||||
button.setTitleColor(textColor.withAlphaComponent(0.5), for: .highlighted)
|
|
||||||
button.setTitle(title, for: .normal)
|
|
||||||
button.contentEdgeInsets = UIEdgeInsets(top: 1, left: 16, bottom: 1, right: 16)
|
|
||||||
button.titleLabel?.font = .systemFont(ofSize: 15, weight: .bold)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#if canImport(SwiftUI) && DEBUG
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
struct HomeTimelineNavigationBarTitleView_Previews: PreviewProvider {
|
|
||||||
|
|
||||||
static var previews: some View {
|
|
||||||
Group {
|
|
||||||
UIViewPreview(width: 375) {
|
|
||||||
let titleView = HomeTimelineNavigationBarTitleView()
|
|
||||||
titleView.configure(state: .logo)
|
|
||||||
return titleView
|
|
||||||
}
|
|
||||||
.previewLayout(.fixed(width: 375, height: 44))
|
|
||||||
UIViewPreview(width: 150) {
|
|
||||||
let titleView = HomeTimelineNavigationBarTitleView()
|
|
||||||
titleView.configure(state: .newPostButton)
|
|
||||||
return titleView
|
|
||||||
}
|
|
||||||
.previewLayout(.fixed(width: 150, height: 24))
|
|
||||||
UIViewPreview(width: 120) {
|
|
||||||
let titleView = HomeTimelineNavigationBarTitleView()
|
|
||||||
titleView.configure(state: .offlineButton)
|
|
||||||
return titleView
|
|
||||||
}
|
|
||||||
.previewLayout(.fixed(width: 120, height: 24))
|
|
||||||
UIViewPreview(width: 375) {
|
|
||||||
let titleView = HomeTimelineNavigationBarTitleView()
|
|
||||||
titleView.configure(state: .publishingPostLabel)
|
|
||||||
return titleView
|
|
||||||
}
|
|
||||||
.previewLayout(.fixed(width: 375, height: 44))
|
|
||||||
UIViewPreview(width: 120) {
|
|
||||||
let titleView = HomeTimelineNavigationBarTitleView()
|
|
||||||
titleView.configure(state: .publishedButton)
|
|
||||||
return titleView
|
|
||||||
}
|
|
||||||
.previewLayout(.fixed(width: 120, height: 24))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -1,182 +0,0 @@
|
||||||
//
|
|
||||||
// HomeTimelineNavigationBarTitleViewModel.swift
|
|
||||||
// Mastodon
|
|
||||||
//
|
|
||||||
// Created by sxiaojian on 2021/3/15.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Combine
|
|
||||||
import Foundation
|
|
||||||
import UIKit
|
|
||||||
import MastodonCore
|
|
||||||
|
|
||||||
final class HomeTimelineNavigationBarTitleViewModel {
|
|
||||||
|
|
||||||
static let offlineCounterLimit = 3
|
|
||||||
|
|
||||||
var disposeBag = Set<AnyCancellable>()
|
|
||||||
private(set) var publishingProgressSubscription: AnyCancellable?
|
|
||||||
|
|
||||||
// input
|
|
||||||
let context: AppContext
|
|
||||||
var networkErrorCount = CurrentValueSubject<Int, Never>(0)
|
|
||||||
var networkErrorPublisher = PassthroughSubject<Void, Never>()
|
|
||||||
|
|
||||||
// output
|
|
||||||
let state = CurrentValueSubject<State, Never>(.logo)
|
|
||||||
let hasNewPosts = CurrentValueSubject<Bool, Never>(false)
|
|
||||||
let isOffline = CurrentValueSubject<Bool, Never>(false)
|
|
||||||
let isPublishingPost = CurrentValueSubject<Bool, Never>(false)
|
|
||||||
let isPublished = CurrentValueSubject<Bool, Never>(false)
|
|
||||||
let publishingProgress = PassthroughSubject<Float, Never>()
|
|
||||||
|
|
||||||
init(context: AppContext) {
|
|
||||||
self.context = context
|
|
||||||
|
|
||||||
networkErrorPublisher
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] _ in
|
|
||||||
guard let self = self else { return }
|
|
||||||
self.networkErrorCount.value = self.networkErrorCount.value + 1
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
networkErrorCount
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.map { count in
|
|
||||||
return count >= HomeTimelineNavigationBarTitleViewModel.offlineCounterLimit
|
|
||||||
}
|
|
||||||
.assign(to: \.value, on: isOffline)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
Publishers.CombineLatest(
|
|
||||||
context.publisherService.$statusPublishers,
|
|
||||||
context.publisherService.statusPublishResult.prepend(.failure(AppError.badRequest))
|
|
||||||
)
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.sink { [weak self] statusPublishers, publishResult in
|
|
||||||
guard let self = self else { return }
|
|
||||||
|
|
||||||
if statusPublishers.isEmpty {
|
|
||||||
self.isPublishingPost.value = false
|
|
||||||
self.isPublished.value = false
|
|
||||||
} else {
|
|
||||||
self.isPublishingPost.value = true
|
|
||||||
switch publishResult {
|
|
||||||
case .success:
|
|
||||||
self.isPublished.value = true
|
|
||||||
case .failure:
|
|
||||||
self.isPublished.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
Publishers.CombineLatest4(
|
|
||||||
hasNewPosts.eraseToAnyPublisher(),
|
|
||||||
isOffline.eraseToAnyPublisher(),
|
|
||||||
isPublishingPost.eraseToAnyPublisher(),
|
|
||||||
isPublished.eraseToAnyPublisher()
|
|
||||||
)
|
|
||||||
.map { hasNewPosts, isOffline, isPublishingPost, isPublished -> State in
|
|
||||||
guard !isPublished else { return .publishedButton }
|
|
||||||
guard !isPublishingPost else { return .publishingPostLabel }
|
|
||||||
guard !isOffline else { return .offlineButton }
|
|
||||||
guard !hasNewPosts else { return .newPostButton }
|
|
||||||
return .logo
|
|
||||||
}
|
|
||||||
.receive(on: DispatchQueue.main)
|
|
||||||
.assign(to: \.value, on: state)
|
|
||||||
.store(in: &disposeBag)
|
|
||||||
|
|
||||||
// state
|
|
||||||
// .removeDuplicates()
|
|
||||||
// .receive(on: DispatchQueue.main)
|
|
||||||
// .sink { [weak self] state in
|
|
||||||
// guard let self = self else { return }
|
|
||||||
// switch state {
|
|
||||||
// case .publishingPostLabel:
|
|
||||||
// self.setupPublishingProgress()
|
|
||||||
// default:
|
|
||||||
// self.suspendPublishingProgress()
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .store(in: &disposeBag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension HomeTimelineNavigationBarTitleViewModel {
|
|
||||||
// state order by priority from low to high
|
|
||||||
enum State: String {
|
|
||||||
case logo
|
|
||||||
case newPostButton
|
|
||||||
case offlineButton
|
|
||||||
case publishingPostLabel
|
|
||||||
case publishedButton
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - New post state
|
|
||||||
extension HomeTimelineNavigationBarTitleViewModel {
|
|
||||||
|
|
||||||
func newPostsIncoming() {
|
|
||||||
hasNewPosts.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
private func resetNewPostState() {
|
|
||||||
hasNewPosts.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Offline state
|
|
||||||
extension HomeTimelineNavigationBarTitleViewModel {
|
|
||||||
|
|
||||||
func resetOfflineCounterListener() {
|
|
||||||
networkErrorCount.value = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func receiveLoadingStateCompletion(_ completion: Subscribers.Completion<Error>) {
|
|
||||||
switch completion {
|
|
||||||
case .failure:
|
|
||||||
networkErrorPublisher.send()
|
|
||||||
case .finished:
|
|
||||||
resetOfflineCounterListener()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleScrollViewDidScroll(_ scrollView: UIScrollView) {
|
|
||||||
guard hasNewPosts.value else { return }
|
|
||||||
|
|
||||||
let contentOffsetY = scrollView.contentOffset.y
|
|
||||||
let isScrollToTop = contentOffsetY < -scrollView.contentInset.top
|
|
||||||
guard isScrollToTop else { return }
|
|
||||||
resetNewPostState()
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Publish post state
|
|
||||||
//extension HomeTimelineNavigationBarTitleViewModel {
|
|
||||||
//
|
|
||||||
// func setupPublishingProgress() {
|
|
||||||
// let progressUpdatePublisher = Timer.publish(every: 0.016, on: .main, in: .common) // ~ 60FPS
|
|
||||||
// .autoconnect()
|
|
||||||
// .share()
|
|
||||||
// .eraseToAnyPublisher()
|
|
||||||
//
|
|
||||||
// publishingProgressSubscription = progressUpdatePublisher
|
|
||||||
// .map { _ in Float(0) }
|
|
||||||
// .scan(0.0) { progress, _ -> Float in
|
|
||||||
// return 0.95 * progress + 0.05 // progress + 0.05 * (1.0 - progress). ~ 1 sec to 0.95 (under 60FPS)
|
|
||||||
// }
|
|
||||||
// .subscribe(publishingProgress)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// func suspendPublishingProgress() {
|
|
||||||
// publishingProgressSubscription = nil
|
|
||||||
// publishingProgress.send(0)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
|
|
Loading…
Reference in New Issue