Compare commits

..

8 Commits

18 changed files with 204 additions and 110 deletions

View File

@ -17,11 +17,12 @@ 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 return lhs.id == rhs.id && lhs.title == rhs.title
} }
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]> {

View File

@ -1616,7 +1616,7 @@
TargetAttributes = { TargetAttributes = {
D6D4DDCB212518A000E1C4BB = { D6D4DDCB212518A000E1C4BB = {
CreatedOnToolsVersion = 10.0; CreatedOnToolsVersion = 10.0;
LastSwiftMigration = 1200; LastSwiftMigration = 1410;
}; };
D6D4DDDF212518A200E1C4BB = { D6D4DDDF212518A200E1C4BB = {
CreatedOnToolsVersion = 10.0; CreatedOnToolsVersion = 10.0;

View File

@ -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)
NotificationCenter.default.post(name: .listsChanged, object: nil) mastodonController.addedList(list)
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,7 +64,3 @@ class CreateListService {
} }
} }
extension Foundation.Notification.Name {
static let listsChanged = Notification.Name("listsChanged")
}

View File

@ -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)
NotificationCenter.default.post(name: .listsChanged, object: nil) mastodonController.deletedList(list)
} 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))

View File

@ -46,6 +46,7 @@ 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]()
@ -119,6 +120,15 @@ 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))
@ -264,4 +274,53 @@ 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)
}
} }

View File

@ -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)
NotificationCenter.default.post(name: .listRenamed, object: list.id, userInfo: ["list": list]) mastodonController.renamedList(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,7 +63,3 @@ class RenameListService {
} }
} }
extension Foundation.Notification.Name {
static let listRenamed = Notification.Name("listRenamed")
}

View File

@ -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 {
controller.getOwnAccount() try? await controller.initialize()
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)

View File

@ -50,8 +50,9 @@ class ComposeSceneDelegate: UIResponder, UIWindowSceneDelegate {
} }
session.mastodonController = controller session.mastodonController = controller
controller.getOwnAccount() Task {
controller.getOwnInstance() try? await controller.initialize()
}
let composeVC = ComposeHostingController(draft: draft, mastodonController: controller) let composeVC = ComposeHostingController(draft: draft, mastodonController: controller)
composeVC.delegate = self composeVC.delegate = self

View File

