Compare commits

..

5 Commits

12 changed files with 88 additions and 52 deletions

View File

@ -167,11 +167,23 @@ extension DraftAttachment: NSItemProviderReading {
type = .png
}
// Read the caption from the image itself, if there is one.
let caption: String
if let source = CGImageSourceCreateWithData(data as CFData, [kCGImageSourceTypeIdentifierHint: typeIdentifier as CFString] as CFDictionary),
let properties = CGImageSourceCopyPropertiesAtIndex(source, 0, nil) as? [String: Any],
// This is the dictionary for TIFF properties, but it's present for other image types too
let tiffProperties = properties[kCGImagePropertyTIFFDictionary as String] as? [String: Any],
let imageDescription = tiffProperties[kCGImagePropertyTIFFImageDescription as String] as? String {
caption = imageDescription
} else {
caption = ""
}
let attachment = DraftAttachment(entity: DraftsPersistentContainer.shared.persistentStoreCoordinator.managedObjectModel.entitiesByName["DraftAttachment"]!, insertInto: nil)
attachment.id = UUID()
attachment.fileURL = try writeDataToFile(data, id: attachment.id, type: type)
attachment.fileType = type.identifier
attachment.attachmentDescription = ""
attachment.attachmentDescription = caption
return attachment
}

View File

