Compare commits

..

No commits in common. "da6ff67a514dcd4b9c0928ed52ec2cb97d34e8d9" and "c85836eda6b8c02a7b5e92c3782f9e62e1bcb8f0" have entirely different histories.

19 changed files with 131 additions and 304 deletions

View File

@ -89,7 +89,6 @@
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; }; D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.swift */; };
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.swift */; }; D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.swift */; };
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; }; D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; }; D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; };
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */; }; D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */; };
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */; }; D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */; };
@ -349,7 +348,6 @@
D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; }; D6333B362137838300CE884A /* AttributedString+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Helpers.swift"; sourceTree = "<group>"; };
D6333B762138D94E00CE884A /* ComposeMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMediaView.swift; sourceTree = "<group>"; }; D6333B762138D94E00CE884A /* ComposeMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMediaView.swift; sourceTree = "<group>"; };
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; }; D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesNavigationController.swift; sourceTree = "<group>"; };
D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = DomainBlocks.plist; sourceTree = "<group>"; }; D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = DomainBlocks.plist; sourceTree = "<group>"; };
D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; }; D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsTableViewController.swift; sourceTree = "<group>"; };
D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineTextAttachment.swift; sourceTree = "<group>"; }; D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InlineTextAttachment.swift; sourceTree = "<group>"; };
@ -813,7 +811,6 @@
D641C789213DD87E004B4513 /* Preferences */ = { D641C789213DD87E004B4513 /* Preferences */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */,
04586B4022B2FFB10021BD04 /* PreferencesView.swift */, 04586B4022B2FFB10021BD04 /* PreferencesView.swift */,
04586B4222B301470021BD04 /* AppearancePrefsView.swift */, 04586B4222B301470021BD04 /* AppearancePrefsView.swift */,
0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */, 0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */,
@ -1596,7 +1593,6 @@
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */, D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */, D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */, D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */, D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */,
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */, D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */, D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,

View File

@ -8,6 +8,7 @@
import Foundation import Foundation
import Pachyderm import Pachyderm
import SwiftUI
import Combine import Combine
class Preferences: Codable, ObservableObject { class Preferences: Codable, ObservableObject {
@ -44,7 +45,6 @@ class Preferences: Codable, ObservableObject {
self.defaultPostVisibility = try container.decode(Status.Visibility.self, forKey: .defaultPostVisibility) self.defaultPostVisibility = try container.decode(Status.Visibility.self, forKey: .defaultPostVisibility)
self.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts) self.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts)
self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode) self.contentWarningCopyMode = try container.decode(ContentWarningCopyMode.self, forKey: .contentWarningCopyMode)
self.blurAllMedia = try container.decode(Bool.self, forKey: .blurAllMedia)
self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps) self.openLinksInApps = try container.decode(Bool.self, forKey: .openLinksInApps)
self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari) self.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari)
self.inAppSafariAutomaticReaderMode = try container.decode(Bool.self, forKey: .inAppSafariAutomaticReaderMode) self.inAppSafariAutomaticReaderMode = try container.decode(Bool.self, forKey: .inAppSafariAutomaticReaderMode)
@ -66,7 +66,6 @@ class Preferences: Codable, ObservableObject {
try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility) try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility)
try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts) try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts)
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode) try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
try container.encode(blurAllMedia, forKey: .blurAllMedia)
try container.encode(openLinksInApps, forKey: .openLinksInApps) try container.encode(openLinksInApps, forKey: .openLinksInApps)
try container.encode(useInAppSafari, forKey: .useInAppSafari) try container.encode(useInAppSafari, forKey: .useInAppSafari)
try container.encode(inAppSafariAutomaticReaderMode, forKey: .inAppSafariAutomaticReaderMode) try container.encode(inAppSafariAutomaticReaderMode, forKey: .inAppSafariAutomaticReaderMode)
@ -87,7 +86,6 @@ class Preferences: Codable, ObservableObject {
@Published var defaultPostVisibility = Status.Visibility.public @Published var defaultPostVisibility = Status.Visibility.public
@Published var automaticallySaveDrafts = true @Published var automaticallySaveDrafts = true
@Published var contentWarningCopyMode = ContentWarningCopyMode.asIs @Published var contentWarningCopyMode = ContentWarningCopyMode.asIs
@Published var blurAllMedia = false
@Published var openLinksInApps = true @Published var openLinksInApps = true
@Published var useInAppSafari = true @Published var useInAppSafari = true
@Published var inAppSafariAutomaticReaderMode = false @Published var inAppSafariAutomaticReaderMode = false
@ -108,7 +106,6 @@ class Preferences: Codable, ObservableObject {
case defaultPostVisibility case defaultPostVisibility
case automaticallySaveDrafts case automaticallySaveDrafts
case contentWarningCopyMode case contentWarningCopyMode
case blurAllMedia
case openLinksInApps case openLinksInApps
case useInAppSafari case useInAppSafari
case inAppSafariAutomaticReaderMode case inAppSafariAutomaticReaderMode

View File

@ -32,7 +32,8 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
return true return true
} }
override var childForHomeIndicatorAutoHidden: UIViewController? { override var childForHomeIndicatorAutoHidden: UIViewController? {
return viewControllers?.first return
viewControllers?.first
} }
init(attachments: [Attachment], sourcesInfo: [LargeImageViewController.SourceInfo?], startIndex: Int) { init(attachments: [Attachment], sourcesInfo: [LargeImageViewController.SourceInfo?], startIndex: Int) {

View File

@ -141,7 +141,18 @@ class NotificationsTableViewController: EnhancedTableViewController {
override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? { override func tableView(_ tableView: UITableView, trailingSwipeActionsConfigurationForRowAt indexPath: IndexPath) -> UISwipeActionsConfiguration? {
let dismissAction = UIContextualAction(style: .destructive, title: NSLocalizedString("Dismiss", comment: "dismiss notification swipe action title")) { (action, view, completion) in let dismissAction = UIContextualAction(style: .destructive, title: NSLocalizedString("Dismiss", comment: "dismiss notification swipe action title")) { (action, view, completion) in
self.dismissNotificationsInGroup(at: indexPath) { let group = DispatchGroup()
self.groups[indexPath.row].notificationIDs
.map(Pachyderm.Notification.dismiss(id:))
.forEach { (request) in
group.enter()
MastodonController.client.run(request) { (response) in
group.leave()
}
}
group.notify(queue: .main) {
self.groups.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
completion(true) completion(true)
} }
} }
@ -158,31 +169,6 @@ class NotificationsTableViewController: EnhancedTableViewController {
return config return config
} }
override func getSuggestedContextMenuActions(tableView: UITableView, indexPath: IndexPath, point: CGPoint) -> [UIAction] {
return [
UIAction(title: "Dismiss Notification", image: UIImage(systemName: "clear.fill"), identifier: .init("dismissnotification"), discoverabilityTitle: nil, attributes: [], state: .off, handler: { (_) in
self.dismissNotificationsInGroup(at: indexPath)
})
]
}
func dismissNotificationsInGroup(at indexPath: IndexPath, completion: (() -> Void)? = nil) {
let group = DispatchGroup()
groups[indexPath.row].notificationIDs
.map(Pachyderm.Notification.dismiss(id:))
.forEach { (request) in
group.enter()
MastodonController.client.run(request) { (response) in
group.leave()
}
}
group.notify(queue: .main) {
self.groups.remove(at: indexPath.row)
self.tableView.deleteRows(at: [indexPath], with: .automatic)
completion?()
}
}
@objc func refreshNotifications(_ sender: Any) { @objc func refreshNotifications(_ sender: Any) {
guard let newer = newer else { return } guard let newer = newer else { return }

View File

@ -15,7 +15,6 @@ struct BehaviorPrefsView: View {
List { List {
section1 section1
section2 section2
section3
}.listStyle(GroupedListStyle()) }.listStyle(GroupedListStyle())
.navigationBarTitle(Text("Behavior")) .navigationBarTitle(Text("Behavior"))
} }
@ -45,14 +44,6 @@ struct BehaviorPrefsView: View {
var section2: some View { var section2: some View {
Section(header: Text("READING")) { Section(header: Text("READING")) {
Toggle(isOn: $preferences.blurAllMedia) {
Text("Blur All Media")
}
}
}
var section3: some View {
Section(header: Text("LINKS")) {
Toggle(isOn: $preferences.openLinksInApps) { Toggle(isOn: $preferences.openLinksInApps) {
Text("Open Links in Apps") Text("Open Links in Apps")
} }

View File

@ -1,35 +0,0 @@
//
// PreferencesHostingController.swift
// Tusker
//
// Created by Shadowfacts on 11/17/19.
// Copyright © 2019 Shadowfacts. All rights reserved.
//
import UIKit
import SwiftUI
class PreferencesNavigationController: UINavigationController {
init() {
let hostingController = UIHostingController(rootView: PreferencesView())
super.init(rootViewController: hostingController)
hostingController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(donePressed))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// workaround for onDisappear not being called when a modally presented UIHostingController is dismissed
NotificationCenter.default.post(name: .preferencesChanged, object: nil)
}
@objc func donePressed() {
dismiss(animated: true)
}
}

View File

@ -51,7 +51,11 @@ class MyProfileTableViewController: ProfileTableViewController {
} }
@objc func preferencesPressed() { @objc func preferencesPressed() {
present(PreferencesNavigationController(), animated: true) let view = PreferencesView().environmentObject(Preferences.shared)
let hostingController = UIHostingController(rootView: view)
let navigationController = UINavigationController(rootViewController: hostingController)
hostingController.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(closePreferences))
present(navigationController, animated: true)
} }
@objc func closePreferences() { @objc func closePreferences() {

View File

@ -22,13 +22,6 @@ class ProfileTableViewController: EnhancedTableViewController {
} }
} }
var pinnedStatusIDs: [String] = [] {
didSet {
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
var timelineSegments: [TimelineSegment<Status>] = [] { var timelineSegments: [TimelineSegment<Status>] = [] {
didSet { didSet {
DispatchQueue.main.async { DispatchQueue.main.async {
@ -105,13 +98,6 @@ class ProfileTableViewController: EnhancedTableViewController {
updateUIForPreferences() updateUIForPreferences()
getStatuses(onlyPinned: true) { (response) in
guard case let .success(statuses, _) = response else { fatalError() }
MastodonCache.addAll(statuses: statuses)
self.pinnedStatusIDs = statuses.map { $0.id }
}
getStatuses() { response in getStatuses() { response in
guard case let .success(statuses, pagination) = response else { fatalError() } guard case let .success(statuses, pagination) = response else { fatalError() }
@ -128,8 +114,8 @@ class ProfileTableViewController: EnhancedTableViewController {
navigationItem.title = account.realDisplayName navigationItem.title = account.realDisplayName
} }
func getStatuses(for range: RequestRange = .default, onlyPinned: Bool = false, completion: @escaping Client.Callback<[Status]>) { func getStatuses(for range: RequestRange = .default, completion: @escaping Client.Callback<[Status]>) {
let request = Account.getStatuses(accountID, range: range, onlyMedia: false, pinned: onlyPinned, excludeReplies: !Preferences.shared.showRepliesInProfiles) let request = Account.getStatuses(accountID, range: range, onlyMedia: false, pinned: false, excludeReplies: !Preferences.shared.showRepliesInProfiles)
MastodonController.client.run(request, completion: completion) MastodonController.client.run(request, completion: completion)
} }
@ -142,38 +128,27 @@ class ProfileTableViewController: EnhancedTableViewController {
// MARK: - Table view data source // MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int { override func numberOfSections(in tableView: UITableView) -> Int {
// 1 section for header, 1 section for pinned, rest for timeline return 1 + timelineSegments.count
return 2 + timelineSegments.count
} }
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if section == 0 { if section == 0 {
return accountID == nil || MastodonCache.account(for: accountID) == nil ? 0 : 1 return accountID == nil || MastodonCache.account(for: accountID) == nil ? 0 : 1
} else if section == 1 {
return pinnedStatusIDs.count
} else { } else {
return timelineSegments[section - 2].count return timelineSegments[section - 1].count
} }
} }
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
switch indexPath.section { if indexPath.section == 0 {
case 0:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as? ProfileHeaderTableViewCell else { fatalError() } guard let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as? ProfileHeaderTableViewCell else { fatalError() }
cell.selectionStyle = .none cell.selectionStyle = .none
cell.delegate = self cell.delegate = self
cell.updateUI(for: accountID) cell.updateUI(for: accountID)
return cell return cell
case 1: } else {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() } guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() }
let statusID = pinnedStatusIDs[indexPath.row] let statusID = timelineSegments[indexPath.section - 1][indexPath.row]
cell.showPinned = true
cell.updateUI(statusID: statusID)
cell.delegate = self
return cell
default:
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() }
let statusID = timelineSegments[indexPath.section - 2][indexPath.row]
cell.updateUI(statusID: statusID) cell.updateUI(statusID: statusID)
cell.delegate = self cell.delegate = self
return cell return cell
@ -181,14 +156,14 @@ class ProfileTableViewController: EnhancedTableViewController {
} }
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
if timelineSegments.count > 0 && indexPath.section - 1 == timelineSegments.count && indexPath.row == timelineSegments[indexPath.section - 2].count - 1 { if timelineSegments.count > 0 && indexPath.section == timelineSegments.count && indexPath.row == timelineSegments[indexPath.section - 1].count - 1 {
guard let older = older else { return } guard let older = older else { return }
getStatuses(for: older) { response in getStatuses(for: older) { response in
guard case let .success(newStatuses, pagination) = response else { fatalError() } guard case let .success(newStatuses, pagination) = response else { fatalError() }
MastodonCache.addAll(statuses: newStatuses) MastodonCache.addAll(statuses: newStatuses)
self.timelineSegments[indexPath.section - 2].append(objects: newStatuses) self.timelineSegments[indexPath.section - 1].append(objects: newStatuses)
self.older = pagination?.older self.older = pagination?.older
} }
@ -249,11 +224,13 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
if let relationship = relationship { if let relationship = relationship {
let toggleFollowActivity = relationship.following ? UnfollowAccountActivity() : FollowAccountActivity() let toggleFollowActivity = relationship.following ? UnfollowAccountActivity() : FollowAccountActivity()
customActivities = [ customActivities = [
SendMessageActivity(),
toggleFollowActivity, toggleFollowActivity,
OpenInSafariActivity() OpenInSafariActivity()
] ]
} else { } else {
customActivities = [ customActivities = [
SendMessageActivity(),
OpenInSafariActivity() OpenInSafariActivity()
] ]
} }
@ -269,8 +246,8 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
extension ProfileTableViewController: UITableViewDataSourcePrefetching { extension ProfileTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths where indexPath.section > 1 { for indexPath in indexPaths where indexPath.section > 0 {
let statusID = timelineSegments[indexPath.section - 2][indexPath.row] let statusID = timelineSegments[indexPath.section - 1][indexPath.row]
guard let status = MastodonCache.status(for: statusID) else { continue } guard let status = MastodonCache.status(for: statusID) else { continue }
ImageCache.avatars.get(status.account.avatar, completion: nil) ImageCache.avatars.get(status.account.avatar, completion: nil)
for attachment in status.attachments { for attachment in status.attachments {
@ -280,8 +257,8 @@ extension ProfileTableViewController: UITableViewDataSourcePrefetching {
} }
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) { func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
for indexPath in indexPaths where indexPath.section > 1 { for indexPath in indexPaths where indexPath.section > 0 {
let statusID = timelineSegments[indexPath.section - 2][indexPath.row] let statusID = timelineSegments[indexPath.section - 1][indexPath.row]
guard let status = MastodonCache.status(for: statusID) else { continue } guard let status = MastodonCache.status(for: statusID) else { continue }
ImageCache.avatars.cancel(status.account.avatar) ImageCache.avatars.cancel(status.account.avatar)
for attachment in status.attachments { for attachment in status.attachments {

View File

@ -46,9 +46,8 @@ extension EnhancedTableViewController {
guard let (previewProvider, actionsProvider) = cell.getPreviewProviders(for: cellLocation, sourceViewController: self) else { guard let (previewProvider, actionsProvider) = cell.getPreviewProviders(for: cellLocation, sourceViewController: self) else {
return nil return nil
} }
let actionProvider: UIContextMenuActionProvider = { (_) in let actionProvider: UIContextMenuActionProvider = { (elements) in
let suggested = self.getSuggestedContextMenuActions(tableView: tableView, indexPath: indexPath, point: point) return UIMenu(title: "", image: nil, identifier: nil, options: [], children: elements + actionsProvider())
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: suggested + actionsProvider())
} }
return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider, actionProvider: actionProvider) return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider, actionProvider: actionProvider)
} else { } else {
@ -56,11 +55,6 @@ extension EnhancedTableViewController {
} }
} }
// todo: replace this with the UIKit suggested actions, if possible
@objc open func getSuggestedContextMenuActions(tableView: UITableView, indexPath: IndexPath, point: CGPoint) -> [UIAction] {
return []
}
override func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) { override func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
if let viewController = animator.previewViewController { if let viewController = animator.previewViewController {
animator.preferredCommitStyle = .pop animator.preferredCommitStyle = .pop

View File

@ -69,6 +69,13 @@ extension MenuPreviewProvider {
] ]
} }
func actionsForNotificationGroup(_ group: NotificationGroup) -> [UIAction] {
// let notifications = group.notificationIDs.compactMap(MastodonCache.notification(for:))
return [
// todo: clear notifications option
]
}
private func createAction(identifier: String, title: String, systemImageName: String, handler: @escaping UIActionHandler) -> UIAction { private func createAction(identifier: String, title: String, systemImageName: String, handler: @escaping UIActionHandler) -> UIAction {
return UIAction(title: title, image: UIImage(systemName: systemImageName), identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler) return UIAction(title: title, image: UIImage(systemName: systemImageName), identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler)
} }

View File

@ -105,7 +105,14 @@ class AttachmentView: UIImageView, GIFAnimatable {
} }
@objc func imagePressed() { @objc func imagePressed() {
delegate?.showAttachmentsGallery(startingAt: index) // switch attachment.kind {
// case .image:
delegate?.showAttachmentsGallery(startingAt: index)
// case .video:
// delegate?.showVideo(attachment: attachment)
// default:
// fatalError()
// }
} }
} }

View File

@ -19,14 +19,18 @@ class AttachmentsContainerView: UIView {
let attachmentViews: NSHashTable<AttachmentView> = .weakObjects() let attachmentViews: NSHashTable<AttachmentView> = .weakObjects()
var blurView: UIVisualEffectView? var blurView: UIVisualEffectView?
var hideButtonView: UIVisualEffectView? var hideButton: UIButton?
var contentHidden: Bool! { var contentHidden: Bool! {
didSet { didSet {
guard let blurView = blurView, guard let blurView = blurView,
let hideButtonView = hideButtonView else { return } let hideButton = hideButton else { return }
blurView.alpha = self.contentHidden ? 1 : 0 blurView.alpha = contentHidden ? 0 : 1
hideButtonView.alpha = self.contentHidden ? 0 : 1 hideButton.alpha = contentHidden ? 1 : 0
UIView.animate(withDuration: 0.2) {
blurView.alpha = self.contentHidden ? 1 : 0
hideButton.alpha = self.contentHidden ? 0 : 1
}
} }
} }
@ -34,11 +38,6 @@ class AttachmentsContainerView: UIView {
super.awakeFromNib() super.awakeFromNib()
self.isUserInteractionEnabled = true self.isUserInteractionEnabled = true
createBlurView()
createHideButton()
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
} }
func getAttachmentView(for attachment: Attachment) -> AttachmentView? { func getAttachmentView(for attachment: Attachment) -> AttachmentView? {
@ -51,8 +50,8 @@ class AttachmentsContainerView: UIView {
self.statusID = status.id self.statusID = status.id
attachments = status.attachments.filter { $0.kind == .image || $0.kind == .video } attachments = status.attachments.filter { $0.kind == .image || $0.kind == .video }
attachmentViews.allObjects.forEach { $0.removeFromSuperview() }
attachmentViews.removeAllObjects() attachmentViews.removeAllObjects()
subviews.forEach { $0.removeFromSuperview() }
if attachments.count > 0 { if attachments.count > 0 {
self.isHidden = false self.isHidden = false
@ -63,17 +62,14 @@ class AttachmentsContainerView: UIView {
case 1: case 1:
let attachmentView = createAttachmentView(index: 0) let attachmentView = createAttachmentView(index: 0)
fillView(attachmentView) fillView(attachmentView)
sendSubviewToBack(attachmentView)
accessibilityElements.append(attachmentView) accessibilityElements.append(attachmentView)
case 2: case 2:
let left = createAttachmentView(index: 0) let left = createAttachmentView(index: 0)
let right = createAttachmentView(index: 1) let right = createAttachmentView(index: 1)
let stack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left, left,
right right
]) ]))
fillView(stack)
sendSubviewToBack(stack)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
left.halfWidth() left.halfWidth()
]) ])
@ -83,15 +79,13 @@ class AttachmentsContainerView: UIView {
let left = createAttachmentView(index: 0) let left = createAttachmentView(index: 0)
let topRight = createAttachmentView(index: 1) let topRight = createAttachmentView(index: 1)
let bottomRight = createAttachmentView(index: 2) let bottomRight = createAttachmentView(index: 2)
let outerStack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left, left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [ createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight, topRight,
bottomRight bottomRight
]) ])
]) ]))
fillView(outerStack)
sendSubviewToBack(outerStack)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
left.halfWidth(), left.halfWidth(),
topRight.halfHeight(), topRight.halfHeight(),
@ -108,15 +102,13 @@ class AttachmentsContainerView: UIView {
]) ])
let topRight = createAttachmentView(index: 1) let topRight = createAttachmentView(index: 1)
let bottomRight = createAttachmentView(index: 3) let bottomRight = createAttachmentView(index: 3)
let outerStack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left, left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [ createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight, topRight,
bottomRight bottomRight
]) ])
]) ]))
fillView(outerStack)
sendSubviewToBack(outerStack)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
left.halfWidth(), left.halfWidth(),
topLeft.halfHeight(), topLeft.halfHeight(),
@ -137,7 +129,6 @@ class AttachmentsContainerView: UIView {
moreLabel.textColor = .secondaryLabel moreLabel.textColor = .secondaryLabel
moreLabel.textAlignment = .center moreLabel.textAlignment = .center
moreLabel.translatesAutoresizingMaskIntoConstraints = false moreLabel.translatesAutoresizingMaskIntoConstraints = false
moreView.addSubview(moreLabel)
moreView.accessibilityLabel = moreLabel.text moreView.accessibilityLabel = moreLabel.text
let topLeft = createAttachmentView(index: 0) let topLeft = createAttachmentView(index: 0)
@ -147,15 +138,13 @@ class AttachmentsContainerView: UIView {
bottomLeft bottomLeft
]) ])
let topRight = createAttachmentView(index: 1) let topRight = createAttachmentView(index: 1)
let outerStack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [ fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
left, left,
createAttachmentsStack(axis: .vertical, arrangedSubviews: [ createAttachmentsStack(axis: .vertical, arrangedSubviews: [
topRight, topRight,
moreView moreView
]) ])
]) ]))
fillView(outerStack)
sendSubviewToBack(outerStack)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
left.halfWidth(), left.halfWidth(),
topLeft.halfHeight(), topLeft.halfHeight(),
@ -176,11 +165,11 @@ class AttachmentsContainerView: UIView {
self.isHidden = true self.isHidden = true
} }
updateUIForPreferences() if status.sensitive {
} contentHidden = true
createBlurView()
@objc func updateUIForPreferences() { createHideButton()
contentHidden = Preferences.shared.blurAllMedia || (MastodonCache.status(for: statusID)?.sensitive ?? false) }
} }
private func createAttachmentView(index: Int) -> AttachmentView { private func createAttachmentView(index: Int) -> AttachmentView {
@ -205,7 +194,7 @@ class AttachmentsContainerView: UIView {
private func createBlurView() { private func createBlurView() {
let blur = UIBlurEffect(style: .dark) let blur = UIBlurEffect(style: .dark)
let blurView = UIVisualEffectView(effect: blur) let blurView = UIVisualEffectView(effect: blur)
blurView.alpha = 0 blurView.effect = blur
blurView.translatesAutoresizingMaskIntoConstraints = false blurView.translatesAutoresizingMaskIntoConstraints = false
fillView(blurView) fillView(blurView)
let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blur, style: .label)) let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blur, style: .label))
@ -221,7 +210,7 @@ class AttachmentsContainerView: UIView {
let stack = UIStackView(arrangedSubviews: [ let stack = UIStackView(arrangedSubviews: [
imageView, imageView,
label label
]) ])
stack.axis = .vertical stack.axis = .vertical
stack.alignment = .center stack.alignment = .center
stack.translatesAutoresizingMaskIntoConstraints = false stack.translatesAutoresizingMaskIntoConstraints = false
@ -241,43 +230,21 @@ class AttachmentsContainerView: UIView {
} }
private func createHideButton() { private func createHideButton() {
let blurEffect = UIBlurEffect(style: .regular) let hideButton = UIButton()
let hideButtonBlurView = UIVisualEffectView(effect: blurEffect) hideButton.translatesAutoresizingMaskIntoConstraints = false
hideButtonBlurView.translatesAutoresizingMaskIntoConstraints = false hideButton.alpha = 0
hideButtonBlurView.alpha = 1 hideButton.layer.cornerRadius = 2
hideButtonBlurView.isUserInteractionEnabled = true hideButton.layer.masksToBounds = true
hideButtonBlurView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hideButtonTapped))) hideButton.setImage(UIImage(systemName: "eye.slash.fill"), for: .normal)
addSubview(hideButtonBlurView) hideButton.addTarget(self, action: #selector(hideButtonTapped), for: .touchUpInside)
self.hideButtonView = hideButtonBlurView
let maskLayer = CALayer()
let image = UIImage(systemName: "eye.slash.fill")!
maskLayer.contents = image.cgImage!
maskLayer.frame = CGRect(origin: .zero, size: image.size)
hideButtonBlurView.layer.mask = maskLayer
let hideButtonVibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect, style: .label))
hideButtonVibrancyView.translatesAutoresizingMaskIntoConstraints = false
hideButtonBlurView.contentView.addSubview(hideButtonVibrancyView)
let fillView = UIView()
fillView.translatesAutoresizingMaskIntoConstraints = false
fillView.backgroundColor = UIColor(displayP3Red: 0, green: 0, blue: 0, alpha: 0.5)
hideButtonVibrancyView.contentView.addSubview(fillView)
addSubview(hideButton)
NSLayoutConstraint.activate([ NSLayoutConstraint.activate([
hideButtonBlurView.topAnchor.constraint(equalTo: topAnchor, constant: 8), hideButton.topAnchor.constraint(equalTo: topAnchor, constant: 8),
hideButtonBlurView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8), hideButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8)
hideButtonBlurView.widthAnchor.constraint(equalToConstant: image.size.width),
hideButtonBlurView.heightAnchor.constraint(equalToConstant: image.size.height),
hideButtonVibrancyView.leadingAnchor.constraint(equalTo: hideButtonBlurView.contentView.leadingAnchor),
hideButtonVibrancyView.trailingAnchor.constraint(equalTo: hideButtonBlurView.contentView.trailingAnchor),
hideButtonVibrancyView.topAnchor.constraint(equalTo: hideButtonBlurView.contentView.topAnchor),
hideButtonVibrancyView.bottomAnchor.constraint(equalTo: hideButtonBlurView.contentView.bottomAnchor),
fillView.leadingAnchor.constraint(equalTo: hideButtonBlurView.contentView.leadingAnchor),
fillView.trailingAnchor.constraint(equalTo: hideButtonBlurView.contentView.trailingAnchor),
fillView.topAnchor.constraint(equalTo: hideButtonBlurView.contentView.topAnchor),
fillView.bottomAnchor.constraint(equalTo: hideButtonBlurView.contentView.bottomAnchor),
]) ])
self.hideButton = hideButton
} }
private func fillView(_ view: UIView, in parentView: UIView? = nil) { private func fillView(_ view: UIView, in parentView: UIView? = nil) {
@ -294,15 +261,11 @@ class AttachmentsContainerView: UIView {
// MARK: - Interaction // MARK: - Interaction
@objc func blurViewTapped() { @objc func blurViewTapped() {
UIView.animate(withDuration: 0.2) { contentHidden = false
self.contentHidden = false
}
} }
@objc func hideButtonTapped() { @objc func hideButtonTapped() {
UIView.animate(withDuration: 0.2) { contentHidden = true
self.contentHidden = true
}
} }
@objc func showSensitiveContent() { @objc func showSensitiveContent() {

View File

@ -189,7 +189,7 @@ extension ActionNotificationGroupTableViewCell: MenuPreviewProvider {
} }
return self.delegate?.statusActionAccountList(action: action, statusID: self.statusID, accountIDs: accountIDs) return self.delegate?.statusActionAccountList(action: action, statusID: self.statusID, accountIDs: accountIDs)
}, actions: { }, actions: {
return [] return self.actionsForNotificationGroup(self.group)
}) })
} }

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14865.1" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/> <device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.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"/>
</dependencies> </dependencies>
@ -45,7 +45,7 @@
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lc7-zZ-HrZ"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="5" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lc7-zZ-HrZ">
<rect key="frame" x="0.0" y="79" width="230" height="74"/> <rect key="frame" x="0.0" y="79" width="230" height="74"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>

