forked from shadowfacts/Tusker
parent
c256fb4cbd
commit
e04cdd16d6
|
@ -44,6 +44,8 @@
|
|||
D61DC84628F498F200B82C6E /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61DC84528F498F200B82C6E /* Logging.swift */; };
|
||||
D61DC84B28F4FD2000B82C6E /* ProfileHeaderCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61DC84A28F4FD2000B82C6E /* ProfileHeaderCollectionViewCell.swift */; };
|
||||
D61DC84D28F500D200B82C6E /* ProfileViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61DC84C28F500D200B82C6E /* ProfileViewController.swift */; };
|
||||
D61F75882932DB6000C0B37F /* StatusSwipeAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F75872932DB6000C0B37F /* StatusSwipeAction.swift */; };
|
||||
D61F758A2932E1FC00C0B37F /* SwipeActionsPrefsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D61F75892932E1FC00C0B37F /* SwipeActionsPrefsView.swift */; };
|
||||
D620483423D3801D008A63EF /* LinkTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483323D3801D008A63EF /* LinkTextView.swift */; };
|
||||
D620483623D38075008A63EF /* ContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483523D38075008A63EF /* ContentTextView.swift */; };
|
||||
D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483723D38190008A63EF /* StatusContentTextView.swift */; };
|
||||
|
@ -406,6 +408,8 @@
|
|||
D61DC84528F498F200B82C6E /* Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
|
||||
D61DC84A28F4FD2000B82C6E /* ProfileHeaderCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileHeaderCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
D61DC84C28F500D200B82C6E /* ProfileViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileViewController.swift; sourceTree = "<group>"; };
|
||||
D61F75872932DB6000C0B37F /* StatusSwipeAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusSwipeAction.swift; sourceTree = "<group>"; };
|
||||
D61F75892932E1FC00C0B37F /* SwipeActionsPrefsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeActionsPrefsView.swift; sourceTree = "<group>"; };
|
||||
D620483323D3801D008A63EF /* LinkTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkTextView.swift; sourceTree = "<group>"; };
|
||||
D620483523D38075008A63EF /* ContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentTextView.swift; sourceTree = "<group>"; };
|
||||
D620483723D38190008A63EF /* StatusContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentTextView.swift; sourceTree = "<group>"; };
|
||||
|
@ -1031,6 +1035,7 @@
|
|||
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */,
|
||||
04586B4022B2FFB10021BD04 /* PreferencesView.swift */,
|
||||
04586B4222B301470021BD04 /* AppearancePrefsView.swift */,
|
||||
D61F75892932E1FC00C0B37F /* SwipeActionsPrefsView.swift */,
|
||||
0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */,
|
||||
D6B17254254F88B800128392 /* OppositeCollapseKeywordsView.swift */,
|
||||
D680153F2401A6BA00D6103B /* ComposingPrefsView.swift */,
|
||||
|
@ -1133,6 +1138,7 @@
|
|||
D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */,
|
||||
D6DD353E22F502EC00A9563A /* Preferences+Notification.swift */,
|
||||
D6BC9DB4232D4CE3002CA326 /* NotificationsMode.swift */,
|
||||
D61F75872932DB6000C0B37F /* StatusSwipeAction.swift */,
|
||||
);
|
||||
path = Preferences;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1849,6 +1855,7 @@
|
|||
D6B9366F2828452F00237D0E /* SavedHashtag.swift in Sources */,
|
||||
D6B9366D2828445000237D0E /* SavedInstance.swift in Sources */,
|
||||
D60E2F272442372B005F8713 /* StatusMO.swift in Sources */,
|
||||
D61F758A2932E1FC00C0B37F /* SwipeActionsPrefsView.swift in Sources */,
|
||||
D60E2F2C24423EAD005F8713 /* LazilyDecoding.swift in Sources */,
|
||||
D662AEF2263A4BE10082A153 /* ComposePollView.swift in Sources */,
|
||||
D677284A24ECBDF400C732D3 /* ComposeCurrentAccount.swift in Sources */,
|
||||
|
@ -1952,6 +1959,7 @@
|
|||
D6C82B5625C5F3F20017F1E6 /* ExpandThreadTableViewCell.swift in Sources */,
|
||||
D686BBE324FBF8110068E6AA /* WrappedProgressView.swift in Sources */,
|
||||
D620483423D3801D008A63EF /* LinkTextView.swift in Sources */,
|
||||
D61F75882932DB6000C0B37F /* StatusSwipeAction.swift in Sources */,
|
||||
D6D12B58292D5B2C00D528E1 /* StatusActionAccountListViewController.swift in Sources */,
|
||||
D6412B0D24B0D4CF00F5412E /* ProfileHeaderView.swift in Sources */,
|
||||
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */,
|
||||
|
|
|
@ -91,11 +91,20 @@ struct InstanceFeatures {
|
|||
} else if nodeInfo?.software.name == "hometown" {
|
||||
var mastoVersion: Version?
|
||||
var hometownVersion: Version?
|
||||
// like "1.0.6+3.5.2"
|
||||
let parts = ver.split(separator: "+")
|
||||
if parts.count == 2 {
|
||||
if parts.count == 2,
|
||||
let first = Version(string: String(parts[0])) {
|
||||
if first > Version(1, 0, 8) {
|
||||
// like 3.5.5+hometown-1.0.9
|
||||
mastoVersion = first
|
||||
if parts[1].starts(with: "hometown-") {
|
||||
hometownVersion = Version(string: String(parts[1][parts[1].index(parts[1].startIndex, offsetBy: "hometown-".count + 1)...]))
|
||||
}
|
||||
} else {
|
||||
// like "1.0.6+3.5.2"
|
||||
hometownVersion = first
|
||||
mastoVersion = Version(string: String(parts[1]))
|
||||
hometownVersion = Version(string: String(parts[0]))
|
||||
}
|
||||
} else {
|
||||
mastoVersion = Version(string: ver)
|
||||
}
|
||||
|
|
|
@ -125,6 +125,8 @@ class Preferences: Codable, ObservableObject {
|
|||
@Published var showIsStatusReplyIcon = false
|
||||
@Published var alwaysShowStatusVisibilityIcon = false
|
||||
@Published var hideActionsInTimeline = false
|
||||
@Published var leadingStatusSwipeActions: [StatusSwipeAction] = [.favorite, .reblog]
|
||||
@Published var trailingStatusSwipeActions: [StatusSwipeAction] = [.reply, .share]
|
||||
|
||||
// MARK: Composing
|
||||
@Published var defaultPostVisibility = Status.Visibility.public
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
//
|
||||
// StatusSwipeAction.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 11/26/22.
|
||||
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Pachyderm
|
||||
|
||||
enum StatusSwipeAction: String, Codable, Hashable, CaseIterable {
|
||||
case reply
|
||||
case favorite
|
||||
case reblog
|
||||
case share
|
||||
case bookmark
|
||||
case openInSafari
|
||||
|
||||
var displayName: String {
|
||||
switch self {
|
||||
case .reply:
|
||||
return "Reply"
|
||||
case .favorite:
|
||||
return "Favorite"
|
||||
case .reblog:
|
||||
return "Reblog"
|
||||
case .share:
|
||||
return "Share"
|
||||
case .bookmark:
|
||||
return "Bookmark"
|
||||
case .openInSafari:
|
||||
return "Open in Safari"
|
||||
}
|
||||
}
|
||||
|
||||
var systemImageName: String {
|
||||
switch self {
|
||||
case .reply:
|
||||
return "arrowshape.turn.up.left.fill"
|
||||
case .favorite:
|
||||
return "star.fill"
|
||||
case .reblog:
|
||||
return "repeat"
|
||||
case .share:
|
||||
return "square.and.arrow.up"
|
||||
case .bookmark:
|
||||
return "bookmark.fill"
|
||||
case .openInSafari:
|
||||
return "safari"
|
||||
}
|
||||
}
|
||||
|
||||
func createAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? {
|
||||
switch self {
|
||||
case .reply:
|
||||
return createReplyAction(status: status, container: container)
|
||||
case .favorite:
|
||||
return createFavoriteAction(status: status, container: container)
|
||||
case .reblog:
|
||||
return createReblogAction(status: status, container: container)
|
||||
case .share:
|
||||
return createShareAction(status: status, container: container)
|
||||
case .bookmark:
|
||||
return createBookmarkAction(status: status, container: container)
|
||||
case .openInSafari:
|
||||
return createOpenInSafariAction(status: status, container: container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protocol StatusSwipeActionContainer: UIView {
|
||||
var mastodonController: MastodonController! { get }
|
||||
var navigationDelegate: any TuskerNavigationDelegate { get }
|
||||
var toastableViewController: ToastableViewController? { get }
|
||||
|
||||
// necessary b/c the reblog-handling logic only exists in the cells
|
||||
func performReplyAction()
|
||||
}
|
||||
|
||||
private func createReplyAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? {
|
||||
guard container.mastodonController.loggedIn else {
|
||||
return nil
|
||||
}
|
||||
let action = UIContextualAction(style: .normal, title: "Reply") { [unowned container] _, _, completion in
|
||||
container.performReplyAction()
|
||||
completion(true)
|
||||
}
|
||||
action.image = UIImage(systemName: "arrowshape.turn.up.left.fill")
|
||||
action.backgroundColor = container.tintColor
|
||||
return action
|
||||
}
|
||||
|
||||
private func createFavoriteAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? {
|
||||
guard container.mastodonController.loggedIn else {
|
||||
return nil
|
||||
}
|
||||
let title = status.favourited ? "Unfavorite" : "Favorite"
|
||||
let action = UIContextualAction(style: .normal, title: title) { [unowned container] _, _, completion in
|
||||
Task {
|
||||
await FavoriteService(status: status, mastodonController: container.mastodonController, presenter: container.navigationDelegate).toggleFavorite()
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
action.image = UIImage(systemName: "star.fill")
|
||||
action.backgroundColor = status.favourited ? UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) : UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1)
|
||||
return action
|
||||
}
|
||||
|
||||
private func createReblogAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? {
|
||||
guard container.mastodonController.loggedIn else {
|
||||
return nil
|
||||
}
|
||||
let title = status.reblogged ? "Unreblog" : "Reblog"
|
||||
let action = UIContextualAction(style: .normal, title: title) { [unowned container] _, _, completion in
|
||||
Task {
|
||||
await ReblogService(status: status, mastodonController: container.mastodonController, presenter: container.navigationDelegate).toggleReblog()
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
action.image = UIImage(systemName: "repeat")
|
||||
action.backgroundColor = status.reblogged ? UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) : container.tintColor
|
||||
return action
|
||||
}
|
||||
|
||||
private func createShareAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction {
|
||||
let action = UIContextualAction(style: .normal, title: "Share") { [unowned container] _, _, completion in
|
||||
container.navigationDelegate.showMoreOptions(forStatus: status.id, sourceView: container)
|
||||
completion(true)
|
||||
}
|
||||
// bold to more closesly match other action symbols
|
||||
let config = UIImage.SymbolConfiguration(weight: .bold)
|
||||
action.image = UIImage(systemName: "square.and.arrow.up")!.applyingSymbolConfiguration(config)!
|
||||
action.backgroundColor = .lightGray
|
||||
return action
|
||||
}
|
||||
|
||||
private func createBookmarkAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction? {
|
||||
guard container.mastodonController.loggedIn else {
|
||||
return nil
|
||||
}
|
||||
let bookmarked = status.bookmarked ?? false
|
||||
let title = bookmarked ? "Unbookmark" : "Bookmark"
|
||||
let action = UIContextualAction(style: .normal, title: title) { [unowned container] _, _, completion in
|
||||
Task { @MainActor in
|
||||
let request = (bookmarked ? Status.unbookmark : Status.bookmark)(status.id)
|
||||
do {
|
||||
let (status, _) = try await container.mastodonController.run(request)
|
||||
container.mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||
} catch {
|
||||
if let toastable = container.toastableViewController {
|
||||
let config = ToastConfiguration(from: error, with: "Error \(bookmarked ? "Unb" : "B")ookmarking", in: toastable, retryAction: nil)
|
||||
toastable.showToast(configuration: config, animated: true)
|
||||
}
|
||||
}
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
action.image = UIImage(systemName: "bookmark.fill")
|
||||
action.backgroundColor = .systemRed
|
||||
return action
|
||||
}
|
||||
|
||||
private func createOpenInSafariAction(status: StatusMO, container: StatusSwipeActionContainer) -> UIContextualAction {
|
||||
let action = UIContextualAction(style: .normal, title: "Open in Safari") { [unowned container] _, _, completion in
|
||||
container.navigationDelegate.selected(url: status.url!, allowUniversalLinks: false)
|
||||
completion(true)
|
||||
}
|
||||
action.image = UIImage(systemName: "safari")
|
||||
action.backgroundColor = container.tintColor
|
||||
return action
|
||||
}
|
|
@ -59,6 +59,16 @@ struct AppearancePrefsView : View {
|
|||
Toggle(isOn: $preferences.hideActionsInTimeline) {
|
||||
Text("Hide Actions on Timeline")
|
||||
}
|
||||
NavigationLink("Leading Swipe Actions") {
|
||||
SwipeActionsPrefsView(selection: $preferences.leadingStatusSwipeActions)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.navigationTitle("Leading Swipe Actions")
|
||||
}
|
||||
NavigationLink("Trailing Swipe Actions") {
|
||||
SwipeActionsPrefsView(selection: $preferences.trailingStatusSwipeActions)
|
||||
.edgesIgnoringSafeArea(.all)
|
||||
.navigationTitle("Trailing Swipe Actions")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
//
|
||||
// SwipeActionsPrefsView.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 11/26/22.
|
||||
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SwipeActionsPrefsView: UIViewControllerRepresentable {
|
||||
@Binding var selection: [StatusSwipeAction]
|
||||
|
||||
typealias UIViewControllerType = SwipeActionsPrefsViewController
|
||||
|
||||
func makeUIViewController(context: Context) -> SwipeActionsPrefsViewController {
|
||||
return SwipeActionsPrefsViewController(selection: $selection)
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: SwipeActionsPrefsViewController, context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
class SwipeActionsPrefsViewController: UIViewController, UICollectionViewDelegate {
|
||||
@Binding var selection: [StatusSwipeAction]
|
||||
|
||||
private var collectionView: UICollectionView {
|
||||
view as! UICollectionView
|
||||
}
|
||||
private var dataSource: UICollectionViewDiffableDataSource<Section, Item>!
|
||||
|
||||
init(selection: Binding<[StatusSwipeAction]>) {
|
||||
self._selection = selection
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
let layout = UICollectionViewCompositionalLayout { [unowned self] sectionIndex, environment in
|
||||
var config = UICollectionLayoutListConfiguration(appearance: .insetGrouped)
|
||||
if dataSource.sectionIdentifier(for: sectionIndex) == .selected {
|
||||
config.headerMode = .supplementary
|
||||
}
|
||||
return .list(using: config, layoutEnvironment: environment)
|
||||
}
|
||||
view = UICollectionView(frame: .zero, collectionViewLayout: layout)
|
||||
collectionView.delegate = self
|
||||
dataSource = createDataSource()
|
||||
}
|
||||
|
||||
private func createDataSource() -> UICollectionViewDiffableDataSource<Section, Item> {
|
||||
let listCell = UICollectionView.CellRegistration<UICollectionViewListCell, StatusSwipeAction> { cell, indexPath, item in
|
||||
var config = cell.defaultContentConfiguration()
|
||||
config.text = item.displayName
|
||||
config.image = UIImage(systemName: item.systemImageName)
|
||||
cell.contentConfiguration = config
|
||||
cell.accessories = [.reorder(displayed: .always)]
|
||||
}
|
||||
let dataSource = UICollectionViewDiffableDataSource<Section, Item>(collectionView: collectionView) { collectionView, indexPath, itemIdentifier in
|
||||
return collectionView.dequeueConfiguredReusableCell(using: listCell, for: indexPath, item: itemIdentifier)
|
||||
}
|
||||
let headerCell = UICollectionView.SupplementaryRegistration<UICollectionViewListCell>(elementKind: UICollectionView.elementKindSectionHeader) { supplementaryView, elementKind, indexPath in
|
||||
var config = supplementaryView.defaultContentConfiguration()
|
||||
config.text = "Selected"
|
||||
supplementaryView.contentConfiguration = config
|
||||
}
|
||||
dataSource.supplementaryViewProvider = { collectionView, kind, indexPath in
|
||||
return collectionView.dequeueConfiguredReusableSupplementary(using: headerCell, for: indexPath)
|
||||
}
|
||||
dataSource.reorderingHandlers.canReorderItem = { _ in
|
||||
return true
|
||||
}
|
||||
dataSource.reorderingHandlers.didReorder = { [unowned self] transaction in
|
||||
guard let selectedSection = transaction.sectionTransactions.first(where: { $0.sectionIdentifier == .selected }) else {
|
||||
return
|
||||
}
|
||||
self.selection = self.selection.applying(selectedSection.difference)!
|
||||
}
|
||||
return dataSource
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
setEditing(true, animated: false)
|
||||
applySnapshot(animated: false)
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
|
||||
collectionView.deselectItem(at: indexPath, animated: true)
|
||||
guard let item = dataSource.itemIdentifier(for: indexPath),
|
||||
let section = dataSource.sectionIdentifier(for: indexPath.section) else {
|
||||
return
|
||||
}
|
||||
switch section {
|
||||
case .selected:
|
||||
selection.removeAll(where: { $0 == item })
|
||||
case .remainder:
|
||||
selection.append(item)
|
||||
}
|
||||
applySnapshot(animated: true)
|
||||
}
|
||||
|
||||
private func applySnapshot(animated: Bool) {
|
||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
||||
snapshot.appendSections([.selected, .remainder])
|
||||
snapshot.appendItems(selection, toSection: .selected)
|
||||
snapshot.appendItems(StatusSwipeAction.allCases.filter { !selection.contains($0) }, toSection: .remainder)
|
||||
dataSource.apply(snapshot, animatingDifferences: animated)
|
||||
}
|
||||
|
||||
enum Section {
|
||||
case selected
|
||||
case remainder
|
||||
}
|
||||
|
||||
typealias Item = StatusSwipeAction
|
||||
}
|
|
@ -578,58 +578,17 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
|
|||
}
|
||||
|
||||
func leadingSwipeActions() -> UISwipeActionsConfiguration? {
|
||||
guard mastodonController.loggedIn,
|
||||
let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let favoriteTitle = status.favourited ? "Unfavorite" : "Favorite"
|
||||
let favorite = UIContextualAction(style: .normal, title: favoriteTitle) { [unowned self] _, _, completion in
|
||||
Task {
|
||||
await FavoriteService(status: status, mastodonController: self.mastodonController, presenter: self.delegate!).toggleFavorite()
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
favorite.image = UIImage(systemName: "star.fill")
|
||||
favorite.backgroundColor = status.favourited ? UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) : UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1)
|
||||
|
||||
let reblogTitle = status.reblogged ? "Unreblog" : "Reblog"
|
||||
let reblog = UIContextualAction(style: .normal, title: reblogTitle) { _, _, completion in
|
||||
Task {
|
||||
await ReblogService(status: status, mastodonController: self.mastodonController, presenter: self.delegate!).toggleReblog()
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
reblog.image = UIImage(systemName: "repeat")
|
||||
reblog.backgroundColor = status.reblogged ? UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1) : tintColor
|
||||
|
||||
return UISwipeActionsConfiguration(actions: [favorite, reblog])
|
||||
return UISwipeActionsConfiguration(actions: Preferences.shared.leadingStatusSwipeActions.compactMap { $0.createAction(status: status, container: self) })
|
||||
}
|
||||
|
||||
func trailingSwipeActions() -> UISwipeActionsConfiguration? {
|
||||
var actions = [UIContextualAction]()
|
||||
|
||||
let share = UIContextualAction(style: .normal, title: "Share") { [unowned self] _, _, completion in
|
||||
self.delegate?.showMoreOptions(forStatus: statusID, sourceView: self)
|
||||
completion(true)
|
||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||
return nil
|
||||
}
|
||||
// bold to more closesly match other action symbols
|
||||
let config = UIImage.SymbolConfiguration(weight: .bold)
|
||||
share.image = UIImage(systemName: "square.and.arrow.up")!.applyingSymbolConfiguration(config)!
|
||||
share.backgroundColor = .lightGray
|
||||
actions.append(share)
|
||||
|
||||
if mastodonController.loggedIn {
|
||||
let reply = UIContextualAction(style: .normal, title: "Reply") { [unowned self] _, _, completion in
|
||||
self.replyPressed()
|
||||
completion(true)
|
||||
}
|
||||
reply.image = UIImage(systemName: "arrowshape.turn.up.left.fill")
|
||||
reply.backgroundColor = tintColor
|
||||
actions.insert(reply, at: 0)
|
||||
}
|
||||
|
||||
return UISwipeActionsConfiguration(actions: actions)
|
||||
return UISwipeActionsConfiguration(actions: Preferences.shared.trailingStatusSwipeActions.compactMap { $0.createAction(status: status, container: self) })
|
||||
}
|
||||
|
||||
func dragItemsForBeginning(session: UIDragSession) -> [UIDragItem] {
|
||||
|
@ -705,3 +664,12 @@ extension TimelineStatusCollectionViewCell: UIPointerInteractionDelegate {
|
|||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
extension TimelineStatusCollectionViewCell: StatusSwipeActionContainer {
|
||||
var navigationDelegate: TuskerNavigationDelegate { delegate! }
|
||||
var toastableViewController: ToastableViewController? { delegate }
|
||||
|
||||
func performReplyAction() {
|
||||
self.replyPressed()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -319,90 +319,17 @@ extension TimelineStatusTableViewCell: SelectableTableViewCell {
|
|||
extension TimelineStatusTableViewCell: TableViewSwipeActionProvider {
|
||||
|
||||
func leadingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? {
|
||||
guard let mastodonController = mastodonController,
|
||||
mastodonController.loggedIn else { return nil }
|
||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
|
||||
|
||||
let favoriteTitle: String
|
||||
let favoriteRequest: Request<Status>
|
||||
let favoriteColor: UIColor
|
||||
if status.favourited {
|
||||
favoriteTitle = "Unfavorite"
|
||||
favoriteRequest = Status.unfavourite(status.id)
|
||||
favoriteColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1)
|
||||
|
||||
} else {
|
||||
favoriteTitle = "Favorite"
|
||||
favoriteRequest = Status.favourite(status.id)
|
||||
favoriteColor = UIColor(displayP3Red: 1, green: 204/255, blue: 0, alpha: 1)
|
||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||
return nil
|
||||
}
|
||||
let favorite = UIContextualAction(style: .normal, title: favoriteTitle) { (action, view, completion) in
|
||||
mastodonController.run(favoriteRequest, completion: { response in
|
||||
DispatchQueue.main.async {
|
||||
guard case let .success(status, _) = response else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
completion(true)
|
||||
mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||
}
|
||||
})
|
||||
}
|
||||
favorite.image = UIImage(systemName: "star.fill")
|
||||
favorite.backgroundColor = favoriteColor
|
||||
|
||||
let reblogTitle: String
|
||||
let reblogRequest: Request<Status>
|
||||
let reblogColor: UIColor
|
||||
if status.reblogged {
|
||||
reblogTitle = "Unreblog"
|
||||
reblogRequest = Status.unreblog(status.id)
|
||||
reblogColor = UIColor(displayP3Red: 235/255, green: 77/255, blue: 62/255, alpha: 1)
|
||||
} else {
|
||||
reblogTitle = "Reblog"
|
||||
reblogRequest = Status.reblog(status.id)
|
||||
reblogColor = tintColor
|
||||
}
|
||||
let reblog = UIContextualAction(style: .normal, title: reblogTitle) { (action, view, completion) in
|
||||
mastodonController.run(reblogRequest, completion: { response in
|
||||
DispatchQueue.main.async {
|
||||
guard case let .success(status, _) = response else {
|
||||
completion(false)
|
||||
return
|
||||
}
|
||||
completion(true)
|
||||
mastodonController.persistentContainer.addOrUpdate(status: status)
|
||||
}
|
||||
})
|
||||
}
|
||||
reblog.image = UIImage(systemName: "repeat")
|
||||
reblog.backgroundColor = reblogColor
|
||||
|
||||
return UISwipeActionsConfiguration(actions: [favorite, reblog])
|
||||
return UISwipeActionsConfiguration(actions: Preferences.shared.leadingStatusSwipeActions.compactMap { $0.createAction(status: status, container: self) })
|
||||
}
|
||||
|
||||
func trailingSwipeActionsConfiguration() -> UISwipeActionsConfiguration? {
|
||||
let share = UIContextualAction(style: .normal, title: "Share") { (action, view, completion) in
|
||||
completion(true)
|
||||
self.delegate?.showMoreOptions(forStatus: self.statusID, sourceView: self)
|
||||
guard let status = mastodonController.persistentContainer.status(for: statusID) else {
|
||||
return nil
|
||||
}
|
||||
// Bold to more closely match the other action symbols
|
||||
let config = UIImage.SymbolConfiguration(weight: .bold)
|
||||
share.image = UIImage(systemName: "square.and.arrow.up")!.applyingSymbolConfiguration(config)!
|
||||
share.backgroundColor = .lightGray
|
||||
|
||||
guard mastodonController.loggedIn else {
|
||||
return UISwipeActionsConfiguration(actions: [share])
|
||||
}
|
||||
|
||||
let reply = UIContextualAction(style: .normal, title: "Reply") { (action, view, completion) in
|
||||
completion(true)
|
||||
self.reply()
|
||||
}
|
||||
reply.image = UIImage(systemName: "arrowshape.turn.up.left.fill")
|
||||
reply.backgroundColor = tintColor
|
||||
|
||||
return UISwipeActionsConfiguration(actions: [reply, share])
|
||||
return UISwipeActionsConfiguration(actions: Preferences.shared.trailingStatusSwipeActions.compactMap { $0.createAction(status: status, container: self) })
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -461,3 +388,12 @@ extension TimelineStatusTableViewCell: MenuPreviewProvider {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension TimelineStatusTableViewCell: StatusSwipeActionContainer {
|
||||
var navigationDelegate: TuskerNavigationDelegate { delegate! }
|
||||
var toastableViewController: ToastableViewController? { delegate }
|
||||
|
||||
func performReplyAction() {
|
||||
self.replyPressed()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue