2019-11-19 17:08:11 +00:00
//
// T i m e l i n e S t a t u s T a b l e V i e w C e l l . s w i f t
// T u s k e r
//
// C r e a t e d b y S h a d o w f a c t s o n 8 / 1 6 / 1 8 .
// C o p y r i g h t © 2 0 1 8 S h a d o w f a c t s . A l l r i g h t s r e s e r v e d .
//
import UIKit
import Combine
import Pachyderm
class TimelineStatusTableViewCell : BaseStatusTableViewCell {
static let relativeDateFormatter : RelativeDateTimeFormatter = {
let formatter = RelativeDateTimeFormatter ( )
formatter . dateTimeStyle = . numeric
formatter . unitsStyle = . short
return formatter
} ( )
@IBOutlet weak var reblogLabel : UILabel !
@IBOutlet weak var timestampLabel : UILabel !
@IBOutlet weak var pinImageView : UIImageView !
var reblogStatusID : String ?
var rebloggerID : String ?
var showPinned : Bool = false
var updateTimestampWorkItem : DispatchWorkItem ?
var rebloggerAccountUpdater : Cancellable ?
deinit {
rebloggerAccountUpdater ? . cancel ( )
2020-01-20 04:10:52 +00:00
updateTimestampWorkItem ? . cancel ( )
2019-11-19 17:08:11 +00:00
}
override func awakeFromNib ( ) {
super . awakeFromNib ( )
reblogLabel . addGestureRecognizer ( UITapGestureRecognizer ( target : self , action : #selector ( reblogLabelPressed ) ) )
accessibilityElements ! . insert ( reblogLabel ! , at : 0 )
}
2020-01-06 00:54:28 +00:00
override func createObserversIfNecessary ( ) {
super . createObserversIfNecessary ( )
if rebloggerAccountUpdater = = nil {
rebloggerAccountUpdater = mastodonController . cache . accountSubject
2020-01-20 04:14:51 +00:00
. filter { [ unowned self ] in $0 . id = = self . rebloggerID }
2020-01-06 00:54:28 +00:00
. receive ( on : DispatchQueue . main )
2020-01-20 04:14:51 +00:00
. sink { [ unowned self ] in self . updateRebloggerLabel ( reblogger : $0 ) }
2020-01-06 00:54:28 +00:00
}
}
2019-11-28 23:36:58 +00:00
override func updateUI ( statusID : String , state : StatusState ) {
2020-01-06 00:54:28 +00:00
guard var status = mastodonController . cache . status ( for : statusID ) else { fatalError ( " Missing cached status \( statusID ) " ) }
2019-11-19 17:08:11 +00:00
let realStatusID : String
if let rebloggedStatusID = status . reblog ? . id ,
2020-01-06 00:54:28 +00:00
let rebloggedStatus = mastodonController . cache . status ( for : rebloggedStatusID ) {
2019-11-19 17:08:11 +00:00
reblogStatusID = statusID
rebloggerID = status . account . id
status = rebloggedStatus
realStatusID = rebloggedStatus . id
reblogLabel . isHidden = false
} else {
reblogStatusID = nil
rebloggerID = nil
reblogLabel . isHidden = true
realStatusID = statusID
}
2019-11-28 23:36:58 +00:00
super . updateUI ( statusID : realStatusID , state : state )
2019-11-19 17:08:11 +00:00
updateTimestamp ( )
let pinned = status . pinned ? ? false
pinImageView . isHidden = ! ( pinned && showPinned )
timestampLabel . isHidden = ! pinImageView . isHidden
}
@objc override func updateUIForPreferences ( ) {
super . updateUIForPreferences ( )
if let rebloggerID = rebloggerID ,
2020-01-06 00:54:28 +00:00
let reblogger = mastodonController . cache . account ( for : rebloggerID ) {
2019-11-19 17:08:11 +00:00
updateRebloggerLabel ( reblogger : reblogger )
}
}
private func updateRebloggerLabel ( reblogger : Account ) {
reblogLabel . text = " Reblogged by \( reblogger . realDisplayName ) "
}
func updateTimestamp ( ) {
2020-01-25 15:06:27 +00:00
guard let mastodonController = mastodonController , let status = mastodonController . cache . status ( for : statusID ) else { fatalError ( " Missing cached status \( statusID ! ) " ) }
2019-11-19 17:08:11 +00:00
timestampLabel . text = status . createdAt . timeAgoString ( )
timestampLabel . accessibilityLabel = TimelineStatusTableViewCell . relativeDateFormatter . localizedString ( for : status . createdAt , relativeTo : Date ( ) )
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 {
2020-01-20 04:10:52 +00:00
updateTimestampWorkItem = DispatchWorkItem { [ unowned self ] in
2019-11-19 17:08:11 +00:00
self . updateTimestamp ( )
}
DispatchQueue . main . asyncAfter ( deadline : . now ( ) + delay , execute : updateTimestampWorkItem ! )
} else {
updateTimestampWorkItem = nil
}
}
2020-01-20 04:48:36 +00:00
func reply ( ) {
if Preferences . shared . mentionReblogger ,
let rebloggerID = rebloggerID ,
2020-01-20 17:16:03 +00:00
let rebloggerAccount = mastodonController . cache . account ( for : rebloggerID ) {
2020-01-20 04:48:36 +00:00
delegate ? . reply ( to : statusID , mentioningAcct : rebloggerAccount . acct )
} else {
delegate ? . reply ( to : statusID )
}
}
2019-11-19 17:08:11 +00:00
override func prepareForReuse ( ) {
super . prepareForReuse ( )
updateTimestampWorkItem ? . cancel ( )
updateTimestampWorkItem = nil
showPinned = false
}
@objc func reblogLabelPressed ( ) {
guard let rebloggerID = rebloggerID else { return }
delegate ? . selected ( account : rebloggerID )
}
2020-01-20 04:48:36 +00:00
override func replyPressed ( ) {
reply ( )
}
2019-11-19 17:08:11 +00:00
override func getStatusCellPreviewProviders ( for location : CGPoint , sourceViewController : UIViewController ) -> BaseStatusTableViewCell . PreviewProviders ? {
2020-01-05 20:25:07 +00:00
guard let mastodonController = mastodonController else { return nil }
2019-11-19 17:08:11 +00:00
return (
2020-01-05 20:25:07 +00:00
content : { ConversationTableViewController ( for : self . statusID , state : self . statusState . copy ( ) , mastodonController : mastodonController ) } ,
2020-01-18 02:29:53 +00:00
actions : { self . actionsForStatus ( statusID : self . statusID , sourceView : self ) }
2019-11-19 17:08:11 +00:00
)
}
}
2019-12-14 16:59:31 +00:00
extension TimelineStatusTableViewCell : SelectableTableViewCell {
func didSelectCell ( ) {
delegate ? . selected ( status : statusID , state : statusState . copy ( ) )
}
}
2019-11-19 17:08:11 +00:00
extension TimelineStatusTableViewCell : TableViewSwipeActionProvider {
func leadingSwipeActionsConfiguration ( ) -> UISwipeActionsConfiguration ? {
2020-01-05 20:25:07 +00:00
guard let mastodonController = mastodonController else { return nil }
2020-01-06 00:54:28 +00:00
guard let status = mastodonController . cache . status ( for : statusID ) else { fatalError ( " Missing cached status \( statusID ! ) " ) }
2019-11-19 17:08:11 +00:00
let favoriteTitle : String
let favoriteRequest : Request < Status >
let favoriteColor : UIColor
if status . favourited ? ? false {
favoriteTitle = " Unfavorite "
favoriteRequest = Status . unfavourite ( status )
favoriteColor = UIColor ( displayP3Red : 235 / 255 , green : 77 / 255 , blue : 62 / 255 , alpha : 1 )
} else {
favoriteTitle = " Favorite "
favoriteRequest = Status . favourite ( status )
favoriteColor = UIColor ( displayP3Red : 1 , green : 204 / 255 , blue : 0 , alpha : 1 )
}
let favorite = UIContextualAction ( style : . normal , title : favoriteTitle ) { ( action , view , completion ) in
2020-01-05 20:25:07 +00:00
mastodonController . run ( favoriteRequest , completion : { response in
2019-11-19 17:08:11 +00:00
DispatchQueue . main . async {
guard case let . success ( status , _ ) = response else {
completion ( false )
return
}
completion ( true )
2020-01-06 00:54:28 +00:00
mastodonController . cache . add ( status : status )
2019-11-19 17:08:11 +00:00
}
} )
}
favorite . image = UIImage ( systemName : " star.fill " )
favorite . backgroundColor = favoriteColor
let reblogTitle : String
let reblogRequest : Request < Status >
let reblogColor : UIColor
if status . reblogged ? ? false {
reblogTitle = " Unreblog "
reblogRequest = Status . unreblog ( status )
reblogColor = UIColor ( displayP3Red : 235 / 255 , green : 77 / 255 , blue : 62 / 255 , alpha : 1 )
} else {
reblogTitle = " Reblog "
reblogRequest = Status . reblog ( status )
reblogColor = tintColor
}
let reblog = UIContextualAction ( style : . normal , title : reblogTitle ) { ( action , view , completion ) in
2020-01-05 20:25:07 +00:00
mastodonController . run ( reblogRequest , completion : { response in
2019-11-19 17:08:11 +00:00
DispatchQueue . main . async {
guard case let . success ( status , _ ) = response else {
completion ( false )
return
}
completion ( true )
2020-01-06 00:54:28 +00:00
mastodonController . cache . add ( status : status )
2019-11-19 17:08:11 +00:00
}
} )
}
reblog . image = UIImage ( systemName : " repeat " )
reblog . backgroundColor = reblogColor
return UISwipeActionsConfiguration ( actions : [ favorite , reblog ] )
}
func trailingSwipeActionsConfiguration ( ) -> UISwipeActionsConfiguration ? {
let reply = UIContextualAction ( style : . normal , title : " Reply " ) { ( action , view , completion ) in
completion ( true )
2020-01-20 04:48:36 +00:00
self . reply ( )
2019-11-19 17:08:11 +00:00
}
reply . image = UIImage ( systemName : " arrowshape.turn.up.left.fill " )
reply . backgroundColor = tintColor
let more = UIContextualAction ( style : . normal , title : " More " ) { ( action , view , completion ) in
completion ( true )
2020-01-18 02:29:53 +00:00
self . delegate ? . showMoreOptions ( forStatus : self . statusID , sourceView : self )
2019-11-19 17:08:11 +00:00
}
more . image = UIImage ( systemName : " ellipsis " )
more . backgroundColor = . gray
return UISwipeActionsConfiguration ( actions : [ reply , more ] )
}
}