View File

@ -142,7 +142,7 @@ extension FollowNotificationGroupTableViewCell: MenuPreviewProvider {
return AccountListTableViewController(accountIDs: accountIDs) return AccountListTableViewController(accountIDs: accountIDs)
} }
}, actions: { }, actions: {
return [] return self.actionsForNotificationGroup(self.group)
}) })
} }

View File

@ -27,7 +27,6 @@ class ProfileHeaderTableViewCell: UITableViewCell {
@IBOutlet weak var fieldsStackView: UIStackView! @IBOutlet weak var fieldsStackView: UIStackView!
@IBOutlet weak var fieldNamesStackView: UIStackView! @IBOutlet weak var fieldNamesStackView: UIStackView!
@IBOutlet weak var fieldValuesStack: UIStackView! @IBOutlet weak var fieldValuesStack: UIStackView!
@IBOutlet weak var moreButtonVisualEffectView: UIVisualEffectView!
var accountID: String! var accountID: String!
@ -41,12 +40,6 @@ class ProfileHeaderTableViewCell: UITableViewCell {
avatarImageView.isUserInteractionEnabled = true avatarImageView.isUserInteractionEnabled = true
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed))) headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
headerImageView.isUserInteractionEnabled = true headerImageView.isUserInteractionEnabled = true
moreButtonVisualEffectView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(morePressed)))
let maskLayer = CAShapeLayer()
maskLayer.frame = moreButtonVisualEffectView.bounds
maskLayer.path = CGPath(ellipseIn: moreButtonVisualEffectView.bounds, transform: nil)
moreButtonVisualEffectView.layer.mask = maskLayer
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
} }
@ -136,7 +129,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
} }
} }
@objc func morePressed() { @IBAction func morePressed(_ sender: Any) {
delegate?.showMoreOptions() delegate?.showMoreOptions()
} }

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.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"/>
</dependencies> </dependencies>
@ -85,49 +85,18 @@
</stackView> </stackView>
</subviews> </subviews>
</stackView> </stackView>
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mQY-XN-PfZ"> <button opaque="NO" alpha="0.59999999999999998" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qiv-gB-kiX">
<rect key="frame" x="335" y="110" width="32" height="32"/> <rect key="frame" x="323.5" y="120" width="35.5" height="22"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="0Ol-1d-la6"> <accessibility key="accessibilityConfiguration" label="More Actions"/>
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/> <fontDescription key="fontDescription" type="system" pointSize="18"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<subviews> <state key="normal" image="ellipsis" catalog="system">
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t0d-eE-mbc"> <preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="32" scale="default"/>
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/> </state>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="TgJ-FF-QyB"> <connections>
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/> <action selector="morePressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="0go-4p-qDa"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> </connections>
<subviews> </button>
<imageView opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="ellipsis" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cLs-dC-SWU">
<rect key="frame" x="2" y="12.5" width="28" height="7"/>
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="24"/>
</imageView>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstItem="cLs-dC-SWU" firstAttribute="leading" secondItem="TgJ-FF-QyB" secondAttribute="leading" constant="2" id="7nV-7d-GAY"/>
<constraint firstAttribute="bottom" secondItem="cLs-dC-SWU" secondAttribute="bottom" constant="2" id="8sP-mZ-ZSQ"/>
<constraint firstAttribute="trailing" secondItem="cLs-dC-SWU" secondAttribute="trailing" constant="2" id="iBQ-oA-yOm"/>
<constraint firstItem="cLs-dC-SWU" firstAttribute="top" secondItem="TgJ-FF-QyB" secondAttribute="top" constant="2" id="jSB-2f-sZF"/>
</constraints>
</view>
<vibrancyEffect style="label">
<blurEffect style="prominent"/>
</vibrancyEffect>
</visualEffectView>
</subviews>
<constraints>
<constraint firstItem="t0d-eE-mbc" firstAttribute="leading" secondItem="0Ol-1d-la6" secondAttribute="leading" id="6Py-U4-Jlo"/>
<constraint firstAttribute="bottom" secondItem="t0d-eE-mbc" secondAttribute="bottom" id="OT5-Yh-eiG"/>
<constraint firstAttribute="trailing" secondItem="t0d-eE-mbc" secondAttribute="trailing" id="a8T-dS-dc8"/>
<constraint firstItem="t0d-eE-mbc" firstAttribute="top" secondItem="0Ol-1d-la6" secondAttribute="top" id="xKq-qM-vmk"/>
</constraints>
</view>
<constraints>
<constraint firstAttribute="height" constant="32" id="Zye-sh-FIH"/>
<constraint firstAttribute="width" constant="32" id="hpF-s0-dbt"/>
</constraints>
<blurEffect style="prominent"/>
</visualEffectView>
</subviews> </subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
@ -141,7 +110,6 @@
<constraint firstItem="Fw7-OL-iy5" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="LqH-lE-AIe"/> <constraint firstItem="Fw7-OL-iy5" firstAttribute="trailing" secondItem="vUN-kp-3ea" secondAttribute="trailing" id="LqH-lE-AIe"/>
<constraint firstItem="KyB-ey-l11" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="NN7-5B-k1Q"/> <constraint firstItem="KyB-ey-l11" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="NN7-5B-k1Q"/>
<constraint firstItem="MIj-OR-NOR" firstAttribute="bottom" secondItem="tH8-sR-DHC" secondAttribute="bottom" id="PhQ-El-olR"/> <constraint firstItem="MIj-OR-NOR" firstAttribute="bottom" secondItem="tH8-sR-DHC" secondAttribute="bottom" id="PhQ-El-olR"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="mQY-XN-PfZ" secondAttribute="trailing" constant="8" id="TZn-8m-0Wq"/>
<constraint firstItem="sHU-GU-klv" firstAttribute="top" secondItem="DfO-uD-UNI" secondAttribute="bottom" constant="8" id="Vza-1s-qbG"/> <constraint firstItem="sHU-GU-klv" firstAttribute="top" secondItem="DfO-uD-UNI" secondAttribute="bottom" constant="8" id="Vza-1s-qbG"/>
<constraint firstAttribute="trailingMargin" secondItem="sHU-GU-klv" secondAttribute="trailing" id="XJa-zP-Ma2"/> <constraint firstAttribute="trailingMargin" secondItem="sHU-GU-klv" secondAttribute="trailing" id="XJa-zP-Ma2"/>
<constraint firstItem="sHU-GU-klv" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leadingMargin" id="cSX-WD-2aJ"/> <constraint firstItem="sHU-GU-klv" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leadingMargin" id="cSX-WD-2aJ"/>
@ -150,8 +118,9 @@
<constraint firstItem="MIj-OR-NOR" firstAttribute="leading" secondItem="KyB-ey-l11" secondAttribute="trailing" constant="8" id="iG7-yZ-9u3"/> <constraint firstItem="MIj-OR-NOR" firstAttribute="leading" secondItem="KyB-ey-l11" secondAttribute="trailing" constant="8" id="iG7-yZ-9u3"/>
<constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="sHU-GU-klv" secondAttribute="bottom" constant="8" id="iRf-l0-ZZX"/> <constraint firstItem="vUN-kp-3ea" firstAttribute="bottom" secondItem="sHU-GU-klv" secondAttribute="bottom" constant="8" id="iRf-l0-ZZX"/>
<constraint firstItem="MIj-OR-NOR" firstAttribute="top" relation="greaterThanOrEqual" secondItem="LjK-72-Bez" secondAttribute="bottom" id="nMM-6t-bjX"/> <constraint firstItem="MIj-OR-NOR" firstAttribute="top" relation="greaterThanOrEqual" secondItem="LjK-72-Bez" secondAttribute="bottom" id="nMM-6t-bjX"/>
<constraint firstAttribute="trailing" secondItem="qiv-gB-kiX" secondAttribute="trailing" constant="16" id="nYG-p6-Ezm"/>
<constraint firstItem="qiv-gB-kiX" firstAttribute="bottom" secondItem="Fw7-OL-iy5" secondAttribute="bottom" constant="-8" id="pg7-L7-u2f"/>
<constraint firstItem="DfO-uD-UNI" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="pqd-E3-Aw4"/> <constraint firstItem="DfO-uD-UNI" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="16" id="pqd-E3-Aw4"/>
<constraint firstItem="LjK-72-Bez" firstAttribute="top" secondItem="mQY-XN-PfZ" secondAttribute="bottom" constant="16" id="rTO-fy-u0V"/>
</constraints> </constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/> <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/> <viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
@ -164,7 +133,6 @@
<outlet property="fieldsStackView" destination="sHU-GU-klv" id="Gli-Gf-Ubh"/> <outlet property="fieldsStackView" destination="sHU-GU-klv" id="Gli-Gf-Ubh"/>
<outlet property="followsYouLabel" destination="a32-1a-xXZ" id="phY-0L-NnN"/> <outlet property="followsYouLabel" destination="a32-1a-xXZ" id="phY-0L-NnN"/>
<outlet property="headerImageView" destination="Fw7-OL-iy5" id="6sv-E5-D73"/> <outlet property="headerImageView" destination="Fw7-OL-iy5" id="6sv-E5-D73"/>
<outlet property="moreButtonVisualEffectView" destination="mQY-XN-PfZ" id="t7l-wg-nj0"/>
<outlet property="noteLabel" destination="I0n-aP-dJP" id="7yW-mE-jxY"/> <outlet property="noteLabel" destination="I0n-aP-dJP" id="7yW-mE-jxY"/>
<outlet property="usernameLabel" destination="MIj-OR-NOR" id="e1I-N7-rKx"/> <outlet property="usernameLabel" destination="MIj-OR-NOR" id="e1I-N7-rKx"/>
</connections> </connections>

