Compare commits
7 Commits
e522e30ce5
...
51db0066ac
Author | SHA1 | Date |
---|---|---|
Shadowfacts | 51db0066ac | |
Shadowfacts | 9763edef47 | |
Shadowfacts | 442f57bfc4 | |
Shadowfacts | ae7101bb30 | |
Shadowfacts | 490d48c635 | |
Shadowfacts | 69ee3bb4f0 | |
Shadowfacts | 46b455c3d1 |
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,5 +1,15 @@
|
|||
# Changelog
|
||||
|
||||
## 2024.1 (117)
|
||||
Features/Improvements:
|
||||
- Add See Results button to polls
|
||||
|
||||
Bugfixes:
|
||||
- Fix race condition when presenting gallery for 4th of more than 4 attachments
|
||||
- Fix gallery interactive dismissal not working for 4th or later attachments on posts with more than 4 attachments
|
||||
- Pixelfed: Fix crash when there are multiple follow notifications from the same account
|
||||
- macOS: Fix gallery being positioned incorrectly when Reduce Motion is on
|
||||
|
||||
## 2024.1 (116)
|
||||
Features/Improvements:
|
||||
- Display message on empty list timelines
|
||||
|
|
|
@ -52,6 +52,9 @@ class GalleryDismissAnimationController: NSObject, UIViewControllerAnimatedTrans
|
|||
appliedSourceToDestTransform = false
|
||||
}
|
||||
|
||||
to.view.frame = container.bounds
|
||||
from.view.frame = container.bounds
|
||||
|
||||
let content = itemViewController.takeContent()
|
||||
content.view.translatesAutoresizingMaskIntoConstraints = true
|
||||
content.view.layer.masksToBounds = true
|
||||
|
@ -112,6 +115,8 @@ class GalleryDismissAnimationController: NSObject, UIViewControllerAnimatedTrans
|
|||
return
|
||||
}
|
||||
|
||||
toVC.view.frame = transitionContext.containerView.bounds
|
||||
fromVC.view.frame = transitionContext.containerView.bounds
|
||||
transitionContext.containerView.addSubview(toVC.view)
|
||||
transitionContext.containerView.addSubview(fromVC.view)
|
||||
|
||||
|
|
|
@ -109,8 +109,6 @@ class GalleryPresentationAnimationController: NSObject, UIViewControllerAnimated
|
|||
itemViewController.addContent()
|
||||
|
||||
transitionContext.completeTransition(true)
|
||||
|
||||
to.presentationAnimationCompleted()
|
||||
}
|
||||
|
||||
animator.startAnimation()
|
||||
|
@ -121,8 +119,9 @@ class GalleryPresentationAnimationController: NSObject, UIViewControllerAnimated
|
|||
return
|
||||
}
|
||||
|
||||
transitionContext.containerView.addSubview(to.view)
|
||||
to.view.alpha = 0
|
||||
to.view.frame = transitionContext.containerView.bounds
|
||||
transitionContext.containerView.addSubview(to.view)
|
||||
|
||||
let duration = transitionDuration(using: transitionContext)
|
||||
let animator = UIViewPropertyAnimator(duration: duration, curve: .easeInOut)
|
||||
|
@ -131,8 +130,6 @@ class GalleryPresentationAnimationController: NSObject, UIViewControllerAnimated
|
|||
}
|
||||
animator.addCompletion { _ in
|
||||
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
|
||||
|
||||
to.presentationAnimationCompleted()
|
||||
}
|
||||
animator.startAnimation()
|
||||
}
|
||||
|
|
|
@ -68,6 +68,17 @@ public class GalleryViewController: UIPageViewController {
|
|||
setViewControllers([makeItemVC(index: initialItemIndex)], direction: .forward, animated: false)
|
||||
}
|
||||
|
||||
public override func viewDidAppear(_ animated: Bool) {
|
||||
super.viewDidAppear(animated)
|
||||
|
||||
if animated {
|
||||
// Wait until the transition is no longer in-progress, otherwise things will just get deferred again.
|
||||
DispatchQueue.main.async {
|
||||
self.presentationAnimationCompleted()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override func viewWillDisappear(_ animated: Bool) {
|
||||
super.viewWillDisappear(animated)
|
||||
|
||||
|
|
|
@ -255,7 +255,6 @@
|
|||
D6B81F442560390300F6E31D /* MenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B81F432560390300F6E31D /* MenuController.swift */; };
|
||||
D6B8DB342182A59300424AF7 /* UIAlertController+Visibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */; };
|
||||
D6B93667281D937300237D0E /* MainSidebarMyProfileCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B93666281D937300237D0E /* MainSidebarMyProfileCollectionViewCell.swift */; };
|
||||
D6B9366B281EE77E00237D0E /* PollVoteButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B9366A281EE77E00237D0E /* PollVoteButton.swift */; };
|
||||
D6B9366D2828445000237D0E /* SavedInstance.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B9366C2828444F00237D0E /* SavedInstance.swift */; };
|
||||
D6B9366F2828452F00237D0E /* SavedHashtag.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B9366E2828452F00237D0E /* SavedHashtag.swift */; };
|
||||
D6B936712829F72900237D0E /* NSManagedObjectContext+Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6B936702829F72900237D0E /* NSManagedObjectContext+Helpers.swift */; };
|
||||
|
@ -658,7 +657,6 @@
|
|||
D6B81F432560390300F6E31D /* MenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuController.swift; sourceTree = "<group>"; };
|
||||
D6B8DB332182A59300424AF7 /* UIAlertController+Visibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIAlertController+Visibility.swift"; sourceTree = "<group>"; };
|
||||
D6B93666281D937300237D0E /* MainSidebarMyProfileCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSidebarMyProfileCollectionViewCell.swift; sourceTree = "<group>"; };
|
||||
D6B9366A281EE77E00237D0E /* PollVoteButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PollVoteButton.swift; sourceTree = "<group>"; };
|
||||
D6B9366C2828444F00237D0E /* SavedInstance.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedInstance.swift; sourceTree = "<group>"; };
|
||||
D6B9366E2828452F00237D0E /* SavedHashtag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavedHashtag.swift; sourceTree = "<group>"; };
|
||||
D6B936702829F72900237D0E /* NSManagedObjectContext+Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Helpers.swift"; sourceTree = "<group>"; };
|
||||
|
@ -898,7 +896,6 @@
|
|||
D6A00B1C26379FC900316AD4 /* PollOptionsView.swift */,
|
||||
D623A5402635FB3C0095BD04 /* PollOptionView.swift */,
|
||||
D623A542263634100095BD04 /* PollOptionCheckboxView.swift */,
|
||||
D6B9366A281EE77E00237D0E /* PollVoteButton.swift */,
|
||||
);
|
||||
path = Poll;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2201,7 +2198,6 @@
|
|||
D68232F72464F4FD00325FB8 /* ComposeDrawingViewController.swift in Sources */,
|
||||
04586B4122B2FFB10021BD04 /* PreferencesView.swift in Sources */,
|
||||
D6620ACE2511A0ED00312CA0 /* StatusStateResolver.swift in Sources */,
|
||||
D6B9366B281EE77E00237D0E /* PollVoteButton.swift in Sources */,
|
||||
D68A76DA29511CA6001DA1B3 /* AccountPreferences.swift in Sources */,
|
||||
D68015402401A6BA00D6103B /* ComposingPrefsView.swift in Sources */,
|
||||
D6895DC428D65342006341DA /* ConfirmReblogStatusPreviewView.swift in Sources */,
|
||||
|
|
|
@ -15,10 +15,12 @@ import AVFoundation
|
|||
class StatusAttachmentsGalleryDataSource: GalleryDataSource {
|
||||
let attachments: [Attachment]
|
||||
let sourceViews: NSHashTable<AttachmentView>
|
||||
weak var moreView: UIView?
|
||||
|
||||
init(attachments: [Attachment], sourceViews: NSHashTable<AttachmentView>) {
|
||||
init(attachments: [Attachment], sourceViews: NSHashTable<AttachmentView>, moreView: UIView?) {
|
||||
self.attachments = attachments
|
||||
self.sourceViews = sourceViews
|
||||
self.moreView = moreView
|
||||
}
|
||||
|
||||
func galleryItemsCount() -> Int {
|
||||
|
@ -39,6 +41,21 @@ class StatusAttachmentsGalleryDataSource: GalleryDataSource {
|
|||
// TODO: if automatically play gifs is off, this will start the source view playing too
|
||||
gifController: view.gifController
|
||||
)
|
||||
} else if let entry = ImageCache.attachments.get(attachment.url, loadOriginal: true) {
|
||||
let gifController: GIFController? =
|
||||
if attachment.url.pathExtension == "gif",
|
||||
let data = entry.data {
|
||||
GIFController(gifData: data)
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
return ImageGalleryContentViewController(
|
||||
url: attachment.url,
|
||||
caption: attachment.description,
|
||||
originalData: entry.data,
|
||||
image: entry.image,
|
||||
gifController: gifController
|
||||
)
|
||||
} else {
|
||||
return LoadingGalleryContentViewController {
|
||||
let (data, image) = await ImageCache.attachments.get(attachment.url, loadOriginal: true)
|
||||
|
@ -92,9 +109,13 @@ class StatusAttachmentsGalleryDataSource: GalleryDataSource {
|
|||
}
|
||||
|
||||
func galleryContentTransitionSourceView(forItemAt index: Int) -> UIView? {
|
||||
if attachments.count > 4 && index >= 3 {
|
||||
return moreView
|
||||
} else {
|
||||
let attachment = attachments[index]
|
||||
return attachmentView(for: attachment)
|
||||
}
|
||||
}
|
||||
|
||||
func galleryApplicationActivities(forItemAt index: Int) -> [UIActivity]? {
|
||||
[SaveToPhotosActivity()]
|
||||
|
|
|
@ -135,7 +135,9 @@ class ActionNotificationGroupCollectionViewCell: UICollectionViewListCell {
|
|||
|
||||
updateTimestamp()
|
||||
|
||||
let people = group.notifications.compactMap {
|
||||
let people = group.notifications
|
||||
.uniques(by: \.account.id)
|
||||
.compactMap {
|
||||
mastodonController.persistentContainer.account(for: $0.account.id)
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,11 @@ class FollowNotificationGroupCollectionViewCell: UICollectionViewListCell {
|
|||
}
|
||||
self.group = group
|
||||
|
||||
let people = group.notifications.compactMap { mastodonController.persistentContainer.account(for: $0.account.id) }
|
||||
let people = group.notifications
|
||||
.uniques(by: \.account.id)
|
||||
.compactMap {
|
||||
mastodonController.persistentContainer.account(for: $0.account.id)
|
||||
}
|
||||
|
||||
actionLabel.setEmojis(pairs: people.map {
|
||||
($0.displayOrUserName, $0.emojis)
|
||||
|
|
|
@ -627,11 +627,11 @@ extension NotificationsCollectionViewController: UICollectionViewDelegate {
|
|||
case .favourite, .reblog:
|
||||
let type = group.kind == .favourite ? StatusActionAccountListViewController.ActionType.favorite : .reblog
|
||||
let statusID = group.notifications.first!.status!.id
|
||||
let accountIDs = group.notifications.map(\.account.id)
|
||||
let accountIDs = group.notifications.map(\.account.id).uniques()
|
||||
let vc = StatusActionAccountListViewController(actionType: type, statusID: statusID, statusState: .unknown, accountIDs: accountIDs, mastodonController: mastodonController)
|
||||
show(vc)
|
||||
case .follow:
|
||||
let accountIDs = group.notifications.map(\.account.id)
|
||||
let accountIDs = group.notifications.map(\.account.id).uniques()
|
||||
switch accountIDs.count {
|
||||
case 0:
|
||||
collectionView.deselectItem(at: indexPath, animated: true)
|
||||
|
@ -670,11 +670,11 @@ extension NotificationsCollectionViewController: UICollectionViewDelegate {
|
|||
return UIContextMenuConfiguration(previewProvider: {
|
||||
let type = group.kind == .favourite ? StatusActionAccountListViewController.ActionType.favorite : .reblog
|
||||
let statusID = group.notifications.first!.status!.id
|
||||
let accountIDs = group.notifications.map(\.account.id)
|
||||
let accountIDs = group.notifications.map(\.account.id).uniques()
|
||||
return StatusActionAccountListViewController(actionType: type, statusID: statusID, statusState: .unknown, accountIDs: accountIDs, mastodonController: self.mastodonController)
|
||||
})
|
||||
case .follow:
|
||||
let accountIDs = group.notifications.map(\.account.id)
|
||||
let accountIDs = group.notifications.map(\.account.id).uniques()
|
||||
return UIContextMenuConfiguration {
|
||||
if accountIDs.count == 1 {
|
||||
return ProfileViewController(accountID: accountIDs.first!, mastodonController: self.mastodonController)
|
||||
|
|
|
@ -238,7 +238,8 @@ extension StatusEditCollectionViewCell: AttachmentViewDelegate {
|
|||
func attachmentViewGallery(startingAt index: Int) -> UIViewController? {
|
||||
let attachments = attachmentsView.attachments!
|
||||
let sourceViews = attachmentsView.attachmentViews.copy() as! NSHashTable<AttachmentView>
|
||||
return GalleryVC.GalleryViewController(dataSource: StatusAttachmentsGalleryDataSource(attachments: attachments, sourceViews: sourceViews), initialItemIndex: index)
|
||||
let dataSource = StatusAttachmentsGalleryDataSource(attachments: attachments, sourceViews: sourceViews, moreView: attachmentsView.moreView)
|
||||
return GalleryVC.GalleryViewController(dataSource: dataSource, initialItemIndex: index)
|
||||
}
|
||||
|
||||
func attachmentViewPresent(_ vc: UIViewController, animated: Bool) {
|
||||
|
|
|
@ -18,7 +18,7 @@ class AttachmentsContainerView: UIView {
|
|||
|
||||
let attachmentViews: NSHashTable<AttachmentView> = .weakObjects()
|
||||
let attachmentStacks: NSHashTable<UIStackView> = .weakObjects()
|
||||
var moreView: UIView?
|
||||
private(set) var moreView: UIView?
|
||||
private var aspectRatioConstraint: NSLayoutConstraint?
|
||||
private(set) var aspectRatio: CGFloat = 16/9 {
|
||||
didSet {
|
||||
|
|
|
@ -75,7 +75,7 @@ class PollOptionView: UIView {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updateUI(poll: Poll, option: Poll.Option, ownVoted: Bool, mastodonController: MastodonController) {
|
||||
func updateUI(poll: Poll, option: Poll.Option, ownVoted: Bool, showResults: Bool, mastodonController: MastodonController) {
|
||||
let showCheckbox = poll.ownVotes?.isEmpty == false || !poll.effectiveExpired
|
||||
if showCheckbox {
|
||||
checkbox.isChecked = ownVoted
|
||||
|
@ -100,7 +100,7 @@ class PollOptionView: UIView {
|
|||
|
||||
accessibilityLabel = option.title
|
||||
|
||||
if (poll.voted ?? false) || poll.effectiveExpired,
|
||||
if showResults,
|
||||
let optionVotes = option.votesCount {
|
||||
let frac: CGFloat
|
||||
if poll.multiple,
|
||||
|
|
|
@ -65,7 +65,7 @@ class PollOptionsView: UIControl {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func updateUI(poll: Poll) {
|
||||
func updateUI(poll: Poll, showResults: Bool) {
|
||||
self.poll = poll
|
||||
|
||||
if poll.options.count > options.count {
|
||||
|
@ -81,7 +81,7 @@ class PollOptionsView: UIControl {
|
|||
}
|
||||
|
||||
for (index, (view, opt)) in zip(options, poll.options).enumerated() {
|
||||
view.updateUI(poll: poll, option: opt, ownVoted: poll.ownVotes?.contains(index) ?? false, mastodonController: mastodonController)
|
||||
view.updateUI(poll: poll, option: opt, ownVoted: poll.ownVotes?.contains(index) ?? false, showResults: showResults, mastodonController: mastodonController)
|
||||
view.checkboxIfInitialized?.readOnly = !isEnabled
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +0,0 @@
|
|||
//
|
||||
// PollVoteButton.swift
|
||||
// Tusker
|
||||
//
|
||||
// Created by Shadowfacts on 5/1/22.
|
||||
// Copyright © 2022 Shadowfacts. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
/// Wraps a UILabel and UIButton to allow setting disabled titles on Catalyst, where `setTitle(_:for:)` only works for the normal state.
|
||||
class PollVoteButton: UIView {
|
||||
|
||||
var disabledTitle: String = "" {
|
||||
didSet {
|
||||
update()
|
||||
}
|
||||
}
|
||||
var isEnabled = true {
|
||||
didSet {
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
private var button = UIButton(type: .system)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
private var label = UILabel()
|
||||
#endif
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
button.translatesAutoresizingMaskIntoConstraints = false
|
||||
button.setTitleColor(.secondaryLabel, for: .disabled)
|
||||
button.contentHorizontalAlignment = .trailing
|
||||
embedSubview(button)
|
||||
#if targetEnvironment(macCatalyst)
|
||||
label.textColor = .secondaryLabel
|
||||
label.translatesAutoresizingMaskIntoConstraints = false
|
||||
label.textAlignment = .right
|
||||
embedSubview(label)
|
||||
#endif
|
||||
|
||||
update()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func addTarget(_ target: Any, action: Selector) {
|
||||
button.addTarget(target, action: action, for: .touchUpInside)
|
||||
}
|
||||
|
||||
func setFont(_ font: UIFont) {
|
||||
button.titleLabel!.font = font
|
||||
#if targetEnvironment(macCatalyst)
|
||||
label.font = font
|
||||
#endif
|
||||
}
|
||||
|
||||
private func update() {
|
||||
button.isEnabled = isEnabled
|
||||
if isEnabled {
|
||||
#if targetEnvironment(macCatalyst)
|
||||
label.isHidden = true
|
||||
button.isHidden = false
|
||||
#endif
|
||||
button.setTitle("Vote", for: .normal)
|
||||
} else {
|
||||
#if targetEnvironment(macCatalyst)
|
||||
label.text = disabledTitle
|
||||
label.isHidden = false
|
||||
button.isHidden = true
|
||||
#else
|
||||
button.setTitle(disabledTitle, for: .disabled)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -15,7 +15,7 @@ class StatusPollView: UIView, StatusContentView {
|
|||
let f = DateComponentsFormatter()
|
||||
f.includesTimeRemainingPhrase = true
|
||||
f.maximumUnitCount = 1
|
||||
f.unitsStyle = .full
|
||||
f.unitsStyle = .abbreviated
|
||||
f.allowedUnits = [.weekOfMonth, .day, .hour, .minute]
|
||||
return f
|
||||
}()
|
||||
|
@ -25,9 +25,10 @@ class StatusPollView: UIView, StatusContentView {
|
|||
|
||||
private var statusID: String!
|
||||
private(set) var poll: Poll?
|
||||
private var showingResults = false
|
||||
|
||||
private var optionsView: PollOptionsView!
|
||||
private var voteButton: PollVoteButton!
|
||||
private var voteButton: UIButton!
|
||||
private var infoLabel: UILabel!
|
||||
|
||||
private var canVote = true
|
||||
|
@ -63,11 +64,11 @@ class StatusPollView: UIView, StatusContentView {
|
|||
infoLabel.adjustsFontSizeToFitWidth = true
|
||||
addSubview(infoLabel)
|
||||
|
||||
voteButton = PollVoteButton()
|
||||
voteButton = UIButton(configuration: .plain())
|
||||
voteButton.translatesAutoresizingMaskIntoConstraints = false
|
||||
voteButton.addTarget(self, action: #selector(votePressed))
|
||||
voteButton.setFont(infoLabel.font)
|
||||
voteButton.addTarget(self, action: #selector(votePressed), for: .touchUpInside)
|
||||
voteButton.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||
voteButton.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||
addSubview(voteButton)
|
||||
|
||||
NSLayoutConstraint.activate([
|
||||
|
@ -76,20 +77,24 @@ class StatusPollView: UIView, StatusContentView {
|
|||
optionsView.topAnchor.constraint(equalTo: topAnchor),
|
||||
|
||||
infoLabel.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||
infoLabel.topAnchor.constraint(equalTo: optionsView.bottomAnchor),
|
||||
infoLabel.topAnchor.constraint(equalTo: optionsView.bottomAnchor, constant: 4),
|
||||
infoLabel.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
infoLabel.trailingAnchor.constraint(equalTo: voteButton.leadingAnchor, constant: -8),
|
||||
|
||||
voteButton.widthAnchor.constraint(greaterThanOrEqualToConstant: 44),
|
||||
voteButton.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||
voteButton.topAnchor.constraint(equalTo: optionsView.bottomAnchor),
|
||||
voteButton.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||
voteButton.topAnchor.constraint(equalTo: infoLabel.topAnchor),
|
||||
voteButton.bottomAnchor.constraint(equalTo: infoLabel.bottomAnchor),
|
||||
])
|
||||
|
||||
accessibilityElements = [optionsView!, infoLabel!, voteButton!]
|
||||
}
|
||||
|
||||
func updateUI(status: StatusMO, poll: Poll?) {
|
||||
if statusID != status.id {
|
||||
showingResults = false
|
||||
}
|
||||
|
||||
self.statusID = status.id
|
||||
self.poll = poll
|
||||
|
||||
|
@ -104,19 +109,17 @@ class StatusPollView: UIView, StatusContentView {
|
|||
|
||||
optionsView.mastodonController = mastodonController
|
||||
optionsView.isEnabled = canVote
|
||||
optionsView.updateUI(poll: poll)
|
||||
optionsView.updateUI(poll: poll, showResults: showingResults || !canVote)
|
||||
|
||||
var expired = false
|
||||
let expiryText: String?
|
||||
if let expiresAt = poll.expiresAt {
|
||||
if expiresAt > Date() {
|
||||
expiryText = StatusPollView.formatter.string(from: Date(), to: expiresAt)
|
||||
} else {
|
||||
expired = true
|
||||
expiryText = nil
|
||||
}
|
||||
} else {
|
||||
expiryText = "Does not expire"
|
||||
expiryText = "No expiry"
|
||||
}
|
||||
|
||||
let format = NSLocalizedString("poll votes count", comment: "poll total votes count")
|
||||
|
@ -125,20 +128,7 @@ class StatusPollView: UIView, StatusContentView {
|
|||
infoLabel.text! += ", \(expiryText)"
|
||||
}
|
||||
|
||||
if expired {
|
||||
voteButton.disabledTitle = "Expired"
|
||||
} else if poll.voted ?? false {
|
||||
if status.account.id == mastodonController.account?.id {
|
||||
voteButton.isHidden = true
|
||||
} else {
|
||||
voteButton.disabledTitle = "Voted"
|
||||
}
|
||||
} else if poll.multiple {
|
||||
voteButton.disabledTitle = "Select multiple"
|
||||
} else {
|
||||
voteButton.disabledTitle = "Select one"
|
||||
}
|
||||
voteButton.isEnabled = false
|
||||
updateVoteButton(status: status, poll: poll)
|
||||
}
|
||||
|
||||
func estimateHeight(effectiveWidth: CGFloat) -> CGFloat {
|
||||
|
@ -146,11 +136,54 @@ class StatusPollView: UIView, StatusContentView {
|
|||
return optionsView.estimateHeight(effectiveWidth: effectiveWidth) + infoLabel.sizeThatFits(UIView.layoutFittingExpandedSize).height
|
||||
}
|
||||
|
||||
private func updateVoteButton(status: StatusMO, poll: Poll) {
|
||||
let buttonTitle: String
|
||||
let buttonEnabled: Bool
|
||||
if let expiresAt = poll.expiresAt,
|
||||
expiresAt <= Date() {
|
||||
buttonTitle = "Expired"
|
||||
buttonEnabled = false
|
||||
} else if poll.voted ?? false {
|
||||
if status.account.id == mastodonController.account?.id {
|
||||
voteButton.isHidden = true
|
||||
return
|
||||
} else {
|
||||
buttonTitle = "Voted"
|
||||
buttonEnabled = false
|
||||
}
|
||||
} else if optionsView.checkedOptionIndices.isEmpty {
|
||||
buttonTitle = "See Results"
|
||||
buttonEnabled = true
|
||||
} else {
|
||||
buttonTitle = "Vote"
|
||||
buttonEnabled = true
|
||||
}
|
||||
var config = UIButton.Configuration.plain()
|
||||
config.attributedTitle = AttributedString(buttonTitle)
|
||||
config.attributedTitle!.font = infoLabel.font
|
||||
config.contentInsets = .zero
|
||||
// Necessary on Catalyst for some reason.
|
||||
config.baseForegroundColor = .tintColor
|
||||
voteButton.configuration = config
|
||||
voteButton.isEnabled = buttonEnabled
|
||||
}
|
||||
|
||||
private func checkedOptionsChanged() {
|
||||
voteButton.isEnabled = optionsView.checkedOptionIndices.count > 0
|
||||
if let status = mastodonController.persistentContainer.status(for: statusID),
|
||||
let poll = status.poll {
|
||||
updateVoteButton(status: status, poll: poll)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func votePressed() {
|
||||
if !optionsView.checkedOptionIndices.isEmpty {
|
||||
doVote()
|
||||
} else {
|
||||
showResults()
|
||||
}
|
||||
}
|
||||
|
||||
private func doVote() {
|
||||
guard let statusID,
|
||||
let poll else {
|
||||
return
|
||||
|
@ -158,7 +191,6 @@ class StatusPollView: UIView, StatusContentView {
|
|||
|
||||
optionsView.isEnabled = false
|
||||
voteButton.isEnabled = false
|
||||
voteButton.disabledTitle = "Voted"
|
||||
|
||||
#if !os(visionOS)
|
||||
UIImpactFeedbackGenerator(style: .light).impactOccurred()
|
||||
|
@ -191,4 +223,11 @@ class StatusPollView: UIView, StatusContentView {
|
|||
}
|
||||
}
|
||||
|
||||
private func showResults() {
|
||||
showingResults = true
|
||||
if let status = mastodonController.persistentContainer.status(for: statusID) {
|
||||
updateUI(status: status, poll: status.poll)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -334,7 +334,8 @@ extension StatusCollectionViewCell {
|
|||
return nil
|
||||
}
|
||||
let sourceViews = attachmentsView.attachmentViews.copy() as! NSHashTable<AttachmentView>
|
||||
return GalleryVC.GalleryViewController(dataSource: StatusAttachmentsGalleryDataSource(attachments: status.attachments, sourceViews: sourceViews), initialItemIndex: index)
|
||||
let dataSource = StatusAttachmentsGalleryDataSource(attachments: status.attachments, sourceViews: sourceViews, moreView: attachmentsView.moreView)
|
||||
return GalleryVC.GalleryViewController(dataSource: dataSource, initialItemIndex: index)
|
||||
}
|
||||
|
||||
func attachmentViewPresent(_ vc: UIViewController, animated: Bool) {
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
// https://help.apple.com/xcode/#/dev745c5c974
|
||||
|
||||
MARKETING_VERSION = 2024.1
|
||||
CURRENT_PROJECT_VERSION = 116
|
||||
CURRENT_PROJECT_VERSION = 117
|
||||
CURRENT_PROJECT_VERSION = $(inherited)$(CURRENT_PROJECT_VERSION_BUILD_SUFFIX_$(CONFIGURATION))
|
||||
|
||||
CURRENT_PROJECT_VERSION_BUILD_SUFFIX_Debug=-dev
|
||||
|
|
Loading…
Reference in New Issue