Compare commits
8 Commits
2a53b24487
...
e3c480131a
Author | SHA1 | Date |
---|---|---|
Shadowfacts | e3c480131a | |
Shadowfacts | 575166f5b4 | |
Shadowfacts | c60aa3e3f3 | |
Shadowfacts | 75f0d12c82 | |
Shadowfacts | 5cf2bc4fbf | |
Shadowfacts | 908b499f8f | |
Shadowfacts | 67c7905acf | |
Shadowfacts | eacafe87b3 |
|
@ -52,15 +52,22 @@ class GalleryDismissAnimationController: NSObject, UIViewControllerAnimatedTrans
|
||||||
appliedSourceToDestTransform = false
|
appliedSourceToDestTransform = false
|
||||||
}
|
}
|
||||||
|
|
||||||
to.view.frame = container.bounds
|
// Moving `to.view` to the container is necessary when the presenting VC (i.e., `to`)
|
||||||
|
// is in the window's root presentation.
|
||||||
|
// But it breaks when the gallery is presented from a sheet-presented VC--in which case
|
||||||
|
// `to.view` is already in the view hierarchy at this point; and adding it to the
|
||||||
|
// container causees it to be removed when the transition completes.
|
||||||
|
if to.view.superview == nil {
|
||||||
|
to.view.frame = container.bounds
|
||||||
|
container.addSubview(to.view)
|
||||||
|
}
|
||||||
|
|
||||||
from.view.frame = container.bounds
|
from.view.frame = container.bounds
|
||||||
|
container.addSubview(from.view)
|
||||||
|
|
||||||
let content = itemViewController.takeContent()
|
let content = itemViewController.takeContent()
|
||||||
content.view.translatesAutoresizingMaskIntoConstraints = true
|
content.view.translatesAutoresizingMaskIntoConstraints = true
|
||||||
content.view.layer.masksToBounds = true
|
content.view.layer.masksToBounds = true
|
||||||
|
|
||||||
container.addSubview(to.view)
|
|
||||||
container.addSubview(from.view)
|
|
||||||
container.addSubview(content.view)
|
container.addSubview(content.view)
|
||||||
|
|
||||||
content.view.frame = destFrameInContainer
|
content.view.frame = destFrameInContainer
|
||||||
|
|
|
@ -75,6 +75,7 @@
|
||||||
D620483423D3801D008A63EF /* LinkTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483323D3801D008A63EF /* LinkTextView.swift */; };
|
D620483423D3801D008A63EF /* LinkTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483323D3801D008A63EF /* LinkTextView.swift */; };
|
||||||
D620483623D38075008A63EF /* ContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483523D38075008A63EF /* ContentTextView.swift */; };
|
D620483623D38075008A63EF /* ContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483523D38075008A63EF /* ContentTextView.swift */; };
|
||||||
D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483723D38190008A63EF /* StatusContentTextView.swift */; };
|
D620483823D38190008A63EF /* StatusContentTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D620483723D38190008A63EF /* StatusContentTextView.swift */; };
|
||||||
|
D6210D762C0B924F009BB569 /* RemoveProfileSuggestionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6210D752C0B924F009BB569 /* RemoveProfileSuggestionService.swift */; };
|
||||||
D621733328F1D5ED004C7DB1 /* ReblogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D621733228F1D5ED004C7DB1 /* ReblogService.swift */; };
|
D621733328F1D5ED004C7DB1 /* ReblogService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D621733228F1D5ED004C7DB1 /* ReblogService.swift */; };
|
||||||
D62275A824F1CA2800B82A16 /* ComposeReplyContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62275A724F1CA2800B82A16 /* ComposeReplyContentView.swift */; };
|
D62275A824F1CA2800B82A16 /* ComposeReplyContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D62275A724F1CA2800B82A16 /* ComposeReplyContentView.swift */; };
|
||||||
D623A53D2635F5590095BD04 /* StatusPollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A53C2635F5590095BD04 /* StatusPollView.swift */; };
|
D623A53D2635F5590095BD04 /* StatusPollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D623A53C2635F5590095BD04 /* StatusPollView.swift */; };
|
||||||
|
@ -506,6 +507,7 @@
|
||||||
D620483323D3801D008A63EF /* LinkTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkTextView.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>"; };
|
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>"; };
|
D620483723D38190008A63EF /* StatusContentTextView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusContentTextView.swift; sourceTree = "<group>"; };
|
||||||
|
D6210D752C0B924F009BB569 /* RemoveProfileSuggestionService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveProfileSuggestionService.swift; sourceTree = "<group>"; };
|
||||||
D621733228F1D5ED004C7DB1 /* ReblogService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReblogService.swift; sourceTree = "<group>"; };
|
D621733228F1D5ED004C7DB1 /* ReblogService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReblogService.swift; sourceTree = "<group>"; };
|
||||||
D62275A724F1CA2800B82A16 /* ComposeReplyContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeReplyContentView.swift; sourceTree = "<group>"; };
|
D62275A724F1CA2800B82A16 /* ComposeReplyContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeReplyContentView.swift; sourceTree = "<group>"; };
|
||||||
D623A53C2635F5590095BD04 /* StatusPollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusPollView.swift; sourceTree = "<group>"; };
|
D623A53C2635F5590095BD04 /* StatusPollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StatusPollView.swift; sourceTree = "<group>"; };
|
||||||
|
@ -1757,6 +1759,7 @@
|
||||||
D6DD8FFC298495A8002AD3FD /* LogoutService.swift */,
|
D6DD8FFC298495A8002AD3FD /* LogoutService.swift */,
|
||||||
D6D79F252A0C8D2700AB2315 /* FetchStatusSourceService.swift */,
|
D6D79F252A0C8D2700AB2315 /* FetchStatusSourceService.swift */,
|
||||||
D630C3CB2BC5FD4600208903 /* GetAuthorizationTokenService.swift */,
|
D630C3CB2BC5FD4600208903 /* GetAuthorizationTokenService.swift */,
|
||||||
|
D6210D752C0B924F009BB569 /* RemoveProfileSuggestionService.swift */,
|
||||||
);
|
);
|
||||||
path = API;
|
path = API;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -2362,6 +2365,7 @@
|
||||||
D698F46D2BD0B8310054DB14 /* AnnouncementsCollection.swift in Sources */,
|
D698F46D2BD0B8310054DB14 /* AnnouncementsCollection.swift in Sources */,
|
||||||
D61F75AF293AF50C00C0B37F /* EditedFilter.swift in Sources */,
|
D61F75AF293AF50C00C0B37F /* EditedFilter.swift in Sources */,
|
||||||
D691771929A7B8820054D7EF /* ProfileHeaderMovedOverlayView.swift in Sources */,
|
D691771929A7B8820054D7EF /* ProfileHeaderMovedOverlayView.swift in Sources */,
|
||||||
|
D6210D762C0B924F009BB569 /* RemoveProfileSuggestionService.swift in Sources */,
|
||||||
D61F75B9293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift in Sources */,
|
D61F75B9293C15A000C0B37F /* ZeroHeightCollectionViewCell.swift in Sources */,
|
||||||
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */,
|
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */,
|
||||||
04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */,
|
04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */,
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// RemoveProfileSuggestionService.swift
|
||||||
|
// Tusker
|
||||||
|
//
|
||||||
|
// Created by Shadowfacts on 6/1/24.
|
||||||
|
// Copyright © 2024 Shadowfacts. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Pachyderm
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
class RemoveProfileSuggestionService {
|
||||||
|
private let accountID: String
|
||||||
|
private let mastodonController: MastodonController
|
||||||
|
private let presenter: any TuskerNavigationDelegate
|
||||||
|
private let completionHandler: @MainActor () -> Void
|
||||||
|
|
||||||
|
init(accountID: String, mastodonController: MastodonController, presenter: any TuskerNavigationDelegate, completionHandler: @MainActor @escaping () -> Void) {
|
||||||
|
self.accountID = accountID
|
||||||
|
self.mastodonController = mastodonController
|
||||||
|
self.presenter = presenter
|
||||||
|
self.completionHandler = completionHandler
|
||||||
|
}
|
||||||
|
|
||||||
|
func run() async {
|
||||||
|
let req = Suggestion.remove(accountID: accountID)
|
||||||
|
do {
|
||||||
|
_ = try await mastodonController.run(req)
|
||||||
|
completionHandler()
|
||||||
|
} catch {
|
||||||
|
let config = ToastConfiguration(from: error, with: "Error Removing Suggestion", in: presenter) { toast in
|
||||||
|
toast.dismissToast(animated: true)
|
||||||
|
await self.run()
|
||||||
|
}
|
||||||
|
self.presenter.showToast(configuration: config, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -184,7 +184,19 @@ extension SuggestedProfilesViewController: UICollectionViewDelegate {
|
||||||
return UIContextMenuConfiguration {
|
return UIContextMenuConfiguration {
|
||||||
ProfileViewController(accountID: id, mastodonController: self.mastodonController)
|
ProfileViewController(accountID: id, mastodonController: self.mastodonController)
|
||||||
} actionProvider: { _ in
|
} actionProvider: { _ in
|
||||||
UIMenu(children: self.actionsForProfile(accountID: id, source: .view(cell)))
|
let dismiss = UIAction(title: "Remove Suggestion", image: UIImage(systemName: "trash"), attributes: .destructive) { [unowned self] _ in
|
||||||
|
let service = RemoveProfileSuggestionService(accountID: id, mastodonController: self.mastodonController, presenter: self) { [weak self] in
|
||||||
|
guard let self else { return }
|
||||||
|
var snapshot = self.dataSource.snapshot()
|
||||||
|
// the source here doesn't matter, since it's ignored by the equatable and hashable impls
|
||||||
|
snapshot.deleteItems([.account(id, .global)])
|
||||||
|
self.dataSource.apply(snapshot, animatingDifferences: true)
|
||||||
|
}
|
||||||
|
Task {
|
||||||
|
await service.run()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UIMenu(children: [UIMenu(options: .displayInline, children: [dismiss])] + self.actionsForProfile(accountID: id, source: .view(cell)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -368,20 +368,14 @@ class TrendsViewController: UIViewController, CollectionViewController {
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
private func removeProfileSuggestion(accountID: String) async {
|
private func removeProfileSuggestion(accountID: String) async {
|
||||||
let req = Suggestion.remove(accountID: accountID)
|
let service = RemoveProfileSuggestionService(accountID: accountID, mastodonController: mastodonController, presenter: self) { [weak self] in
|
||||||
do {
|
guard let self else { return }
|
||||||
_ = try await mastodonController.run(req)
|
var snapshot = self.dataSource.snapshot()
|
||||||
var snapshot = dataSource.snapshot()
|
|
||||||
// the source here doesn't matter, since it's ignored by the equatable and hashable impls
|
// the source here doesn't matter, since it's ignored by the equatable and hashable impls
|
||||||
snapshot.deleteItems([.account(accountID, .global)])
|
snapshot.deleteItems([.account(accountID, .global)])
|
||||||
await apply(snapshot: snapshot)
|
self.dataSource.apply(snapshot, animatingDifferences: true)
|
||||||
} catch {
|
|
||||||
let config = ToastConfiguration(from: error, with: "Error Removing Suggestion", in: self) { [unowned self] toast in
|
|
||||||
toast.dismissToast(animated: true)
|
|
||||||
_ = await self.removeProfileSuggestion(accountID: accountID)
|
|
||||||
}
|
|
||||||
self.showToast(configuration: config, animated: true)
|
|
||||||
}
|
}
|
||||||
|
await service.run()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,7 @@ class AccountSwitchingContainerViewController: UIViewController {
|
||||||
oldRoot.view.transform = CGAffineTransform(translationX: 0, y: -newInitialOffset).scaledBy(x: scale, y: scale)
|
oldRoot.view.transform = CGAffineTransform(translationX: 0, y: -newInitialOffset).scaledBy(x: scale, y: scale)
|
||||||
newRoot.view.transform = .identity
|
newRoot.view.transform = .identity
|
||||||
} completion: { (_) in
|
} completion: { (_) in
|
||||||
|
oldRoot.view.transform = .identity
|
||||||
oldRoot.additionalSafeAreaInsets = origAdditionalSafeAreaInsets
|
oldRoot.additionalSafeAreaInsets = origAdditionalSafeAreaInsets
|
||||||
oldRoot.removeViewAndController()
|
oldRoot.removeViewAndController()
|
||||||
newRoot.view.layer.masksToBounds = false
|
newRoot.view.layer.masksToBounds = false
|
||||||
|
|
|
@ -180,26 +180,41 @@ class MainSplitViewController: UISplitViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleSidebarCommandTimelines() {
|
@objc func handleSidebarCommandTimelines() {
|
||||||
|
if let previous = sidebar.selectedItem {
|
||||||
|
navigationStacks[previous] = secondaryNavController.viewControllers
|
||||||
|
}
|
||||||
sidebar.select(item: .tab(.timelines), animated: false)
|
sidebar.select(item: .tab(.timelines), animated: false)
|
||||||
select(item: .tab(.timelines))
|
select(item: .tab(.timelines))
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleSidebarCommandNotifications() {
|
@objc func handleSidebarCommandNotifications() {
|
||||||
|
if let previous = sidebar.selectedItem {
|
||||||
|
navigationStacks[previous] = secondaryNavController.viewControllers
|
||||||
|
}
|
||||||
sidebar.select(item: .tab(.notifications), animated: false)
|
sidebar.select(item: .tab(.notifications), animated: false)
|
||||||
select(item: .tab(.notifications))
|
select(item: .tab(.notifications))
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleSidebarCommandExplore() {
|
@objc func handleSidebarCommandExplore() {
|
||||||
|
if let previous = sidebar.selectedItem {
|
||||||
|
navigationStacks[previous] = secondaryNavController.viewControllers
|
||||||
|
}
|
||||||
sidebar.select(item: .tab(.explore), animated: false)
|
sidebar.select(item: .tab(.explore), animated: false)
|
||||||
select(item: .tab(.explore))
|
select(item: .tab(.explore))
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleSidebarCommandBookmarks() {
|
@objc func handleSidebarCommandBookmarks() {
|
||||||
|
if let previous = sidebar.selectedItem {
|
||||||
|
navigationStacks[previous] = secondaryNavController.viewControllers
|
||||||
|
}
|
||||||
sidebar.select(item: .bookmarks, animated: false)
|
sidebar.select(item: .bookmarks, animated: false)
|
||||||
select(item: .bookmarks)
|
select(item: .bookmarks)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleSidebarCommandMyProfile() {
|
@objc func handleSidebarCommandMyProfile() {
|
||||||
|
if let previous = sidebar.selectedItem {
|
||||||
|
navigationStacks[previous] = secondaryNavController.viewControllers
|
||||||
|
}
|
||||||
sidebar.select(item: .tab(.myProfile), animated: false)
|
sidebar.select(item: .tab(.myProfile), animated: false)
|
||||||
select(item: .tab(.myProfile))
|
select(item: .tab(.myProfile))
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,12 +273,17 @@ private class ColumnView: UIView {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func installCloseBarButton(navigationItem: UINavigationItem) {
|
private func installCloseBarButton(navigationItem: UINavigationItem) {
|
||||||
let item = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .done, target: self, action: #selector(closeNavigationColumn))
|
func makeItem() -> UIBarButtonItem {
|
||||||
item.accessibilityLabel = "Close Column"
|
let item = UIBarButtonItem(image: UIImage(systemName: "xmark"), style: .done, target: self, action: #selector(closeNavigationColumn))
|
||||||
if navigationItem.leftBarButtonItems != nil {
|
item.accessibilityLabel = "Close Column"
|
||||||
navigationItem.leftBarButtonItems!.insert(item, at: 0)
|
return item
|
||||||
|
}
|
||||||
|
if let leftItems = navigationItem.leftBarButtonItems {
|
||||||
|
if !leftItems.contains(where: { $0.action == #selector(closeNavigationColumn) }) {
|
||||||
|
navigationItem.leftBarButtonItems!.insert(makeItem(), at: 0)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
navigationItem.leftBarButtonItems = [item]
|
navigationItem.leftBarButtonItems = [makeItem()]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,8 +50,11 @@ extension UIViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeViewAndController() {
|
func removeViewAndController() {
|
||||||
|
beginAppearanceTransition(false, animated: false)
|
||||||
view.removeFromSuperview()
|
view.removeFromSuperview()
|
||||||
|
willMove(toParent: nil)
|
||||||
removeFromParent()
|
removeFromParent()
|
||||||
|
endAppearanceTransition()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,11 @@ class CachedImageView: UIImageView {
|
||||||
commonInit()
|
commonInit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deinit {
|
||||||
|
fetchTask?.cancel()
|
||||||
|
blurHashTask?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
private func commonInit() {
|
private func commonInit() {
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(preferencesChanged), name: .preferencesChanged, object: nil)
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,9 @@ class ProfileHeaderView: UIView {
|
||||||
weak var delegate: ProfileHeaderViewDelegate?
|
weak var delegate: ProfileHeaderViewDelegate?
|
||||||
var mastodonController: MastodonController! { delegate?.apiController }
|
var mastodonController: MastodonController! { delegate?.apiController }
|
||||||
|
|
||||||
@IBOutlet weak var headerImageView: UIImageView!
|
@IBOutlet weak var headerImageView: CachedImageView!
|
||||||
@IBOutlet weak var avatarContainerView: UIView!
|
@IBOutlet weak var avatarContainerView: UIView!
|
||||||
@IBOutlet weak var avatarImageView: UIImageView!
|
@IBOutlet weak var avatarImageView: CachedImageView!
|
||||||
@IBOutlet weak var moreButton: ProfileHeaderButton!
|
@IBOutlet weak var moreButton: ProfileHeaderButton!
|
||||||
@IBOutlet weak var followButton: ProfileHeaderButton!
|
@IBOutlet weak var followButton: ProfileHeaderButton!
|
||||||
@IBOutlet weak var displayNameLabel: AccountDisplayNameLabel!
|
@IBOutlet weak var displayNameLabel: AccountDisplayNameLabel!
|
||||||
|
@ -44,8 +44,6 @@ class ProfileHeaderView: UIView {
|
||||||
|
|
||||||
var accountID: String!
|
var accountID: String!
|
||||||
|
|
||||||
private var imagesTask: Task<Void, Never>?
|
|
||||||
|
|
||||||
private var isGrayscale = false
|
private var isGrayscale = false
|
||||||
private var followButtonMode = FollowButtonMode.follow {
|
private var followButtonMode = FollowButtonMode.follow {
|
||||||
didSet {
|
didSet {
|
||||||
|
@ -56,10 +54,6 @@ class ProfileHeaderView: UIView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
|
||||||
imagesTask?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
override func awakeFromNib() {
|
override func awakeFromNib() {
|
||||||
super.awakeFromNib()
|
super.awakeFromNib()
|
||||||
|
|
||||||
|
@ -69,11 +63,13 @@ class ProfileHeaderView: UIView {
|
||||||
avatarContainerView.layer.cornerCurve = .continuous
|
avatarContainerView.layer.cornerCurve = .continuous
|
||||||
// Set zPositions so the gallery presentation/dismissal animation looks correct.
|
// Set zPositions so the gallery presentation/dismissal animation looks correct.
|
||||||
avatarContainerView.layer.zPosition = 2
|
avatarContainerView.layer.zPosition = 2
|
||||||
|
avatarImageView.cache = .avatars
|
||||||
avatarImageView.layer.masksToBounds = true
|
avatarImageView.layer.masksToBounds = true
|
||||||
avatarImageView.layer.cornerCurve = .continuous
|
avatarImageView.layer.cornerCurve = .continuous
|
||||||
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(avatarPressed)))
|
avatarImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(avatarPressed)))
|
||||||
avatarImageView.isUserInteractionEnabled = true
|
avatarImageView.isUserInteractionEnabled = true
|
||||||
|
|
||||||
|
headerImageView.cache = .headers
|
||||||
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
|
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
|
||||||
headerImageView.isUserInteractionEnabled = true
|
headerImageView.isUserInteractionEnabled = true
|
||||||
headerImageView.layer.zPosition = 1
|
headerImageView.layer.zPosition = 1
|
||||||
|
@ -138,11 +134,11 @@ class ProfileHeaderView: UIView {
|
||||||
usernameLabel.text = "@\(account.acct)"
|
usernameLabel.text = "@\(account.acct)"
|
||||||
lockImageView.isHidden = !account.locked
|
lockImageView.isHidden = !account.locked
|
||||||
|
|
||||||
imagesTask?.cancel()
|
if let avatar = account.avatar {
|
||||||
let avatar = account.avatar
|
avatarImageView.update(for: avatar)
|
||||||
let header = account.header
|
}
|
||||||
imagesTask = Task {
|
if let header = account.header {
|
||||||
await updateImages(avatar: avatar, header: header)
|
headerImageView.update(for: header)
|
||||||
}
|
}
|
||||||
|
|
||||||
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: delegate?.actionsForProfile(accountID: accountID, source: .view(moreButton), fetchRelationship: false) ?? [])
|
moreButton.menu = UIMenu(title: "", image: nil, identifier: nil, options: [], children: delegate?.actionsForProfile(accountID: accountID, source: .view(moreButton), fetchRelationship: false) ?? [])
|
||||||
|
@ -294,44 +290,6 @@ class ProfileHeaderView: UIView {
|
||||||
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
|
avatarContainerView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarContainerView)
|
||||||
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
||||||
displayNameLabel.updateForAccountDisplayName(account: account)
|
displayNameLabel.updateForAccountDisplayName(account: account)
|
||||||
|
|
||||||
if isGrayscale != Preferences.shared.grayscaleImages {
|
|
||||||
isGrayscale = Preferences.shared.grayscaleImages
|
|
||||||
imagesTask?.cancel()
|
|
||||||
let avatar = account.avatar
|
|
||||||
let header = account.header
|
|
||||||
imagesTask = Task {
|
|
||||||
await updateImages(avatar: avatar, header: header)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private nonisolated func updateImages(avatar: URL?, header: URL?) async {
|
|
||||||
await withTaskGroup(of: Void.self) { group in
|
|
||||||
group.addTask {
|
|
||||||
guard let avatar,
|
|
||||||
let image = await ImageCache.avatars.get(avatar, loadOriginal: true).1,
|
|
||||||
let transformedImage = await ImageGrayscalifier.convertIfNecessary(url: avatar, image: image),
|
|
||||||
!Task.isCancelled else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await MainActor.run {
|
|
||||||
self.avatarImageView.image = transformedImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
group.addTask {
|
|
||||||
guard let header,
|
|
||||||
let image = await ImageCache.avatars.get(header, loadOriginal: true).1,
|
|
||||||
let transformedImage = await ImageGrayscalifier.convertIfNecessary(url: header, image: image),
|
|
||||||
!Task.isCancelled else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
await MainActor.run {
|
|
||||||
self.headerImageView.image = transformedImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await group.waitForAll()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func formatBigNumber(_ value: Int) -> (String, String) {
|
private func formatBigNumber(_ value: Int) -> (String, String) {
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="iOS"/>
|
<deployment identifier="iOS"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22684"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
@ -14,7 +14,7 @@
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iN0-l3-epB" customClass="ProfileHeaderView" customModule="Tusker" customModuleProvider="target">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="iN0-l3-epB" customClass="ProfileHeaderView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dgG-dR-lSv">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dgG-dR-lSv" customClass="CachedImageView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="48" width="414" height="150"/>
|
<rect key="frame" x="0.0" y="48" width="414" height="150"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="150" id="aCE-CA-XWm"/>
|
<constraint firstAttribute="height" constant="150" id="aCE-CA-XWm"/>
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wT9-2J-uSY">
|
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wT9-2J-uSY">
|
||||||
<rect key="frame" x="16" y="138" width="120" height="120"/>
|
<rect key="frame" x="16" y="138" width="120" height="120"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TkY-oK-if4">
|
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="TkY-oK-if4" customClass="CachedImageView" customModule="Tusker" customModuleProvider="target">
|
||||||
<rect key="frame" x="2" y="2" width="116" height="116"/>
|
<rect key="frame" x="2" y="2" width="116" height="116"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="height" constant="116" id="eDg-Vc-o8R"/>
|
<constraint firstAttribute="height" constant="116" id="eDg-Vc-o8R"/>
|
||||||
|
|
|
@ -640,7 +640,7 @@ extension ConversationMainStatusCollectionViewCell: UIPointerInteractionDelegate
|
||||||
return defaultRegion
|
return defaultRegion
|
||||||
} else if let button = interaction.view as? UIButton,
|
} else if let button = interaction.view as? UIButton,
|
||||||
actionButtons.contains(button) {
|
actionButtons.contains(button) {
|
||||||
var rect = button.convert(button.imageView!.bounds, to: button.imageView!)
|
var rect = button.convert(button.imageView!.bounds, from: button.imageView!)
|
||||||
rect = rect.insetBy(dx: -24, dy: -24)
|
rect = rect.insetBy(dx: -24, dy: -24)
|
||||||
return UIPointerRegion(rect: rect)
|
return UIPointerRegion(rect: rect)
|
||||||
}
|
}
|
||||||
|
@ -654,8 +654,8 @@ extension ConversationMainStatusCollectionViewCell: UIPointerInteractionDelegate
|
||||||
} else if let button = interaction.view as? UIButton,
|
} else if let button = interaction.view as? UIButton,
|
||||||
actionButtons.contains(button) {
|
actionButtons.contains(button) {
|
||||||
let preview = UITargetedPreview(view: button.imageView!)
|
let preview = UITargetedPreview(view: button.imageView!)
|
||||||
var rect = button.convert(button.imageView!.bounds, to: button.imageView!)
|
var rect = button.convert(button.imageView!.bounds, from: button.imageView!)
|
||||||
rect = rect.insetBy(dx: -24, dy: -24)
|
rect = rect.insetBy(dx: -8, dy: -8)
|
||||||
return UIPointerStyle(effect: .highlight(preview), shape: .roundedRect(rect))
|
return UIPointerStyle(effect: .highlight(preview), shape: .roundedRect(rect))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
Loading…
Reference in New Issue