View File

@ -42,7 +42,6 @@ class StatusTableViewCell: UITableViewCell {
@IBOutlet weak var favoriteButton: UIButton! @IBOutlet weak var favoriteButton: UIButton!
@IBOutlet weak var reblogButton: UIButton! @IBOutlet weak var reblogButton: UIButton!
@IBOutlet weak var moreButton: UIButton! @IBOutlet weak var moreButton: UIButton!
@IBOutlet weak var pinImageView: UIImageView!
var statusID: String! var statusID: String!
var accountID: String! var accountID: String!
@ -59,7 +58,6 @@ class StatusTableViewCell: UITableViewCell {
reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
} }
} }
var showPinned: Bool = false
var collapsible = false { var collapsible = false {
didSet { didSet {
@ -112,7 +110,10 @@ class StatusTableViewCell: UITableViewCell {
rebloggerAccountUpdater = MastodonCache.accountSubject rebloggerAccountUpdater = MastodonCache.accountSubject
.filter { $0.id == self.rebloggerID } .filter { $0.id == self.rebloggerID }
.receive(on: DispatchQueue.main) .receive(on: DispatchQueue.main)
.sink(receiveValue: updateRebloggerLabel(reblogger:)) .sink(receiveValue: { (_) in
// this method is responsible for setting the reblog label text
self.updateUIForPreferences()
})
} }
func updateUI(statusID: String) { func updateUI(statusID: String) {
@ -152,17 +153,6 @@ class StatusTableViewCell: UITableViewCell {
setCollapsed(collapsible, animated: false) setCollapsed(collapsible, animated: false)
contentWarningLabel.text = status.spoilerText contentWarningLabel.text = status.spoilerText
contentWarningLabel.isHidden = status.spoilerText.isEmpty contentWarningLabel.isHidden = status.spoilerText.isEmpty
if !collapsed,
let text = contentLabel.text,
text.count > 500 {
collapsible = true
setCollapsed(true, animated: false)
}
let pinned = status.pinned ?? false
pinImageView.isHidden = !(pinned && showPinned)
timestampLabel.isHidden = !pinImageView.isHidden
} }
private func updateStatusState(status: Status) { private func updateStatusState(status: Status) {
@ -199,15 +189,11 @@ class StatusTableViewCell: UITableViewCell {
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView) avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
if let rebloggerID = rebloggerID, if let rebloggerID = rebloggerID,
let reblogger = MastodonCache.account(for: rebloggerID) { let reblogger = MastodonCache.account(for: rebloggerID) {
updateRebloggerLabel(reblogger: reblogger) reblogLabel.text = "Reblogged by \(reblogger.realDisplayName)"
} }
displayNameLabel.text = account.realDisplayName displayNameLabel.text = account.realDisplayName
} }
func updateRebloggerLabel(reblogger: Account) {
reblogLabel.text = "Reblogged by \(reblogger.realDisplayName)"
}
func updateTimestamp() { func updateTimestamp() {
guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") } guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
@ -239,8 +225,7 @@ class StatusTableViewCell: UITableViewCell {
} }
updateTimestampWorkItem?.cancel() updateTimestampWorkItem?.cancel()
updateTimestampWorkItem = nil updateTimestampWorkItem = nil
attachmentsView.attachmentViews.allObjects.forEach { $0.removeFromSuperview() } attachmentsView.subviews.forEach { $0.removeFromSuperview() }
showPinned = false
} }
override func setSelected(_ selected: Bool, animated: Bool) { override func setSelected(_ selected: Bool, animated: Bool) {
@ -454,8 +439,8 @@ extension StatusTableViewCell: MenuPreviewProvider {
return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.accountID) }) return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.accountID) })
} else if attachmentsView.frame.contains(location) { } else if attachmentsView.frame.contains(location) {
let attachmentsViewLocation = attachmentsView.convert(location, from: self) let attachmentsViewLocation = attachmentsView.convert(location, from: self)
if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView, if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView {
let image = attachmentView.image { let image = attachmentView.image!
let description = attachmentView.attachment.description let description = attachmentView.attachment.description
return (content: { self.delegate?.largeImage(image, description: description, sourceView: attachmentView) }, actions: { [] }) return (content: { self.delegate?.largeImage(image, description: description, sourceView: attachmentView) }, actions: { [] })
} }

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES"> <document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait" appearance="light"/> <device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies> <dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/> <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/> <capability name="Safe area layout guides" minToolsVersion="9.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"/>
</dependencies> </dependencies>
@ -23,7 +23,7 @@
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="ve3-Y1-NQH"> <view contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="ve3-Y1-NQH">
<rect key="frame" x="0.0" y="28.5" width="343" height="103.5"/> <rect key="frame" x="0.0" y="28.5" width="343" height="165.5"/>
<subviews> <subviews>
<imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QMP-j2-HLn"> <imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QMP-j2-HLn">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/> <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
@ -37,7 +37,7 @@
</constraints> </constraints>
</imageView> </imageView>
<stackView opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="751" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="gIY-Wp-RSk"> <stackView opaque="NO" contentMode="scaleToFill" verticalCompressionResistancePriority="751" axis="vertical" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="gIY-Wp-RSk">
<rect key="frame" x="58" y="0.0" width="277" height="103.5"/> <rect key="frame" x="58" y="0.0" width="277" height="165.5"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3Sm-P0-ySf"> <stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3Sm-P0-ySf">
<rect key="frame" x="0.0" y="0.0" width="277" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="277" height="20.5"/>
@ -62,11 +62,6 @@
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<imageView hidden="YES" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="pin.fill" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="LRh-Cc-1br">
<rect key="frame" x="248.5" y="-0.5" width="0.0" height="22"/>
<color key="tintColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" label="Pinned Status"/>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="752" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="35d-EA-ReR"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="752" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="35d-EA-ReR">
<rect key="frame" x="252.5" y="0.0" width="24.5" height="20.5"/> <rect key="frame" x="252.5" y="0.0" width="24.5" height="20.5"/>
<accessibility key="accessibilityConfiguration"> <accessibility key="accessibilityConfiguration">
@ -106,7 +101,7 @@
</connections> </connections>
</button> </button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="TopLeft" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HrJ-t9-KcD" customClass="StatusContentLabel" customModule="Tusker" customModuleProvider="target"> <label opaque="NO" userInteractionEnabled="NO" contentMode="TopLeft" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HrJ-t9-KcD" customClass="StatusContentLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="83" width="277" height="20.5"/> <rect key="frame" x="0.0" y="83" width="277" height="82.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
@ -125,17 +120,17 @@
</constraints> </constraints>
</view> </view>
<view hidden="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="nbq-yr-2mA" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target"> <view hidden="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="nbq-yr-2mA" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="136" width="343" height="0.0"/> <rect key="frame" x="0.0" y="198" width="343" height="0.0"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" priority="999" constant="200" id="J42-49-2MU"/> <constraint firstAttribute="height" priority="999" constant="200" id="J42-49-2MU"/>
</constraints> </constraints>
</view> </view>
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" alignment="bottom" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw"> <stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" alignment="bottom" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
<rect key="frame" x="0.0" y="140" width="343" height="84"/> <rect key="frame" x="0.0" y="202" width="343" height="22"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa"> <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
<rect key="frame" x="0.0" y="62" width="21" height="22"/> <rect key="frame" x="0.0" y="0.0" width="21" height="22"/>
<accessibility key="accessibilityConfiguration" label="Reply"/> <accessibility key="accessibilityConfiguration" label="Reply"/>
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/> <state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
<connections> <connections>
@ -143,7 +138,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4">
<rect key="frame" x="107" y="62" width="22" height="22"/> <rect key="frame" x="107" y="0.0" width="22" height="22"/>
<accessibility key="accessibilityConfiguration" label="Favorite"/> <accessibility key="accessibilityConfiguration" label="Favorite"/>
<state key="normal" image="star.fill" catalog="system"/> <state key="normal" image="star.fill" catalog="system"/>
<connections> <connections>
@ -151,7 +146,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9">
<rect key="frame" x="215.5" y="62" width="22.5" height="22"/> <rect key="frame" x="215.5" y="0.0" width="22.5" height="22"/>
<accessibility key="accessibilityConfiguration" label="Reblog"/> <accessibility key="accessibilityConfiguration" label="Reblog"/>
<state key="normal" image="repeat" catalog="system"/> <state key="normal" image="repeat" catalog="system"/>
<connections> <connections>
@ -159,7 +154,7 @@
</connections> </connections>
</button> </button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="982-J4-NGl"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="982-J4-NGl">
<rect key="frame" x="324" y="62" width="19" height="22"/> <rect key="frame" x="324" y="0.0" width="19" height="22"/>
<accessibility key="accessibilityConfiguration" label="More Actions"/> <accessibility key="accessibilityConfiguration" label="More Actions"/>
<state key="normal" image="ellipsis" catalog="system"/> <state key="normal" image="ellipsis" catalog="system"/>
<connections> <connections>
@ -194,7 +189,6 @@
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/> <outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
<outlet property="favoriteButton" destination="x0t-TR-jJ4" id="Ohz-bs-Ebr"/> <outlet property="favoriteButton" destination="x0t-TR-jJ4" id="Ohz-bs-Ebr"/>
<outlet property="moreButton" destination="982-J4-NGl" id="Xga-I4-CzK"/> <outlet property="moreButton" destination="982-J4-NGl" id="Xga-I4-CzK"/>
<outlet property="pinImageView" destination="LRh-Cc-1br" id="9jn-0V-PdJ"/>
<outlet property="reblogButton" destination="6tW-z8-Qh9" id="i9h-QA-ZPd"/> <outlet property="reblogButton" destination="6tW-z8-Qh9" id="i9h-QA-ZPd"/>
<outlet property="reblogLabel" destination="lDH-50-AJZ" id="uJf-Pt-cEP"/> <outlet property="reblogLabel" destination="lDH-50-AJZ" id="uJf-Pt-cEP"/>
<outlet property="replyButton" destination="rKF-yF-KIa" id="rul-lk-bIR"/> <outlet property="replyButton" destination="rKF-yF-KIa" id="rul-lk-bIR"/>
@ -208,7 +202,6 @@
<image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/> <image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/>
<image name="chevron.down" catalog="system" width="64" height="36"/> <image name="chevron.down" catalog="system" width="64" height="36"/>
<image name="ellipsis" catalog="system" width="64" height="18"/> <image name="ellipsis" catalog="system" width="64" height="18"/>
<image name="pin.fill" catalog="system" width="58" height="64"/>
<image name="repeat" catalog="system" width="64" height="48"/> <image name="repeat" catalog="system" width="64" height="48"/>
<image name="star.fill" catalog="system" width="64" height="58"/> <image name="star.fill" catalog="system" width="64" height="58"/>
</resources> </resources>