Compare commits

..

No commits in common. "ec75906bc18cc368efa48a707789d3a68bf0a5ae" and "597dd56032e2fd6c51cbdb1f788cf9a809efe4ba" have entirely different histories.

10 changed files with 47 additions and 193 deletions

View File

@ -199,10 +199,8 @@ public class Client {
return Request<Account>(method: .get, path: "/api/v1/accounts/verify_credentials") return Request<Account>(method: .get, path: "/api/v1/accounts/verify_credentials")
} }
public static func getFavourites(range: RequestRange = .default) -> Request<[Status]> { public static func getFavourites() -> Request<[Status]> {
var request = Request<[Status]>(method: .get, path: "/api/v1/favourites") return Request<[Status]>(method: .get, path: "/api/v1/favourites")
request.range = range
return request
} }
public static func getRelationships(accounts: [String]? = nil) -> Request<[Relationship]> { public static func getRelationships(accounts: [String]? = nil) -> Request<[Relationship]> {

View File

@ -295,8 +295,6 @@
D6BED174212667E900F02DA0 /* TimelineStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BED173212667E900F02DA0 /* TimelineStatusTableViewCell.swift */; }; D6BED174212667E900F02DA0 /* TimelineStatusTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6BED173212667E900F02DA0 /* TimelineStatusTableViewCell.swift */; };
D6C143DA253510F4007DC240 /* ComposeEmojiTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C143D9253510F4007DC240 /* ComposeEmojiTextField.swift */; }; D6C143DA253510F4007DC240 /* ComposeEmojiTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C143D9253510F4007DC240 /* ComposeEmojiTextField.swift */; };
D6C1B2082545D1EC00DAAA66 /* StatusCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C1B2072545D1EC00DAAA66 /* StatusCardView.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 */; };
D6C693EF216192C2007D6A6D /* TuskerNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */; }; D6C693EF216192C2007D6A6D /* TuskerNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */; };
D6C693FC2162FE6F007D6A6D /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */; }; D6C693FC2162FE6F007D6A6D /* LoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */; };
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */; }; D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */; };
@ -323,7 +321,6 @@
D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; }; D6D4DDF0212518A200E1C4BB /* TuskerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D4DDEF212518A200E1C4BB /* TuskerUITests.swift */; };
D6D706A029466649000827ED /* ScrollingSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D7069F29466649000827ED /* ScrollingSegmentedControl.swift */; }; D6D706A029466649000827ED /* ScrollingSegmentedControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D7069F29466649000827ED /* ScrollingSegmentedControl.swift */; };
D6D706A72948D4D0000827ED /* TimlineState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D706A62948D4D0000827ED /* TimlineState.swift */; }; D6D706A72948D4D0000827ED /* TimlineState.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D706A62948D4D0000827ED /* TimlineState.swift */; };
D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6D9498E298EB79400C59229 /* CopyableLable.swift */; };
D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */; }; D6DD2A3F273C1F4900386A6C /* ComposeAttachmentImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */; };
D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A44273D6C5700386A6C /* GIFImageView.swift */; }; D6DD2A45273D6C5700386A6C /* GIFImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD2A44273D6C5700386A6C /* GIFImageView.swift */; };
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; }; D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */; };
@ -704,8 +701,6 @@
D6BED173212667E900F02DA0 /* TimelineStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStatusTableViewCell.swift; sourceTree = "<group>"; }; D6BED173212667E900F02DA0 /* TimelineStatusTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineStatusTableViewCell.swift; sourceTree = "<group>"; };
D6C143D9253510F4007DC240 /* ComposeEmojiTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeEmojiTextField.swift; sourceTree = "<group>"; }; D6C143D9253510F4007DC240 /* ComposeEmojiTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeEmojiTextField.swift; sourceTree = "<group>"; };
D6C1B2072545D1EC00DAAA66 /* StatusCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusCardView.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>"; };
D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerNavigationDelegate.swift; sourceTree = "<group>"; }; D6C693EE216192C2007D6A6D /* TuskerNavigationDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TuskerNavigationDelegate.swift; sourceTree = "<group>"; };
D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = "<group>"; }; D6C693FB2162FE6F007D6A6D /* LoadingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingViewController.swift; sourceTree = "<group>"; };
D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = "<group>"; }; D6C693FD2162FEEA007D6A6D /* UIViewController+Children.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Children.swift"; sourceTree = "<group>"; };
@ -739,7 +734,6 @@
D6D7069F29466649000827ED /* ScrollingSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollingSegmentedControl.swift; sourceTree = "<group>"; }; D6D7069F29466649000827ED /* ScrollingSegmentedControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollingSegmentedControl.swift; sourceTree = "<group>"; };
D6D706A62948D4D0000827ED /* TimlineState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimlineState.swift; sourceTree = "<group>"; }; D6D706A62948D4D0000827ED /* TimlineState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimlineState.swift; sourceTree = "<group>"; };
D6D706A829498C82000827ED /* Tusker.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Tusker.xcconfig; sourceTree = "<group>"; }; D6D706A829498C82000827ED /* Tusker.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Tusker.xcconfig; sourceTree = "<group>"; };
D6D9498E298EB79400C59229 /* CopyableLable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableLable.swift; sourceTree = "<group>"; };
D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAttachmentImage.swift; sourceTree = "<group>"; }; D6DD2A3E273C1F4900386A6C /* ComposeAttachmentImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeAttachmentImage.swift; sourceTree = "<group>"; };
D6DD2A44273D6C5700386A6C /* GIFImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GIFImageView.swift; sourceTree = "<group>"; }; D6DD2A44273D6C5700386A6C /* GIFImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GIFImageView.swift; sourceTree = "<group>"; };
D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; }; D6DD353C22F28CD000A9563A /* ContentWarningCopyMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentWarningCopyMode.swift; sourceTree = "<group>"; };
@ -941,14 +935,12 @@
path = Explore; path = Explore;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D627944823A6AD5100D38C68 /* Local Predicate Statuses List */ = { D627944823A6AD5100D38C68 /* Bookmarks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D6C3F4F4298ED0890009FCFF /* LocalPredicateStatusesViewController.swift */,
D6DD8FFE2984D327002AD3FD /* BookmarksViewController.swift */, D6DD8FFE2984D327002AD3FD /* BookmarksViewController.swift */,
D6C3F4F6298ED7F70009FCFF /* FavoritesViewController.swift */,
); );
path = "Local Predicate Statuses List"; path = Bookmarks;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D627944B23A9A02400D38C68 /* Lists */ = { D627944B23A9A02400D38C68 /* Lists */ = {
@ -1011,6 +1003,7 @@
D6A3BC822321F69400FD64D5 /* Account List */, D6A3BC822321F69400FD64D5 /* Account List */,
D6B053A023BD2BED00A066FA /* Asset Picker */, D6B053A023BD2BED00A066FA /* Asset Picker */,
0411610522B457290030A9B7 /* Attachment Gallery */, 0411610522B457290030A9B7 /* Attachment Gallery */,
D627944823A6AD5100D38C68 /* Bookmarks */,
D641C787213DD862004B4513 /* Compose */, D641C787213DD862004B4513 /* Compose */,
D641C785213DD83B004B4513 /* Conversation */, D641C785213DD83B004B4513 /* Conversation */,
D6F2E960249E772F005846BB /* Crash Reporter */, D6F2E960249E772F005846BB /* Crash Reporter */,
@ -1019,7 +1012,6 @@
D61F759729384D4200C0B37F /* Customize Timelines */, D61F759729384D4200C0B37F /* Customize Timelines */,
D641C788213DD86D004B4513 /* Large Image */, D641C788213DD86D004B4513 /* Large Image */,
D627944B23A9A02400D38C68 /* Lists */, D627944B23A9A02400D38C68 /* Lists */,
D627944823A6AD5100D38C68 /* Local Predicate Statuses List */,
D641C782213DD7F0004B4513 /* Main */, D641C782213DD7F0004B4513 /* Main */,
D6F6A555291F4F0C00F496A8 /* Mute */, D6F6A555291F4F0C00F496A8 /* Mute */,
D641C786213DD852004B4513 /* Notifications */, D641C786213DD852004B4513 /* Notifications */,
@ -1422,7 +1414,6 @@
D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */, D6ADB6EF28ED1F25009924AB /* CachedImageView.swift */,
D6895DC328D65342006341DA /* ConfirmReblogStatusPreviewView.swift */, D6895DC328D65342006341DA /* ConfirmReblogStatusPreviewView.swift */,
D620483523D38075008A63EF /* ContentTextView.swift */, D620483523D38075008A63EF /* ContentTextView.swift */,
D6D9498E298EB79400C59229 /* CopyableLable.swift */,
D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */, D6E426B225337C7000C02E1C /* CustomEmojiImageView.swift */,
D6969E9F240C8384002843CE /* EmojiLabel.swift */, D6969E9F240C8384002843CE /* EmojiLabel.swift */,
D61F75B8293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift */, D61F75B8293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift */,
@ -1945,7 +1936,6 @@
D6285B5321EA708700FE4B39 /* StatusFormat.swift in Sources */, D6285B5321EA708700FE4B39 /* StatusFormat.swift in Sources */,
D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */, D6DD353D22F28CD000A9563A /* ContentWarningCopyMode.swift in Sources */,
0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */, 0427033A22B31269000D31B6 /* AdvancedPrefsView.swift in Sources */,
D6C3F4F5298ED0890009FCFF /* LocalPredicateStatusesViewController.swift in Sources */,
D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */, D662AEEF263A3B880082A153 /* PollFinishedTableViewCell.swift in Sources */,
D626493C23C1000300612E6E /* AlbumTableViewCell.swift in Sources */, D626493C23C1000300612E6E /* AlbumTableViewCell.swift in Sources */,
D62275A624F1C81800B82A16 /* ComposeReplyView.swift in Sources */, D62275A624F1C81800B82A16 /* ComposeReplyView.swift in Sources */,
@ -2127,7 +2117,6 @@
D663626221360B1900C9CBA2 /* Preferences.swift in Sources */, D663626221360B1900C9CBA2 /* Preferences.swift in Sources */,
D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */, D626493823C0FD0000612E6E /* AllPhotosTableViewCell.swift in Sources */,
D627943B23A55BA600D38C68 /* NavigableTableViewCell.swift in Sources */, D627943B23A55BA600D38C68 /* NavigableTableViewCell.swift in Sources */,
D6D9498F298EB79400C59229 /* CopyableLable.swift in Sources */,
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */, D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */,
D6D706A029466649000827ED /* ScrollingSegmentedControl.swift in Sources */, D6D706A029466649000827ED /* ScrollingSegmentedControl.swift in Sources */,
D653F411267D1E32004E32B1 /* DiffableTimelineLikeTableViewController.swift in Sources */, D653F411267D1E32004E32B1 /* DiffableTimelineLikeTableViewController.swift in Sources */,
@ -2158,7 +2147,6 @@
D6D706A72948D4D0000827ED /* TimlineState.swift in Sources */, D6D706A72948D4D0000827ED /* TimlineState.swift in Sources */,
D66C900B28DAB7FD00217BF2 /* TimelineViewController.swift in Sources */, D66C900B28DAB7FD00217BF2 /* TimelineViewController.swift in Sources */,
D61F758D2933C69C00C0B37F /* StatusUpdatedNotificationTableViewCell.swift in Sources */, D61F758D2933C69C00C0B37F /* StatusUpdatedNotificationTableViewCell.swift in Sources */,
D6C3F4F7298ED7F70009FCFF /* FavoritesViewController.swift in Sources */,
D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */, D6AEBB432321685E00E5038B /* OpenInSafariActivity.swift in Sources */,
D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */, D6C693FE2162FEEA007D6A6D /* UIViewController+Children.swift in Sources */,
D61A45EA28DF51EE002BE511 /* TimelineLikeCollectionViewController.swift in Sources */, D61A45EA28DF51EE002BE511 /* TimelineLikeCollectionViewController.swift in Sources */,

View File

@ -1,23 +1,20 @@
// //
// LocalPredicateStatusesViewController.swift // BookmarksViewController.swift
// Tusker // Tusker
// //
// Created by Shadowfacts on 2/4/23. // Created by Shadowfacts on 12/15/19.
// Copyright © 2023 Shadowfacts. All rights reserved. // Copyright © 2019 Shadowfacts. All rights reserved.
// //
import UIKit import UIKit
import Pachyderm import Pachyderm
import CoreData import CoreData
class LocalPredicateStatusesViewController: UIViewController, CollectionViewController, RefreshableViewController { class BookmarksViewController: UIViewController, CollectionViewController, RefreshableViewController {
private static let pageSize = 40 private static let pageSize = 40
let mastodonController: MastodonController let mastodonController: MastodonController
private let predicate: (StatusMO) -> Bool
private let predicateTitle: String
private let request: (RequestRange) -> Request<[Status]>
var collectionView: UICollectionView! { var collectionView: UICollectionView! {
view as? UICollectionView view as? UICollectionView
@ -28,15 +25,12 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
private var newer: RequestRange? private var newer: RequestRange?
private var older: RequestRange? private var older: RequestRange?
init(predicate: @escaping (StatusMO) -> Bool, predicateTitle: String, request: @escaping (RequestRange) -> Request<[Status]>, mastodonController: MastodonController) { init(mastodonController: MastodonController) {
self.mastodonController = mastodonController self.mastodonController = mastodonController
self.predicate = predicate
self.predicateTitle = predicateTitle
self.request = request
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
self.title = predicateTitle title = NSLocalizedString("Bookmarks", comment: "bookmarks screen title")
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
@ -56,7 +50,7 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
return sectionConfig return sectionConfig
} }
var config = sectionConfig var config = sectionConfig
if item.hideSeparators { if item.hideIndicators {
config.topSeparatorVisibility = .hidden config.topSeparatorVisibility = .hidden
config.bottomSeparatorVisibility = .hidden config.bottomSeparatorVisibility = .hidden
} else { } else {
@ -106,7 +100,7 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
collectionView.refreshControl!.addTarget(self, action: #selector(refresh), for: .valueChanged) collectionView.refreshControl!.addTarget(self, action: #selector(refresh), for: .valueChanged)
#endif #endif
addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: "Refresh \(predicateTitle)")) addKeyCommand(MenuController.refreshCommand(discoverabilityTitle: "Refresh Bookmarks"))
NotificationCenter.default.addObserver(self, selector: #selector(handleStatusDeleted), name: .statusDeleted, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(handleStatusDeleted), name: .statusDeleted, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: mastodonController.persistentContainer.viewContext) NotificationCenter.default.addObserver(self, selector: #selector(managedObjectsDidChange), name: .NSManagedObjectContextObjectsDidChange, object: mastodonController.persistentContainer.viewContext)
@ -135,12 +129,12 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
state = .loadingInitial state = .loadingInitial
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.statuses]) snapshot.appendSections([.bookmarks])
snapshot.appendItems([.loadingIndicator]) snapshot.appendItems([.loadingIndicator])
await apply(snapshot: snapshot, animatingDifferences: false) await apply(snapshot: snapshot, animatingDifferences: false)
do { do {
let req = request(.count(Self.pageSize)) let req = Client.getBookmarks(range: .count(BookmarksViewController.pageSize))
let (statuses, pagination) = try await mastodonController.run(req) let (statuses, pagination) = try await mastodonController.run(req)
newer = pagination?.newer newer = pagination?.newer
older = pagination?.older older = pagination?.older
@ -148,14 +142,14 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
await mastodonController.persistentContainer.addAll(statuses: statuses) await mastodonController.persistentContainer.addAll(statuses: statuses)
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections([.statuses]) snapshot.appendSections([.bookmarks])
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, addedLocally: false) }) snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, addedLocally: false) })
await apply(snapshot: snapshot, animatingDifferences: true) await apply(snapshot: snapshot, animatingDifferences: true)
state = .loaded state = .loaded
} catch { } catch {
let config = ToastConfiguration(from: error, with: "Error Loading \(predicateTitle)", in: self) { [weak self] toast in let config = ToastConfiguration(from: error, with: "Error Loading Bookmarks", in: self) { [weak self] toast in
toast.dismissToast(animated: true) toast.dismissToast(animated: true)
await self?.loadInitial() await self?.loadInitial()
} }
@ -180,7 +174,7 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
await apply(snapshot: snapshot, animatingDifferences: false) await apply(snapshot: snapshot, animatingDifferences: false)
do { do {
let req = request(older.withCount(Self.pageSize)) let req = Client.getBookmarks(range: older.withCount(BookmarksViewController.pageSize))
let (statuses, pagination) = try await mastodonController.run(req) let (statuses, pagination) = try await mastodonController.run(req)
self.older = pagination?.older self.older = pagination?.older
@ -190,13 +184,12 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, addedLocally: false) }) snapshot.appendItems(statuses.map { .status(id: $0.id, state: .unknown, addedLocally: false) })
await apply(snapshot: snapshot, animatingDifferences: true) await apply(snapshot: snapshot, animatingDifferences: true)
} catch { } catch {
let config = ToastConfiguration(from: error, with: "Error Loading Older \(predicateTitle)", in: self) { [weak self] toast in let config = ToastConfiguration(from: error, with: "Error Loading Older Bookmarks", in: self) { [weak self] toast in
toast.dismissToast(animated: true) toast.dismissToast(animated: true)
await self?.loadOlder() await self?.loadOlder()
} }
showToast(configuration: config, animated: true) showToast(configuration: config, animated: true)
var snapshot = dataSource.snapshot()
snapshot.deleteItems([.loadingIndicator]) snapshot.deleteItems([.loadingIndicator])
await apply(snapshot: snapshot, animatingDifferences: false) await apply(snapshot: snapshot, animatingDifferences: false)
} }
@ -241,7 +234,7 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
} }
var hasChanges = false var hasChanges = false
if let inserted = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject> { if let inserted = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject> {
for case let status as StatusMO in inserted where predicate(status) { for case let status as StatusMO in inserted where status.bookmarked == true {
prepend(item: .status(id: status.id, state: .unknown, addedLocally: true)) prepend(item: .status(id: status.id, state: .unknown, addedLocally: true))
hasChanges = true hasChanges = true
} }
@ -250,10 +243,10 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
for case let status as StatusMO in updated { for case let status as StatusMO in updated {
let item = Item.status(id: status.id, state: .unknown, addedLocally: true) let item = Item.status(id: status.id, state: .unknown, addedLocally: true)
let exists = snapshot.itemIdentifiers.contains(item) let exists = snapshot.itemIdentifiers.contains(item)
if predicate(status) && !exists { if status.bookmarked == true && !exists {
prepend(item: item) prepend(item: item)
hasChanges = true hasChanges = true
} else if !predicate(status) && exists { } else if status.bookmarked == false && exists {
snapshot.deleteItems([item]) snapshot.deleteItems([item])
hasChanges = true hasChanges = true
} }
@ -279,21 +272,22 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
state = .loadingNewer state = .loadingNewer
Task { Task {
do { do {
let req = request(newer.withCount(Self.pageSize)) let req = Client.getBookmarks(range: newer.withCount(BookmarksViewController.pageSize))
let (statuses, pagination) = try await mastodonController.run(req) let (statuses, pagination) = try await mastodonController.run(req)
self.newer = pagination?.newer self.newer = pagination?.newer
await mastodonController.persistentContainer.addAll(statuses: statuses) await mastodonController.persistentContainer.addAll(statuses: statuses)
var snapshot = dataSource.snapshot() var snapshot = dataSource.snapshot()
let localItems: [String: CollapseState] = Dictionary(uniqueKeysWithValues: snapshot.itemIdentifiers.compactMap { let localItems: [String: CollapseState] = Dictionary(uniqueKeysWithValues: snapshot.itemIdentifiers.compactMap({
if case .status(id: let id, state: let state, addedLocally: true) = $0 { if case .status(id: let id, state: let state, addedLocally: true) = $0 {
return (id, state) return (id, state)
} else { } else {
return nil return nil
} }
}) }))
var newItems: [Item] = [] var newItems: [Item] = []
for status in statuses { for status in statuses {
let state: CollapseState let state: CollapseState
@ -311,9 +305,8 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
snapshot.appendItems(newItems) snapshot.appendItems(newItems)
} }
await apply(snapshot: snapshot, animatingDifferences: true) await apply(snapshot: snapshot, animatingDifferences: true)
} catch { } catch {
let config = ToastConfiguration(from: error, with: "Error Refreshing \(predicateTitle)", in: self) { [weak self] toast in let config = ToastConfiguration(from: error, with: "Error Refreshing Bookmarks", in: self) { [weak self] toast in
toast.dismissToast(animated: true) toast.dismissToast(animated: true)
self?.refresh() self?.refresh()
} }
@ -329,15 +322,15 @@ class LocalPredicateStatusesViewController: UIViewController, CollectionViewCont
} }
extension LocalPredicateStatusesViewController { extension BookmarksViewController {
enum Section { enum Section {
case statuses case bookmarks
} }
enum Item: Equatable, Hashable { enum Item: Equatable, Hashable {
case status(id: String, state: CollapseState, addedLocally: Bool) case status(id: String, state: CollapseState, addedLocally: Bool)
case loadingIndicator case loadingIndicator
var hideSeparators: Bool { var hideIndicators: Bool {
switch self { switch self {
case .loadingIndicator: case .loadingIndicator:
return true return true
@ -369,7 +362,7 @@ extension LocalPredicateStatusesViewController {
} }
} }
extension LocalPredicateStatusesViewController { extension BookmarksViewController {
enum State { enum State {
case unloaded case unloaded
case loadingInitial case loadingInitial
@ -379,7 +372,7 @@ extension LocalPredicateStatusesViewController {
} }
} }
extension LocalPredicateStatusesViewController: UICollectionViewDelegate { extension BookmarksViewController: UICollectionViewDelegate {
func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, willDisplay cell: UICollectionViewCell, forItemAt indexPath: IndexPath) {
if indexPath.section == 0, if indexPath.section == 0,
indexPath.row == collectionView.numberOfItems(inSection: indexPath.section) - 1 { indexPath.row == collectionView.numberOfItems(inSection: indexPath.section) - 1 {
@ -404,7 +397,7 @@ extension LocalPredicateStatusesViewController: UICollectionViewDelegate {
} }
func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? { func collectionView(_ collectionView: UICollectionView, contextMenuConfigurationForItemAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
return (collectionView.cellForItem(at: indexPath) as? TimelineStatusCollectionViewCell)?.contextMenuConfiguration() (collectionView.cellForItem(at: indexPath) as? TimelineStatusCollectionViewCell)?.contextMenuConfiguration()
} }
func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { func collectionView(_ collectionView: UICollectionView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
@ -412,17 +405,17 @@ extension LocalPredicateStatusesViewController: UICollectionViewDelegate {
} }
} }
extension LocalPredicateStatusesViewController: UICollectionViewDragDelegate { extension BookmarksViewController: UICollectionViewDragDelegate {
func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] { func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
return (collectionView.cellForItem(at: indexPath) as? TimelineStatusCollectionViewCell)?.dragItemsForBeginning(session: session) ?? [] (collectionView.cellForItem(at: indexPath) as? TimelineStatusCollectionViewCell)?.dragItemsForBeginning(session: session) ?? []
} }
} }
extension LocalPredicateStatusesViewController: TuskerNavigationDelegate { extension BookmarksViewController: TuskerNavigationDelegate {
var apiController: MastodonController! { mastodonController } var apiController: MastodonController! { mastodonController }
} }
extension LocalPredicateStatusesViewController: StatusCollectionViewCellDelegate { extension BookmarksViewController: StatusCollectionViewCellDelegate {
func statusCellNeedsReconfigure(_ cell: StatusCollectionViewCell, animated: Bool, completion: (() -> Void)?) { func statusCellNeedsReconfigure(_ cell: StatusCollectionViewCell, animated: Bool, completion: (() -> Void)?) {
if let indexPath = collectionView.indexPath(for: cell) { if let indexPath = collectionView.indexPath(for: cell) {
var snapshot = dataSource.snapshot() var snapshot = dataSource.snapshot()
@ -432,17 +425,17 @@ extension LocalPredicateStatusesViewController: StatusCollectionViewCellDelegate
} }
func statusCellShowFiltered(_ cell: StatusCollectionViewCell) { func statusCellShowFiltered(_ cell: StatusCollectionViewCell) {
// filtering isn't supported here // bookmarks aren't filtered
} }
} }
extension LocalPredicateStatusesViewController: TabBarScrollableViewController { extension BookmarksViewController: TabBarScrollableViewController {
func tabBarScrollToTop() { func tabBarScrollToTop() {
collectionView.scrollToTop() collectionView.scrollToTop()
} }
} }
extension LocalPredicateStatusesViewController: StatusBarTappableViewController { extension BookmarksViewController: StatusBarTappableViewController {
func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult { func handleStatusBarTapped(xPosition: CGFloat) -> StatusBarTapActionResult {
collectionView.scrollToTop() collectionView.scrollToTop()
return .stop return .stop

View File

@ -154,7 +154,7 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate, Collect
private func applyInitialSnapshot() { private func applyInitialSnapshot() {
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>() var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
snapshot.appendSections(Section.allCases.filter { $0 != .discover }) snapshot.appendSections(Section.allCases.filter { $0 != .discover })
snapshot.appendItems([.bookmarks, .favorites], toSection: .bookmarks) snapshot.appendItems([.bookmarks], toSection: .bookmarks)
if mastodonController.instanceFeatures.trends, if mastodonController.instanceFeatures.trends,
!Preferences.shared.hideDiscover { !Preferences.shared.hideDiscover {
addDiscoverSection(to: &snapshot) addDiscoverSection(to: &snapshot)
@ -336,9 +336,6 @@ class ExploreViewController: UIViewController, UICollectionViewDelegate, Collect
case .bookmarks: case .bookmarks:
show(BookmarksViewController(mastodonController: mastodonController), sender: nil) show(BookmarksViewController(mastodonController: mastodonController), sender: nil)
case .favorites:
show(FavoritesViewController(mastodonController: mastodonController), sender: nil)
case .trendingStatuses: case .trendingStatuses:
show(TrendingStatusesViewController(mastodonController: mastodonController), sender: nil) show(TrendingStatusesViewController(mastodonController: mastodonController), sender: nil)
@ -411,7 +408,6 @@ extension ExploreViewController {
enum Item: Hashable { enum Item: Hashable {
case bookmarks case bookmarks
case favorites
case trendingStatuses case trendingStatuses
case trendingTags case trendingTags
case trendingLinks case trendingLinks
@ -427,8 +423,6 @@ extension ExploreViewController {
switch self { switch self {
case .bookmarks: case .bookmarks:
return NSLocalizedString("Bookmarks", comment: "bookmarks nav item title") return NSLocalizedString("Bookmarks", comment: "bookmarks nav item title")
case .favorites:
return NSLocalizedString("Favorites", comment: "favorites nav item title")
case .trendingStatuses: case .trendingStatuses:
return NSLocalizedString("Trending Posts", comment: "trending statuses nav item title") return NSLocalizedString("Trending Posts", comment: "trending statuses nav item title")
case .trendingTags: case .trendingTags:
@ -457,8 +451,6 @@ extension ExploreViewController {
switch self { switch self {
case .bookmarks: case .bookmarks:
name = "bookmark.fill" name = "bookmark.fill"
case .favorites:
name = "star.fill"
case .trendingStatuses: case .trendingStatuses:
name = "doc.text.image" name = "doc.text.image"
case .trendingTags: case .trendingTags:
@ -485,8 +477,6 @@ extension ExploreViewController {
switch (lhs, rhs) { switch (lhs, rhs) {
case (.bookmarks, .bookmarks): case (.bookmarks, .bookmarks):
return true return true
case (.favorites, .favorites):
return true
case (.trendingStatuses, .trendingStatuses): case (.trendingStatuses, .trendingStatuses):
return true return true
case (.trendingTags, .trendingTags): case (.trendingTags, .trendingTags):
@ -516,8 +506,6 @@ extension ExploreViewController {
switch self { switch self {
case .bookmarks: case .bookmarks:
hasher.combine("bookmarks") hasher.combine("bookmarks")
case .favorites:
hasher.combine("favorites")
case .trendingStatuses: case .trendingStatuses:
hasher.combine("trendingStatuses") hasher.combine("trendingStatuses")
case .trendingTags: case .trendingTags:

View File

@ -1,27 +0,0 @@
//
// BookmarksViewController.swift
// Tusker
//
// Created by Shadowfacts on 12/15/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
class BookmarksViewController: LocalPredicateStatusesViewController {
init(mastodonController: MastodonController) {
super.init(
predicate: { $0.bookmarked ?? false },
predicateTitle: "Bookmarks",
request: { Client.getBookmarks(range: $0) },
mastodonController: mastodonController
)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -1,27 +0,0 @@
//
// FavoritesViewController.swift
// Tusker
//
// Created by Shadowfacts on 2/4/23.
// Copyright © 2023 Shadowfacts. All rights reserved.
//
import UIKit
import Pachyderm
class FavoritesViewController: LocalPredicateStatusesViewController {
init(mastodonController: MastodonController) {
super.init(
predicate: \.favourited,
predicateTitle: "Favorites",
request: { Client.getFavourites(range: $0) },
mastodonController: mastodonController
)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

View File

@ -41,7 +41,7 @@ class MainSidebarViewController: UIViewController {
} }
var exploreTabItems: [Item] { var exploreTabItems: [Item] {
var items: [Item] = [.explore, .bookmarks, .favorites, .profileDirectory] var items: [Item] = [.explore, .bookmarks, .profileDirectory]
let snapshot = dataSource.snapshot() let snapshot = dataSource.snapshot()
for case let .list(list) in snapshot.itemIdentifiers(inSection: .lists) { for case let .list(list) in snapshot.itemIdentifiers(inSection: .lists) {
items.append(.list(list)) items.append(.list(list))
@ -172,7 +172,6 @@ class MainSidebarViewController: UIViewController {
.tab(.notifications), .tab(.notifications),
.explore, .explore,
.bookmarks, .bookmarks,
.favorites,
.tab(.myProfile) .tab(.myProfile)
], toSection: .tabs) ], toSection: .tabs)
snapshot.appendItems([ snapshot.appendItems([
@ -385,7 +384,7 @@ extension MainSidebarViewController {
} }
enum Item: Hashable { enum Item: Hashable {
case tab(MainTabBarViewController.Tab) case tab(MainTabBarViewController.Tab)
case explore, bookmarks, favorites case explore, bookmarks
case discoverHeader, profileDirectory case discoverHeader, profileDirectory
case listsHeader, list(List), addList case listsHeader, list(List), addList
case savedHashtagsHeader, savedHashtag(Hashtag), addSavedHashtag case savedHashtagsHeader, savedHashtag(Hashtag), addSavedHashtag
@ -399,8 +398,6 @@ extension MainSidebarViewController {
return "Explore" return "Explore"
case .bookmarks: case .bookmarks:
return "Bookmarks" return "Bookmarks"
case .favorites:
return "Favorites"
case .discoverHeader: case .discoverHeader:
return "Discover" return "Discover"
case .profileDirectory: case .profileDirectory:
@ -434,8 +431,6 @@ extension MainSidebarViewController {
return "magnifyingglass" return "magnifyingglass"
case .bookmarks: case .bookmarks:
return "bookmark" return "bookmark"
case .favorites:
return "star"
case .profileDirectory: case .profileDirectory:
return "person.2.fill" return "person.2.fill"
case .list(_): case .list(_):

View File

@ -232,9 +232,9 @@ extension MainSplitViewController: UISplitViewControllerDelegate {
tabBarViewController.select(tab: .explore) tabBarViewController.select(tab: .explore)
case .bookmarks, .favorites, .profileDirectory, .list(_), .savedHashtag(_), .savedInstance(_): case .bookmarks, .profileDirectory, .list(_), .savedHashtag(_), .savedInstance(_):
tabBarViewController.select(tab: .explore) tabBarViewController.select(tab: .explore)
// Make sure the Explore VC doesn't show its search bar when it appears, in case the user was previously // Make sure the Explore VC doesn't show it's search bar when it appears, in case the user was previously
// in compact mode and performing a search. // in compact mode and performing a search.
let exploreNav = tabBarViewController.viewController(for: .explore) as! UINavigationController let exploreNav = tabBarViewController.viewController(for: .explore) as! UINavigationController
let explore = exploreNav.viewControllers.first as! ExploreViewController let explore = exploreNav.viewControllers.first as! ExploreViewController
@ -305,8 +305,6 @@ extension MainSplitViewController: UISplitViewControllerDelegate {
switch tabNavigationStack[1] { switch tabNavigationStack[1] {
case is BookmarksViewController: case is BookmarksViewController:
exploreItem = .bookmarks exploreItem = .bookmarks
case is FavoritesViewController:
exploreItem = .favorites
case let listVC as ListTimelineViewController: case let listVC as ListTimelineViewController:
exploreItem = .list(listVC.list) exploreItem = .list(listVC.list)
case let hashtagVC as HashtagTimelineViewController: case let hashtagVC as HashtagTimelineViewController:
@ -382,8 +380,6 @@ fileprivate extension MainSidebarViewController.Item {
return SearchViewController(mastodonController: mastodonController) return SearchViewController(mastodonController: mastodonController)
case .bookmarks: case .bookmarks:
return BookmarksViewController(mastodonController: mastodonController) return BookmarksViewController(mastodonController: mastodonController)
case .favorites:
return FavoritesViewController(mastodonController: mastodonController)
case .profileDirectory: case .profileDirectory:
return ProfileDirectoryViewController(mastodonController: mastodonController) return ProfileDirectoryViewController(mastodonController: mastodonController)
case let .list(list): case let .list(list):

View File

@ -1,50 +0,0 @@
//
// CopyableLable.swift
// Tusker
//
// Created by Shadowfacts on 2/4/23.
// Copyright © 2023 Shadowfacts. All rights reserved.
//
import UIKit
class CopyableLable: UILabel {
private var _editMenuInteraction: Any!
@available(iOS 16.0, *)
private var editMenuInteraction: UIEditMenuInteraction {
get { _editMenuInteraction as! UIEditMenuInteraction }
set { _editMenuInteraction = newValue }
}
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
if #available(iOS 16.0, *) {
editMenuInteraction = UIEditMenuInteraction(delegate: nil)
addInteraction(editMenuInteraction)
isUserInteractionEnabled = true
addGestureRecognizer(UILongPressGestureRecognizer(target: self, action: #selector(longPressed)))
}
}
override func copy(_ sender: Any?) {
UIPasteboard.general.string = text
}
@available(iOS 16.0, *)
@objc private func longPressed(_ recognizer: UILongPressGestureRecognizer) {
if recognizer.state == .began {
editMenuInteraction.presentEditMenu(with: UIEditMenuConfiguration(identifier: nil, sourcePoint: CGPoint(x: bounds.midX, y: bounds.midY)))
}
}
}

View File

@ -111,7 +111,7 @@
<stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="jwU-EH-hmC"> <stackView opaque="NO" contentMode="scaleToFill" alignment="center" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="jwU-EH-hmC">
<rect key="frame" x="144" y="235" width="103.5" height="23"/> <rect key="frame" x="144" y="235" width="103.5" height="23"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1C3-Pd-QiL" customClass="CopyableLable" customModule="Tusker" customModuleProvider="target"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="751" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1C3-Pd-QiL">
<rect key="frame" x="0.0" y="2.5" width="81" height="18"/> <rect key="frame" x="0.0" y="2.5" width="81" height="18"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/> <fontDescription key="fontDescription" type="system" weight="light" pointSize="15"/>
<color key="textColor" systemColor="secondaryLabelColor"/> <color key="textColor" systemColor="secondaryLabelColor"/>