Fahim/cw-filter #342

Closed
Fahim wants to merge 3 commits from Fahim:Fahim/cw-filter into develop
10 changed files with 114 additions and 107 deletions

View File

@ -24,8 +24,8 @@ let package = Package(
.target( .target(
name: "Duckable", name: "Duckable",
dependencies: []), dependencies: []),
.testTarget( // .testTarget(
name: "DuckableTests", // name: "DuckableTests",
dependencies: ["Duckable"]), // dependencies: ["Duckable"]),
] ]
) )

View File

@ -11,6 +11,7 @@ import Foundation
public class CollapseState: Equatable { public class CollapseState: Equatable {
public var collapsible: Bool? public var collapsible: Bool?
public var collapsed: Bool? public var collapsed: Bool?
public var reason = ""
public var unknown: Bool { public var unknown: Bool {
collapsible == nil || collapsed == nil collapsible == nil || collapsed == nil

View File

@ -16,6 +16,7 @@ extension CollapseState {
if Preferences.shared.collapseLongPosts, if Preferences.shared.collapseLongPosts,
height > 600 || (textLength != nil && textLength! > 500) { height > 600 || (textLength != nil && textLength! > 500) {
longEnoughToCollapse = true longEnoughToCollapse = true
self.reason = "Read More ..."
} else { } else {
longEnoughToCollapse = false longEnoughToCollapse = false
} }
@ -25,13 +26,16 @@ extension CollapseState {
let collapseDueToContentWarning: Bool? let collapseDueToContentWarning: Bool?
if contentWarningCollapsible { if contentWarningCollapsible {
let lowercased = status.spoilerText.lowercased() let lowercased = status.spoilerText.lowercased()
let opposite = Preferences.shared.oppositeCollapseKeywords.contains { lowercased.contains($0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()) } let opposite = Preferences.shared.oppositeCollapseKeywords.contains { lowercased.contains($0.trimmingCharacters(in: .whitespacesAndNewlines).lowercased())
}
if Preferences.shared.expandAllContentWarnings { if Preferences.shared.expandAllContentWarnings {
collapseDueToContentWarning = opposite collapseDueToContentWarning = opposite
} else { } else {
collapseDueToContentWarning = !opposite collapseDueToContentWarning = !opposite
} }
if let collapse = collapseDueToContentWarning, collapse {
self.reason = "Content Warning"
}
} else { } else {
collapseDueToContentWarning = nil collapseDueToContentWarning = nil
} }

View File

@ -31,7 +31,7 @@ class BaseStatusTableViewCell: UITableViewCell {
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var metaIndicatorsView: StatusMetaIndicatorsView! @IBOutlet weak var metaIndicatorsView: StatusMetaIndicatorsView!
@IBOutlet weak var contentWarningLabel: EmojiLabel! @IBOutlet weak var contentWarningLabel: EmojiLabel!
@IBOutlet weak var collapseButton: UIButton! @IBOutlet weak var collapseButton: StatusCollapseButton!
@IBOutlet weak var contentTextView: StatusContentTextView! @IBOutlet weak var contentTextView: StatusContentTextView!
@IBOutlet weak var cardView: StatusCardView! @IBOutlet weak var cardView: StatusCardView!
@IBOutlet weak var attachmentsView: AttachmentsContainerView! @IBOutlet weak var attachmentsView: AttachmentsContainerView!
@ -91,10 +91,7 @@ class BaseStatusTableViewCell: UITableViewCell {
avatarImageView.addInteraction(UIDragInteraction(delegate: self)) avatarImageView.addInteraction(UIDragInteraction(delegate: self))
attachmentsView.delegate = self attachmentsView.delegate = self
collapseButton.layer.masksToBounds = true
collapseButton.layer.cornerRadius = 5
accessibilityElements = [displayNameLabel!, contentWarningLabel!, collapseButton!, contentTextView!, attachmentsView!, pollView!] accessibilityElements = [displayNameLabel!, contentWarningLabel!, collapseButton!, contentTextView!, attachmentsView!, pollView!]
moreButton.showsMenuAsPrimaryAction = true moreButton.showsMenuAsPrimaryAction = true
@ -188,6 +185,7 @@ class BaseStatusTableViewCell: UITableViewCell {
state.collapsed = false state.collapsed = false
} }
} }
collapseButton.title = statusState.reason
collapsible = state.collapsible! collapsible = state.collapsible!
setCollapsed(state.collapsed!, animated: false) setCollapsed(state.collapsed!, animated: false)
} }
@ -360,40 +358,6 @@ class BaseStatusTableViewCell: UITableViewCell {
pollView.isHidden = pollView.poll == nil || collapsed pollView.isHidden = pollView.poll == nil || collapsed
let buttonImage = UIImage(systemName: collapsed ? "chevron.down" : "chevron.up")! let buttonImage = UIImage(systemName: collapsed ? "chevron.down" : "chevron.up")!
if let buttonImageView = collapseButton.imageView {
collapseButton.setImage(buttonImage, for: .normal)
if animated {
buttonImageView.layer.opacity = 0
// this whole hack is necessary because when just rotating buttonImageView, it moves to the left of the button and then animates back to the center
let imageView = UIImageView(image: buttonImageView.image)
imageView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(imageView)
NSLayoutConstraint.activate([
imageView.widthAnchor.constraint(equalTo: buttonImageView.widthAnchor),
imageView.heightAnchor.constraint(equalTo: buttonImageView.heightAnchor),
imageView.centerXAnchor.constraint(equalTo: collapseButton.centerXAnchor),
imageView.centerYAnchor.constraint(equalTo: collapseButton.centerYAnchor),
])
imageView.tintColor = .white
UIView.animate(withDuration: 0.3, delay: 0) {
imageView.transform = CGAffineTransform(rotationAngle: .pi)
} completion: { _ in
imageView.removeFromSuperview()
buttonImageView.layer.opacity = 1
}
}
}
if collapsed {
collapseButton.accessibilityLabel = NSLocalizedString("Expand Status", comment: "expand status button accessibility label")
} else {
collapseButton.accessibilityLabel = NSLocalizedString("Collapse Status", comment: "collapse status button accessibility label")
}
} }
@IBAction func replyPressed() { @IBAction func replyPressed() {

View File

@ -104,16 +104,9 @@ class ConversationMainStatusCollectionViewCell: UICollectionViewListCell, Status
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(collapseButtonPressed))) $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(collapseButtonPressed)))
} }
private(set) lazy var collapseButton = StatusCollapseButton(configuration: { private(set) lazy var collapseButton = StatusCollapseButton {
var config = UIButton.Configuration.filled() self.toggleCollapse()
config.image = UIImage(systemName: "chevron.down") }
return config
}()).configure {
// this button is so big that dimming its background color is visually distracting
$0.tintAdjustmentMode = .normal
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
$0.addTarget(self, action: #selector(collapseButtonPressed), for: .touchUpInside)
}
let contentContainer = StatusContentContainer(useTopSpacer: true).configure { let contentContainer = StatusContentContainer(useTopSpacer: true).configure {
$0.contentTextView.defaultFont = ConversationMainStatusCollectionViewCell.contentFont $0.contentTextView.defaultFont = ConversationMainStatusCollectionViewCell.contentFont

View File

@ -8,18 +8,90 @@
import UIKit import UIKit
class StatusCollapseButton: UIButton { class StatusCollapseButton: UIView {
var action: (()->Void)!
var title: String {
get {
return lblTitle.text ?? ""
}
set {
lblTitle.text = newValue
}
}
private var lblTitle = UILabel()
private var imgView = UIImageView()
private var isCollapsed = true
convenience init(action: @escaping (()->Void)) {
self.init(frame: CGRect.zero)
self.action = action
}
private var interactionBounds: CGRect! override init(frame: CGRect) {
super.init(frame: frame)
override func layoutSubviews() { setup()
super.layoutSubviews() }
interactionBounds = bounds.inset(by: UIEdgeInsets(top: -8, left: 0, bottom: 0, right: 0))
}
override func point(inside point: CGPoint, with event: UIEvent?) -> Bool {
return interactionBounds.contains(point)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// Eat event
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// Eat event
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
// Eat event
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if isCollapsed {
imgView.image = UIImage(systemName: "chevron.compact.up")
accessibilityLabel = NSLocalizedString("Collapse Status", comment: "collapse status button accessibility label")
} else {
imgView.image = UIImage(systemName: "chevron.compact.down")
accessibilityLabel = NSLocalizedString("Expand Status", comment: "expand status button accessibility label")
}
isCollapsed.toggle()
action()
}
// MARK: - Private Methods
private func setup() {
self.isUserInteractionEnabled = true
layer.cornerRadius = 8
backgroundColor = .lightGray
accessibilityLabel = NSLocalizedString("Expand Status", comment: "expand status button accessibility label")
// Title
lblTitle.translatesAutoresizingMaskIntoConstraints = false
lblTitle.textColor = .white
lblTitle.textAlignment = .center
addSubview(lblTitle)
// Chevron
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.tintColor = .white
imgView.image = UIImage(systemName: "chevron.compact.down")
addSubview(imgView)
NSLayoutConstraint.activate([
// Main view
heightAnchor.constraint(equalToConstant: 30),
// Title
lblTitle.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
lblTitle.topAnchor.constraint(equalTo: topAnchor, constant: 8),
lblTitle.trailingAnchor.constraint(equalTo: imgView.leadingAnchor, constant: -4),
lblTitle.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
// Chevron
imgView.widthAnchor.constraint(equalToConstant: 20),
imgView.topAnchor.constraint(equalTo: topAnchor, constant: 5),
imgView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
imgView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -5)
])
}
} }

View File

@ -113,17 +113,16 @@ extension StatusCollectionViewCell {
if statusState.collapsible! && showStatusAutomatically { if statusState.collapsible! && showStatusAutomatically {
statusState.collapsed = false statusState.collapsed = false
} }
} }
collapseButton.title = statusState.reason
collapseButton.isHidden = !statusState.collapsible! collapseButton.isHidden = !statusState.collapsible!
contentContainer.setCollapsed(statusState.collapsed!) contentContainer.setCollapsed(statusState.collapsed!)
if statusState.collapsed! { if statusState.collapsed! {
contentContainer.alpha = 0 contentContainer.alpha = 0
// TODO: is this accessing the image view before the button's been laid out? // TODO: is this accessing the image view before the button's been laid out?
collapseButton.imageView!.transform = CGAffineTransform(rotationAngle: 0)
collapseButton.accessibilityLabel = NSLocalizedString("Expand Status", comment: "expand status button accessibility label") collapseButton.accessibilityLabel = NSLocalizedString("Expand Status", comment: "expand status button accessibility label")
} else { } else {
contentContainer.alpha = 1 contentContainer.alpha = 1
collapseButton.imageView!.transform = CGAffineTransform(rotationAngle: .pi)
collapseButton.accessibilityLabel = NSLocalizedString("Collapse Status", comment: "collapse status button accessibility label") collapseButton.accessibilityLabel = NSLocalizedString("Collapse Status", comment: "collapse status button accessibility label")
} }
} }

