diff --git a/Tusker.xcodeproj/project.pbxproj b/Tusker.xcodeproj/project.pbxproj index 68b3027b..4be137ad 100644 --- a/Tusker.xcodeproj/project.pbxproj +++ b/Tusker.xcodeproj/project.pbxproj @@ -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 = ""; }; D6333B362137838300CE884A /* AttributedString+Trim.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AttributedString+Trim.swift"; sourceTree = ""; }; D6333B762138D94E00CE884A /* ComposeMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeMediaView.swift; sourceTree = ""; }; + D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+TimeAgo.swift"; sourceTree = ""; }; D64A0CD22132153900640E3B /* HTMLContentLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTMLContentLabel.swift; sourceTree = ""; }; D64D0AAC2128D88B005A6F37 /* LocalData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalData.swift; sourceTree = ""; }; D64D0AAE2128D954005A6F37 /* Onboarding.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Onboarding.storyboard; sourceTree = ""; }; @@ -185,6 +187,7 @@ D66362722136FFC600C9CBA2 /* UITextView+Placeholder.swift */, D66362742137068A00C9CBA2 /* Visibility+Helpers.swift */, D6333B362137838300CE884A /* AttributedString+Trim.swift */, + D6333B782139AEFD00CE884A /* Date+TimeAgo.swift */, ); path = Extensions; sourceTree = ""; @@ -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 */, diff --git a/Tusker/Extensions/Date+TimeAgo.swift b/Tusker/Extensions/Date+TimeAgo.swift new file mode 100644 index 00000000..bbb5f389 --- /dev/null +++ b/Tusker/Extensions/Date+TimeAgo.swift @@ -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([.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([.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)") + } + } + +} diff --git a/Tusker/Views/ConversationMainStatusTableViewCell.swift b/Tusker/Views/ConversationMainStatusTableViewCell.swift index 043f11b9..10d45435 100644 --- a/Tusker/Views/ConversationMainStatusTableViewCell.swift +++ b/Tusker/Views/ConversationMainStatusTableViewCell.swift @@ -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) diff --git a/Tusker/Views/ConversationMainStatusTableViewCell.xib b/Tusker/Views/ConversationMainStatusTableViewCell.xib index 5da1b90b..75defe0c 100644 --- a/Tusker/Views/ConversationMainStatusTableViewCell.xib +++ b/Tusker/Views/ConversationMainStatusTableViewCell.xib @@ -28,8 +28,8 @@ -