Compare commits
No commits in common. "810ae718323e5266e9d9311026752a0f89dabeeb" and "c8319d8af230b853fb7891026e2784fd2d3df9d6" have entirely different histories.
810ae71832
...
c8319d8af2
|
@ -17,12 +17,11 @@ public class List: Decodable, Equatable, Hashable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: List, rhs: List) -> Bool {
|
public static func ==(lhs: List, rhs: List) -> Bool {
|
||||||
return lhs.id == rhs.id && lhs.title == rhs.title
|
return lhs.id == rhs.id
|
||||||
}
|
}
|
||||||
|
|
||||||
public func hash(into hasher: inout Hasher) {
|
public func hash(into hasher: inout Hasher) {
|
||||||
hasher.combine(id)
|
hasher.combine(id)
|
||||||
hasher.combine(title)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func getAccounts(_ list: List, range: RequestRange = .default) -> Request<[Account]> {
|
public static func getAccounts(_ list: List, range: RequestRange = .default) -> Request<[Account]> {
|
||||||
|
|
|
@ -1616,7 +1616,7 @@
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
D6D4DDCB212518A000E1C4BB = {
|
D6D4DDCB212518A000E1C4BB = {
|
||||||
CreatedOnToolsVersion = 10.0;
|
CreatedOnToolsVersion = 10.0;
|
||||||
LastSwiftMigration = 1410;
|
LastSwiftMigration = 1200;
|
||||||
};
|
};
|
||||||
D6D4DDDF212518A200E1C4BB = {
|
D6D4DDDF212518A200E1C4BB = {
|
||||||
CreatedOnToolsVersion = 10.0;
|
CreatedOnToolsVersion = 10.0;
|
||||||
|
|
|
@ -49,7 +49,7 @@ class CreateListService {
|
||||||
do {
|
do {
|
||||||
let request = Client.createList(title: title)
|
let request = Client.createList(title: title)
|
||||||
let (list, _) = try await mastodonController.run(request)
|
let (list, _) = try await mastodonController.run(request)
|
||||||
mastodonController.addedList(list)
|
NotificationCenter.default.post(name: .listsChanged, object: nil)
|
||||||
self.didCreateList?(list)
|
self.didCreateList?(list)
|
||||||
} catch {
|
} catch {
|
||||||
let alert = UIAlertController(title: "Error Creating List", message: error.localizedDescription, preferredStyle: .alert)
|
let alert = UIAlertController(title: "Error Creating List", message: error.localizedDescription, preferredStyle: .alert)
|
||||||
|
@ -64,3 +64,7 @@ class CreateListService {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Foundation.Notification.Name {
|
||||||
|
static let listsChanged = Notification.Name("listsChanged")
|
||||||
|
}
|
||||||
|
|
|
@ -50,7 +50,7 @@ class DeleteListService {
|
||||||
do {
|
do {
|
||||||
let request = List.delete(list)
|
let request = List.delete(list)
|
||||||
_ = try await mastodonController.run(request)
|
_ = try await mastodonController.run(request)
|
||||||
mastodonController.deletedList(list)
|
NotificationCenter.default.post(name: .listsChanged, object: nil)
|
||||||
} catch {
|
} catch {
|
||||||
let alert = UIAlertController(title: "Error Deleting List", message: error.localizedDescription, preferredStyle: .alert)
|
let alert = UIAlertController(title: "Error Deleting List", message: error.localizedDescription, preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||||
|
|
|
@ -46,7 +46,6 @@ class MastodonController: ObservableObject {
|
||||||
@Published private(set) var instance: Instance!
|
@Published private(set) var instance: Instance!
|
||||||
@Published private(set) var nodeInfo: NodeInfo!
|
@Published private(set) var nodeInfo: NodeInfo!
|
||||||
@Published private(set) var instanceFeatures = InstanceFeatures()
|
@Published private(set) var instanceFeatures = InstanceFeatures()
|
||||||
@Published private(set) var lists: [List] = []
|
|
||||||
private(set) var customEmojis: [Emoji]?
|
private(set) var customEmojis: [Emoji]?
|
||||||
|
|
||||||
private var pendingOwnInstanceRequestCallbacks = [(Result<Instance, Client.Error>) -> Void]()
|
private var pendingOwnInstanceRequestCallbacks = [(Result<Instance, Client.Error>) -> Void]()
|
||||||
|
@ -120,15 +119,6 @@ class MastodonController: ObservableObject {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func initialize() async throws {
|
|
||||||
async let ownAccount = try getOwnAccount()
|
|
||||||
async let ownInstance = try getOwnInstance()
|
|
||||||
|
|
||||||
_ = try await (ownAccount, ownInstance)
|
|
||||||
|
|
||||||
loadLists()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOwnAccount(completion: ((Result<Account, Client.Error>) -> Void)? = nil) {
|
func getOwnAccount(completion: ((Result<Account, Client.Error>) -> Void)? = nil) {
|
||||||
if account != nil {
|
if account != nil {
|
||||||
completion?(.success(account))
|
completion?(.success(account))
|
||||||
|
@ -274,53 +264,4 @@ class MastodonController: ObservableObject {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func loadLists() {
|
|
||||||
let req = Client.getLists()
|
|
||||||
run(req) { response in
|
|
||||||
if case .success(let lists, _) = response {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.lists = lists.sorted(using: ListComparator())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
func addedList(_ list: List) {
|
|
||||||
var new = self.lists
|
|
||||||
new.append(list)
|
|
||||||
new.sort { $0.title < $1.title }
|
|
||||||
self.lists = new
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
func deletedList(_ list: List) {
|
|
||||||
self.lists.removeAll(where: { $0.id == list.id })
|
|
||||||
}
|
|
||||||
|
|
||||||
@MainActor
|
|
||||||
func renamedList(_ list: List) {
|
|
||||||
var new = self.lists
|
|
||||||
if let index = new.firstIndex(where: { $0.id == list.id }) {
|
|
||||||
new[index] = list
|
|
||||||
}
|
|
||||||
new.sort(using: ListComparator())
|
|
||||||
self.lists = new
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private struct ListComparator: SortComparator {
|
|
||||||
typealias Compared = List
|
|
||||||
|
|
||||||
var underlying = String.Comparator(options: .caseInsensitive)
|
|
||||||
|
|
||||||
var order: SortOrder {
|
|
||||||
get { underlying.order }
|
|
||||||
set { underlying.order = newValue }
|
|
||||||
}
|
|
||||||
|
|
||||||
func compare(_ lhs: List, _ rhs: List) -> ComparisonResult {
|
|
||||||
return underlying.compare(lhs.title, rhs.title)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ class RenameListService {
|
||||||
do {
|
do {
|
||||||
let req = List.update(list, title: title)
|
let req = List.update(list, title: title)
|
||||||
let (list, _) = try await mastodonController.run(req)
|
let (list, _) = try await mastodonController.run(req)
|
||||||
mastodonController.renamedList(list)
|
NotificationCenter.default.post(name: .listRenamed, object: list.id, userInfo: ["list": list])
|
||||||
} catch {
|
} catch {
|
||||||
let alert = UIAlertController(title: "Error Updating List", message: error.localizedDescription, preferredStyle: .alert)
|
let alert = UIAlertController(title: "Error Updating List", message: error.localizedDescription, preferredStyle: .alert)
|
||||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||||
|
@ -63,3 +63,7 @@ class RenameListService {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Foundation.Notification.Name {
|
||||||
|
static let listRenamed = Notification.Name("listRenamed")
|
||||||
|
}
|
||||||
|
|
|
@ -42,9 +42,9 @@ class AuxiliarySceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
|
|
||||||
let controller = MastodonController.getForAccount(account)
|
let controller = MastodonController.getForAccount(account)
|
||||||
session.mastodonController = controller
|
session.mastodonController = controller
|
||||||
Task {
|
|
||||||
try? await controller.initialize()
|
controller.getOwnAccount()
|
||||||
}
|
controller.getOwnInstance()
|
||||||
|
|
||||||
guard let rootVC = viewController(for: activity, mastodonController: controller) else {
|
guard let rootVC = viewController(for: activity, mastodonController: controller) else {
|
||||||
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
|
||||||
|
|
|
@ -50,9 +50,8 @@ class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate {
|
||||||
}
|
}
|
||||||
|
|
||||||
session.mastodonController = controller
|
session.mastodonController = controller
|
||||||
Task {
|
controller.getOwnAccount()
|
||||||
try? await controller.initialize()
|
controller.getOwnInstance()
|
||||||
}
|
|
||||||
|
|
||||||
let composeVC = ComposeHostingController(draft: draft, mastodonController: controller)
|
let composeVC = ComposeHostingController(draft: draft, mastodonController: controller)
|
||||||
composeVC.delegate = self
|
composeVC.delegate = self
|
||||||
|
|
|
@ -203,9 +203,8 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
|
||||||
|
|
||||||
func createAppUI() -> TuskerRootViewController {
|
func createAppUI() -> TuskerRootViewController {
|
||||||
let mastodonController = window!.windowScene!.session.mastodonController!
|
let mastodonController = window!.windowScene!.session.mastodonController!
|
||||||
Task {
|
mastodonController.getOwnAccount()
|
||||||
try? await mastodonController.initialize()
|
mastodonController.getOwnInstance()
|
||||||
}
|
|
||||||
|
|
||||||
let split = MainSplitViewController(mastodonController: mastodonController)
|
let split = MainSplitViewController(mastodonController: mastodonController)
|
||||||
if UIDevice.current.userInterfaceIdiom == .phone,
|
if UIDevice.current.userInterfaceIdiom == .phone,
|
||||||
|
|
|
@ -51,20 +51,9 @@ struct ComposePollView: View {
|
||||||
.hoverEffect()
|
.hoverEffect()
|
||||||
}
|
}
|
||||||
|
|
||||||
List {
|
|
||||||
ForEach(Array(poll.options.enumerated()), id: \.element.id) { (e) in
|
ForEach(Array(poll.options.enumerated()), id: \.element.id) { (e) in
|
||||||
ComposePollOption(poll: poll, option: e.element, optionIndex: e.offset)
|
ComposePollOption(poll: poll, option: e.element, optionIndex: e.offset)
|
||||||
.frame(height: 36)
|
|
||||||
.listRowInsets(EdgeInsets(top: 4, leading: 0, bottom: 4, trailing: 0))
|
|
||||||
.listRowSeparator(.hidden)
|
|
||||||
.listRowBackground(Color.clear)
|
|
||||||
}
|
}
|
||||||
.onMove { indices, newIndex in
|
|
||||||
poll.options.move(fromOffsets: indices, toOffset: newIndex)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.listStyle(.plain)
|
|
||||||
.frame(height: 44 * CGFloat(poll.options.count))
|
|
||||||
|
|
||||||
Button(action: self.addOption) {
|
Button(action: self.addOption) {
|
||||||
Label("Add Option", systemImage: "plus")
|
Label("Add Option", systemImage: "plus")
|
||||||
|
|
|
@ -49,7 +49,6 @@ struct ComposeView: View {
|
||||||
@State private var globalFrameOutsideList: CGRect = .zero
|
@State private var globalFrameOutsideList: CGRect = .zero
|
||||||
@State private var contentWarningBecomeFirstResponder = false
|
@State private var contentWarningBecomeFirstResponder = false
|
||||||
@State private var mainComposeTextViewBecomeFirstResponder = false
|
@State private var mainComposeTextViewBecomeFirstResponder = false
|
||||||
@StateObject private var keyboardReader = KeyboardReader()
|
|
||||||
|
|
||||||
@OptionalStateObject private var poster: PostService?
|
@OptionalStateObject private var poster: PostService?
|
||||||
@State private var isShowingPostErrorAlert = false
|
@State private var isShowingPostErrorAlert = false
|
||||||
|
@ -108,8 +107,6 @@ struct ComposeView: View {
|
||||||
|
|
||||||
ComposeToolbar(draft: draft)
|
ComposeToolbar(draft: draft)
|
||||||
}
|
}
|
||||||
// on iPadOS15, the toolbar ends up below the keyboard's toolbar without this
|
|
||||||
.padding(.bottom, keyboardInset)
|
|
||||||
.transition(.move(edge: .bottom))
|
.transition(.move(edge: .bottom))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,17 +135,6 @@ struct ComposeView: View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS, obsoleted: 16.0)
|
|
||||||
private var keyboardInset: CGFloat {
|
|
||||||
if #unavailable(iOS 16.0),
|
|
||||||
UIDevice.current.userInterfaceIdiom == .pad,
|
|
||||||
keyboardReader.isVisible {
|
|
||||||
return 44
|
|
||||||
} else {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewBuilder
|
@ViewBuilder
|
||||||
private var autocompleteSuggestions: some View {
|
private var autocompleteSuggestions: some View {
|
||||||
if let state = uiState.autocompleteState {
|
if let state = uiState.autocompleteState {
|
||||||
|
@ -330,26 +316,6 @@ private struct GlobalFrameOutsideListPrefKey: PreferenceKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS, obsoleted: 16.0)
|
|
||||||
private class KeyboardReader: ObservableObject {
|
|
||||||
@Published var isVisible = false
|
|
||||||
|
|
||||||
init() {
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(willShow), name: UIResponder.keyboardWillShowNotification, object: nil)
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(willHide), name: UIResponder.keyboardWillHideNotification, object: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func willShow(_ notification: Foundation.Notification) {
|
|
||||||
// when a hardware keyboard is connected, the height is very short, so we don't consider that being "visible"
|
|
||||||
let endFrame = notification.userInfo![UIResponder.keyboardFrameEndUserInfoKey] as! CGRect
|
|
||||||
isVisible = endFrame.height > 72
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func willHide() {
|
|
||||||
isVisible = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//struct ComposeView_Previews: PreviewProvider {
|
//struct ComposeView_Previews: PreviewProvider {
|
||||||
// static var previews: some View {
|
// static var previews: some View {
|
||||||
// ComposeView()
|
// ComposeView()
|
||||||
|
|
|
@ -24,8 +24,6 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
|
|
||||||
var searchControllerStatusOnAppearance: Bool? = nil
|
var searchControllerStatusOnAppearance: Bool? = nil
|
||||||
|
|
||||||
private var listsCancellable: AnyCancellable?
|
|
||||||
|
|
||||||
init(mastodonController: MastodonController) {
|
init(mastodonController: MastodonController) {
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
|
|
||||||
|
@ -72,10 +70,9 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(savedHashtagsChanged), name: .savedHashtagsChanged, object: nil)
|
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(reloadLists), name: .listsChanged, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, 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
|
|
||||||
.sink { [unowned self] in self.reloadLists($0) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillAppear(_ animated: Bool) {
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
@ -161,7 +158,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
snapshot.appendItems([.findInstance], toSection: .savedInstances)
|
snapshot.appendItems([.findInstance], toSection: .savedInstances)
|
||||||
dataSource.apply(snapshot, animatingDifferences: false)
|
dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
|
||||||
reloadLists(mastodonController.lists)
|
reloadLists()
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addDiscoverSection(to snapshot: inout NSDiffableDataSourceSnapshot<Section, Item>) {
|
private func addDiscoverSection(to snapshot: inout NSDiffableDataSourceSnapshot<Section, Item>) {
|
||||||
|
@ -183,14 +180,40 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
|
||||||
self.dataSource.apply(snapshot)
|
self.dataSource.apply(snapshot)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reloadLists(_ lists: [List]) {
|
@objc private func reloadLists() {
|
||||||
|
let request = Client.getLists()
|
||||||
|
mastodonController.run(request) { (response) in
|
||||||
|
guard case let .success(lists, _) = response else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
var snapshot = self.dataSource.snapshot()
|
var snapshot = self.dataSource.snapshot()
|
||||||
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .lists))
|
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .lists))
|
||||||
snapshot.appendItems(lists.map { .list($0) }, toSection: .lists)
|
snapshot.appendItems(lists.map { .list($0) }, toSection: .lists)
|
||||||
snapshot.appendItems([.addList], toSection: .lists)
|
snapshot.appendItems([.addList], toSection: .lists)
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
self.dataSource.apply(snapshot)
|
self.dataSource.apply(snapshot)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||||
|
let list = notification.userInfo!["list"] as! List
|
||||||
|
var snapshot = dataSource.snapshot()
|
||||||
|
let existing = snapshot.itemIdentifiers(inSection: .lists).first(where: {
|
||||||
|
if case .list(let existingList) = $0, existingList.id == list.id {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
if let existing {
|
||||||
|
snapshot.insertItems([.list(list)], afterItem: existing)
|
||||||
|
snapshot.deleteItems([existing])
|
||||||
|
dataSource.apply(snapshot)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func fetchSavedHashtags() -> [SavedHashtag] {
|
private func fetchSavedHashtags() -> [SavedHashtag] {
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import Combine
|
|
||||||
|
|
||||||
class EditListAccountsViewController: EnhancedTableViewController {
|
class EditListAccountsViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
|
@ -23,8 +22,6 @@ class EditListAccountsViewController: EnhancedTableViewController {
|
||||||
var searchResultsController: EditListSearchResultsContainerViewController!
|
var searchResultsController: EditListSearchResultsContainerViewController!
|
||||||
var searchController: UISearchController!
|
var searchController: UISearchController!
|
||||||
|
|
||||||
private var listRenamedCancellable: AnyCancellable?
|
|
||||||
|
|
||||||
init(list: List, mastodonController: MastodonController) {
|
init(list: List, mastodonController: MastodonController) {
|
||||||
self.list = list
|
self.list = list
|
||||||
self.mastodonController = mastodonController
|
self.mastodonController = mastodonController
|
||||||
|
@ -33,13 +30,7 @@ class EditListAccountsViewController: EnhancedTableViewController {
|
||||||
|
|
||||||
listChanged()
|
listChanged()
|
||||||
|
|
||||||
listRenamedCancellable = mastodonController.$lists
|
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, object: list.id)
|
||||||
.compactMap { $0.first { $0.id == list.id } }
|
|
||||||
.removeDuplicates(by: { $0.title == $1.title })
|
|
||||||
.sink { [unowned self] in
|
|
||||||
self.list = $0
|
|
||||||
self.listChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
|
@ -97,6 +88,12 @@ class EditListAccountsViewController: EnhancedTableViewController {
|
||||||
title = String(format: NSLocalizedString("Edit %@", comment: "edit list screen title"), list.title)
|
title = String(format: NSLocalizedString("Edit %@", comment: "edit list screen title"), list.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||||
|
let list = notification.userInfo!["list"] as! List
|
||||||
|
self.list = list
|
||||||
|
self.listChanged()
|
||||||
|
}
|
||||||
|
|
||||||
func loadAccounts() async {
|
func loadAccounts() async {
|
||||||
do {
|
do {
|
||||||
let request = List.getAccounts(list)
|
let request = List.getAccounts(list)
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import Combine
|
|
||||||
|
|
||||||
class ListTimelineViewController: TimelineViewController {
|
class ListTimelineViewController: TimelineViewController {
|
||||||
|
|
||||||
|
@ -16,8 +15,6 @@ class ListTimelineViewController: TimelineViewController {
|
||||||
|
|
||||||
var presentEditOnAppear = false
|
var presentEditOnAppear = false
|
||||||
|
|
||||||
private var listRenamedCancellable: AnyCancellable?
|
|
||||||
|
|
||||||
init(for list: List, mastodonController: MastodonController) {
|
init(for list: List, mastodonController: MastodonController) {
|
||||||
self.list = list
|
self.list = list
|
||||||
|
|
||||||
|
@ -25,13 +22,7 @@ class ListTimelineViewController: TimelineViewController {
|
||||||
|
|
||||||
listChanged()
|
listChanged()
|
||||||
|
|
||||||
listRenamedCancellable = mastodonController.$lists
|
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, object: list.id)
|
||||||
.compactMap { $0.first { $0.id == list.id } }
|
|
||||||
.removeDuplicates(by: { $0.title == $1.title })
|
|
||||||
.sink { [unowned self] in
|
|
||||||
self.list = $0
|
|
||||||
self.listChanged()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
|
@ -49,7 +40,6 @@ class ListTimelineViewController: TimelineViewController {
|
||||||
|
|
||||||
if presentEditOnAppear {
|
if presentEditOnAppear {
|
||||||
presentEdit(animated: animated)
|
presentEdit(animated: animated)
|
||||||
presentEditOnAppear = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,6 +47,12 @@ class ListTimelineViewController: TimelineViewController {
|
||||||
title = list.title
|
title = list.title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||||
|
let list = notification.userInfo!["list"] as! List
|
||||||
|
self.list = list
|
||||||
|
self.listChanged()
|
||||||
|
}
|
||||||
|
|
||||||
func presentEdit(animated: Bool) {
|
func presentEdit(animated: Bool) {
|
||||||
let editListAccountsController = EditListAccountsViewController(list: list, mastodonController: mastodonController)
|
let editListAccountsController = EditListAccountsViewController(list: list, mastodonController: mastodonController)
|
||||||
editListAccountsController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(editListDoneButtonPressed))
|
editListAccountsController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(editListDoneButtonPressed))
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import Pachyderm
|
import Pachyderm
|
||||||
import Combine
|
|
||||||
|
|
||||||
protocol MainSidebarViewControllerDelegate: AnyObject {
|
protocol MainSidebarViewControllerDelegate: AnyObject {
|
||||||
func sidebarRequestPresentCompose(_ sidebarViewController: MainSidebarViewController)
|
func sidebarRequestPresentCompose(_ sidebarViewController: MainSidebarViewController)
|
||||||
|
@ -29,8 +28,6 @@ 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?
|
|
||||||
|
|
||||||
var allItems: [Item] {
|
var allItems: [Item] {
|
||||||
[
|
[
|
||||||
.tab(.timelines),
|
.tab(.timelines),
|
||||||
|
@ -102,11 +99,10 @@ class MainSidebarViewController: UIViewController {
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(reloadSavedHashtags), name: .savedHashtagsChanged, object: nil)
|
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(reloadLists), name: .listsChanged, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, 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
|
|
||||||
.sink { [unowned self] in self.reloadLists($0) }
|
|
||||||
|
|
||||||
onViewDidLoad?()
|
onViewDidLoad?()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,7 +170,7 @@ class MainSidebarViewController: UIViewController {
|
||||||
dataSource.apply(snapshot, animatingDifferences: false)
|
dataSource.apply(snapshot, animatingDifferences: false)
|
||||||
|
|
||||||
applyDiscoverSectionSnapshot()
|
applyDiscoverSectionSnapshot()
|
||||||
reloadLists(mastodonController.lists)
|
reloadLists()
|
||||||
reloadSavedHashtags()
|
reloadSavedHashtags()
|
||||||
reloadSavedInstances()
|
reloadSavedInstances()
|
||||||
}
|
}
|
||||||
|
@ -207,28 +203,42 @@ class MainSidebarViewController: UIViewController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reloadLists(_ lists: [List]) {
|
@objc private func reloadLists() {
|
||||||
|
let request = Client.getLists()
|
||||||
|
mastodonController.run(request) { [weak self] (response) in
|
||||||
|
guard let self = self, case let .success(lists, _) = response else { return }
|
||||||
|
|
||||||
var exploreSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
|
var exploreSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
|
||||||
exploreSnapshot.append([.listsHeader])
|
exploreSnapshot.append([.listsHeader])
|
||||||
exploreSnapshot.expand([.listsHeader])
|
exploreSnapshot.expand([.listsHeader])
|
||||||
exploreSnapshot.append(lists.map { .list($0) }, to: .listsHeader)
|
exploreSnapshot.append(lists.map { .list($0) }, to: .listsHeader)
|
||||||
exploreSnapshot.append([.addList], to: .listsHeader)
|
exploreSnapshot.append([.addList], to: .listsHeader)
|
||||||
var selectedItem: Item?
|
DispatchQueue.main.async {
|
||||||
if let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first,
|
let selected = self.collectionView.indexPathsForSelectedItems?.first
|
||||||
let item = dataSource.itemIdentifier(for: selectedIndexPath) {
|
|
||||||
if case .list(let list) = item,
|
self.dataSource.apply(exploreSnapshot, to: .lists) {
|
||||||
let newList = lists.first(where: { $0.id == list.id }) {
|
if let selected = selected {
|
||||||
selectedItem = .list(newList)
|
self.collectionView.selectItem(at: selected, animated: false, scrollPosition: .centeredVertically)
|
||||||
} else {
|
}
|
||||||
selectedItem = item
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.dataSource.apply(exploreSnapshot, to: .lists) {
|
@objc private func listRenamed(_ notification: Foundation.Notification) {
|
||||||
if let selectedItem,
|
let list = notification.userInfo!["list"] as! List
|
||||||
let indexPath = self.dataSource.indexPath(for: selectedItem) {
|
var snapshot = dataSource.snapshot()
|
||||||
self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)
|
let existing = snapshot.itemIdentifiers(inSection: .lists).first(where: {
|
||||||
|
if case .list(let existingList) = $0, existingList.id == list.id {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
if let existing {
|
||||||
|
snapshot.insertItems([.list(list)], afterItem: existing)
|
||||||
|
snapshot.deleteItems([existing])
|
||||||
|
dataSource.apply(snapshot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,13 +61,6 @@ class ProfileHeaderCollectionViewCell: UICollectionViewCell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// overrides an internal method
|
|
||||||
// when the super impl is used, preferredLayoutAttributesFitting(_:) isn't called while the view is offscreen (i.e., window == nil)
|
|
||||||
// and so the collection view imposes a height of 44pts which breaks the layout
|
|
||||||
@objc func _preferredLayoutAttributesFittingAttributes(_ attributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes {
|
|
||||||
return preferredLayoutAttributesFitting(attributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
enum State {
|
enum State {
|
||||||
case unloaded
|
case unloaded
|
||||||
case placeholder(heightConstraint: NSLayoutConstraint)
|
case placeholder(heightConstraint: NSLayoutConstraint)
|
||||||
|
|
|
@ -66,22 +66,8 @@ extension MenuActionProvider {
|
||||||
]
|
]
|
||||||
var suppressSection: [UIMenuElement] = []
|
var suppressSection: [UIMenuElement] = []
|
||||||
|
|
||||||
if let ownAccount = mastodonController.account,
|
if accountID != loggedInAccountID {
|
||||||
accountID != ownAccount.id {
|
|
||||||
actionsSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.followAction(for: $0, mastodonController: $1) }))
|
actionsSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.followAction(for: $0, mastodonController: $1) }))
|
||||||
actionsSection.append(UIDeferredMenuElement.uncached({ elementHandler in
|
|
||||||
let listActions = mastodonController.lists.map { list in
|
|
||||||
UIAction(title: list.title, image: UIImage(systemName: "plus")) { [unowned self] _ in
|
|
||||||
let req = List.add(list, accounts: [accountID])
|
|
||||||
mastodonController.run(req) { response in
|
|
||||||
if case .failure(let error) = response {
|
|
||||||
self.handleError(error, title: "Error Adding to List")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
elementHandler([UIMenu(title: "Add to List", image: UIImage(systemName: "list.bullet"), children: listActions)])
|
|
||||||
}))
|
|
||||||
suppressSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.blockAction(for: $0, mastodonController: $1) }))
|
suppressSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.blockAction(for: $0, mastodonController: $1) }))
|
||||||
suppressSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.muteAction(for: $0, mastodonController: $1) }))
|
suppressSection.append(relationshipAction(accountID: accountID, mastodonController: mastodonController, builder: { [unowned self] in self.muteAction(for: $0, mastodonController: $1) }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ class VisualEffectImageButton: UIControl {
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
|
|
||||||
let blur = UIBlurEffect(style: .systemThickMaterial)
|
let blur = UIBlurEffect(style: .prominent)
|
||||||
let blurView = UIVisualEffectView(effect: blur)
|
let blurView = UIVisualEffectView(effect: blur)
|
||||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
blurView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
let vibrancy = UIVibrancyEffect(blurEffect: blur, style: .label)
|
let vibrancy = UIVibrancyEffect(blurEffect: blur, style: .label)
|
||||||
|
|
Loading…
Reference in New Issue