@ -203,8 +203,9 @@ class MainSceneDelegate: UIResponder, UIWindowSceneDelegate, TuskerSceneDelegate
func createAppUI() -> TuskerRootViewController { func createAppUI() -> TuskerRootViewController {
let mastodonController = window!.windowScene!.session.mastodonController! let mastodonController = window!.windowScene!.session.mastodonController!
mastodonController.getOwnAccount() Task {
mastodonController.getOwnInstance() try? await mastodonController.initialize()
}
let split = MainSplitViewController(mastodonController: mastodonController) let split = MainSplitViewController(mastodonController: mastodonController)
if UIDevice.current.userInterfaceIdiom == .phone, if UIDevice.current.userInterfaceIdiom == .phone,

View File

@ -51,9 +51,20 @@ struct ComposePollView: View {
.hoverEffect() .hoverEffect()
} }
ForEach(Array(poll.options.enumerated()), id: \.element.id) { (e) in List {
ComposePollOption(poll: poll, option: e.element, optionIndex: e.offset) ForEach(Array(poll.options.enumerated()), id: \.element.id) { (e) in
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")

View File

@ -49,6 +49,7 @@ 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
@ -107,6 +108,8 @@ 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))
} }
} }
@ -135,6 +138,17 @@ 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 {
@ -316,6 +330,26 @@ 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()

View File

@ -24,6 +24,8 @@ 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
@ -70,9 +72,10 @@ 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) {
@ -158,7 +161,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() reloadLists(mastodonController.lists)
} }
private func addDiscoverSection(to snapshot: inout NSDiffableDataSourceSnapshot<Section, Item>) { private func addDiscoverSection(to snapshot: inout NSDiffableDataSourceSnapshot<Section, Item>) {
@ -180,39 +183,13 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate {
self.dataSource.apply(snapshot) self.dataSource.apply(snapshot)
} }
@objc private func reloadLists() { private func reloadLists(_ lists: [List]) {
let request = Client.getLists() var snapshot = self.dataSource.snapshot()
mastodonController.run(request) { (response) in snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .lists))
guard case let .success(lists, _) = response else { snapshot.appendItems(lists.map { .list($0) }, toSection: .lists)
return snapshot.appendItems([.addList], toSection: .lists)
}
var snapshot = self.dataSource.snapshot() self.dataSource.apply(snapshot)
snapshot.deleteItems(snapshot.itemIdentifiers(inSection: .lists))
snapshot.appendItems(lists.map { .list($0) }, toSection: .lists)
snapshot.appendItems([.addList], toSection: .lists)
DispatchQueue.main.async {
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

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import Combine
class EditListAccountsViewController: EnhancedTableViewController { class EditListAccountsViewController: EnhancedTableViewController {
@ -22,6 +23,8 @@ 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
@ -30,7 +33,13 @@ class EditListAccountsViewController: EnhancedTableViewController {
listChanged() listChanged()
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, object: list.id) 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()
}
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@ -88,12 +97,6 @@ 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)

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import Combine
class ListTimelineViewController: TimelineViewController { class ListTimelineViewController: TimelineViewController {
@ -15,6 +16,8 @@ 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
@ -22,7 +25,13 @@ class ListTimelineViewController: TimelineViewController {
listChanged() listChanged()
NotificationCenter.default.addObserver(self, selector: #selector(listRenamed(_:)), name: .listRenamed, object: list.id) 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()
}
} }
required init?(coder aDecoder: NSCoder) { required init?(coder aDecoder: NSCoder) {
@ -40,6 +49,7 @@ class ListTimelineViewController: TimelineViewController {
if presentEditOnAppear { if presentEditOnAppear {
presentEdit(animated: animated) presentEdit(animated: animated)
presentEditOnAppear = false
} }
} }
@ -47,12 +57,6 @@ 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))

View File

@ -8,6 +8,7 @@
import UIKit import UIKit
import Pachyderm import Pachyderm
import Combine
protocol MainSidebarViewControllerDelegate: AnyObject { protocol MainSidebarViewControllerDelegate: AnyObject {
func sidebarRequestPresentCompose(_ sidebarViewController: MainSidebarViewController) func sidebarRequestPresentCompose(_ sidebarViewController: MainSidebarViewController)
@ -28,6 +29,8 @@ 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),
@ -99,10 +102,11 @@ 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?()
} }
@ -170,7 +174,7 @@ class MainSidebarViewController: UIViewController {
dataSource.apply(snapshot, animatingDifferences: false) dataSource.apply(snapshot, animatingDifferences: false)
applyDiscoverSectionSnapshot() applyDiscoverSectionSnapshot()
reloadLists() reloadLists(mastodonController.lists)
reloadSavedHashtags() reloadSavedHashtags()
reloadSavedInstances() reloadSavedInstances()
} }
@ -203,42 +207,28 @@ class MainSidebarViewController: UIViewController {
} }
} }
@objc private func reloadLists() { private func reloadLists(_ lists: [List]) {
let request = Client.getLists() var exploreSnapshot = NSDiffableDataSourceSectionSnapshot<Item>()
mastodonController.run(request) { [weak self] (response) in exploreSnapshot.append([.listsHeader])
guard let self = self, case let .success(lists, _) = response else { return } exploreSnapshot.expand([.listsHeader])
exploreSnapshot.append(lists.map { .list($0) }, to: .listsHeader)
var exploreSnapshot = NSDiffableDataSourceSectionSnapshot<Item>() exploreSnapshot.append([.addList], to: .listsHeader)
exploreSnapshot.append([.listsHeader]) var selectedItem: Item?
exploreSnapshot.expand([.listsHeader]) if let selectedIndexPath = collectionView.indexPathsForSelectedItems?.first,
exploreSnapshot.append(lists.map { .list($0) }, to: .listsHeader) let item = dataSource.itemIdentifier(for: selectedIndexPath) {
exploreSnapshot.append([.addList], to: .listsHeader) if case .list(let list) = item,
DispatchQueue.main.async { let newList = lists.first(where: { $0.id == list.id }) {
let selected = self.collectionView.indexPathsForSelectedItems?.first selectedItem = .list(newList)
} else {
self.dataSource.apply(exploreSnapshot, to: .lists) { selectedItem = item
if let selected = selected {
self.collectionView.selectItem(at: selected, animated: false, scrollPosition: .centeredVertically)
}
}
} }
} }
}
@objc private func listRenamed(_ notification: Foundation.Notification) { self.dataSource.apply(exploreSnapshot, to: .lists) {
let list = notification.userInfo!["list"] as! List if let selectedItem,
var snapshot = dataSource.snapshot() let indexPath = self.dataSource.indexPath(for: selectedItem) {
let existing = snapshot.itemIdentifiers(inSection: .lists).first(where: { self.collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)
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)
} }
} }

View File

@ -61,6 +61,13 @@ 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)

View File

@ -66,8 +66,22 @@ extension MenuActionProvider {
] ]
var suppressSection: [UIMenuElement] = [] var suppressSection: [UIMenuElement] = []
if accountID != loggedInAccountID { if let ownAccount = mastodonController.account,
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) }))
} }

View File

@ -24,7 +24,7 @@ class VisualEffectImageButton: UIControl {
override func awakeFromNib() { override func awakeFromNib() {
super.awakeFromNib() super.awakeFromNib()
let blur = UIBlurEffect(style: .prominent) let blur = UIBlurEffect(style: .systemThickMaterial)
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)