Compare commits
4 Commits
7f4ab57a1d
...
2891f47cb3
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 2891f47cb3 | |
Shadowfacts | 3c80ec8b43 | |
Shadowfacts | 478ba3db28 | |
Shadowfacts | f96cd1b5e2 |
|
@ -354,6 +354,7 @@ class ConversationTableViewController: EnhancedTableViewController {
|
||||||
case let .status(id: id, state: state) = dataSource.itemIdentifier(for: IndexPath(row: indexPath.row - 1, section: indexPath.section)) {
|
case let .status(id: id, state: state) = dataSource.itemIdentifier(for: IndexPath(row: indexPath.row - 1, section: indexPath.section)) {
|
||||||
let conv = ConversationTableViewController(for: id, state: state, mastodonController: mastodonController)
|
let conv = ConversationTableViewController(for: id, state: state, mastodonController: mastodonController)
|
||||||
conv.statusIDToScrollToOnLoad = childThreads.first!.status.id
|
conv.statusIDToScrollToOnLoad = childThreads.first!.status.id
|
||||||
|
conv.showStatusesAutomatically = showStatusesAutomatically
|
||||||
show(conv)
|
show(conv)
|
||||||
} else {
|
} else {
|
||||||
super.tableView(tableView, didSelectRowAt: indexPath)
|
super.tableView(tableView, didSelectRowAt: indexPath)
|
||||||
|
|
|
@ -84,7 +84,7 @@ class AddSavedHashtagViewController: UIViewController {
|
||||||
navigationItem.searchController = searchController
|
navigationItem.searchController = searchController
|
||||||
navigationItem.hidesSearchBarWhenScrolling = false
|
navigationItem.hidesSearchBarWhenScrolling = false
|
||||||
|
|
||||||
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(cancelButtonPressed))
|
navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(doneButtonPressed))
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -107,15 +107,12 @@ class AddSavedHashtagViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func selectHashtag(_ hashtag: Hashtag) {
|
private func selectHashtag(_ hashtag: Hashtag) {
|
||||||
let context = mastodonController.persistentContainer.viewContext
|
show(HashtagTimelineViewController(for: hashtag, mastodonController: mastodonController), sender: nil)
|
||||||
_ = SavedHashtag(hashtag: hashtag, context: context)
|
|
||||||
try! context.save()
|
|
||||||
presentingViewController!.dismiss(animated: true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Interaction
|
// MARK: - Interaction
|
||||||
|
|
||||||
@objc func cancelButtonPressed() {
|
@objc func doneButtonPressed() {
|
||||||
dismiss(animated: true)
|
dismiss(animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,11 +125,6 @@ extension AddSavedHashtagViewController {
|
||||||
enum Item: Hashable {
|
enum Item: Hashable {
|
||||||
case tag(Hashtag)
|
case tag(Hashtag)
|
||||||
}
|
}
|
||||||
// class DataSource: UITableViewDiffableDataSource<Section, Item> {
|
|
||||||
// override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AddSavedHashtagViewController: UICollectionViewDelegate {
|
extension AddSavedHashtagViewController: UICollectionViewDelegate {
|
||||||
|
|
|
@ -24,7 +24,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
|
|
||||||
var searchControllerStatusOnAppearance: Bool? = nil
|
var searchControllerStatusOnAppearance: Bool? = nil
|
||||||
|
|
||||||
private var listsCancellable: AnyCancellable?
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(mastodonController: MastodonController) {
|
init(mastodonController: MastodonController) {
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
|
@ -70,12 +70,26 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
navigationItem.searchController = searchController
|
navigationItem.searchController = searchController
|
||||||
navigationItem.hidesSearchBarWhenScrolling = false
|
navigationItem.hidesSearchBarWhenScrolling = false
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(savedHashtagsChanged), name: .savedHashtagsChanged, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(savedInstancesChanged), name: .savedInstancesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(savedInstancesChanged), name: .savedInstancesChanged, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||||
|
|
||||||
listsCancellable = mastodonController.$lists
|
mastodonController.$lists
|
||||||
.sink { [unowned self] in self.reloadLists($0) }
|
.sink { [unowned self] in self.reloadLists($0) }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
mastodonController.$followedHashtags
|
||||||
|
.merge(with:
|
||||||
|
NotificationCenter.default.publisher(for: .savedHashtagsChanged)
|
||||||
|
.map { [unowned self] _ in self.mastodonController.followedHashtags }
|
||||||
|
)
|
||||||
|
.sink { [unowned self] in self.updateHashtagsSection(followed: $0) }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
|
let a = PassthroughSubject<Int, Never>()
|
||||||
|
let b = PassthroughSubject<Int, Never>()
|
||||||
|
|
||||||
|
a.merge(with: b)
|
||||||
|
.sink(receiveValue: { print($0) })
|
||||||
|
.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -149,9 +163,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
addDiscoverSection(to: &snapshot)
|
addDiscoverSection(to: &snapshot)
|
||||||
}
|
}
|
||||||
snapshot.appendItems([.addList], toSection: .lists)
|
snapshot.appendItems([.addList], toSection: .lists)
|
||||||
let hashtags = fetchSavedHashtags().map {
|
let hashtags = fetchHashtagItems(followed: mastodonController.followedHashtags)
|
||||||
Item.savedHashtag(Hashtag(name: $0.name, url: $0.url))
|
|
||||||
}
|
|
||||||
snapshot.appendItems(hashtags, toSection: .savedHashtags)
|
snapshot.appendItems(hashtags, toSection: .savedHashtags)
|
||||||
snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags)
|
snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags)
|
||||||
let instances = fetchSavedInstances().map {
|
let instances = fetchSavedInstances().map {
|
||||||
|
@ -193,14 +205,16 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func fetchSavedHashtags() -> [SavedHashtag] {
|
private func fetchHashtagItems(followed: [FollowedHashtag]) -> [Item] {
|
||||||
let req = SavedHashtag.fetchRequest()
|
let saved = (try? mastodonController.persistentContainer.viewContext.fetch(SavedHashtag.fetchRequest())) ?? []
|
||||||
req.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.localizedCompare(_:)))]
|
var items = saved.map {
|
||||||
do {
|
Item.savedHashtag(Hashtag(name: $0.name, url: $0.url))
|
||||||
return try mastodonController.persistentContainer.viewContext.fetch(req)
|
|
||||||
} catch {
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
for followed in followed where !saved.contains(where: { $0.name == followed.name }) {
|
||||||
|
items.append(.savedHashtag(Hashtag(name: followed.name, url: followed.url)))
|
||||||
|
}
|
||||||
|
items.sort(using: SemiCaseSensitiveComparator.keyPath(\.label))
|
||||||
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
@ -214,12 +228,10 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func savedHashtagsChanged() {
|
private func updateHashtagsSection(followed: [FollowedHashtag]) {
|
||||||
var snapshot = dataSource.snapshot()
|
var snapshot = dataSource.snapshot()
|
||||||
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedHashtags))
|
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .savedHashtags))
|
||||||
let hashtags = fetchSavedHashtags().map {
|
let hashtags = fetchHashtagItems(followed: followed)
|
||||||
Item.savedHashtag(Hashtag(name: $0.name, url: $0.url))
|
|
||||||
}
|
|
||||||
snapshot.appendItems(hashtags, toSection: .savedHashtags)
|
snapshot.appendItems(hashtags, toSection: .savedHashtags)
|
||||||
snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags)
|
snapshot.appendItems([.addSavedHashtag], toSection: .savedHashtags)
|
||||||
dataSource.apply(snapshot)
|
dataSource.apply(snapshot)
|
||||||
|
@ -386,7 +398,7 @@ extension ExploreViewController {
|
||||||
case .lists:
|
case .lists:
|
||||||
return NSLocalizedString("Lists", comment: "explore lists section title")
|
return NSLocalizedString("Lists", comment: "explore lists section title")
|
||||||
case .savedHashtags:
|
case .savedHashtags:
|
||||||
return NSLocalizedString("Saved Hashtags", comment: "explore saved hashtags section title")
|
return NSLocalizedString("Hashtags", comment: "explore saved hashtags section title")
|
||||||
case .savedInstances:
|
case .savedInstances:
|
||||||
return NSLocalizedString("Instance Timelines", comment: "explore instance timelines section title")
|
return NSLocalizedString("Instance Timelines", comment: "explore instance timelines section title")
|
||||||
}
|
}
|
||||||
|
@ -425,7 +437,7 @@ extension ExploreViewController {
|
||||||
case let .savedHashtag(hashtag):
|
case let .savedHashtag(hashtag):
|
||||||
return hashtag.name
|
return hashtag.name
|
||||||
case .addSavedHashtag:
|
case .addSavedHashtag:
|
||||||
return NSLocalizedString("Save Hashtag...", comment: "save hashtag nav item title")
|
return NSLocalizedString("Add Hashtag...", comment: "save hashtag nav item title")
|
||||||
case let .savedInstance(url):
|
case let .savedInstance(url):
|
||||||
return url.host!
|
return url.host!
|
||||||
case .findInstance:
|
case .findInstance:
|
||||||
|
|
|
@ -30,7 +30,7 @@ class MainSidebarViewController: UIViewController {
|
||||||
private var collectionView: UICollectionView!
|
private var collectionView: UICollectionView!
|
||||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||||
|
|
||||||
private var listsCancellable: AnyCancellable?
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
var allItems: [Item] {
|
var allItems: [Item] {
|
||||||
[
|
[
|
||||||
|
@ -101,12 +101,19 @@ class MainSidebarViewController: UIViewController {
|
||||||
|
|
||||||
select(item: .tab(.timelines), animated: false)
|
select(item: .tab(.timelines), animated: false)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedHashtags), name: .savedHashtagsChanged, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedInstances), name: .savedInstancesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedInstances), name: .savedInstancesChanged, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||||
|
|
||||||
listsCancellable = mastodonController.$lists
|
mastodonController.$lists
|
||||||
.sink { [unowned self] in self.reloadLists($0) }
|
.sink { [unowned self] in self.reloadLists($0) }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
mastodonController.$followedHashtags
|
||||||
|
.merge(with:
|
||||||
|
NotificationCenter.default.publisher(for: .savedHashtagsChanged)
|
||||||
|
.map { [unowned self] _ in self.mastodonController.followedHashtags }
|
||||||
|
)
|
||||||
|
.sink { [unowned self] in self.updateHashtagsSection(followed: $0) }
|
||||||
|
.store(in: &cancellables)
|
||||||
|
|
||||||
onViewDidLoad?()
|
onViewDidLoad?()
|
||||||
}
|
}
|
||||||
|
@ -176,7 +183,7 @@ class MainSidebarViewController: UIViewController {
|
||||||
|
|
||||||
applyDiscoverSectionSnapshot()
|
applyDiscoverSectionSnapshot()
|
||||||
reloadLists(mastodonController.lists)
|
reloadLists(mastodonController.lists)
|
||||||
reloadSavedHashtags()
|
updateHashtagsSection(followed: mastodonController.followedHashtags)
|
||||||
reloadSavedInstances()
|
reloadSavedInstances()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,14 +231,16 @@ class MainSidebarViewController: UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func fetchSavedHashtags() -> [SavedHashtag] {
|
private func fetchHashtagItems(followed: [FollowedHashtag]) -> [Item] {
|
||||||
let req = SavedHashtag.fetchRequest()
|
let saved = (try? mastodonController.persistentContainer.viewContext.fetch(SavedHashtag.fetchRequest())) ?? []
|
||||||
req.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true, selector: #selector(NSString.localizedCompare(_:)))]
|
var items = saved.map {
|
||||||
do {
|
Item.savedHashtag(Hashtag(name: $0.name, url: $0.url))
|
||||||
return try mastodonController.persistentContainer.viewContext.fetch(req)
|
|
||||||
} catch {
|
|
||||||
return []
|
|
||||||
}
|
}
|
||||||
|
for followed in followed where !saved.contains(where: { $0.name == followed.name }) {
|
||||||
|
items.append(.savedHashtag(Hashtag(name: followed.name, url: followed.url)))
|
||||||
|
}
|
||||||
|
items.sort(using: SemiCaseSensitiveComparator.keyPath(\.title))
|
||||||
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
|
@ -245,10 +254,8 @@ class MainSidebarViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc private func reloadSavedHashtags() {
|
private func updateHashtagsSection(followed: [FollowedHashtag]) {
|
||||||
let hashtags = fetchSavedHashtags().map {
|
let hashtags = fetchHashtagItems(followed: followed)
|
||||||
Item.savedHashtag(Hashtag(name: $0.name, url: $0.url))
|
|
||||||
}
|
|
||||||
if let selectedItem,
|
if let selectedItem,
|
||||||
case .savedHashtag(_) = selectedItem,
|
case .savedHashtag(_) = selectedItem,
|
||||||
!hashtags.contains(selectedItem) {
|
!hashtags.contains(selectedItem) {
|
||||||
|
@ -403,13 +410,13 @@ extension MainSidebarViewController {
|
||||||
case .addList:
|
case .addList:
|
||||||
return "New List..."
|
return "New List..."
|
||||||
case .savedHashtagsHeader:
|
case .savedHashtagsHeader:
|
||||||
return "Saved Hashtags"
|
return "Hashtags"
|
||||||
case let .savedHashtag(hashtag):
|
case let .savedHashtag(hashtag):
|
||||||
return hashtag.name
|
return hashtag.name
|
||||||
case .addSavedHashtag:
|
case .addSavedHashtag:
|
||||||
return "Save Hashtag..."
|
return "Add Hashtag..."
|
||||||
case .savedInstancesHeader:
|
case .savedInstancesHeader:
|
||||||
return "Saved Instances"
|
return "Instance Timelines"
|
||||||
case let .savedInstance(url):
|
case let .savedInstance(url):
|
||||||
return url.host!
|
return url.host!
|
||||||
case .addSavedInstance:
|
case .addSavedInstance:
|
||||||
|
|
|
@ -83,7 +83,8 @@ class TimelinesPageViewController: SegmentedPageViewController<TimelinesPageView
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
selectPage(page, animated: false)
|
selectPage(page, animated: false)
|
||||||
let timelineVC = pageControllers[currentIndex] as! TimelineViewController
|
// can't use currentIndex here because the view isn't loaded yet, and so the page wasn't actually updated by the selectPage call
|
||||||
|
let timelineVC = pageControllers[pages.firstIndex(of: page)!] as! TimelineViewController
|
||||||
timelineVC.restoreActivity(activity)
|
timelineVC.restoreActivity(activity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ class SegmentedPageViewController<Page: Hashable>: UIPageViewController, UIPageV
|
||||||
self.selectPage(option, animated: true)
|
self.selectPage(option, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO: double check this with the custom segmented control
|
// TODO: the custom segmented control isn't treated as a group and I have no idea how to change that
|
||||||
// the segemented control itself is only focusable when VoiceOver is in Group navigation mode,
|
// the segemented control itself is only focusable when VoiceOver is in Group navigation mode,
|
||||||
// so make it clear that to switch tabs the user needs to enter the group
|
// so make it clear that to switch tabs the user needs to enter the group
|
||||||
segmentedControl.accessibilityHint = "Enter group to select timeline"
|
segmentedControl.accessibilityHint = "Enter group to select timeline"
|
||||||
|
|
Loading…
Reference in New Issue