Add saved/followed hashtags to new sidebar
This commit is contained in:
parent
59d43fd3f6
commit
0d9eed73dd
|
@ -24,6 +24,7 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
private var favoritesTab: UITab!
|
private var favoritesTab: UITab!
|
||||||
private var myProfileTab: UITab!
|
private var myProfileTab: UITab!
|
||||||
private var listsGroup: UITabGroup!
|
private var listsGroup: UITabGroup!
|
||||||
|
private var hashtagsGroup: UITabGroup!
|
||||||
|
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
|
@ -67,6 +68,17 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
]
|
]
|
||||||
reloadLists(mastodonController.lists)
|
reloadLists(mastodonController.lists)
|
||||||
|
|
||||||
|
hashtagsGroup = UITabGroup(title: "Hashtags", image: nil, identifier: Tab.hashtags.rawValue, children: []) { _ in
|
||||||
|
return AdaptableNavigationController()
|
||||||
|
}
|
||||||
|
hashtagsGroup.preferredPlacement = .sidebarOnly
|
||||||
|
hashtagsGroup.sidebarActions = [
|
||||||
|
UIAction(title: "Add Hashtag…", image: UIImage(systemName: "plus"), handler: { [unowned self] _ in
|
||||||
|
self.showAddSavedHashtag()
|
||||||
|
})
|
||||||
|
]
|
||||||
|
reloadHashtags()
|
||||||
|
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone {
|
if UIDevice.current.userInterfaceIdiom == .phone {
|
||||||
self.tabs = [
|
self.tabs = [
|
||||||
homeTab,
|
homeTab,
|
||||||
|
@ -83,13 +95,19 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
let vcToUpdate = self.selectedTab!.parent?.viewController ?? self.selectedTab!.viewController!
|
let vcToUpdate = self.selectedTab!.parent?.viewController ?? self.selectedTab!.viewController!
|
||||||
self.updateViewControllerSafeAreaInsets(vcToUpdate)
|
self.updateViewControllerSafeAreaInsets(vcToUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mastodonController.$lists
|
||||||
|
.sink { [unowned self] in self.reloadLists($0) }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
mastodonController.$followedHashtags
|
||||||
|
.map { _ in () }
|
||||||
|
.merge(with: NotificationCenter.default.publisher(for: .savedHashtagsChanged).map { _ in () })
|
||||||
|
.sink { [unowned self] in self.reloadHashtags() }
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
setupFastAccountSwitcher()
|
setupFastAccountSwitcher()
|
||||||
|
|
||||||
mastodonController.$lists
|
|
||||||
.sink { [unowned self] in self.reloadLists($0) }
|
|
||||||
.store(in: &cancellables)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updatePadTabs() {
|
private func updatePadTabs() {
|
||||||
|
@ -99,8 +117,9 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
isCompact = true
|
isCompact = true
|
||||||
|
|
||||||
var exploreNavStack: [UIViewController]? = nil
|
var exploreNavStack: [UIViewController]? = nil
|
||||||
if selectedTab?.parent == listsGroup {
|
if let parent = selectedTab?.parent,
|
||||||
let nav = listsGroup.viewController as! any NavigationControllerProtocol
|
parent === listsGroup || parent === hashtagsGroup {
|
||||||
|
let nav = parent.viewController as! any NavigationControllerProtocol
|
||||||
exploreNavStack = nav.viewControllers
|
exploreNavStack = nav.viewControllers
|
||||||
nav.viewControllers = []
|
nav.viewControllers = []
|
||||||
}
|
}
|
||||||
|
@ -121,24 +140,33 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
} else {
|
} else {
|
||||||
isCompact = false
|
isCompact = false
|
||||||
|
|
||||||
var newTab: (UITab, [UIViewController])? = nil
|
var newTabAndNavigationStack: (UITab, [UIViewController])? = nil
|
||||||
if wasCompact == true,
|
if wasCompact == true,
|
||||||
selectedTab == exploreTab {
|
selectedTab == exploreTab {
|
||||||
let nav = exploreTab.viewController as! any NavigationControllerProtocol
|
let nav = exploreTab.viewController as! any NavigationControllerProtocol
|
||||||
// skip over the ExploreViewController
|
// skip over the ExploreViewController
|
||||||
if nav.viewControllers.count > 1 {
|
if nav.viewControllers.count > 1 {
|
||||||
|
var newTab: UITab?
|
||||||
switch nav.viewControllers[1] {
|
switch nav.viewControllers[1] {
|
||||||
case let listVC as ListTimelineViewController:
|
case let listVC as ListTimelineViewController:
|
||||||
if let tab = listsGroup.tab(forIdentifier: ListTab.identifier(for: listVC.list)) {
|
if let tab = listsGroup.tab(forIdentifier: ListTab.identifier(for: listVC.list)) {
|
||||||
newTab = (tab, Array(nav.viewControllers[1...]))
|
newTab = tab
|
||||||
nav.viewControllers = [
|
}
|
||||||
nav.viewControllers[0], // leave the ExploreVC in place
|
case let hashtagVC as HashtagTimelineViewController:
|
||||||
InlineTrendsViewController(mastodonController: mastodonController), // re-insert an InlineTrendsVC
|
if let tab = hashtagsGroup.tab(forIdentifier: HashtagTab.identifier(for: hashtagVC.hashtagName)) {
|
||||||
]
|
newTab = tab
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let newTab {
|
||||||
|
newTabAndNavigationStack = (newTab, Array(nav.viewControllers[1...]))
|
||||||
|
nav.viewControllers = [
|
||||||
|
nav.viewControllers[0], // leave the ExploreVC in place
|
||||||
|
InlineTrendsViewController(mastodonController: mastodonController), // re-insert an InlineTrendsVC
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -151,9 +179,10 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
myProfileTab,
|
myProfileTab,
|
||||||
composeTab,
|
composeTab,
|
||||||
listsGroup,
|
listsGroup,
|
||||||
|
hashtagsGroup,
|
||||||
]
|
]
|
||||||
|
|
||||||
if let (tab, navStack) = newTab {
|
if let (tab, navStack) = newTabAndNavigationStack {
|
||||||
let nav = tab.parent!.viewController as! any NavigationControllerProtocol
|
let nav = tab.parent!.viewController as! any NavigationControllerProtocol
|
||||||
nav.viewControllers = navStack
|
nav.viewControllers = navStack
|
||||||
// Setting the tab now seems to be clobbered by the UITabBarController itself updating in response
|
// Setting the tab now seems to be clobbered by the UITabBarController itself updating in response
|
||||||
|
@ -193,7 +222,7 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
root = FavoritesViewController(mastodonController: mastodonController)
|
root = FavoritesViewController(mastodonController: mastodonController)
|
||||||
case .myProfile:
|
case .myProfile:
|
||||||
root = MyProfileViewController(mastodonController: mastodonController)
|
root = MyProfileViewController(mastodonController: mastodonController)
|
||||||
case .lists:
|
case .lists, .hashtags:
|
||||||
fatalError("unreachable")
|
fatalError("unreachable")
|
||||||
}
|
}
|
||||||
return embedInNavigationController(root)
|
return embedInNavigationController(root)
|
||||||
|
@ -217,11 +246,37 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reloadLists(_ lists: [List]) {
|
private func reloadLists(_ lists: [List]) {
|
||||||
listsGroup.children = lists.map { list in
|
let viewControllerProvider = { [unowned self] (tab: UITab) in
|
||||||
ListTab(list: list) { [unowned self] _ in
|
let tab = tab as! ListTab
|
||||||
return ListTimelineViewController(for: list, mastodonController: self.mastodonController)
|
return ListTimelineViewController(for: tab.list, mastodonController: self.mastodonController)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
listsGroup.children = lists.map { list in
|
||||||
|
ListTab(list: list, viewControllerProvider: viewControllerProvider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func reloadHashtags() {
|
||||||
|
let viewControllerProvider = { [unowned self] (tab: UITab) in
|
||||||
|
let tab = tab as! HashtagTab
|
||||||
|
return HashtagTimelineViewController(forNamed: tab.hashtagName, mastodonController: self.mastodonController)
|
||||||
|
}
|
||||||
|
var seenTags: Set<String> = []
|
||||||
|
var tabs: [UITab] = []
|
||||||
|
let savedReq = SavedHashtag.fetchRequest(account: mastodonController.accountInfo!)
|
||||||
|
let saved = (try? mastodonController.persistentContainer.viewContext.fetch(savedReq)) ?? []
|
||||||
|
for hashtag in saved {
|
||||||
|
seenTags.insert(hashtag.name)
|
||||||
|
tabs.append(HashtagTab(hashtagName: hashtag.name, viewControllerProvider: viewControllerProvider))
|
||||||
|
}
|
||||||
|
|
||||||
|
let followedReq = FollowedHashtag.fetchRequest()
|
||||||
|
let followed = (try? mastodonController.persistentContainer.viewContext.fetch(followedReq)) ?? []
|
||||||
|
for hashtag in followed where !seenTags.contains(hashtag.name) {
|
||||||
|
tabs.append(HashtagTab(hashtagName: hashtag.name, viewControllerProvider: viewControllerProvider))
|
||||||
|
}
|
||||||
|
|
||||||
|
tabs.sort(using: SemiCaseSensitiveComparator.keyPath(\.title))
|
||||||
|
hashtagsGroup.children = tabs
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleComposeKeyCommand() {
|
@objc func handleComposeKeyCommand() {
|
||||||
|
@ -244,6 +299,12 @@ final class NewMainTabBarViewController: BaseMainTabBarViewController {
|
||||||
service.run()
|
service.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func showAddSavedHashtag() {
|
||||||
|
let addController = AddSavedHashtagViewController(mastodonController: mastodonController)
|
||||||
|
let nav = EnhancedNavigationViewController(rootViewController: addController)
|
||||||
|
present(nav, animated: true)
|
||||||
|
}
|
||||||
|
|
||||||
fileprivate func updateViewControllerSafeAreaInsets(_ vc: UIViewController) {
|
fileprivate func updateViewControllerSafeAreaInsets(_ vc: UIViewController) {
|
||||||
guard vc is MultiColumnNavigationController || (vc as? AdaptableNavigationController)?.current is MultiColumnNavigationController else {
|
guard vc is MultiColumnNavigationController || (vc as? AdaptableNavigationController)?.current is MultiColumnNavigationController else {
|
||||||
return
|
return
|
||||||
|
@ -317,6 +378,7 @@ extension NewMainTabBarViewController {
|
||||||
case myProfile
|
case myProfile
|
||||||
|
|
||||||
case lists
|
case lists
|
||||||
|
case hashtags
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,7 +418,7 @@ extension NewMainTabBarViewController: UITabBarControllerDelegate {
|
||||||
// get the new transition animation.
|
// get the new transition animation.
|
||||||
// This would be much less complicated if the controller just used the individual VCs of items in a group.
|
// This would be much less complicated if the controller just used the individual VCs of items in a group.
|
||||||
if let group = newTab.parent,
|
if let group = newTab.parent,
|
||||||
group.identifier == Tab.lists.rawValue,
|
group === listsGroup || group === hashtagsGroup,
|
||||||
let nav = group.viewController as? any NavigationControllerProtocol {
|
let nav = group.viewController as? any NavigationControllerProtocol {
|
||||||
updateViewControllerSafeAreaInsets(nav)
|
updateViewControllerSafeAreaInsets(nav)
|
||||||
|
|
||||||
|
@ -430,6 +492,13 @@ extension NewMainTabBarViewController: UITabBarController.Sidebar.Delegate {
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
} else if let hashtagTab = tab as? HashtagTab {
|
||||||
|
let timelineActivity = UserActivityManager.showTimelineActivity(timeline: .tag(hashtag: hashtagTab.hashtagName), accountID: id)
|
||||||
|
if let timelineActivity {
|
||||||
|
activity = timelineActivity
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
} else if let tabID = Tab(rawValue: tab.identifier) {
|
} else if let tabID = Tab(rawValue: tab.identifier) {
|
||||||
switch tabID {
|
switch tabID {
|
||||||
case .home:
|
case .home:
|
||||||
|
@ -448,7 +517,7 @@ extension NewMainTabBarViewController: UITabBarController.Sidebar.Delegate {
|
||||||
return nil
|
return nil
|
||||||
case .compose:
|
case .compose:
|
||||||
activity = UserActivityManager.newPostActivity(accountID: id)
|
activity = UserActivityManager.newPostActivity(accountID: id)
|
||||||
case .lists:
|
case .lists, .hashtags:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -671,3 +740,17 @@ private class ListTab: UITab {
|
||||||
"list:\(list.id)"
|
"list:\(list.id)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@available(iOS 18.0, *)
|
||||||
|
private class HashtagTab: UITab {
|
||||||
|
let hashtagName: String
|
||||||
|
|
||||||
|
init(hashtagName: String, viewControllerProvider: @escaping (UITab) -> UIViewController) {
|
||||||
|
self.hashtagName = hashtagName
|
||||||
|
super.init(title: hashtagName, image: UIImage(systemName: "number"), identifier: Self.identifier(for: hashtagName), viewControllerProvider: viewControllerProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func identifier(for name: String) -> String {
|
||||||
|
"hashtag:\(name)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue