Compare commits
14 Commits
c85836eda6
...
da6ff67a51
Author | SHA1 | Date |
---|---|---|
Shadowfacts | da6ff67a51 | |
Shadowfacts | a92d9ddc6f | |
Shadowfacts | eb8afdaab8 | |
Shadowfacts | d4fa2f36e3 | |
Shadowfacts | 4cfe5e0fa5 | |
Shadowfacts | 975fb23292 | |
Shadowfacts | 85812d774d | |
Shadowfacts | 150adeb581 | |
Shadowfacts | 81a5fce602 | |
Shadowfacts | 6ce96764f3 | |
Shadowfacts | 42a0a8890c | |
Shadowfacts | 56d4a6690f | |
Shadowfacts | c91a7baaa6 | |
Shadowfacts | af65aa88e0 |
|
@ -89,6 +89,7 @@
|
|||
D6333B372137838300CE884A /* AttributedString+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Helpers.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 */; };
|
||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */; };
|
||||
D640D76922BAF5E6004FBE69 /* DomainBlocks.plist in Resources */ = {isa = PBXBuildFile; fileRef = D640D76822BAF5E6004FBE69 /* DomainBlocks.plist */; };
|
||||
D641C773213CAA25004B4513 /* NotificationsTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C772213CAA25004B4513 /* NotificationsTableViewController.swift */; };
|
||||
D641C77F213DC78A004B4513 /* InlineTextAttachment.swift in Sources */ = {isa = PBXBuildFile; fileRef = D641C77E213DC78A004B4513 /* InlineTextAttachment.swift */; };
|
||||
|
@ -348,6 +349,7 @@
|
|||
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>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -811,6 +813,7 @@
|
|||
D641C789213DD87E004B4513 /* Preferences */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
D63661BF2381C144004B9E16 /* PreferencesNavigationController.swift */,
|
||||
04586B4022B2FFB10021BD04 /* PreferencesView.swift */,
|
||||
04586B4222B301470021BD04 /* AppearancePrefsView.swift */,
|
||||
0427033722B30F5F000D31B6 /* BehaviorPrefsView.swift */,
|
||||
|
@ -1593,6 +1596,7 @@
|
|||
D663626421360D2300C9CBA2 /* AvatarStyle.swift in Sources */,
|
||||
D679C09F215850EF00DA27FE /* XCBActions.swift in Sources */,
|
||||
D6DD353F22F502EC00A9563A /* Preferences+Notification.swift in Sources */,
|
||||
D63661C02381C144004B9E16 /* PreferencesNavigationController.swift in Sources */,
|
||||
D6AEBB4523216AF800E5038B /* FollowAccountActivity.swift in Sources */,
|
||||
D6538945214D6D7500E3CEFC /* TableViewSwipeActionProvider.swift in Sources */,
|
||||
D6E0DC8E216EDF1E00369478 /* Previewing.swift in Sources */,
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
|
||||
import Foundation
|
||||
import Pachyderm
|
||||
import SwiftUI
|
||||
import Combine
|
||||
|
||||
class Preferences: Codable, ObservableObject {
|
||||
|
@ -45,6 +44,7 @@ class Preferences: Codable, ObservableObject {
|
|||
self.defaultPostVisibility = try container.decode(Status.Visibility.self, forKey: .defaultPostVisibility)
|
||||
self.automaticallySaveDrafts = try container.decode(Bool.self, forKey: .automaticallySaveDrafts)
|
||||
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.useInAppSafari = try container.decode(Bool.self, forKey: .useInAppSafari)
|
||||
self.inAppSafariAutomaticReaderMode = try container.decode(Bool.self, forKey: .inAppSafariAutomaticReaderMode)
|
||||
|
@ -66,6 +66,7 @@ class Preferences: Codable, ObservableObject {
|
|||
try container.encode(defaultPostVisibility, forKey: .defaultPostVisibility)
|
||||
try container.encode(automaticallySaveDrafts, forKey: .automaticallySaveDrafts)
|
||||
try container.encode(contentWarningCopyMode, forKey: .contentWarningCopyMode)
|
||||
try container.encode(blurAllMedia, forKey: .blurAllMedia)
|
||||
try container.encode(openLinksInApps, forKey: .openLinksInApps)
|
||||
try container.encode(useInAppSafari, forKey: .useInAppSafari)
|
||||
try container.encode(inAppSafariAutomaticReaderMode, forKey: .inAppSafariAutomaticReaderMode)
|
||||
|
@ -86,6 +87,7 @@ class Preferences: Codable, ObservableObject {
|
|||
@Published var defaultPostVisibility = Status.Visibility.public
|
||||
@Published var automaticallySaveDrafts = true
|
||||
@Published var contentWarningCopyMode = ContentWarningCopyMode.asIs
|
||||
@Published var blurAllMedia = false
|
||||
@Published var openLinksInApps = true
|
||||
@Published var useInAppSafari = true
|
||||
@Published var inAppSafariAutomaticReaderMode = false
|
||||
|
@ -106,6 +108,7 @@ class Preferences: Codable, ObservableObject {
|
|||
case defaultPostVisibility
|
||||
case automaticallySaveDrafts
|
||||
case contentWarningCopyMode
|
||||
case blurAllMedia
|
||||
case openLinksInApps
|
||||
case useInAppSafari
|
||||
case inAppSafariAutomaticReaderMode
|
||||
|
|
|
@ -32,8 +32,7 @@ class GalleryViewController: UIPageViewController, UIPageViewControllerDataSourc
|
|||
return true
|
||||
}
|
||||
override var childForHomeIndicatorAutoHidden: UIViewController? {
|
||||
return
|
||||
viewControllers?.first
|
||||
return viewControllers?.first
|
||||
}
|
||||
|
||||
init(attachments: [Attachment], sourcesInfo: [LargeImageViewController.SourceInfo?], startIndex: Int) {
|
||||
|
|
|
@ -141,18 +141,7 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
|||
|
||||
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 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)
|
||||
self.dismissNotificationsInGroup(at: indexPath) {
|
||||
completion(true)
|
||||
}
|
||||
}
|
||||
|
@ -169,6 +158,31 @@ class NotificationsTableViewController: EnhancedTableViewController {
|
|||
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) {
|
||||
guard let newer = newer else { return }
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ struct BehaviorPrefsView: View {
|
|||
List {
|
||||
section1
|
||||
section2
|
||||
section3
|
||||
}.listStyle(GroupedListStyle())
|
||||
.navigationBarTitle(Text("Behavior"))
|
||||
}
|
||||
|
@ -44,6 +45,14 @@ struct BehaviorPrefsView: View {
|
|||
|
||||
var section2: some View {
|
||||
Section(header: Text("READING")) {
|
||||
Toggle(isOn: $preferences.blurAllMedia) {
|
||||
Text("Blur All Media")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var section3: some View {
|
||||
Section(header: Text("LINKS")) {
|
||||
Toggle(isOn: $preferences.openLinksInApps) {
|
||||
Text("Open Links in Apps")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// 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)
|
||||
}
|
||||
|
||||
}
|
|
@ -51,11 +51,7 @@ class MyProfileTableViewController: ProfileTableViewController {
|
|||
}
|
||||
|
||||
@objc func preferencesPressed() {
|
||||
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)
|
||||
present(PreferencesNavigationController(), animated: true)
|
||||
}
|
||||
|
||||
@objc func closePreferences() {
|
||||
|
|
|
@ -22,6 +22,13 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
}
|
||||
|
||||
var pinnedStatusIDs: [String] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
self.tableView.reloadData()
|
||||
}
|
||||
}
|
||||
}
|
||||
var timelineSegments: [TimelineSegment<Status>] = [] {
|
||||
didSet {
|
||||
DispatchQueue.main.async {
|
||||
|
@ -98,6 +105,13 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
|
||||
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
|
||||
guard case let .success(statuses, pagination) = response else { fatalError() }
|
||||
|
||||
|
@ -114,8 +128,8 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
navigationItem.title = account.realDisplayName
|
||||
}
|
||||
|
||||
func getStatuses(for range: RequestRange = .default, completion: @escaping Client.Callback<[Status]>) {
|
||||
let request = Account.getStatuses(accountID, range: range, onlyMedia: false, pinned: false, excludeReplies: !Preferences.shared.showRepliesInProfiles)
|
||||
func getStatuses(for range: RequestRange = .default, onlyPinned: Bool = false, completion: @escaping Client.Callback<[Status]>) {
|
||||
let request = Account.getStatuses(accountID, range: range, onlyMedia: false, pinned: onlyPinned, excludeReplies: !Preferences.shared.showRepliesInProfiles)
|
||||
MastodonController.client.run(request, completion: completion)
|
||||
}
|
||||
|
||||
|
@ -128,27 +142,38 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
// MARK: - Table view data source
|
||||
|
||||
override func numberOfSections(in tableView: UITableView) -> Int {
|
||||
return 1 + timelineSegments.count
|
||||
// 1 section for header, 1 section for pinned, rest for timeline
|
||||
return 2 + timelineSegments.count
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
|
||||
if section == 0 {
|
||||
return accountID == nil || MastodonCache.account(for: accountID) == nil ? 0 : 1
|
||||
} else if section == 1 {
|
||||
return pinnedStatusIDs.count
|
||||
} else {
|
||||
return timelineSegments[section - 1].count
|
||||
return timelineSegments[section - 2].count
|
||||
}
|
||||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
|
||||
if indexPath.section == 0 {
|
||||
switch indexPath.section {
|
||||
case 0:
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "headerCell", for: indexPath) as? ProfileHeaderTableViewCell else { fatalError() }
|
||||
cell.selectionStyle = .none
|
||||
cell.delegate = self
|
||||
cell.updateUI(for: accountID)
|
||||
return cell
|
||||
} else {
|
||||
case 1:
|
||||
guard let cell = tableView.dequeueReusableCell(withIdentifier: "statusCell", for: indexPath) as? StatusTableViewCell else { fatalError() }
|
||||
let statusID = timelineSegments[indexPath.section - 1][indexPath.row]
|
||||
let statusID = pinnedStatusIDs[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.delegate = self
|
||||
return cell
|
||||
|
@ -156,14 +181,14 @@ class ProfileTableViewController: EnhancedTableViewController {
|
|||
}
|
||||
|
||||
override func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||
if timelineSegments.count > 0 && indexPath.section == timelineSegments.count && indexPath.row == timelineSegments[indexPath.section - 1].count - 1 {
|
||||
if timelineSegments.count > 0 && indexPath.section - 1 == timelineSegments.count && indexPath.row == timelineSegments[indexPath.section - 2].count - 1 {
|
||||
guard let older = older else { return }
|
||||
|
||||
getStatuses(for: older) { response in
|
||||
guard case let .success(newStatuses, pagination) = response else { fatalError() }
|
||||
|
||||
MastodonCache.addAll(statuses: newStatuses)
|
||||
self.timelineSegments[indexPath.section - 1].append(objects: newStatuses)
|
||||
self.timelineSegments[indexPath.section - 2].append(objects: newStatuses)
|
||||
|
||||
self.older = pagination?.older
|
||||
}
|
||||
|
@ -224,13 +249,11 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
|
|||
if let relationship = relationship {
|
||||
let toggleFollowActivity = relationship.following ? UnfollowAccountActivity() : FollowAccountActivity()
|
||||
customActivities = [
|
||||
SendMessageActivity(),
|
||||
toggleFollowActivity,
|
||||
OpenInSafariActivity()
|
||||
]
|
||||
} else {
|
||||
customActivities = [
|
||||
SendMessageActivity(),
|
||||
OpenInSafariActivity()
|
||||
]
|
||||
}
|
||||
|
@ -246,8 +269,8 @@ extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
|
|||
|
||||
extension ProfileTableViewController: UITableViewDataSourcePrefetching {
|
||||
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths where indexPath.section > 0 {
|
||||
let statusID = timelineSegments[indexPath.section - 1][indexPath.row]
|
||||
for indexPath in indexPaths where indexPath.section > 1 {
|
||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row]
|
||||
guard let status = MastodonCache.status(for: statusID) else { continue }
|
||||
ImageCache.avatars.get(status.account.avatar, completion: nil)
|
||||
for attachment in status.attachments {
|
||||
|
@ -257,8 +280,8 @@ extension ProfileTableViewController: UITableViewDataSourcePrefetching {
|
|||
}
|
||||
|
||||
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt indexPaths: [IndexPath]) {
|
||||
for indexPath in indexPaths where indexPath.section > 0 {
|
||||
let statusID = timelineSegments[indexPath.section - 1][indexPath.row]
|
||||
for indexPath in indexPaths where indexPath.section > 1 {
|
||||
let statusID = timelineSegments[indexPath.section - 2][indexPath.row]
|
||||
guard let status = MastodonCache.status(for: statusID) else { continue }
|
||||
ImageCache.avatars.cancel(status.account.avatar)
|
||||
for attachment in status.attachments {
|
||||
|
|
|
@ -46,8 +46,9 @@ extension EnhancedTableViewController {
|
|||
guard let (previewProvider, actionsProvider) = cell.getPreviewProviders(for: cellLocation, sourceViewController: self) else {
|
||||
return nil
|
||||
}
|
||||
let actionProvider: UIContextMenuActionProvider = { (elements) in
|
||||
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: elements + actionsProvider())
|
||||
let actionProvider: UIContextMenuActionProvider = { (_) in
|
||||
let suggested = self.getSuggestedContextMenuActions(tableView: tableView, indexPath: indexPath, point: point)
|
||||
return UIMenu(title: "", image: nil, identifier: nil, options: [], children: suggested + actionsProvider())
|
||||
}
|
||||
return UIContextMenuConfiguration(identifier: nil, previewProvider: previewProvider, actionProvider: actionProvider)
|
||||
} else {
|
||||
|
@ -55,6 +56,11 @@ 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) {
|
||||
if let viewController = animator.previewViewController {
|
||||
animator.preferredCommitStyle = .pop
|
||||
|
|
|
@ -69,13 +69,6 @@ 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 {
|
||||
return UIAction(title: title, image: UIImage(systemName: systemImageName), identifier: UIAction.Identifier(identifier), discoverabilityTitle: nil, attributes: [], state: .off, handler: handler)
|
||||
}
|
||||
|
|
|
@ -105,14 +105,7 @@ class AttachmentView: UIImageView, GIFAnimatable {
|
|||
}
|
||||
|
||||
@objc func imagePressed() {
|
||||
// switch attachment.kind {
|
||||
// case .image:
|
||||
delegate?.showAttachmentsGallery(startingAt: index)
|
||||
// case .video:
|
||||
// delegate?.showVideo(attachment: attachment)
|
||||
// default:
|
||||
// fatalError()
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,18 +19,14 @@ class AttachmentsContainerView: UIView {
|
|||
let attachmentViews: NSHashTable<AttachmentView> = .weakObjects()
|
||||
|
||||
var blurView: UIVisualEffectView?
|
||||
var hideButton: UIButton?
|
||||
var hideButtonView: UIVisualEffectView?
|
||||
var contentHidden: Bool! {
|
||||
didSet {
|
||||
guard let blurView = blurView,
|
||||
let hideButton = hideButton else { return }
|
||||
let hideButtonView = hideButtonView else { return }
|
||||
|
||||
blurView.alpha = 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
|
||||
}
|
||||
hideButtonView.alpha = self.contentHidden ? 0 : 1
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,6 +34,11 @@ class AttachmentsContainerView: UIView {
|
|||
super.awakeFromNib()
|
||||
|
||||
self.isUserInteractionEnabled = true
|
||||
|
||||
createBlurView()
|
||||
createHideButton()
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
|
||||
}
|
||||
|
||||
func getAttachmentView(for attachment: Attachment) -> AttachmentView? {
|
||||
|
@ -50,8 +51,8 @@ class AttachmentsContainerView: UIView {
|
|||
self.statusID = status.id
|
||||
attachments = status.attachments.filter { $0.kind == .image || $0.kind == .video }
|
||||
|
||||
attachmentViews.allObjects.forEach { $0.removeFromSuperview() }
|
||||
attachmentViews.removeAllObjects()
|
||||
subviews.forEach { $0.removeFromSuperview() }
|
||||
|
||||
if attachments.count > 0 {
|
||||
self.isHidden = false
|
||||
|
@ -62,14 +63,17 @@ class AttachmentsContainerView: UIView {
|
|||
case 1:
|
||||
let attachmentView = createAttachmentView(index: 0)
|
||||
fillView(attachmentView)
|
||||
sendSubviewToBack(attachmentView)
|
||||
accessibilityElements.append(attachmentView)
|
||||
case 2:
|
||||
let left = createAttachmentView(index: 0)
|
||||
let right = createAttachmentView(index: 1)
|
||||
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
let stack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
left,
|
||||
right
|
||||
]))
|
||||
])
|
||||
fillView(stack)
|
||||
sendSubviewToBack(stack)
|
||||
NSLayoutConstraint.activate([
|
||||
left.halfWidth()
|
||||
])
|
||||
|
@ -79,13 +83,15 @@ class AttachmentsContainerView: UIView {
|
|||
let left = createAttachmentView(index: 0)
|
||||
let topRight = createAttachmentView(index: 1)
|
||||
let bottomRight = createAttachmentView(index: 2)
|
||||
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
let outerStack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
left,
|
||||
createAttachmentsStack(axis: .vertical, arrangedSubviews: [
|
||||
topRight,
|
||||
bottomRight
|
||||
])
|
||||
]))
|
||||
])
|
||||
fillView(outerStack)
|
||||
sendSubviewToBack(outerStack)
|
||||
NSLayoutConstraint.activate([
|
||||
left.halfWidth(),
|
||||
topRight.halfHeight(),
|
||||
|
@ -102,13 +108,15 @@ class AttachmentsContainerView: UIView {
|
|||
])
|
||||
let topRight = createAttachmentView(index: 1)
|
||||
let bottomRight = createAttachmentView(index: 3)
|
||||
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
let outerStack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
left,
|
||||
createAttachmentsStack(axis: .vertical, arrangedSubviews: [
|
||||
topRight,
|
||||
bottomRight
|
||||
])
|
||||
]))
|
||||
])
|
||||
fillView(outerStack)
|
||||
sendSubviewToBack(outerStack)
|
||||
NSLayoutConstraint.activate([
|
||||
left.halfWidth(),
|
||||
topLeft.halfHeight(),
|
||||
|
@ -129,6 +137,7 @@ class AttachmentsContainerView: UIView {
|
|||
moreLabel.textColor = .secondaryLabel
|
||||
moreLabel.textAlignment = .center
|
||||
moreLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||
moreView.addSubview(moreLabel)
|
||||
moreView.accessibilityLabel = moreLabel.text
|
||||
|
||||
let topLeft = createAttachmentView(index: 0)
|
||||
|
@ -138,13 +147,15 @@ class AttachmentsContainerView: UIView {
|
|||
bottomLeft
|
||||
])
|
||||
let topRight = createAttachmentView(index: 1)
|
||||
fillView(createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
let outerStack = createAttachmentsStack(axis: .horizontal, arrangedSubviews: [
|
||||
left,
|
||||
createAttachmentsStack(axis: .vertical, arrangedSubviews: [
|
||||
topRight,
|
||||
moreView
|
||||
])
|
||||
]))
|
||||
])
|
||||
fillView(outerStack)
|
||||
sendSubviewToBack(outerStack)
|
||||
NSLayoutConstraint.activate([
|
||||
left.halfWidth(),
|
||||
topLeft.halfHeight(),
|
||||
|
@ -165,11 +176,11 @@ class AttachmentsContainerView: UIView {
|
|||
self.isHidden = true
|
||||
}
|
||||
|
||||
if status.sensitive {
|
||||
contentHidden = true
|
||||
createBlurView()
|
||||
createHideButton()
|
||||
updateUIForPreferences()
|
||||
}
|
||||
|
||||
@objc func updateUIForPreferences() {
|
||||
contentHidden = Preferences.shared.blurAllMedia || (MastodonCache.status(for: statusID)?.sensitive ?? false)
|
||||
}
|
||||
|
||||
private func createAttachmentView(index: Int) -> AttachmentView {
|
||||
|
@ -194,7 +205,7 @@ class AttachmentsContainerView: UIView {
|
|||
private func createBlurView() {
|
||||
let blur = UIBlurEffect(style: .dark)
|
||||
let blurView = UIVisualEffectView(effect: blur)
|
||||
blurView.effect = blur
|
||||
blurView.alpha = 0
|
||||
blurView.translatesAutoresizingMaskIntoConstraints = false
|
||||
fillView(blurView)
|
||||
let vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blur, style: .label))
|
||||
|
@ -230,21 +241,43 @@ class AttachmentsContainerView: UIView {
|
|||
}
|
||||
|
||||
private func createHideButton() {
|
||||
let hideButton = UIButton()
|
||||
hideButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
hideButton.alpha = 0
|
||||
hideButton.layer.cornerRadius = 2
|
||||
hideButton.layer.masksToBounds = true
|
||||
hideButton.setImage(UIImage(systemName: "eye.slash.fill"), for: .normal)
|
||||
hideButton.addTarget(self, action: #selector(hideButtonTapped), for: .touchUpInside)
|
||||
let blurEffect = UIBlurEffect(style: .regular)
|
||||
let hideButtonBlurView = UIVisualEffectView(effect: blurEffect)
|
||||
hideButtonBlurView.translatesAutoresizingMaskIntoConstraints = false
|
||||
hideButtonBlurView.alpha = 1
|
||||
hideButtonBlurView.isUserInteractionEnabled = true
|
||||
hideButtonBlurView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(hideButtonTapped)))
|
||||
addSubview(hideButtonBlurView)
|
||||
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([
|
||||
hideButton.topAnchor.constraint(equalTo: topAnchor, constant: 8),
|
||||
hideButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8)
|
||||
hideButtonBlurView.topAnchor.constraint(equalTo: topAnchor, constant: 8),
|
||||
hideButtonBlurView.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) {
|
||||
|
@ -261,11 +294,15 @@ class AttachmentsContainerView: UIView {
|
|||
// MARK: - Interaction
|
||||
|
||||
@objc func blurViewTapped() {
|
||||
contentHidden = false
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
self.contentHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
@objc func hideButtonTapped() {
|
||||
contentHidden = true
|
||||
UIView.animate(withDuration: 0.2) {
|
||||
self.contentHidden = true
|
||||
}
|
||||
}
|
||||
|
||||
@objc func showSensitiveContent() {
|
||||
|
|
|
@ -189,7 +189,7 @@ extension ActionNotificationGroupTableViewCell: MenuPreviewProvider {
|
|||
}
|
||||
return self.delegate?.statusActionAccountList(action: action, statusID: self.statusID, accountIDs: accountIDs)
|
||||
}, actions: {
|
||||
return self.actionsForNotificationGroup(self.group)
|
||||
return []
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<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">
|
||||
<device id="retina6_1" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14819.2"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<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">
|
||||
<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">
|
||||
<rect key="frame" x="0.0" y="79" width="230" height="74"/>
|
||||
<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"/>
|
||||
|
|
|
@ -142,7 +142,7 @@ extension FollowNotificationGroupTableViewCell: MenuPreviewProvider {
|
|||
return AccountListTableViewController(accountIDs: accountIDs)
|
||||
}
|
||||
}, actions: {
|
||||
return self.actionsForNotificationGroup(self.group)
|
||||
return []
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
|||
@IBOutlet weak var fieldsStackView: UIStackView!
|
||||
@IBOutlet weak var fieldNamesStackView: UIStackView!
|
||||
@IBOutlet weak var fieldValuesStack: UIStackView!
|
||||
@IBOutlet weak var moreButtonVisualEffectView: UIVisualEffectView!
|
||||
|
||||
var accountID: String!
|
||||
|
||||
|
@ -40,6 +41,12 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
|||
avatarImageView.isUserInteractionEnabled = true
|
||||
headerImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(headerPressed)))
|
||||
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)
|
||||
}
|
||||
|
@ -129,7 +136,7 @@ class ProfileHeaderTableViewCell: UITableViewCell {
|
|||
}
|
||||
}
|
||||
|
||||
@IBAction func morePressed(_ sender: Any) {
|
||||
@objc func morePressed() {
|
||||
delegate?.showMoreOptions()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<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">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -85,18 +85,49 @@
|
|||
</stackView>
|
||||
</subviews>
|
||||
</stackView>
|
||||
<button opaque="NO" alpha="0.59999999999999998" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="qiv-gB-kiX">
|
||||
<rect key="frame" x="323.5" y="120" width="35.5" height="22"/>
|
||||
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="18"/>
|
||||
<color key="tintColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<state key="normal" image="ellipsis" catalog="system">
|
||||
<preferredSymbolConfiguration key="preferredSymbolConfiguration" configurationType="pointSize" pointSize="32" scale="default"/>
|
||||
</state>
|
||||
<connections>
|
||||
<action selector="morePressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="0go-4p-qDa"/>
|
||||
</connections>
|
||||
</button>
|
||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="mQY-XN-PfZ">
|
||||
<rect key="frame" x="335" y="110" width="32" height="32"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="0Ol-1d-la6">
|
||||
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<visualEffectView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="t0d-eE-mbc">
|
||||
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="TgJ-FF-QyB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="32" height="32"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<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>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
|
@ -110,6 +141,7 @@
|
|||
<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="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 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"/>
|
||||
|
@ -118,9 +150,8 @@
|
|||
<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="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="LjK-72-Bez" firstAttribute="top" secondItem="mQY-XN-PfZ" secondAttribute="bottom" constant="16" id="rTO-fy-u0V"/>
|
||||
</constraints>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
|
||||
|
@ -133,6 +164,7 @@
|
|||
<outlet property="fieldsStackView" destination="sHU-GU-klv" id="Gli-Gf-Ubh"/>
|
||||
<outlet property="followsYouLabel" destination="a32-1a-xXZ" id="phY-0L-NnN"/>
|
||||
<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="usernameLabel" destination="MIj-OR-NOR" id="e1I-N7-rKx"/>
|
||||
</connections>
|
||||
|
|
|
@ -42,6 +42,7 @@ class StatusTableViewCell: UITableViewCell {
|
|||
@IBOutlet weak var favoriteButton: UIButton!
|
||||
@IBOutlet weak var reblogButton: UIButton!
|
||||
@IBOutlet weak var moreButton: UIButton!
|
||||
@IBOutlet weak var pinImageView: UIImageView!
|
||||
|
||||
var statusID: String!
|
||||
var accountID: String!
|
||||
|
@ -58,6 +59,7 @@ class StatusTableViewCell: UITableViewCell {
|
|||
reblogButton.tintColor = reblogged ? UIColor(displayP3Red: 1, green: 0.8, blue: 0, alpha: 1) : tintColor
|
||||
}
|
||||
}
|
||||
var showPinned: Bool = false
|
||||
|
||||
var collapsible = false {
|
||||
didSet {
|
||||
|
@ -110,10 +112,7 @@ class StatusTableViewCell: UITableViewCell {
|
|||
rebloggerAccountUpdater = MastodonCache.accountSubject
|
||||
.filter { $0.id == self.rebloggerID }
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(receiveValue: { (_) in
|
||||
// this method is responsible for setting the reblog label text
|
||||
self.updateUIForPreferences()
|
||||
})
|
||||
.sink(receiveValue: updateRebloggerLabel(reblogger:))
|
||||
}
|
||||
|
||||
func updateUI(statusID: String) {
|
||||
|
@ -153,6 +152,17 @@ class StatusTableViewCell: UITableViewCell {
|
|||
setCollapsed(collapsible, animated: false)
|
||||
contentWarningLabel.text = status.spoilerText
|
||||
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) {
|
||||
|
@ -189,11 +199,15 @@ class StatusTableViewCell: UITableViewCell {
|
|||
avatarImageView.layer.cornerRadius = Preferences.shared.avatarStyle.cornerRadius(for: avatarImageView)
|
||||
if let rebloggerID = rebloggerID,
|
||||
let reblogger = MastodonCache.account(for: rebloggerID) {
|
||||
reblogLabel.text = "Reblogged by \(reblogger.realDisplayName)"
|
||||
updateRebloggerLabel(reblogger: reblogger)
|
||||
}
|
||||
displayNameLabel.text = account.realDisplayName
|
||||
}
|
||||
|
||||
func updateRebloggerLabel(reblogger: Account) {
|
||||
reblogLabel.text = "Reblogged by \(reblogger.realDisplayName)"
|
||||
}
|
||||
|
||||
func updateTimestamp() {
|
||||
guard let status = MastodonCache.status(for: statusID) else { fatalError("Missing cached status \(statusID!)") }
|
||||
|
||||
|
@ -225,7 +239,8 @@ class StatusTableViewCell: UITableViewCell {
|
|||
}
|
||||
updateTimestampWorkItem?.cancel()
|
||||
updateTimestampWorkItem = nil
|
||||
attachmentsView.subviews.forEach { $0.removeFromSuperview() }
|
||||
attachmentsView.attachmentViews.allObjects.forEach { $0.removeFromSuperview() }
|
||||
showPinned = false
|
||||
}
|
||||
|
||||
override func setSelected(_ selected: Bool, animated: Bool) {
|
||||
|
@ -439,8 +454,8 @@ extension StatusTableViewCell: MenuPreviewProvider {
|
|||
return (content: { ProfileTableViewController(accountID: self.accountID) }, actions: { self.actionsForProfile(accountID: self.accountID) })
|
||||
} else if attachmentsView.frame.contains(location) {
|
||||
let attachmentsViewLocation = attachmentsView.convert(location, from: self)
|
||||
if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView {
|
||||
let image = attachmentView.image!
|
||||
if let attachmentView = attachmentsView.subviews.first(where: { $0.frame.contains(attachmentsViewLocation) }) as? AttachmentView,
|
||||
let image = attachmentView.image {
|
||||
let description = attachmentView.attachment.description
|
||||
return (content: { self.delegate?.largeImage(image, description: description, sourceView: attachmentView) }, actions: { [] })
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<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">
|
||||
<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">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<view contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="ve3-Y1-NQH">
|
||||
<rect key="frame" x="0.0" y="28.5" width="343" height="165.5"/>
|
||||
<rect key="frame" x="0.0" y="28.5" width="343" height="103.5"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
|
@ -37,7 +37,7 @@
|
|||
</constraints>
|
||||
</imageView>
|
||||
<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="165.5"/>
|
||||
<rect key="frame" x="58" y="0.0" width="277" height="103.5"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
|
@ -62,6 +62,11 @@
|
|||
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</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">
|
||||
<rect key="frame" x="252.5" y="0.0" width="24.5" height="20.5"/>
|
||||
<accessibility key="accessibilityConfiguration">
|
||||
|
@ -101,7 +106,7 @@
|
|||
</connections>
|
||||
</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">
|
||||
<rect key="frame" x="0.0" y="83" width="277" height="82.5"/>
|
||||
<rect key="frame" x="0.0" y="83" width="277" height="20.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
|
@ -120,17 +125,17 @@
|
|||
</constraints>
|
||||
</view>
|
||||
<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="198" width="343" height="0.0"/>
|
||||
<rect key="frame" x="0.0" y="136" 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"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" priority="999" constant="200" id="J42-49-2MU"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<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="202" width="343" height="22"/>
|
||||
<rect key="frame" x="0.0" y="140" width="343" height="84"/>
|
||||
<subviews>
|
||||
<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="0.0" width="21" height="22"/>
|
||||
<rect key="frame" x="0.0" y="62" width="21" height="22"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Reply"/>
|
||||
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
|
||||
<connections>
|
||||
|
@ -138,7 +143,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="x0t-TR-jJ4">
|
||||
<rect key="frame" x="107" y="0.0" width="22" height="22"/>
|
||||
<rect key="frame" x="107" y="62" width="22" height="22"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Favorite"/>
|
||||
<state key="normal" image="star.fill" catalog="system"/>
|
||||
<connections>
|
||||
|
@ -146,7 +151,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="6tW-z8-Qh9">
|
||||
<rect key="frame" x="215.5" y="0.0" width="22.5" height="22"/>
|
||||
<rect key="frame" x="215.5" y="62" width="22.5" height="22"/>
|
||||
<accessibility key="accessibilityConfiguration" label="Reblog"/>
|
||||
<state key="normal" image="repeat" catalog="system"/>
|
||||
<connections>
|
||||
|
@ -154,7 +159,7 @@
|
|||
</connections>
|
||||
</button>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="982-J4-NGl">
|
||||
<rect key="frame" x="324" y="0.0" width="19" height="22"/>
|
||||
<rect key="frame" x="324" y="62" width="19" height="22"/>
|
||||
<accessibility key="accessibilityConfiguration" label="More Actions"/>
|
||||
<state key="normal" image="ellipsis" catalog="system"/>
|
||||
<connections>
|
||||
|
@ -189,6 +194,7 @@
|
|||
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
|
||||
<outlet property="favoriteButton" destination="x0t-TR-jJ4" id="Ohz-bs-Ebr"/>
|
||||
<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="reblogLabel" destination="lDH-50-AJZ" id="uJf-Pt-cEP"/>
|
||||
<outlet property="replyButton" destination="rKF-yF-KIa" id="rul-lk-bIR"/>
|
||||
|
@ -202,6 +208,7 @@
|
|||
<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="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="star.fill" catalog="system" width="64" height="58"/>
|
||||
</resources>
|
||||
|
|
Loading…
Reference in New Issue