@ -165,7 +165,6 @@
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = D663626B21361C6700C9CBA2 /* Account+Preferences.swift */; };
D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */; };
D6676CA527A8D0020052936B /* WebURLFoundationExtras in Frameworks */ = {isa = PBXBuildFile; productRef = D6676CA427A8D0020052936B /* WebURLFoundationExtras */; };
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */; };
D66A77BB233838DC0058F1EC /* UIFont+Traits.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */; };
D66C900B28DAB7FD00217BF2 /* TimelineViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */; };
D674A50927F9128D00BA03AC /* Pachyderm in Frameworks */ = {isa = PBXBuildFile; productRef = D674A50827F9128D00BA03AC /* Pachyderm */; };
@ -599,7 +598,6 @@
D6620ACD2511A0ED00312CA0 /* StatusStateResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusStateResolver.swift; sourceTree = "<group>"; };
D663626B21361C6700C9CBA2 /* Account+Preferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Account+Preferences.swift"; sourceTree = "<group>"; };
D6674AE923341F7600E8DF94 /* AppShortcutItems.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppShortcutItems.swift; sourceTree = "<group>"; };
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Mastodon+Equatable.swift"; sourceTree = "<group>"; };
D66A77BA233838DC0058F1EC /* UIFont+Traits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Traits.swift"; sourceTree = "<group>"; };
D66C900A28DAB7FD00217BF2 /* TimelineViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineViewController.swift; sourceTree = "<group>"; };
D671A6BE299DA96100A81FEA /* Tusker-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Tusker-Bridging-Header.h"; sourceTree = "<group>"; };
@ -1324,7 +1322,6 @@
D667E5F62135C2ED0057A976 /* Extensions */ = {
isa = PBXGroup;
children = (
D667E5F72135C3040057A976 /* Mastodon+Equatable.swift */,
D663626B21361C6700C9CBA2 /* Account+Preferences.swift */,
D6F4D79329ECB0AF00351B87 /* UIBackgroundConfiguration+AppColors.swift */,
D6333B362137838300CE884A /* AttributedString+Helpers.swift */,
@ -2216,7 +2213,6 @@
D6674AEA23341F7600E8DF94 /* AppShortcutItems.swift in Sources */,
D6DD8FFF2984D327002AD3FD /* BookmarksViewController.swift in Sources */,
D646DCD22A06F2510059ECEB /* NotificationsCollectionViewController.swift in Sources */,
D667E5F82135C3040057A976 /* Mastodon+Equatable.swift in Sources */,
D6B4A4FF2506B81A000C81C1 /* AccountDisplayNameView.swift in Sources */,
D63D8DF42850FE7A008D95E1 /* ViewTags.swift in Sources */,
D630C3CC2BC5FD4600208903 /* GetAuthorizationTokenService.swift in Sources */,

View File

@ -19,7 +19,7 @@ import UserAccounts
fileprivate let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "PersistentStore")
class MastodonCachePersistentStore: NSPersistentCloudKitContainer {
class MastodonCachePersistentStore: NSPersistentCloudKitContainer, @unchecked Sendable {
private let accountInfo: UserAccountInfo?

View File

@ -1,21 +0,0 @@
//
// Status+Equatable.swift
// Tusker
//
// Created by Shadowfacts on 8/28/18.
// Copyright © 2018 Shadowfacts. All rights reserved.
//
import Pachyderm
extension Status: Equatable {
public static func ==(lhs: Status, rhs: Status) -> Bool {
return lhs.id == rhs.id
}
}
extension Account: Equatable {
public static func ==(lhs: Account, rhs: Account) -> Bool {
return lhs.id == rhs.id
}
}

View File

@ -88,7 +88,7 @@ struct AnnouncementListRow: View {
Button(role: .destructive) {
Task {
await dismissAnnouncement()
await removeAnnouncement()
removeAnnouncement()
}
} label: {
Label("Dismiss", systemImage: "xmark")

View File

@ -89,12 +89,14 @@ class VideoGalleryContentViewController: UIViewController, GalleryContentViewCon
hideControlsWorkItem?.cancel()
if player.rate > 0 && info.oldValue == 0 {
hideControlsWorkItem = DispatchWorkItem { [weak self] in
guard let self,
let container = self.container,
container.galleryControlsVisible else {
return
MainActor.runUnsafely {
guard let self,
let container = self.container,
container.galleryControlsVisible else {
return
}
container.setGalleryControlsVisible(false, animated: true)
}
container.setGalleryControlsVisible(false, animated: true)
}
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5), execute: hideControlsWorkItem!)
}
@ -114,12 +116,45 @@ class VideoGalleryContentViewController: UIViewController, GalleryContentViewCon
MainActor.runUnsafely {
if item.status == .readyToPlay {
self.container?.setGalleryContentLoading(false)
statusObservation = nil
self.statusObservation = nil
} else if item.status == .failed,
let error = item.error {
self.container?.setGalleryContentLoading(false)
self.showErrorView(error)
self.statusObservation = nil
}
}
})
}
private func showErrorView(_ error: any Error) {
let image = UIImageView(image: UIImage(systemName: "exclamationmark.triangle.fill")!)
image.tintColor = .secondaryLabel
image.contentMode = .scaleAspectFit
let label = UILabel()
label.text = "Error Loading"
label.font = .preferredFont(forTextStyle: .title1).withTraits(.traitBold)!
label.textColor = .secondaryLabel
label.adjustsFontForContentSizeCategory = true
let stackView = UIStackView(arrangedSubviews: [
image,
label,
])
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .center
stackView.spacing = 8
view.addSubview(stackView)
NSLayoutConstraint.activate([
image.widthAnchor.constraint(equalToConstant: 64),
image.heightAnchor.constraint(equalToConstant: 64),
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
}
@objc private func preferencesChanged() {
if isGrayscale != Preferences.shared.grayscaleImages {
let isPlaying = player.rate > 0

View File

@ -15,7 +15,7 @@ protocol MainSidebarViewControllerDelegate: AnyObject {
func sidebarRequestPresentCompose(_ sidebarViewController: MainSidebarViewController)
func sidebar(_ sidebarViewController: MainSidebarViewController, didSelectItem item: MainSidebarViewController.Item, previousItem: MainSidebarViewController.Item?)
func sidebar(_ sidebarViewController: MainSidebarViewController, showViewController viewController: UIViewController, previousItem: MainSidebarViewController.Item?)
func sidebar(_ sidebarViewController: MainSidebarViewController, scrollToTopFor item: MainSidebarViewController.Item)
func sidebar(_ sidebarViewController: MainSidebarViewController, didReselectItem item: MainSidebarViewController.Item)
}
class MainSidebarViewController: UIViewController {
@ -452,7 +452,7 @@ extension MainSidebarViewController: UICollectionViewDelegate {
}
itemLastSelectedTimestamps[item] = Date()
if previouslySelectedItem == item {
sidebarDelegate?.sidebar(self, scrollToTopFor: item)
sidebarDelegate?.sidebar(self, didReselectItem: item)
} else if [MainSidebarViewController.Item.tab(.compose), .addList, .addSavedHashtag, .addSavedInstance].contains(item) {
if let previous = previouslySelectedItem, let indexPath = dataSource.indexPath(for: previous) {
collectionView.selectItem(at: indexPath, animated: false, scrollPosition: .centeredVertically)

View File

@ -493,8 +493,12 @@ extension MainSplitViewController: MainSidebarViewControllerDelegate {
secondaryNavController.viewControllers = [viewController]
}
func sidebar(_ sidebarViewController: MainSidebarViewController, scrollToTopFor item: MainSidebarViewController.Item) {
(secondaryNavController as? TabBarScrollableViewController)?.tabBarScrollToTop()
func sidebar(_ sidebarViewController: MainSidebarViewController, didReselectItem item: MainSidebarViewController.Item) {
if secondaryNavController.viewControllers.count == 1 {
(secondaryNavController.topViewController as? TabBarScrollableViewController)?.tabBarScrollToTop()
} else {
secondaryNavController.popToRootViewController(animated: true)
}
}
}

View File

@ -441,10 +441,12 @@ extension NewMainTabBarViewController: UITabBarControllerDelegate {
return true
} else if let selectedTab,
selectedTab == tab,
let nav = selectedViewController as? any NavigationControllerProtocol,
nav.viewControllers.count == 1,
let scrollableVC = nav.viewControllers[0] as? TabBarScrollableViewController {
scrollableVC.tabBarScrollToTop()
let nav = selectedViewController as? any NavigationControllerProtocol {
if nav.viewControllers.count == 1 {
(nav.viewControllers[0] as? TabBarScrollableViewController)?.tabBarScrollToTop()
} else {
nav.popToRootViewController(animated: true)
}
return false
} else {
return true

View File

@ -87,7 +87,7 @@ struct PushInstanceSettingsView: View {
}
let subscription = try await PushManager.shared.createSubscription(account: account)
let mastodonController = await MastodonController.getForAccount(account)
let mastodonController = MastodonController.getForAccount(account)
do {
let result = try await mastodonController.createPushSubscription(subscription: subscription)
PushManager.logger.debug("Push subscription \(result.id, privacy: .public) created on \(account.instanceURL) with endpoint \(result.endpoint, privacy: .public)")
@ -95,25 +95,25 @@ struct PushInstanceSettingsView: View {
return true
} catch {
// if creation failed, remove the subscription locally as well
await PushManager.shared.removeSubscription(account: account)
PushManager.shared.removeSubscription(account: account)
throw error
}
}
private func disableNotifications() async throws {
let mastodonController = await MastodonController.getForAccount(account)
let mastodonController = MastodonController.getForAccount(account)
try await mastodonController.deletePushSubscription()
await PushManager.shared.removeSubscription(account: account)
PushManager.shared.removeSubscription(account: account)
subscription = nil
PushManager.logger.debug("Push subscription removed on \(account.instanceURL)")
}
private func updateSubscription(alerts: PushNotifications.PushSubscription.Alerts, policy: PushNotifications.PushSubscription.Policy) async -> Bool {
let mastodonController = await MastodonController.getForAccount(account)
let mastodonController = MastodonController.getForAccount(account)
do {
let result = try await mastodonController.updatePushSubscription(alerts: alerts, policy: policy)
PushManager.logger.debug("Push subscription \(result.id, privacy: .public) updated on \(account.instanceURL)")
await PushManager.shared.updateSubscription(account: account, alerts: alerts, policy: policy)
PushManager.shared.updateSubscription(account: account, alerts: alerts, policy: policy)
subscription?.alerts = alerts
subscription?.policy = policy
return true

View File

@ -241,13 +241,19 @@ class SplitNavigationController: UIViewController {
// otherwise the secondary nav's contents disappear immediately, rather than sliding off-screen
let animator = UIViewPropertyAnimator(duration: 0.35, curve: .easeInOut) {
self.isLayingOutForAnimation = true
self.setSecondaryVisible(false)
NSLayoutConstraint.deactivate(self.constraints)
self.constraints = [
self.rootNav.view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: self.rootNav.view.bounds.minX),
self.rootNav.view.trailingAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.trailingAnchor),
self.secondaryNav.view.widthAnchor.constraint(equalToConstant: self.secondaryNav.view.bounds.width),
]
NSLayoutConstraint.activate(self.constraints)
self.view.layoutIfNeeded()
}
animator.addCompletion { _ in
self.secondaryNav.viewControllers = []
self.isLayingOutForAnimation = false
// self.updateSecondaryNavVisibility()
self.secondaryNav.viewControllers = []
self.updateSecondaryNavVisibility()
}
animator.startAnimation()
} else {

View File

@ -61,7 +61,9 @@ class GifvController {
private func updatePresentationSizeObservation() {
presentationSizeObservation = item.observe(\.presentationSize, changeHandler: { [unowned self] item, _ in
self.presentationSizeSubject.send(item.presentationSize)
DispatchQueue.main.async {
self.presentationSizeSubject.send(item.presentationSize)
}
})
}