View File

@ -14,7 +14,6 @@ private let reblogIcon = UIImage(systemName: "repeat")
private let hashtagIcon = UIImage(systemName: "number") private let hashtagIcon = UIImage(systemName: "number")
class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollectionViewCell { class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollectionViewCell {
static let separatorInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 0) static let separatorInsets = NSDirectionalEdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 0)
static let contentFont = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 16)) static let contentFont = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 16))
static let monospaceFont = UIFontMetrics.default.scaledFont(for: .monospacedSystemFont(ofSize: 16, weight: .regular)) static let monospaceFont = UIFontMetrics.default.scaledFont(for: .monospacedSystemFont(ofSize: 16, weight: .regular))
@ -154,17 +153,10 @@ class TimelineStatusCollectionViewCell: UICollectionViewListCell, StatusCollecti
$0.isUserInteractionEnabled = true $0.isUserInteractionEnabled = true
$0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(collapseButtonPressed))) $0.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(collapseButtonPressed)))
} }
private(set) lazy var collapseButton = StatusCollapseButton(configuration: { private(set) lazy var collapseButton = StatusCollapseButton {
var config = UIButton.Configuration.filled() self.toggleCollapse()
config.image = UIImage(systemName: "chevron.down") }
return config
}()).configure {
// this button is so big that dimming its background color is visually distracting
$0.tintAdjustmentMode = .normal
$0.setContentHuggingPriority(.defaultHigh, for: .vertical)
$0.addTarget(self, action: #selector(collapseButtonPressed), for: .touchUpInside)
}
let contentContainer = StatusContentContainer(useTopSpacer: false).configure { let contentContainer = StatusContentContainer(useTopSpacer: false).configure {
$0.contentTextView.defaultFont = TimelineStatusCollectionViewCell.contentFont $0.contentTextView.defaultFont = TimelineStatusCollectionViewCell.contentFont

View File

@ -83,6 +83,8 @@ class TimelineStatusTableViewCell: BaseStatusTableViewCell {
]), size: 0) ]), size: 0)
contentWarningLabel.adjustsFontForContentSizeCategory = true contentWarningLabel.adjustsFontForContentSizeCategory = true
collapseButton.action = collapseButtonPressed
contentTextView.defaultFont = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 16)) contentTextView.defaultFont = UIFontMetrics.default.scaledFont(for: .systemFont(ofSize: 16))
contentTextView.monospaceFont = UIFontMetrics.default.scaledFont(for: .monospacedSystemFont(ofSize: 16, weight: .regular)) contentTextView.monospaceFont = UIFontMetrics.default.scaledFont(for: .monospacedSystemFont(ofSize: 16, weight: .regular))
contentTextView.adjustsFontForContentSizeCategory = true contentTextView.adjustsFontForContentSizeCategory = true

View File

@ -103,28 +103,12 @@
<color key="textColor" systemColor="secondaryLabelColor"/> <color key="textColor" systemColor="secondaryLabelColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="252" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="O0E-Vf-XYR" customClass="StatusCollapseButton" customModule="Tusker" customModuleProvider="target"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="H6N-29-e66" customClass="StatusCollapseButton" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="49" width="295" height="30"/> <rect key="frame" x="0.0" y="49" width="295" height="50"/>
<color key="backgroundColor" systemColor="tintColor"/> <color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints> </view>
<constraint firstAttribute="height" constant="30" id="z84-XW-gP3"/>
</constraints>
<color key="tintColor" systemColor="tintColor"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="chevron.down" catalog="system">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="large"/>
</state>
<buttonConfiguration key="configuration" style="filled" image="chevron.down" catalog="system">
<preferredSymbolConfiguration key="preferredSymbolConfigurationForImage" scale="large"/>
<color key="baseForegroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</buttonConfiguration>
<connections>
<action selector="collapseButtonPressed" destination="BR5-ZS-LIo" eventType="touchUpInside" id="twO-rE-1pQ"/>
</connections>
</button>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" delaysContentTouches="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="waJ-f5-LKv" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target"> <textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" delaysContentTouches="NO" editable="NO" textAlignment="natural" selectable="NO" translatesAutoresizingMaskIntoConstraints="NO" id="waJ-f5-LKv" customClass="StatusContentTextView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="83" width="295" height="115.5"/> <rect key="frame" x="0.0" y="103" width="295" height="95.5"/>
<string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string> <string key="text">Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Nam liber te conscient to factor tum poen legum odioque civiuda.</string>
<color key="textColor" systemColor="labelColor"/> <color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/> <fontDescription key="fontDescription" type="system" pointSize="16"/>
@ -257,7 +241,7 @@
<outlet property="attachmentsView" destination="nbq-yr-2mA" id="MAs-nv-cNN"/> <outlet property="attachmentsView" destination="nbq-yr-2mA" id="MAs-nv-cNN"/>
<outlet property="avatarImageView" destination="QMP-j2-HLn" id="73F-6g-drx"/> <outlet property="avatarImageView" destination="QMP-j2-HLn" id="73F-6g-drx"/>
<outlet property="cardView" destination="LKo-VB-XWl" id="Ypd-Cr-fie"/> <outlet property="cardView" destination="LKo-VB-XWl" id="Ypd-Cr-fie"/>
<outlet property="collapseButton" destination="O0E-Vf-XYR" id="oTb-VA-JHD"/> <outlet property="collapseButton" destination="H6N-29-e66" id="fbP-GO-n7c"/>
<outlet property="contentTextView" destination="waJ-f5-LKv" id="Tyd-9N-WxW"/> <outlet property="contentTextView" destination="waJ-f5-LKv" id="Tyd-9N-WxW"/>
<outlet property="contentWarningLabel" destination="inI-Og-YiU" id="TmT-Fq-HVG"/> <outlet property="contentWarningLabel" destination="inI-Og-YiU" id="TmT-Fq-HVG"/>
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="dAN-AD-XMb"/> <outlet property="displayNameLabel" destination="gll-xe-FSr" id="dAN-AD-XMb"/>
@ -278,7 +262,6 @@
</objects> </objects>
<resources> <resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="104"/> <image name="arrowshape.turn.up.left.fill" catalog="system" width="128" height="104"/>
<image name="chevron.down" catalog="system" width="128" height="70"/>
<image name="ellipsis" catalog="system" width="128" height="37"/> <image name="ellipsis" catalog="system" width="128" height="37"/>
<image name="pin.fill" catalog="system" width="116" height="128"/> <image name="pin.fill" catalog="system" width="116" height="128"/>
<image name="repeat" catalog="system" width="128" height="98"/> <image name="repeat" catalog="system" width="128" height="98"/>
@ -295,8 +278,5 @@
<systemColor name="systemBackgroundColor"> <systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor> </systemColor>
<systemColor name="tintColor">
<color red="0.0" green="0.47843137254901963" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources> </resources>
</document> </document>