Collapse statuses with content warnings

Closes #11
This commit is contained in:
Shadowfacts 2019-09-09 18:40:23 -04:00
parent 2e6f7d8878
commit befcc18e4d
Signed by untrusted user: shadowfacts
GPG Key ID: 94A5AB95422746E5
9 changed files with 246 additions and 71 deletions

View File

@ -119,7 +119,13 @@ class ConversationTableViewController: EnhancedTableViewController {
} }
extension ConversationTableViewController: StatusTableViewCellDelegate {} extension ConversationTableViewController: StatusTableViewCellDelegate {
func statusCollapsedStateChanged() {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()
tableView.endUpdates()
}
}
extension ConversationTableViewController: UITableViewDataSourcePrefetching { extension ConversationTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {

View File

@ -173,7 +173,13 @@ class NotificationsTableViewController: EnhancedTableViewController {
} }
extension NotificationsTableViewController: StatusTableViewCellDelegate {} extension NotificationsTableViewController: StatusTableViewCellDelegate {
func statusCollapsedStateChanged() {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()
tableView.endUpdates()
}
}
extension NotificationsTableViewController: UITableViewDataSourcePrefetching { extension NotificationsTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {

View File

@ -205,7 +205,14 @@ class ProfileTableViewController: EnhancedTableViewController {
} }
extension ProfileTableViewController: StatusTableViewCellDelegate {} extension ProfileTableViewController: StatusTableViewCellDelegate {
func statusCollapsedStateChanged() {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()
tableView.endUpdates()
}
}
extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate { extension ProfileTableViewController: ProfileHeaderTableViewCellDelegate {
func showMoreOptions() { func showMoreOptions() {
let account = MastodonCache.account(for: accountID)! let account = MastodonCache.account(for: accountID)!

View File

@ -134,4 +134,10 @@ class StatusActionAccountListTableViewController: EnhancedTableViewController {
} }
extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {} extension StatusActionAccountListTableViewController: StatusTableViewCellDelegate {
func statusCollapsedStateChanged() {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()
tableView.endUpdates()
}
}

View File

@ -151,7 +151,13 @@ class TimelineTableViewController: EnhancedTableViewController {
} }
extension TimelineTableViewController: StatusTableViewCellDelegate {} extension TimelineTableViewController: StatusTableViewCellDelegate {
func statusCollapsedStateChanged() {
// causes the table view to recalculate the cell heights
tableView.beginUpdates()
tableView.endUpdates()
}
}
extension TimelineTableViewController: UITableViewDataSourcePrefetching { extension TimelineTableViewController: UITableViewDataSourcePrefetching {
func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) { func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath]) {

View File

@ -27,6 +27,8 @@ class ConversationMainStatusTableViewCell: UITableViewCell {
@IBOutlet weak var displayNameLabel: UILabel! @IBOutlet weak var displayNameLabel: UILabel!
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var contentWarningLabel: UILabel!
@IBOutlet weak var collapseButton: UIButton!
@IBOutlet weak var contentLabel: StatusContentLabel! @IBOutlet weak var contentLabel: StatusContentLabel!
@IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var totalFavoritesButton: UIButton! @IBOutlet weak var totalFavoritesButton: UIButton!
@ -54,6 +56,13 @@ class ConversationMainStatusTableViewCell: UITableViewCell {
} }
} }
var collapsible = false {
didSet {
collapseButton.isHidden = !collapsible
}
}
var collapsed = false
var avatarURL: URL? var avatarURL: URL?
var statusUpdater: Cancellable? var statusUpdater: Cancellable?
@ -72,6 +81,8 @@ class ConversationMainStatusTableViewCell: UITableViewCell {
attachmentsView.delegate = self attachmentsView.delegate = self
attachmentsView.layer.cornerRadius = 5 attachmentsView.layer.cornerRadius = 5
attachmentsView.layer.masksToBounds = true attachmentsView.layer.masksToBounds = true
collapseButton.layer.masksToBounds = true
collapseButton.layer.cornerRadius = 5
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
@ -107,13 +118,17 @@ class ConversationMainStatusTableViewCell: UITableViewCell {
} }
timestampAndClientLabel.text = timestampAndClientText timestampAndClientLabel.text = timestampAndClientText
attachmentsView.updateUI(status: status) attachmentsView.updateUI(status: status)
let realStatus = status.reblog ?? status let realStatus = status.reblog ?? status
updateStatusState(status: realStatus) updateStatusState(status: realStatus)
contentLabel.statusID = statusID contentLabel.statusID = statusID
collapsible = !status.spoilerText.isEmpty
setCollapsed(collapsible, animated: false)
contentWarningLabel.text = status.spoilerText
contentWarningLabel.isHidden = status.spoilerText.isEmpty
} }
private func updateStatusState(status: Status) { private func updateStatusState(status: Status) {
@ -156,6 +171,37 @@ class ConversationMainStatusTableViewCell: UITableViewCell {
delegate?.selected(account: accountID) delegate?.selected(account: accountID)
} }
@IBAction func collapsePressed(_ sender: Any) {
setCollapsed(!collapsed, animated: true)
delegate?.statusCollapsedStateChanged()
}
func setCollapsed(_ collapsed: Bool, animated: Bool) {
self.collapsed = collapsed
contentLabel.isHidden = collapsed
attachmentsView.isHidden = attachmentsView.attachments.count == 0 || collapsed
let buttonImage = UIImage(systemName: collapsed ? "chevron.down" : "chevron.up")
if animated, let buttonImageView = collapseButton.imageView {
// see comment in StatusTableViewCell.setCollapsed
UIView.animateKeyframes(withDuration: 0.2, delay: 0, options: .calculationModeLinear, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
buttonImageView.transform = CGAffineTransform(rotationAngle: collapsed ? .pi / 2 : -.pi / 2)
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
buttonImageView.transform = CGAffineTransform(rotationAngle: .pi)
}
}, completion: { (finished) in
buttonImageView.transform = .identity
self.collapseButton.setImage(buttonImage, for: .normal)
})
} else {
collapseButton.setImage(buttonImage, for: .normal)
}
}
@IBAction func replyPressed(_ sender: Any) { @IBAction func replyPressed(_ sender: Any) {
delegate?.reply(to: statusID) delegate?.reply(to: statusID)
} }

View File

@ -10,14 +10,14 @@
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ConversationMainStatusTableViewCell" customModule="Tusker" customModuleProvider="target"> <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ConversationMainStatusTableViewCell" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="238"/> <rect key="frame" x="0.0" y="0.0" width="375" height="291"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="GuG-Qd-B8I"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="GuG-Qd-B8I">
<rect key="frame" x="16" y="8" width="343" height="222"/> <rect key="frame" x="16" y="8" width="343" height="275"/>
<subviews> <subviews>
<view contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="Cnd-Fj-B7l"> <view contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="Cnd-Fj-B7l">
<rect key="frame" x="0.0" y="0.0" width="343" height="118.5"/> <rect key="frame" x="0.0" y="0.0" width="343" height="50"/>
<subviews> <subviews>
<imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="mB9-HO-1vf"> <imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="mB9-HO-1vf">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/> <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
@ -38,45 +38,62 @@
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="249" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TgY-hs-Klo" customClass="StatusContentLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="54" width="343" height="64.5"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews> </subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints> <constraints>
<constraint firstItem="TgY-hs-Klo" firstAttribute="leading" secondItem="Cnd-Fj-B7l" secondAttribute="leading" id="2zu-G9-fMv"/>
<constraint firstAttribute="trailing" secondItem="SWg-Ka-QyP" secondAttribute="trailing" id="4g6-BT-eW4"/> <constraint firstAttribute="trailing" secondItem="SWg-Ka-QyP" secondAttribute="trailing" id="4g6-BT-eW4"/>
<constraint firstAttribute="bottom" secondItem="TgY-hs-Klo" secondAttribute="bottom" id="5Og-Pd-Vck"/>
<constraint firstItem="lZY-2e-17d" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="8fU-y9-K5Z"/> <constraint firstItem="lZY-2e-17d" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="8fU-y9-K5Z"/>
<constraint firstAttribute="trailingMargin" secondItem="lZY-2e-17d" secondAttribute="trailing" id="AAJ-pd-omx"/> <constraint firstAttribute="trailingMargin" secondItem="lZY-2e-17d" secondAttribute="trailing" id="AAJ-pd-omx"/>
<constraint firstItem="lZY-2e-17d" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="Aqj-co-Szp"/> <constraint firstItem="lZY-2e-17d" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="Aqj-co-Szp"/>
<constraint firstItem="mB9-HO-1vf" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="R7P-rD-Gbm"/> <constraint firstItem="mB9-HO-1vf" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="R7P-rD-Gbm"/>
<constraint firstAttribute="trailing" secondItem="TgY-hs-Klo" secondAttribute="trailing" id="SOE-Q5-IWd"/> <constraint firstAttribute="bottom" secondItem="mB9-HO-1vf" secondAttribute="bottom" id="Wd0-Qh-idS"/>
<constraint firstItem="mB9-HO-1vf" firstAttribute="leading" secondItem="Cnd-Fj-B7l" secondAttribute="leading" id="bxq-Fs-1aH"/> <constraint firstItem="mB9-HO-1vf" firstAttribute="leading" secondItem="Cnd-Fj-B7l" secondAttribute="leading" id="bxq-Fs-1aH"/>
<constraint firstItem="SWg-Ka-QyP" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="e45-gE-myI"/> <constraint firstItem="SWg-Ka-QyP" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="e45-gE-myI"/>
<constraint firstItem="TgY-hs-Klo" firstAttribute="top" secondItem="mB9-HO-1vf" secondAttribute="bottom" constant="4" id="l6y-Rr-Nmc"/>
<constraint firstItem="SWg-Ka-QyP" firstAttribute="top" secondItem="lZY-2e-17d" secondAttribute="bottom" id="lvX-1b-8cN"/> <constraint firstItem="SWg-Ka-QyP" firstAttribute="top" secondItem="lZY-2e-17d" secondAttribute="bottom" id="lvX-1b-8cN"/>
</constraints> </constraints>
</view> </view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="751" text="Content Warning" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="cwQ-mR-L1b">
<rect key="frame" x="0.0" y="58" width="343" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<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>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8r8-O8-Agh">
<rect key="frame" x="0.0" y="86.5" width="343" height="30"/>
<color key="backgroundColor" systemColor="systemBlueColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="icD-3q-uJ6"/>
</constraints>
<color key="tintColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<state key="normal" image="chevron.down" catalog="system">
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="large"/>
</state>
<connections>
<action selector="collapsePressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="w9d-kB-EaQ"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="TopLeft" horizontalHuggingPriority="251" verticalHuggingPriority="249" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TgY-hs-Klo" customClass="StatusContentLabel" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="124.5" width="343" height="47"/>
<fontDescription key="fontDescription" type="system" pointSize="20"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IF9-9U-Gk0" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target"> <view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IF9-9U-Gk0" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="122.5" width="343" height="0.0"/> <rect key="frame" x="0.0" y="175.5" width="343" height="0.0"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" priority="999" constant="200" id="UMv-Bk-ZyY"/> <constraint firstAttribute="height" priority="999" constant="200" id="UMv-Bk-ZyY"/>
</constraints> </constraints>
</view> </view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ejU-sO-Og5"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ejU-sO-Og5">
<rect key="frame" x="0.0" y="126.5" width="343" height="0.5"/> <rect key="frame" x="0.0" y="179.5" width="343" height="0.5"/>
<color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="0.5" id="DRI-lB-TyG"/> <constraint firstAttribute="height" constant="0.5" id="DRI-lB-TyG"/>
</constraints> </constraints>
</view> </view>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="HZv-qj-gi6"> <stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="HZv-qj-gi6">
<rect key="frame" x="0.0" y="135" width="343" height="18"/> <rect key="frame" x="0.0" y="188" width="343" height="18"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="252" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yyj-Bs-Vjq"> <button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="252" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="yyj-Bs-Vjq">
<rect key="frame" x="0.0" y="0.0" width="163.5" height="18"/> <rect key="frame" x="0.0" y="0.0" width="163.5" height="18"/>
@ -115,27 +132,27 @@
</constraints> </constraints>
</stackView> </stackView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pcy-jH-lL9"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="pcy-jH-lL9">
<rect key="frame" x="0.0" y="161" width="343" height="0.5"/> <rect key="frame" x="0.0" y="214" width="343" height="0.5"/>
<color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="0.5" id="0Ga-Fr-g0g"/> <constraint firstAttribute="height" constant="0.5" id="0Ga-Fr-g0g"/>
</constraints> </constraints>
</view> </view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sep 7, 2019 12:12:53 PM • Web" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YHN-wG-YWi"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Sep 7, 2019 12:12:53 PM • Web" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YHN-wG-YWi">
<rect key="frame" x="0.0" y="169.5" width="343" height="18"/> <rect key="frame" x="0.0" y="222.5" width="343" height="18"/>
<fontDescription key="fontDescription" type="system" pointSize="15"/> <fontDescription key="fontDescription" type="system" pointSize="15"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Fp-Nj-sVj"> <view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3Fp-Nj-sVj">
<rect key="frame" x="0.0" y="195.5" width="343" height="0.5"/> <rect key="frame" x="0.0" y="248.5" width="343" height="0.5"/>
<color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="opaqueSeparatorColor" red="0.77647058820000003" green="0.77647058820000003" blue="0.7843137255" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" constant="0.5" id="akf-Kl-8mK"/> <constraint firstAttribute="height" constant="0.5" id="akf-Kl-8mK"/>
</constraints> </constraints>
</view> </view>
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="3Bg-XP-d13"> <stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="3Bg-XP-d13">
<rect key="frame" x="0.0" y="204" width="343" height="18"/> <rect key="frame" x="0.0" y="257" width="343" height="18"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2cc-lE-AdG"> <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="2cc-lE-AdG">
<rect key="frame" x="0.0" y="0.0" width="21.5" height="18"/> <rect key="frame" x="0.0" y="0.0" width="21.5" height="18"/>
@ -193,7 +210,9 @@
<connections> <connections>
<outlet property="attachmentsView" destination="IF9-9U-Gk0" id="Oxw-sJ-MJE"/> <outlet property="attachmentsView" destination="IF9-9U-Gk0" id="Oxw-sJ-MJE"/>
<outlet property="avatarImageView" destination="mB9-HO-1vf" id="0R0-rt-Osh"/> <outlet property="avatarImageView" destination="mB9-HO-1vf" id="0R0-rt-Osh"/>
<outlet property="collapseButton" destination="8r8-O8-Agh" id="0es-Hi-bpt"/>
<outlet property="contentLabel" destination="TgY-hs-Klo" id="SEi-B2-VQf"/> <outlet property="contentLabel" destination="TgY-hs-Klo" id="SEi-B2-VQf"/>
<outlet property="contentWarningLabel" destination="cwQ-mR-L1b" id="5sm-PC-FIN"/>
<outlet property="displayNameLabel" destination="lZY-2e-17d" id="7og-23-eHy"/> <outlet property="displayNameLabel" destination="lZY-2e-17d" id="7og-23-eHy"/>
<outlet property="favoriteButton" destination="DhN-rJ-jdA" id="b2Q-ch-kSP"/> <outlet property="favoriteButton" destination="DhN-rJ-jdA" id="b2Q-ch-kSP"/>
<outlet property="reblogButton" destination="GUG-f7-Hdy" id="WtT-Ph-DQm"/> <outlet property="reblogButton" destination="GUG-f7-Hdy" id="WtT-Ph-DQm"/>
@ -202,11 +221,12 @@
<outlet property="totalReblogsButton" destination="dem-vG-cPB" id="i9E-Qn-d76"/> <outlet property="totalReblogsButton" destination="dem-vG-cPB" id="i9E-Qn-d76"/>
<outlet property="usernameLabel" destination="SWg-Ka-QyP" id="h2I-g4-AD9"/> <outlet property="usernameLabel" destination="SWg-Ka-QyP" id="h2I-g4-AD9"/>
</connections> </connections>
<point key="canvasLocation" x="40.799999999999997" y="-146.62668665667167"/> <point key="canvasLocation" x="40.799999999999997" y="-122.78860569715144"/>
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/> <image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/>
<image name="chevron.down" catalog="system" width="64" height="34"/>
<image name="ellipsis" catalog="system" width="64" height="20"/> <image name="ellipsis" catalog="system" width="64" height="20"/>
<image name="repeat" catalog="system" width="64" height="50"/> <image name="repeat" catalog="system" width="64" height="50"/>
<image name="star.fill" catalog="system" width="64" height="58"/> <image name="star.fill" catalog="system" width="64" height="58"/>

View File

@ -11,6 +11,7 @@ import Combine
import Pachyderm import Pachyderm
protocol StatusTableViewCellDelegate: TuskerNavigationDelegate { protocol StatusTableViewCellDelegate: TuskerNavigationDelegate {
func statusCollapsedStateChanged()
} }
class StatusTableViewCell: UITableViewCell { class StatusTableViewCell: UITableViewCell {
@ -23,6 +24,8 @@ class StatusTableViewCell: UITableViewCell {
@IBOutlet weak var displayNameLabel: UILabel! @IBOutlet weak var displayNameLabel: UILabel!
@IBOutlet weak var usernameLabel: UILabel! @IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var contentWarningLabel: UILabel!
@IBOutlet weak var collapseButton: UIButton!
@IBOutlet weak var contentLabel: StatusContentLabel! @IBOutlet weak var contentLabel: StatusContentLabel!
@IBOutlet weak var avatarImageView: UIImageView! @IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var reblogLabel: UILabel! @IBOutlet weak var reblogLabel: UILabel!
@ -47,6 +50,13 @@ class StatusTableViewCell: UITableViewCell {
} }
} }
var collapsible = false {
didSet {
collapseButton.isHidden = !collapsible
}
}
var collapsed = false
var avatarURL: URL? var avatarURL: URL?
var updateTimestampWorkItem: DispatchWorkItem? var updateTimestampWorkItem: DispatchWorkItem?
var attachmentDataTasks: [URLSessionDataTask] = [] var attachmentDataTasks: [URLSessionDataTask] = []
@ -70,6 +80,8 @@ class StatusTableViewCell: UITableViewCell {
attachmentsView.delegate = self attachmentsView.delegate = self
attachmentsView.layer.cornerRadius = 5 attachmentsView.layer.cornerRadius = 5
attachmentsView.layer.masksToBounds = true attachmentsView.layer.masksToBounds = true
collapseButton.layer.masksToBounds = true
collapseButton.layer.cornerRadius = 5
NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(updateUIForPreferences), name: .preferencesChanged, object: nil)
@ -122,6 +134,11 @@ class StatusTableViewCell: UITableViewCell {
updateStatusState(status: realStatus) updateStatusState(status: realStatus)
contentLabel.statusID = status.id contentLabel.statusID = status.id
collapsible = !status.spoilerText.isEmpty
setCollapsed(collapsible, animated: false)
contentWarningLabel.text = status.spoilerText
contentWarningLabel.isHidden = status.spoilerText.isEmpty
} }
private func updateStatusState(status: Status) { private func updateStatusState(status: Status) {
@ -192,6 +209,39 @@ class StatusTableViewCell: UITableViewCell {
} }
} }
@IBAction func collapseButtonPressed(_ sender: Any) {
setCollapsed(!collapsed, animated: true)
delegate?.statusCollapsedStateChanged()
}
func setCollapsed(_ collapsed: Bool, animated: Bool) {
self.collapsed = collapsed
contentLabel.isHidden = collapsed
attachmentsView.isHidden = attachmentsView.attachments.count == 0 || collapsed
let buttonImage = UIImage(systemName: collapsed ? "chevron.down" : "chevron.up")
if animated, let buttonImageView = collapseButton.imageView {
// we need to use a keyframe animation for this, because we want to control the direction the chevron rotates
// when rotating ±π, UIKit will always rotate in the same direction
// using a keyframe to set an intermediate point in the animation allows us to force a specific direction
UIView.animateKeyframes(withDuration: 0.2, delay: 0, options: .calculationModeLinear, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
buttonImageView.transform = CGAffineTransform(rotationAngle: collapsed ? .pi / 2 : -.pi / 2)
}
UIView.addKeyframe(withRelativeStartTime: 0.5, relativeDuration: 0.5) {
buttonImageView.transform = CGAffineTransform(rotationAngle: .pi)
}
}, completion: { (finished) in
buttonImageView.transform = .identity
self.collapseButton.setImage(buttonImage, for: .normal)
})
} else {
collapseButton.setImage(buttonImage, for: .normal)
}
}
@IBAction func replyPressed(_ sender: Any) { @IBAction func replyPressed(_ sender: Any) {
delegate?.reply(to: statusID) delegate?.reply(to: statusID)
} }

View File

@ -10,20 +10,20 @@
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/> <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/> <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="StatusTableViewCell" customModule="Tusker" customModuleProvider="target"> <view contentMode="scaleToFill" id="iN0-l3-epB" customClass="StatusTableViewCell" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="150"/> <rect key="frame" x="0.0" y="0.0" width="375" height="240"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews> <subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="yNh-ac-v6c"> <stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="yNh-ac-v6c">
<rect key="frame" x="16" y="8" width="343" height="134"/> <rect key="frame" x="16" y="8" width="343" height="224"/>
<subviews> <subviews>
<label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Reblogged by Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lDH-50-AJZ"> <label opaque="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="751" text="Reblogged by Person" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lDH-50-AJZ">
<rect key="frame" x="0.0" y="0.0" width="163.5" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="163.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<view contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="ve3-Y1-NQH"> <view contentMode="scaleToFill" verticalHuggingPriority="249" translatesAutoresizingMaskIntoConstraints="NO" id="ve3-Y1-NQH">
<rect key="frame" x="0.0" y="28.5" width="343" height="75.5"/> <rect key="frame" x="0.0" y="28.5" width="343" height="165.5"/>
<subviews> <subviews>
<imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QMP-j2-HLn"> <imageView contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="QMP-j2-HLn">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/> <rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
@ -33,62 +33,87 @@
<constraint firstAttribute="height" constant="50" id="nMi-Gq-JyV"/> <constraint firstAttribute="height" constant="50" id="nMi-Gq-JyV"/>
</constraints> </constraints>
</imageView> </imageView>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3Sm-P0-ySf"> <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="285" height="20.5"/> <rect key="frame" x="58" y="0.0" width="277" height="165.5"/>
<subviews> <subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gll-xe-FSr"> <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"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" horizontalCompressionResistancePriority="749" verticalCompressionResistancePriority="752" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gll-xe-FSr">
<rect key="frame" x="0.0" y="0.0" width="107" height="20.5"/> <rect key="frame" x="0.0" y="0.0" width="107" height="20.5"/>
<gestureRecognizers/> <gestureRecognizers/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/> <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="249" verticalHuggingPriority="251" horizontalCompressionResistancePriority="748" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="j89-zc-SFa"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="249" verticalHuggingPriority="252" horizontalCompressionResistancePriority="748" verticalCompressionResistancePriority="752" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="j89-zc-SFa">
<rect key="frame" x="115" y="0.0" width="137.5" height="20.5"/> <rect key="frame" x="115" y="0.0" width="129.5" height="20.5"/>
<gestureRecognizers/> <gestureRecognizers/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/> <fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="35d-EA-ReR"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="752" text="2m" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="35d-EA-ReR">
<rect key="frame" x="260.5" y="0.0" width="24.5" height="20.5"/> <rect key="frame" x="252.5" y="0.0" width="24.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/> <fontDescription key="fontDescription" type="system" weight="light" pointSize="17"/>
<color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/> <color key="textColor" systemColor="secondaryLabelColor" red="0.23529411759999999" green="0.23529411759999999" blue="0.26274509800000001" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
<constraints>
<constraint firstAttribute="height" secondItem="gll-xe-FSr" secondAttribute="height" id="B7p-Pc-fZD"/>
</constraints>
</stackView> </stackView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" text="Content" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="HrJ-t9-KcD" customClass="StatusContentLabel" customModule="Tusker" customModuleProvider="target"> <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="252" verticalCompressionResistancePriority="755" text="Content Warning" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="inI-Og-YiU">
<rect key="frame" x="58" y="24.5" width="285" height="51"/> <rect key="frame" x="0.0" y="24.5" width="277" height="20.5"/>
<fontDescription key="fontDescription" type="boldSystem" pointSize="17"/>
<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>
<button opaque="NO" contentMode="scaleToFill" verticalHuggingPriority="252" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="O0E-Vf-XYR">
<rect key="frame" x="0.0" y="49" width="277" height="30"/>
<color key="backgroundColor" systemColor="systemBlueColor" red="0.0" green="0.47843137250000001" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="z84-XW-gP3"/>
</constraints>
<color key="tintColor" systemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<state key="normal" image="chevron.down" catalog="system">
<preferredSymbolConfiguration key="preferredSymbolConfiguration" scale="large"/>
</state>
<connections>
<action selector="collapseButtonPressed:" destination="iN0-l3-epB" eventType="touchUpInside" id="HNS-rX-gBM"/>
</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"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/> <fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/> <nil key="textColor"/>
<nil key="highlightedColor"/> <nil key="highlightedColor"/>
</label> </label>
</subviews> </subviews>
</stackView>
</subviews>
<constraints> <constraints>
<constraint firstItem="gIY-Wp-RSk" firstAttribute="leading" secondItem="QMP-j2-HLn" secondAttribute="trailing" constant="8" id="0Tm-v7-Ts4"/>
<constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="8" id="2Ao-Gj-fY3"/> <constraint firstAttribute="bottom" relation="greaterThanOrEqual" secondItem="QMP-j2-HLn" secondAttribute="bottom" constant="8" id="2Ao-Gj-fY3"/>
<constraint firstItem="HrJ-t9-KcD" firstAttribute="leading" secondItem="QMP-j2-HLn" secondAttribute="trailing" constant="8" id="3KO-pD-Ldr"/>
<constraint firstItem="3Sm-P0-ySf" firstAttribute="leading" secondItem="QMP-j2-HLn" secondAttribute="trailing" constant="8" id="4EZ-pI-VjW"/>
<constraint firstAttribute="trailing" secondItem="3Sm-P0-ySf" secondAttribute="trailing" id="8wc-sb-HoG"/>
<constraint firstItem="HrJ-t9-KcD" firstAttribute="top" secondItem="3Sm-P0-ySf" secondAttribute="bottom" constant="4" id="Nm5-Qs-HB9"/>
<constraint firstItem="QMP-j2-HLn" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="PC4-Bi-QXm"/> <constraint firstItem="QMP-j2-HLn" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="PC4-Bi-QXm"/>
<constraint firstAttribute="bottom" secondItem="HrJ-t9-KcD" secondAttribute="bottom" id="YAm-mK-YXb"/> <constraint firstItem="gIY-Wp-RSk" firstAttribute="top" secondItem="QMP-j2-HLn" secondAttribute="top" id="fEd-wN-kuQ"/>
<constraint firstItem="3Sm-P0-ySf" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="nFO-c9-JSB"/> <constraint firstAttribute="trailingMargin" secondItem="gIY-Wp-RSk" secondAttribute="trailing" id="hKk-kO-wFT"/>
<constraint firstAttribute="trailing" secondItem="HrJ-t9-KcD" secondAttribute="trailing" id="wCB-bW-AdR"/> <constraint firstAttribute="bottom" secondItem="gIY-Wp-RSk" secondAttribute="bottom" id="kRU-Ct-CIg"/>
<constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/> <constraint firstItem="QMP-j2-HLn" firstAttribute="leading" secondItem="ve3-Y1-NQH" secondAttribute="leading" id="zeW-tQ-uJl"/>
</constraints> </constraints>
</view> </view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="nbq-yr-2mA" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target"> <view hidden="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="1" translatesAutoresizingMaskIntoConstraints="NO" id="nbq-yr-2mA" customClass="AttachmentsContainerView" customModule="Tusker" customModuleProvider="target">
<rect key="frame" x="0.0" y="108" width="343" height="0.0"/> <rect key="frame" x="0.0" y="198" width="343" height="0.0"/>
<color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> <color key="backgroundColor" systemColor="secondarySystemBackgroundColor" red="0.94901960780000005" green="0.94901960780000005" blue="0.96862745100000003" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints> <constraints>
<constraint firstAttribute="height" priority="999" constant="200" id="J42-49-2MU"/> <constraint firstAttribute="height" priority="999" constant="200" id="J42-49-2MU"/>
</constraints> </constraints>
</view> </view>
<stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw"> <stackView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" distribution="equalSpacing" alignment="bottom" translatesAutoresizingMaskIntoConstraints="NO" id="Zlb-yt-NTw">
<rect key="frame" x="0.0" y="112" width="343" height="22"/> <rect key="frame" x="0.0" y="202" width="343" height="22"/>
<subviews> <subviews>
<button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa"> <button opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rKF-yF-KIa">
<rect key="frame" x="0.0" y="0.0" width="21.5" height="22"/> <rect key="frame" x="0.0" y="0.0" width="21.5" height="22"/>
<state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/> <state key="normal" image="arrowshape.turn.up.left.fill" catalog="system"/>
<connections> <connections>
@ -120,7 +145,7 @@
</stackView> </stackView>
</subviews> </subviews>
<constraints> <constraints>
<constraint firstItem="nbq-yr-2mA" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="3Ag-HE-h4m"/> <constraint firstItem="nbq-yr-2mA" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="JCZ-x5-Xa2"/>
<constraint firstItem="Zlb-yt-NTw" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="wxD-pe-Udd"/> <constraint firstItem="Zlb-yt-NTw" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="wxD-pe-Udd"/>
<constraint firstItem="ve3-Y1-NQH" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="xN6-cs-Tnn"/> <constraint firstItem="ve3-Y1-NQH" firstAttribute="width" secondItem="yNh-ac-v6c" secondAttribute="width" id="xN6-cs-Tnn"/>
</constraints> </constraints>
@ -138,7 +163,9 @@
<connections> <connections>
<outlet property="attachmentsView" destination="nbq-yr-2mA" id="GkU-Xk-pc0"/> <outlet property="attachmentsView" destination="nbq-yr-2mA" id="GkU-Xk-pc0"/>
<outlet property="avatarImageView" destination="QMP-j2-HLn" id="CAl-hK-i3j"/> <outlet property="avatarImageView" destination="QMP-j2-HLn" id="CAl-hK-i3j"/>
<outlet property="collapseButton" destination="O0E-Vf-XYR" id="fBb-0C-QA2"/>
<outlet property="contentLabel" destination="HrJ-t9-KcD" id="tbD-3T-nNP"/> <outlet property="contentLabel" destination="HrJ-t9-KcD" id="tbD-3T-nNP"/>
<outlet property="contentWarningLabel" destination="inI-Og-YiU" id="2jf-6J-JUU"/>
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/> <outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
<outlet property="favoriteButton" destination="x0t-TR-jJ4" id="Ohz-bs-Ebr"/> <outlet property="favoriteButton" destination="x0t-TR-jJ4" id="Ohz-bs-Ebr"/>
<outlet property="reblogButton" destination="6tW-z8-Qh9" id="i9h-QA-ZPd"/> <outlet property="reblogButton" destination="6tW-z8-Qh9" id="i9h-QA-ZPd"/>
@ -146,11 +173,12 @@
<outlet property="timestampLabel" destination="35d-EA-ReR" id="8EW-mb-LAb"/> <outlet property="timestampLabel" destination="35d-EA-ReR" id="8EW-mb-LAb"/>
<outlet property="usernameLabel" destination="j89-zc-SFa" id="see-Xd-3e9"/> <outlet property="usernameLabel" destination="j89-zc-SFa" id="see-Xd-3e9"/>
</connections> </connections>
<point key="canvasLocation" x="29.600000000000001" y="38.680659670164921"/> <point key="canvasLocation" x="29.600000000000001" y="79.160419790104953"/>
</view> </view>
</objects> </objects>
<resources> <resources>
<image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/> <image name="arrowshape.turn.up.left.fill" catalog="system" width="64" height="52"/>
<image name="chevron.down" catalog="system" width="64" height="34"/>
<image name="ellipsis" catalog="system" width="64" height="20"/> <image name="ellipsis" catalog="system" width="64" height="20"/>
<image name="repeat" catalog="system" width="64" height="50"/> <image name="repeat" catalog="system" width="64" height="50"/>
<image name="star.fill" catalog="system" width="64" height="58"/> <image name="star.fill" catalog="system" width="64" height="58"/>