forked from zelo72/mastodon-ios
feat: add sensitive hint label for status media
This commit is contained in:
parent
c4ab4f68c6
commit
41e1b75c62
|
@ -129,6 +129,7 @@
|
||||||
"show_post": "Show Post",
|
"show_post": "Show Post",
|
||||||
"show_user_profile": "Show user profile",
|
"show_user_profile": "Show user profile",
|
||||||
"content_warning": "Content Warning",
|
"content_warning": "Content Warning",
|
||||||
|
"sensitive_content": "Sensitive Content",
|
||||||
"media_content_warning": "Tap anywhere to reveal",
|
"media_content_warning": "Tap anywhere to reveal",
|
||||||
"tap_to_reveal": "Tap to reveal",
|
"tap_to_reveal": "Tap to reveal",
|
||||||
"poll": {
|
"poll": {
|
||||||
|
|
|
@ -39,6 +39,8 @@ final class NotificationTimelineViewController: UIViewController, NeedsDependenc
|
||||||
return tableView
|
return tableView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let cellFrameCache = NSCache<NSNumber, NSValue>()
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
os_log(.info, log: .debug, "%{public}s[%{public}ld], %{public}s", ((#file as NSString).lastPathComponent), #line, #function)
|
||||||
}
|
}
|
||||||
|
@ -122,6 +124,16 @@ extension NotificationTimelineViewController {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - CellFrameCacheContainer
|
||||||
|
extension NotificationTimelineViewController: CellFrameCacheContainer {
|
||||||
|
func keyForCache(tableView: UITableView, indexPath: IndexPath) -> NSNumber? {
|
||||||
|
guard let diffableDataSource = viewModel.diffableDataSource else { return nil }
|
||||||
|
guard let item = diffableDataSource.itemIdentifier(for: indexPath) else { return nil }
|
||||||
|
let key = NSNumber(value: item.hashValue)
|
||||||
|
return key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extension NotificationTimelineViewController {
|
extension NotificationTimelineViewController {
|
||||||
|
|
||||||
@objc private func refreshControlValueChanged(_ sender: UIRefreshControl) {
|
@objc private func refreshControlValueChanged(_ sender: UIRefreshControl) {
|
||||||
|
@ -162,6 +174,13 @@ extension NotificationTimelineViewController: UITableViewDelegate, AutoGenerateT
|
||||||
|
|
||||||
// sourcery:end
|
// sourcery:end
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, estimatedHeightForRowAt indexPath: IndexPath) -> CGFloat {
|
||||||
|
guard let frame = retrieveCellFrame(tableView: tableView, indexPath: indexPath) else {
|
||||||
|
return 300
|
||||||
|
}
|
||||||
|
return ceil(frame.height)
|
||||||
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else {
|
guard let item = viewModel.diffableDataSource?.itemIdentifier(for: indexPath) else {
|
||||||
return
|
return
|
||||||
|
@ -172,6 +191,10 @@ extension NotificationTimelineViewController: UITableViewDelegate, AutoGenerateT
|
||||||
await viewModel.loadMore(item: item)
|
await viewModel.loadMore(item: item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tableView(_ tableView: UITableView, didEndDisplaying cell: UITableViewCell, forRowAt indexPath: IndexPath) {
|
||||||
|
cacheCellFrame(tableView: tableView, didEndDisplaying: cell, forRowAt: indexPath)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,22 +48,7 @@ public final class MediaGridContainerView: UIView {
|
||||||
return mediaViews
|
return mediaViews
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let contentWarningOverlay = ContentWarningOverlayView()
|
||||||
// let sensitiveToggleButtonBlurVisualEffectView: UIVisualEffectView = {
|
|
||||||
// let visualEffectView = UIVisualEffectView(effect: ContentWarningOverlayView.blurVisualEffect)
|
|
||||||
// visualEffectView.layer.masksToBounds = true
|
|
||||||
// visualEffectView.layer.cornerRadius = MediaGridContainerView.sensitiveToggleButtonSize.width / 2
|
|
||||||
// visualEffectView.layer.cornerCurve = .continuous
|
|
||||||
// return visualEffectView
|
|
||||||
// }()
|
|
||||||
// let sensitiveToggleButtonVibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: ContentWarningOverlayView.blurVisualEffect))
|
|
||||||
// let sensitiveToggleButton: HitTestExpandedButton = {
|
|
||||||
// let button = HitTestExpandedButton(type: .system)
|
|
||||||
// button.contentEdgeInsets = UIEdgeInsets(top: 4, left: 4, bottom: 4, right: 4)
|
|
||||||
// button.imageView?.contentMode = .scaleAspectFit
|
|
||||||
// button.setImage(UIImage(systemName: "eye.slash.fill"), for: .normal)
|
|
||||||
// return button
|
|
||||||
// }()
|
|
||||||
|
|
||||||
public override init(frame: CGRect) {
|
public override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
@ -86,7 +71,8 @@ public final class MediaGridContainerView: UIView {
|
||||||
|
|
||||||
extension MediaGridContainerView {
|
extension MediaGridContainerView {
|
||||||
private func _init() {
|
private func _init() {
|
||||||
// sensitiveToggleButton.addTarget(self, action: #selector(MediaGridContainerView.sensitiveToggleButtonDidPressed(_:)), for: .touchUpInside)
|
contentWarningOverlay.isUserInteractionEnabled = false
|
||||||
|
contentWarningOverlay.isHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,8 +98,8 @@ extension MediaGridContainerView {
|
||||||
let mediaView = _mediaViews[0]
|
let mediaView = _mediaViews[0]
|
||||||
layout.layout(in: self, mediaView: mediaView)
|
layout.layout(in: self, mediaView: mediaView)
|
||||||
|
|
||||||
// layoutSensitiveToggleButton()
|
layoutContentWarningOverlay()
|
||||||
// bringSubviewToFront(sensitiveToggleButtonBlurVisualEffectView)
|
bringSubviewToFront(contentWarningOverlay)
|
||||||
|
|
||||||
return mediaView
|
return mediaView
|
||||||
}
|
}
|
||||||
|
@ -124,8 +110,8 @@ extension MediaGridContainerView {
|
||||||
let mediaViews = Array(_mediaViews[0..<layout.count])
|
let mediaViews = Array(_mediaViews[0..<layout.count])
|
||||||
layout.layout(in: self, mediaViews: mediaViews)
|
layout.layout(in: self, mediaViews: mediaViews)
|
||||||
|
|
||||||
// layoutSensitiveToggleButton()
|
layoutContentWarningOverlay()
|
||||||
// bringSubviewToFront(sensitiveToggleButtonBlurVisualEffectView)
|
bringSubviewToFront(contentWarningOverlay)
|
||||||
|
|
||||||
return mediaViews
|
return mediaViews
|
||||||
}
|
}
|
||||||
|
@ -147,34 +133,16 @@ extension MediaGridContainerView {
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MediaGridContainerView {
|
extension MediaGridContainerView {
|
||||||
// private func layoutSensitiveToggleButton() {
|
private func layoutContentWarningOverlay() {
|
||||||
// sensitiveToggleButtonBlurVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
contentWarningOverlay.translatesAutoresizingMaskIntoConstraints = false
|
||||||
// addSubview(sensitiveToggleButtonBlurVisualEffectView)
|
addSubview(contentWarningOverlay)
|
||||||
// NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
// sensitiveToggleButtonBlurVisualEffectView.topAnchor.constraint(equalTo: topAnchor, constant: 16),
|
contentWarningOverlay.topAnchor.constraint(equalTo: topAnchor),
|
||||||
// trailingAnchor.constraint(equalTo: sensitiveToggleButtonBlurVisualEffectView.trailingAnchor, constant: 16),
|
contentWarningOverlay.leadingAnchor.constraint(equalTo: leadingAnchor),
|
||||||
// ])
|
contentWarningOverlay.trailingAnchor.constraint(equalTo: trailingAnchor),
|
||||||
//
|
contentWarningOverlay.bottomAnchor.constraint(equalTo: bottomAnchor),
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
])
|
||||||
// sensitiveToggleButtonBlurVisualEffectView.contentView.addSubview(sensitiveToggleButtonVibrancyVisualEffectView)
|
}
|
||||||
// NSLayoutConstraint.activate([
|
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.topAnchor.constraint(equalTo: sensitiveToggleButtonBlurVisualEffectView.contentView.topAnchor),
|
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.leadingAnchor.constraint(equalTo: sensitiveToggleButtonBlurVisualEffectView.contentView.leadingAnchor),
|
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.trailingAnchor.constraint(equalTo: sensitiveToggleButtonBlurVisualEffectView.contentView.trailingAnchor),
|
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.bottomAnchor.constraint(equalTo: sensitiveToggleButtonBlurVisualEffectView.contentView.bottomAnchor),
|
|
||||||
// ])
|
|
||||||
//
|
|
||||||
// sensitiveToggleButton.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.contentView.addSubview(sensitiveToggleButton)
|
|
||||||
// NSLayoutConstraint.activate([
|
|
||||||
// sensitiveToggleButton.topAnchor.constraint(equalTo: sensitiveToggleButtonVibrancyVisualEffectView.contentView.topAnchor),
|
|
||||||
// sensitiveToggleButton.leadingAnchor.constraint(equalTo: sensitiveToggleButtonVibrancyVisualEffectView.contentView.leadingAnchor),
|
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.contentView.trailingAnchor.constraint(equalTo: sensitiveToggleButton.trailingAnchor),
|
|
||||||
// sensitiveToggleButtonVibrancyVisualEffectView.contentView.bottomAnchor.constraint(equalTo: sensitiveToggleButton.bottomAnchor),
|
|
||||||
// sensitiveToggleButton.widthAnchor.constraint(equalToConstant: MediaGridContainerView.sensitiveToggleButtonSize.width).priority(.required - 1),
|
|
||||||
// sensitiveToggleButton.heightAnchor.constraint(equalToConstant: MediaGridContainerView.sensitiveToggleButtonSize.height).priority(.required - 1),
|
|
||||||
// ])
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MediaGridContainerView {
|
extension MediaGridContainerView {
|
||||||
|
|
|
@ -385,6 +385,7 @@ extension StatusView.ViewModel {
|
||||||
|
|
||||||
$isMediaReveal
|
$isMediaReveal
|
||||||
.sink { isMediaReveal in
|
.sink { isMediaReveal in
|
||||||
|
statusView.mediaGridContainerView.contentWarningOverlay.isHidden = isMediaReveal
|
||||||
statusView.mediaGridContainerView.viewModel.isSensitiveToggleButtonDisplay = isMediaReveal
|
statusView.mediaGridContainerView.viewModel.isSensitiveToggleButtonDisplay = isMediaReveal
|
||||||
}
|
}
|
||||||
.store(in: &disposeBag)
|
.store(in: &disposeBag)
|
||||||
|
|
|
@ -7,28 +7,24 @@
|
||||||
|
|
||||||
import os.log
|
import os.log
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import MastodonLocalization
|
||||||
public protocol ContentWarningOverlayViewDelegate: AnyObject {
|
|
||||||
func contentWarningOverlayViewDidPressed(_ contentWarningOverlayView: ContentWarningOverlayView)
|
|
||||||
}
|
|
||||||
|
|
||||||
public final class ContentWarningOverlayView: UIView {
|
public final class ContentWarningOverlayView: UIView {
|
||||||
|
|
||||||
public static let blurVisualEffect = UIBlurEffect(style: .systemUltraThinMaterial)
|
|
||||||
|
|
||||||
let logger = Logger(subsystem: "ContentWarningOverlayView", category: "View")
|
let logger = Logger(subsystem: "ContentWarningOverlayView", category: "View")
|
||||||
|
|
||||||
public weak var delegate: ContentWarningOverlayViewDelegate?
|
let hintLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
public let blurVisualEffectView = UIVisualEffectView(effect: ContentWarningOverlayView.blurVisualEffect)
|
label.font = .systemFont(ofSize: 18, weight: .regular)
|
||||||
public let vibrancyVisualEffectView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: ContentWarningOverlayView.blurVisualEffect))
|
label.text = L10n.Common.Controls.Status.tapToReveal
|
||||||
// let alertImageView: UIImageView = {
|
label.textAlignment = .center
|
||||||
// let imageView = UIImageView()
|
label.textColor = .white.withAlphaComponent(0.7)
|
||||||
// imageView.image = Asset.Indices.exclamationmarkTriangleLarge.image.withRenderingMode(.alwaysTemplate)
|
label.layer.shadowOpacity = 0.3
|
||||||
// return imageView
|
label.layer.shadowOffset = CGSize(width: 0, height: 2)
|
||||||
// }()
|
label.layer.shadowRadius = 2
|
||||||
|
label.layer.shadowColor = UIColor.black.cgColor
|
||||||
public let tapGestureRecognizer = UITapGestureRecognizer.singleTapGestureRecognizer
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
override init(frame: CGRect) {
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
@ -44,40 +40,12 @@ public final class ContentWarningOverlayView: UIView {
|
||||||
|
|
||||||
extension ContentWarningOverlayView {
|
extension ContentWarningOverlayView {
|
||||||
private func _init() {
|
private func _init() {
|
||||||
// overlay
|
hintLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
blurVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
addSubview(hintLabel)
|
||||||
addSubview(blurVisualEffectView)
|
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
blurVisualEffectView.topAnchor.constraint(equalTo: topAnchor),
|
hintLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
|
||||||
blurVisualEffectView.leadingAnchor.constraint(equalTo: leadingAnchor),
|
trailingAnchor.constraint(equalTo: hintLabel.trailingAnchor, constant: 8),
|
||||||
blurVisualEffectView.trailingAnchor.constraint(equalTo: trailingAnchor),
|
centerYAnchor.constraint(equalTo: hintLabel.centerYAnchor, constant: 10),
|
||||||
blurVisualEffectView.bottomAnchor.constraint(equalTo: bottomAnchor),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
vibrancyVisualEffectView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
blurVisualEffectView.contentView.addSubview(vibrancyVisualEffectView)
|
|
||||||
NSLayoutConstraint.activate([
|
|
||||||
vibrancyVisualEffectView.topAnchor.constraint(equalTo: blurVisualEffectView.contentView.topAnchor),
|
|
||||||
vibrancyVisualEffectView.leadingAnchor.constraint(equalTo: blurVisualEffectView.contentView.leadingAnchor),
|
|
||||||
vibrancyVisualEffectView.trailingAnchor.constraint(equalTo: blurVisualEffectView.contentView.trailingAnchor),
|
|
||||||
vibrancyVisualEffectView.bottomAnchor.constraint(equalTo: blurVisualEffectView.contentView.bottomAnchor),
|
|
||||||
])
|
|
||||||
|
|
||||||
// alertImageView.translatesAutoresizingMaskIntoConstraints = false
|
|
||||||
// vibrancyVisualEffectView.contentView.addSubview(alertImageView)
|
|
||||||
// NSLayoutConstraint.activate([
|
|
||||||
// alertImageView.centerXAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.centerXAnchor),
|
|
||||||
// alertImageView.centerYAnchor.constraint(equalTo: vibrancyVisualEffectView.contentView.centerYAnchor),
|
|
||||||
// ])
|
|
||||||
|
|
||||||
tapGestureRecognizer.addTarget(self, action: #selector(ContentWarningOverlayView.tapGestureRecognizerHandler(_:)))
|
|
||||||
addGestureRecognizer(tapGestureRecognizer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ContentWarningOverlayView {
|
|
||||||
@objc private func tapGestureRecognizerHandler(_ sender: UITapGestureRecognizer) {
|
|
||||||
logger.log(level: .debug, "\((#file as NSString).lastPathComponent, privacy: .public)[\(#line, privacy: .public)], \(#function, privacy: .public)")
|
|
||||||
delegate?.contentWarningOverlayViewDidPressed(self)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue