Show timestampts on statuses

This commit is contained in:
Shadowfacts 2018-08-31 13:46:33 -04:00
parent 47edc24ec6
commit 42db93db03
6 changed files with 170 additions and 7 deletions

View File

@ -12,6 +12,7 @@
04DACE8E212CC7CC009840C4 /* AvatarCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04DACE8D212CC7CC009840C4 /* AvatarCache.swift */; };
D6333B372137838300CE884A /* AttributedString+Trim.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B362137838300CE884A /* AttributedString+Trim.swift */; };
D6333B772138D94E00CE884A /* ComposeMediaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B762138D94E00CE884A /* ComposeMediaView.swift */; };
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */; };
D64A0CD32132153900640E3B /* HTMLContentLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64A0CD22132153900640E3B /* HTMLContentLabel.swift */; };
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D64D0AAC2128D88B005A6F37 /* LocalData.swift */; };
D64D0AAF2128D954005A6F37 /* Onboarding.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */; };
@ -92,6 +93,7 @@
04DACE8D212CC7CC009840C4 /* AvatarCache.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarCache.swift; sourceTree = "<group>"; };
D6333B362137838300CE884A /* AttributedString+Trim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Trim.swift"; sourceTree = "<group>"; };
D6333B762138D94E00CE884A /* ComposeMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMediaView.swift; sourceTree = "<group>"; };
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = "<group>"; };
D64A0CD22132153900640E3B /* HTMLContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLContentLabel.swift; sourceTree = "<group>"; };
D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = "<group>"; };
D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = "<group>"; };
@ -185,6 +187,7 @@
D66362722136FFC600C9CBA2 /* UITextView+Placeholder.swift */,
D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */,
D6333B362137838300CE884A /* AttributedString+Trim.swift */,
D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -455,6 +458,7 @@
D6BED174212667E900F02DA0 /* StatusTableViewCell.swift in Sources */,
D64D0AAD2128D88B005A6F37 /* LocalData.swift in Sources */,
D663626221360B1900C9CBA2 /* Preferences.swift in Sources */,
D6333B792139AEFD00CE884A /* Date+TimeAgo.swift in Sources */,
D663626C21361C6700C9CBA2 /* Account+Preferences.swift in Sources */,
D6333B372137838300CE884A /* AttributedString+Trim.swift in Sources */,
D667E5EF2134C39F0057A976 /* StatusContentLabel.swift in Sources */,

View File

@ -0,0 +1,88 @@
//
// Date+TimeAgo.swift
// Tusker
//
// Created by Shadowfacts on 8/31/18.
// Copyright © 2018 Shadowfacts. All rights reserved.
//
import Foundation
extension Date {
// var timeAgo: String {
// let calendar = NSCalendar.current
// let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year])
//
// let components = calendar.dateComponents(unitFlags, from: self, to: Date())
//
// if components.year! >= 1 {
// return "\(components.year!)y"
// } else if components.month! >= 1 {
// return "\(components.month!)mo"
// } else if components.weekOfYear! >= 1 {
// return "\(components.weekOfYear!)w"
// } else if components.day! >= 1 {
// return "\(components.day!)d"
// } else if components.hour! >= 1 {
// return "\(components.hour!)h"
// } else if components.minute! >= 1 {
// return "\(components.minute!)m"
// } else if components.second! >= 3 {
// return "\(components.second!)s"
// } else {
// return "Now"
// }
// }
func timeAgo() -> (Int, Calendar.Component) {
let calendar = NSCalendar.current
let unitFlags = Set<Calendar.Component>([.second, .minute, .hour, .day, .weekOfYear, .month, .year])
let components = calendar.dateComponents(unitFlags, from: self, to: Date())
if components.year! >= 1 {
return (components.year!, .year)
} else if components.month! >= 1 {
return (components.month!, .month)
} else if components.weekOfYear! >= 1 {
return (components.weekOfYear!, .weekOfYear)
} else if components.day! >= 1 {
return (components.day!, .day)
} else if components.hour! >= 1 {
return (components.hour!, .hour)
} else if components.minute! >= 1 {
return (components.minute!, .minute)
} else {
return (components.second!, .second)
}
}
func timeAgoString() -> String {
let (amount, component) = timeAgo()
switch component {
case .year:
return "\(amount)y"
case .month:
return "\(amount)mo"
case .weekOfYear:
return "\(amount)w"
case .day:
return "\(amount)d"
case .hour:
return "\(amount)h"
case .minute:
return "\(amount)m"
case .second:
if amount >= 3 {
return "\(amount)s"
} else {
return "Now"
}
default:
fatalError("Unexpected component: \(component)")
}
}
}

View File

@ -17,12 +17,15 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
@IBOutlet weak var usernameLabel: UILabel!
@IBOutlet weak var contentLabel: StatusContentLabel!
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var timestampLabel: UILabel!
var status: Status!
var account: Account!
var avatarURL: URL?
var updateTimestampWorkItem: DispatchWorkItem?
override func awakeFromNib() {
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
displayNameLabel.isUserInteractionEnabled = true
@ -63,10 +66,32 @@ class ConversationMainStatusTableViewCell: UITableViewCell, PreferencesAdaptive
}
}
contentLabel.status = status
contentLabel.delegate = self
}
func updateTimestamp() {
timestampLabel.text = status.createdAt.timeAgoString()
let delay: DispatchTimeInterval?
switch status.createdAt.timeAgo().1 {
case .second:
delay = .seconds(10)
case .minute:
delay = .seconds(60)
default:
delay = nil
}
if let delay = delay {
updateTimestampWorkItem = DispatchWorkItem {
self.updateTimestamp()
}
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: updateTimestampWorkItem!)
} else {
updateTimestampWorkItem = nil
}
}
override func prepareForReuse() {
if let url = avatarURL {
AvatarCache.shared.cancel(url)

View File

@ -28,8 +28,8 @@
<constraint firstAttribute="width" constant="50" id="Yxp-Vr-dfl"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lZY-2e-17d">
<rect key="frame" x="58" y="0.0" width="285" height="29"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lZY-2e-17d">
<rect key="frame" x="58" y="0.0" width="252.5" height="29"/>
<fontDescription key="fontDescription" type="system" pointSize="24"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
@ -46,6 +46,12 @@
<nil key="textColor"/>
<nil key="highlightedColor"/>
</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="R3N-Pg-4hn">
<rect key="frame" x="318.5" y="0.0" width="24.5" height="29"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
@ -54,13 +60,16 @@
<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="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="Aqj-co-Szp"/>
<constraint firstItem="R3N-Pg-4hn" firstAttribute="top" secondItem="Cnd-Fj-B7l" secondAttribute="top" id="Gl6-Ss-hKQ"/>
<constraint firstAttribute="trailing" secondItem="R3N-Pg-4hn" secondAttribute="trailing" id="Oiy-II-fvT"/>
<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 firstItem="mB9-HO-1vf" firstAttribute="leading" secondItem="Cnd-Fj-B7l" secondAttribute="leading" id="bxq-Fs-1aH"/>
<constraint firstItem="R3N-Pg-4hn" firstAttribute="leading" secondItem="lZY-2e-17d" secondAttribute="trailing" constant="8" id="dVP-Er-Z2l"/>
<constraint firstItem="SWg-Ka-QyP" firstAttribute="leading" secondItem="mB9-HO-1vf" secondAttribute="trailing" constant="8" id="e45-gE-myI"/>
<constraint firstItem="R3N-Pg-4hn" firstAttribute="height" secondItem="lZY-2e-17d" secondAttribute="height" id="fBi-wD-elQ"/>
<constraint firstItem="TgY-hs-Klo" firstAttribute="top" secondItem="mB9-HO-1vf" secondAttribute="bottom" constant="8" id="l6y-Rr-Nmc"/>
<constraint firstItem="SWg-Ka-QyP" firstAttribute="top" secondItem="lZY-2e-17d" secondAttribute="bottom" id="lvX-1b-8cN"/>
<constraint firstAttribute="trailing" secondItem="lZY-2e-17d" secondAttribute="trailing" id="qcG-N7-OIv"/>
</constraints>
</view>
<stackView opaque="NO" contentMode="scaleToFill" spacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="3Bg-XP-d13">
@ -91,6 +100,7 @@
<outlet property="avatarImageView" destination="mB9-HO-1vf" id="0R0-rt-Osh"/>
<outlet property="contentLabel" destination="TgY-hs-Klo" id="SEi-B2-VQf"/>
<outlet property="displayNameLabel" destination="lZY-2e-17d" id="7og-23-eHy"/>
<outlet property="timestampLabel" destination="R3N-Pg-4hn" id="z5j-Tw-Lk5"/>
<outlet property="usernameLabel" destination="SWg-Ka-QyP" id="h2I-g4-AD9"/>
</connections>
<point key="canvasLocation" x="40.799999999999997" y="-163.71814092953525"/>

View File

@ -34,6 +34,7 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
@IBOutlet weak var contentLabel: StatusContentLabel!
@IBOutlet weak var avatarImageView: UIImageView!
@IBOutlet weak var reblogLabel: UILabel!
@IBOutlet weak var timestampLabel: UILabel!
var status: Status!
var account: Account!
@ -41,6 +42,8 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
var avatarURL: URL?
var updateTimestampWorkItem: DispatchWorkItem?
override func awakeFromNib() {
displayNameLabel.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(accountPressed)))
displayNameLabel.isUserInteractionEnabled = true
@ -90,15 +93,39 @@ class StatusTableViewCell: UITableViewCell, PreferencesAdaptive {
}
}
}
updateTimestamp()
contentLabel.status = status
contentLabel.delegate = self
}
func updateTimestamp() {
timestampLabel.text = status.createdAt.timeAgoString()
let delay: DispatchTimeInterval?
switch status.createdAt.timeAgo().1 {
case .second:
delay = .seconds(10)
case .minute:
delay = .seconds(60)
default:
delay = nil
}
if let delay = delay {
updateTimestampWorkItem = DispatchWorkItem {
self.updateTimestamp()
}
DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: updateTimestampWorkItem!)
} else {
updateTimestampWorkItem = nil
}
}
override func prepareForReuse() {
if let url = avatarURL {
AvatarCache.shared.cancel(url)
}
updateTimestampWorkItem?.cancel()
updateTimestampWorkItem = nil
}
@IBAction func replyPressed(_ sender: Any) {

View File

@ -35,7 +35,7 @@
<constraint firstAttribute="height" constant="50" id="nMi-Gq-JyV"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="751" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gll-xe-FSr">
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Display name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gll-xe-FSr">
<rect key="frame" x="58" y="0.0" width="102.5" height="20.5"/>
<gestureRecognizers/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
@ -48,21 +48,29 @@
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="j89-zc-SFa">
<rect key="frame" x="168.5" y="0.0" width="174.5" height="20.5"/>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" horizontalCompressionResistancePriority="749" text="@username" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="j89-zc-SFa">
<rect key="frame" x="168.5" y="0.0" width="142" height="20.5"/>
<gestureRecognizers/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</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">
<rect key="frame" x="318.5" y="0.0" width="24.5" height="20.5"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<constraints>
<constraint firstItem="HrJ-t9-KcD" firstAttribute="top" secondItem="gll-xe-FSr" secondAttribute="bottom" constant="8" id="2jU-Sw-b1A"/>
<constraint firstItem="HrJ-t9-KcD" firstAttribute="leading" secondItem="QMP-j2-HLn" secondAttribute="trailing" constant="8" id="3KO-pD-Ldr"/>
<constraint firstAttribute="trailing" secondItem="j89-zc-SFa" secondAttribute="trailing" id="D9z-3W-FHn"/>
<constraint firstItem="35d-EA-ReR" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="Dcx-0b-u3X"/>
<constraint firstItem="QMP-j2-HLn" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="PC4-Bi-QXm"/>
<constraint firstItem="j89-zc-SFa" firstAttribute="leading" secondItem="gll-xe-FSr" secondAttribute="trailing" constant="8" id="XGL-3q-Urc"/>
<constraint firstAttribute="trailing" secondItem="35d-EA-ReR" secondAttribute="trailing" id="Y5c-ws-TTp"/>
<constraint firstAttribute="bottom" secondItem="HrJ-t9-KcD" secondAttribute="bottom" id="YAm-mK-YXb"/>
<constraint firstItem="35d-EA-ReR" firstAttribute="leading" secondItem="j89-zc-SFa" secondAttribute="trailing" constant="8" id="dOI-mx-C0K"/>
<constraint firstItem="gll-xe-FSr" firstAttribute="leading" secondItem="QMP-j2-HLn" secondAttribute="trailing" constant="8" id="f00-ER-g7Q"/>
<constraint firstItem="gll-xe-FSr" firstAttribute="top" secondItem="ve3-Y1-NQH" secondAttribute="top" id="ikJ-qd-ziU"/>
<constraint firstAttribute="trailing" secondItem="HrJ-t9-KcD" secondAttribute="trailing" id="wCB-bW-AdR"/>
@ -99,6 +107,7 @@
<outlet property="contentLabel" destination="HrJ-t9-KcD" id="tbD-3T-nNP"/>
<outlet property="displayNameLabel" destination="gll-xe-FSr" id="63y-He-xy1"/>
<outlet property="reblogLabel" destination="lDH-50-AJZ" id="uJf-Pt-cEP"/>
<outlet property="timestampLabel" destination="35d-EA-ReR" id="8EW-mb-LAb"/>
<outlet property="usernameLabel" destination="j89-zc-SFa" id="see-Xd-3e9"/>
</connections>
<point key="canvasLocation" x="29.600000000000001" y="38.680659670164921"/>