forked from shadowfacts/Tusker
parent
2464e2530f
commit
bd21e88e8b
|
@ -175,6 +175,14 @@ public class InstanceFeatures: ObservableObject {
|
|||
hasMastodonVersion(2, 8, 0)
|
||||
}
|
||||
|
||||
public var listRepliesPolicy: Bool {
|
||||
hasMastodonVersion(3, 3, 0)
|
||||
}
|
||||
|
||||
public var exclusiveLists: Bool {
|
||||
hasMastodonVersion(4, 2, 0) || instanceType.isMastodon(.hometown(nil))
|
||||
}
|
||||
|
||||
public init() {
|
||||
}
|
||||
|
||||
|
|
|
@ -264,6 +264,7 @@
|
|||
D6BD395B29B64441005FFD2B /* ComposeHostingController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BD395A29B64441005FFD2B /* ComposeHostingController.swift */; };
|
||||
D6BEA245291A0EDE002F4D01 /* Duckable in Frameworks */ = {isa = PBXBuildFile; productRef = D6BEA244291A0EDE002F4D01 /* Duckable */; };
|
||||
D6BEA247291A0F2D002F4D01 /* Duckable+Root.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BEA246291A0F2D002F4D01 /* Duckable+Root.swift */; };
|
||||
D6C041C42AED77730094D32D /* EditListSettingsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C041C32AED77730094D32D /* EditListSettingsService.swift */; };
|
||||
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */; };
|
||||
D6C3F4F5298ED0890009FCFF /* LocalPredicateStatusesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C3F4F4298ED0890009FCFF /* LocalPredicateStatusesViewController.swift */; };
|
||||
D6C3F4F7298ED7F70009FCFF /* FavoritesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C3F4F6298ED7F70009FCFF /* FavoritesViewController.swift */; };
|
||||
|
@ -665,6 +666,7 @@
|
|||
D6BD395C29B789D5005FFD2B /* TuskerComponents */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = TuskerComponents; path = Packages/TuskerComponents; sourceTree = "<group>"; };
|
||||
D6BEA243291A0C83002F4D01 /* Duckable */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = Duckable; path = Packages/Duckable; sourceTree = "<group>"; };
|
||||
D6BEA246291A0F2D002F4D01 /* Duckable+Root.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Duckable+Root.swift"; sourceTree = "<group>"; };
|
||||
D6C041C32AED77730094D32D /* EditListSettingsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditListSettingsService.swift; sourceTree = "<group>"; };
|
||||
D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusCardView.swift; sourceTree = "<group>"; };
|
||||
D6C3F4F4298ED0890009FCFF /* LocalPredicateStatusesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalPredicateStatusesViewController.swift; sourceTree = "<group>"; };
|
||||
D6C3F4F6298ED7F70009FCFF /* FavoritesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesViewController.swift; sourceTree = "<group>"; };
|
||||
|
@ -1636,6 +1638,7 @@
|
|||
D621733228F1D5ED004C7DB1 /* ReblogService.swift */,
|
||||
D6F6A54F291F058600F496A8 /* CreateListService.swift */,
|
||||
D6F6A551291F098700F496A8 /* RenameListService.swift */,
|
||||
D6C041C32AED77730094D32D /* EditListSettingsService.swift */,
|
||||
D6F6A553291F0D9600F496A8 /* DeleteListService.swift */,
|
||||
D61F75952937037800C0B37F /* ToggleFollowHashtagService.swift */,
|
||||
D61F75B0293BD85300C0B37F /* CreateFilterService.swift */,
|
||||
|
@ -2140,6 +2143,7 @@
|
|||
D6F4D79429ECB0AF00351B87 /* UIBackgroundConfiguration+AppColors.swift in Sources */,
|
||||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */,
|
||||
D681A29A249AD62D0085E54E /* LargeImageContentView.swift in Sources */,
|
||||
D6C041C42AED77730094D32D /* EditListSettingsService.swift in Sources */,
|
||||
D6DFC69E242C490400ACC392 /* TrackpadScrollGestureRecognizer.swift in Sources */,
|
||||
D61AC1D8232EA42D00C54D2D /* InstanceTableViewCell.swift in Sources */,
|
||||
D6D3F4C424FDB6B700EC4A6A /* View+ConditionalModifier.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// EditListSettingsService.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 10/28/23.
|
||||
// Copyright © 2023 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
@MainActor
|
||||
class EditListSettingsService {
|
||||
private let list: ListProtocol
|
||||
private let mastodonController: MastodonController
|
||||
private let present: (UIViewController) -> Void
|
||||
|
||||
init(list: ListProtocol, mastodonController: MastodonController, present: @escaping (UIViewController) -> Void) {
|
||||
self.list = list
|
||||
self.mastodonController = mastodonController
|
||||
self.present = present
|
||||
}
|
||||
|
||||
func run(title: String? = nil, replyPolicy: List.ReplyPolicy? = nil, exclusive: Bool? = nil) async {
|
||||
do {
|
||||
let req = List.update(
|
||||
list.id,
|
||||
title: title ?? list.title,
|
||||
replyPolicy: replyPolicy ?? list.replyPolicy,
|
||||
exclusive: exclusive ?? list.exclusive
|
||||
)
|
||||
let (list, _) = try await mastodonController.run(req)
|
||||
mastodonController.updatedList(list)
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Error Updating List", message: error.localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
alert.addAction(UIAlertAction(title: "Retry", style: .default, handler: { _ in
|
||||
Task {
|
||||
await self.run(title: title, replyPolicy: replyPolicy, exclusive: exclusive)
|
||||
}
|
||||
}))
|
||||
present(alert)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -471,7 +471,7 @@ class MastodonController: ObservableObject {
|
|||
}
|
||||
|
||||
@MainActor
|
||||
func renamedList(_ list: List) {
|
||||
func updatedList(_ list: List) {
|
||||
var new = self.lists
|
||||
if let index = new.firstIndex(where: { $0.id == list.id }) {
|
||||
new[index] = list
|
||||
|
|
|
@ -49,7 +49,7 @@ class RenameListService {
|
|||
do {
|
||||
let req = List.update(list.id, title: title, replyPolicy: nil, exclusive: nil)
|
||||
let (list, _) = try await mastodonController.run(req)
|
||||
mastodonController.renamedList(list)
|
||||
mastodonController.updatedList(list)
|
||||
} catch {
|
||||
let alert = UIAlertController(title: "Error Updating List", message: error.localizedDescription, preferredStyle: .alert)
|
||||
alert.addAction(UIAlertAction(title: "Cancel", style: .cancel))
|
||||
|
|
|
@ -38,7 +38,6 @@ class EditListAccountsViewController: UIViewController, CollectionViewController
|
|||
|
||||
listRenamedCancellable = mastodonController.$lists
|
||||
.compactMap { $0.first { $0.id == list.id } }
|
||||
.removeDuplicates(by: { $0.title == $1.title })
|
||||
.sink { [unowned self] in
|
||||
self.list = $0
|
||||
self.listChanged()
|
||||
|
@ -103,9 +102,27 @@ class EditListAccountsViewController: UIViewController, CollectionViewController
|
|||
navigationItem.hidesSearchBarWhenScrolling = false
|
||||
if #available(iOS 16.0, *) {
|
||||
navigationItem.preferredSearchBarPlacement = .stacked
|
||||
|
||||
navigationItem.renameDelegate = self
|
||||
navigationItem.titleMenuProvider = { [unowned self] suggested in
|
||||
var children = suggested
|
||||
children.append(contentsOf: self.listSettingsMenuElements())
|
||||
return UIMenu(children: children)
|
||||
}
|
||||
} else {
|
||||
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Edit", menu: UIMenu(children: [
|
||||
// uncached so that menu always reflects the current state of the list
|
||||
UIDeferredMenuElement.uncached({ [unowned self] elementHandler in
|
||||
var elements = self.listSettingsMenuElements()
|
||||
elements.insert(UIAction(title: "Rename…", image: UIImage(systemName: "pencil"), handler: { [unowned self] _ in
|
||||
RenameListService(list: self.list, mastodonController: self.mastodonController, present: {
|
||||
self.present($0, animated: true)
|
||||
}).run()
|
||||
}), at: 0)
|
||||
elementHandler(elements)
|
||||
})
|
||||
]))
|
||||
}
|
||||
|
||||
navigationItem.rightBarButtonItem = UIBarButtonItem(title: NSLocalizedString("Rename", comment: "rename list button title"), style: .plain, target: self, action: #selector(renameButtonPressed))
|
||||
}
|
||||
|
||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||
|
@ -140,7 +157,31 @@ class EditListAccountsViewController: UIViewController, CollectionViewController
|
|||
}
|
||||
|
||||
private func listChanged() {
|
||||
title = String(format: NSLocalizedString("Edit %@", comment: "edit list screen title"), list.title)
|
||||
title = list.title
|
||||
}
|
||||
|
||||
private func listSettingsMenuElements() -> [UIMenuElement] {
|
||||
var elements = [UIMenuElement]()
|
||||
if mastodonController.instanceFeatures.listRepliesPolicy {
|
||||
let actions = List.ReplyPolicy.allCases.map { policy in
|
||||
UIAction(title: policy.actionTitle, state: list.replyPolicy == policy ? .on : .off) { [unowned self] _ in
|
||||
self.setReplyPolicy(policy)
|
||||
}
|
||||
}
|
||||
elements.append(UIMenu(title: "Show replies…", image: UIImage(systemName: "arrowshape.turn.up.left"), children: actions))
|
||||
}
|
||||
if mastodonController.instanceFeatures.exclusiveLists {
|
||||
let actions = [
|
||||
UIAction(title: "Hidden from Home", state: list.exclusive == true ? .on : .off) { [unowned self] _ in
|
||||
self.setExclusive(true)
|
||||
},
|
||||
UIAction(title: "Shown on Home", state: list.exclusive == false ? .on : .off) { [unowned self] _ in
|
||||
self.setExclusive(false)
|
||||
},
|
||||
]
|
||||
elements.append(UIMenu(title: "Posts from this list are…", children: actions))
|
||||
}
|
||||
return elements
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
@ -254,13 +295,21 @@ class EditListAccountsViewController: UIViewController, CollectionViewController
|
|||
self.showToast(configuration: config, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Interaction
|
||||
|
||||
@objc func renameButtonPressed() {
|
||||
RenameListService(list: list, mastodonController: mastodonController, present: { self.present($0, animated: true) }).run()
|
||||
private func setReplyPolicy(_ replyPolicy: List.ReplyPolicy) {
|
||||
Task {
|
||||
let service = EditListSettingsService(list: list, mastodonController: mastodonController, present: { self.present($0, animated: true) })
|
||||
await service.run(replyPolicy: replyPolicy)
|
||||
}
|
||||
}
|
||||
|
||||
private func setExclusive(_ exclusive: Bool) {
|
||||
Task {
|
||||
let service = EditListSettingsService(list: list, mastodonController: mastodonController, present: { self.present($0, animated: true) })
|
||||
await service.run(exclusive: exclusive)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension EditListAccountsViewController {
|
||||
|
@ -310,3 +359,29 @@ extension EditListAccountsViewController: SearchResultsViewControllerDelegate {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension EditListAccountsViewController: UINavigationItemRenameDelegate {
|
||||
func navigationItem(_: UINavigationItem, shouldEndRenamingWith title: String) -> Bool {
|
||||
!title.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty
|
||||
}
|
||||
|
||||
func navigationItem(_: UINavigationItem, didEndRenamingWith title: String) {
|
||||
Task {
|
||||
let service = EditListSettingsService(list: list, mastodonController: mastodonController, present: { self.present($0, animated: true) })
|
||||
await service.run(title: title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension List.ReplyPolicy {
|
||||
var actionTitle: String {
|
||||
switch self {
|
||||
case .followed:
|
||||
"To accounts you follow"
|
||||
case .list:
|
||||
"To other list members"
|
||||
case .none:
|
||||
"Never"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ class ListTimelineViewController: TimelineViewController {
|
|||
|
||||
func presentEdit(animated: Bool) {
|
||||
let editListAccountsController = EditListAccountsViewController(list: list, mastodonController: mastodonController)
|
||||
editListAccountsController.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(editListDoneButtonPressed))
|
||||
editListAccountsController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(editListDoneButtonPressed))
|
||||
let navController = UINavigationController(rootViewController: editListAccountsController)
|
||||
present(navController, animated: animated)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue