@ -9,10 +9,19 @@ class MessageMetadataViewController: OWSViewController {
static let TAG = " [MessageMetadataViewController] "
let TAG = " [MessageMetadataViewController] "
enum MessageRecipientState {
case uploading
case sending
case sent
case delivered
case read
case failed
}
// MARK: P r o p e r t i e s
let contactsManager : OWSContactsManager
let databaseConnection : YapDatabaseConnection
var message : TSMessage
@ -22,6 +31,7 @@ class MessageMetadataViewController: OWSViewController {
var scrollView : UIScrollView ?
var contentView : UIView ?
var attachment : TSAttachment ?
var dataSource : DataSource ?
var attachmentStream : TSAttachmentStream ?
var messageBody : String ?
@ -135,6 +145,9 @@ class MessageMetadataViewController: OWSViewController {
let contactsManager = Environment . getCurrent ( ) . contactsManager !
let thread = message . thread
// C o n t e n t
rows += contentRows ( )
// S e n d e r ?
if let incomingMessage = message as ? TSIncomingMessage {
let senderId = incomingMessage . authorId
@ -147,32 +160,65 @@ class MessageMetadataViewController: OWSViewController {
// R e c i p i e n t ( s )
if let outgoingMessage = message as ? TSOutgoingMessage {
// TODO: I t ' d b e n i c e t o i n s e t t h e s e d i v i d e r s f r o m t h e e d g e o f t h e s c r e e n .
let addDivider = {
let divider = UIView ( )
divider . backgroundColor = UIColor ( white : 0.9 , alpha : 1.0 )
divider . autoSetDimension ( . height , toSize : 0.5 )
rows . append ( divider )
}
addDivider ( )
for recipientId in thread . recipientIdentifiers {
let recipientStatus = self . recipientStatus ( forOutgoingMessage : outgoingMessage , recipientId : recipientId )
let cell = ContactTableViewCell ( )
cell . configure ( withRecipientId : recipientId , contactsManager : self . contactsManager )
let statusLabel = UILabel ( )
statusLabel . text = recipientStatus
statusLabel . textColor = UIColor . ows_darkGray ( )
statusLabel . font = UIFont . ows_footnote ( )
statusLabel . sizeToFit ( )
cell . accessoryView = statusLabel
cell . autoSetDimension ( . height , toSize : ContactTableViewCell . rowHeight ( ) )
cell . setContentHuggingLow ( )
rows . append ( cell )
addDivider ( )
let recipientStatusGroups : [ MessageRecipientState ] = [
. read ,
. uploading ,
. delivered ,
. sent ,
. sending ,
. failed
]
for recipientStatusGroup in recipientStatusGroups {
var groupRows = [ UIView ] ( )
// TODO: I t ' d b e n i c e t o i n s e t t h e s e d i v i d e r s f r o m t h e e d g e o f t h e s c r e e n .
let addDivider = {
let divider = UIView ( )
divider . backgroundColor = UIColor ( white : 0.9 , alpha : 1.0 )
divider . autoSetDimension ( . height , toSize : 0.5 )
groupRows . append ( divider )
}
for recipientId in thread . recipientIdentifiers {
let ( recipientStatus , statusMessage ) = self . recipientStatus ( outgoingMessage : outgoingMessage , recipientId : recipientId )
guard recipientStatus = = recipientStatusGroup else {
continue
}
if groupRows . count < 1 {
groupRows . append ( valueRow ( name : MessageRecipientStateName ( recipientStatusGroup ) ,
value : " " ) )
addDivider ( )
}
let cell = ContactTableViewCell ( )
cell . configure ( withRecipientId : recipientId , contactsManager : self . contactsManager )
let statusLabel = UILabel ( )
statusLabel . text = statusMessage
statusLabel . textColor = UIColor . ows_darkGray ( )
statusLabel . font = UIFont . ows_footnote ( )
statusLabel . sizeToFit ( )
cell . accessoryView = statusLabel
cell . autoSetDimension ( . height , toSize : ContactTableViewCell . rowHeight ( ) )
cell . setContentHuggingLow ( )
groupRows . append ( cell )
}
if groupRows . count > 0 {
addDivider ( )
let spacer = UIView ( )
spacer . autoSetDimension ( . height , toSize : 10 )
groupRows . append ( spacer )
}
Logger . verbose ( " \( groupRows . count ) rows for \( recipientStatusGroup ) " )
guard groupRows . count > 0 else {
continue
}
rows += groupRows
}
}
@ -180,14 +226,42 @@ class MessageMetadataViewController: OWSViewController {
comment : " Label for the 'sent date & time' field of the 'message metadata' view. " ) ,
value : DateUtil . formatPastTimestampRelativeToNow ( message . timestamp ) ) )
if let _ = message as ? TSIncomingMessage {
if message as ? TSIncomingMessage != nil {
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_RECEIVED_DATE_TIME " ,
comment : " Label for the 'received date & time' field of the 'message metadata' view. " ) ,
value : DateUtil . formatPastTimestampRelativeToNow ( message . timestampForSorting ( ) ) ) )
}
rows += addAttachmentMetadataRows ( )
// TODO: W e c o u l d i n c l u d e t h e " d i s a p p e a r i n g m e s s a g e s " s t a t e h e r e .
var lastRow : UIView ?
for row in rows {
contentView . addSubview ( row )
row . autoPinLeadingToSuperView ( )
row . autoPinTrailingToSuperView ( )
if let lastRow = lastRow {
row . autoPinEdge ( . top , to : . bottom , of : lastRow , withOffset : 5 )
} else {
row . autoPinEdge ( toSuperviewEdge : . top , withInset : 20 )
}
lastRow = row
}
if let lastRow = lastRow {
lastRow . autoPinEdge ( toSuperviewEdge : . bottom , withInset : 20 )
}
if let mediaMessageView = mediaMessageView {
mediaMessageView . autoPinToSquareAspectRatio ( )
}
}
private func contentRows ( ) -> [ UIView ] {
var rows = [ UIView ] ( )
if message . attachmentIds . count > 0 {
rows += addAttachmentRows ( )
} else if let messageBody = message . body {
@ -196,17 +270,47 @@ class MessageMetadataViewController: OWSViewController {
if messageBody . characters . count > 0 {
self . messageBody = messageBody
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_BODY_LABEL " ,
comment : " Label for the message body in the 'message metadata' view. " ) ,
value : " " ) )
let isIncoming = self . message as ? TSIncomingMessage != nil
let bodyLabel = UILabel ( )
bodyLabel . textColor = UIColor. black
bodyLabel . font = UIFont . ows_regularFont ( withSize : 1 4 )
bodyLabel . textColor = isIncoming ? UIColor. black : UIColor . white
bodyLabel . font = UIFont . ows_regularFont ( withSize : 1 6 )
bodyLabel . text = messageBody
bodyLabel . numberOfLines = 0
// O n l y s h o w t h e f i r s t N l i n e s .
bodyLabel . numberOfLines = 10
bodyLabel . lineBreakMode = . byWordWrapping
rows . append ( bodyLabel )
let bubbleView = UIView ( )
bubbleView . backgroundColor = isIncoming ? UIColor . jsq_messageBubbleLightGray ( ) : UIColor . ows_materialBlue ( )
bubbleView . layer . cornerRadius = 10
bubbleView . addSubview ( bodyLabel )
bodyLabel . autoPinLeadingToSuperView ( withMargin : 10 )
bodyLabel . autoPinTrailingToSuperView ( withMargin : 10 )
bodyLabel . autoPinHeightToSuperview ( withMargin : 10 )
let bubbleSpacer = UIView ( )
let row = UIView ( )
row . addSubview ( bubbleView )
row . addSubview ( bubbleSpacer )
bubbleView . autoPinHeightToSuperview ( )
bubbleView . setContentHuggingHorizontalHigh ( )
bubbleView . setCompressionResistanceHigh ( )
bubbleSpacer . autoPinHeightToSuperview ( )
bubbleSpacer . setContentHuggingLow ( )
if isIncoming {
bubbleView . autoPinLeadingToSuperView ( withMargin : 10 )
bubbleSpacer . autoPinLeading ( toTrailingOf : bubbleView )
bubbleSpacer . autoPinTrailingToSuperView ( withMargin : 10 )
} else {
bubbleSpacer . autoPinLeadingToSuperView ( withMargin : 10 )
bubbleView . autoPinLeading ( toTrailingOf : bubbleSpacer )
bubbleView . autoPinTrailingToSuperView ( withMargin : 10 )
}
rows . append ( row )
} else {
// N e i t h e r a t t a c h m e n t n o r b o d y .
owsFail ( " \( self . TAG ) Message has neither attachment nor body. " )
@ -216,27 +320,11 @@ class MessageMetadataViewController: OWSViewController {
}
}
var lastRow : UIView ?
for row in rows {
contentView . addSubview ( row )
row . autoPinLeadingToSuperView ( )
row . autoPinTrailingToSuperView ( )
let spacer = UIView ( )
spacer . autoSetDimension ( . height , toSize : 15 )
rows . append ( spacer )
if let lastRow = lastRow {
row . autoPinEdge ( . top , to : . bottom , of : lastRow , withOffset : 5 )
} else {
row . autoPinEdge ( toSuperviewEdge : . top , withInset : 20 )
}
lastRow = row
}
if let lastRow = lastRow {
lastRow . autoPinEdge ( toSuperviewEdge : . bottom , withInset : 20 )
}
if let mediaMessageView = mediaMessageView {
mediaMessageView . autoPinToSquareAspectRatio ( )
}
return rows
}
private func addAttachmentRows ( ) -> [ UIView ] {
@ -251,17 +339,7 @@ class MessageMetadataViewController: OWSViewController {
owsFail ( " Missing attachment " )
return rows
}
let contentType = attachment . contentType
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_ATTACHMENT_MIME_TYPE " ,
comment : " Label for the MIME type of attachments in the 'message metadata' view. " ) ,
value : contentType ) )
if let sourceFilename = attachment . sourceFilename {
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_SOURCE_FILENAME " ,
comment : " Label for the original filename of any attachment in the 'message metadata' view. " ) ,
value : sourceFilename ) )
}
self . attachment = attachment
guard let attachmentStream = attachment as ? TSAttachmentStream else {
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_ATTACHMENT_NOT_YET_DOWNLOADED " ,
@ -282,21 +360,8 @@ class MessageMetadataViewController: OWSViewController {
return rows
}
let fileSize = dataSource . dataLength ( )
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_ATTACHMENT_FILE_SIZE " ,
comment : " Label for file size of attachments in the 'message metadata' view. " ) ,
value : ViewControllerUtils . formatFileSize ( UInt ( fileSize ) ) ) )
let contentType = attachment . contentType
if let dataUTI = MIMETypeUtil . utiType ( forMIMEType : contentType ) {
if attachment . isVoiceMessage ( ) {
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_VOICE_MESSAGE " ,
comment : " Label for voice messages of the 'message metadata' view. " ) ,
value : " " ) )
} else {
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_MEDIA " ,
comment : " Label for media messages of the 'message metadata' view. " ) ,
value : " " ) )
}
let attachment = SignalAttachment ( dataSource : dataSource , dataUTI : dataUTI )
let mediaMessageView = MediaMessageView ( attachment : attachment )
self . mediaMessageView = mediaMessageView
@ -305,50 +370,83 @@ class MessageMetadataViewController: OWSViewController {
return rows
}
private func recipientStatus ( forOutgoingMessage message : TSOutgoingMessage , recipientId : String ) -> String {
private func addAttachmentMetadataRows ( ) -> [ UIView ] {
var rows = [ UIView ] ( )
if let attachment = self . attachment {
let contentType = attachment . contentType
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_ATTACHMENT_MIME_TYPE " ,
comment : " Label for the MIME type of attachments in the 'message metadata' view. " ) ,
value : contentType ) )
if let sourceFilename = attachment . sourceFilename {
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_SOURCE_FILENAME " ,
comment : " Label for the original filename of any attachment in the 'message metadata' view. " ) ,
value : sourceFilename ) )
}
}
if let dataSource = self . dataSource {
let fileSize = dataSource . dataLength ( )
rows . append ( valueRow ( name : NSLocalizedString ( " MESSAGE_METADATA_VIEW_ATTACHMENT_FILE_SIZE " ,
comment : " Label for file size of attachments in the 'message metadata' view. " ) ,
value : ViewControllerUtils . formatFileSize ( UInt ( fileSize ) ) ) )
}
return rows
}
private func recipientStatus ( outgoingMessage : TSOutgoingMessage , recipientId : String ) -> ( MessageRecipientState , String ) {
// L e g a c y m e s s a g e s d o n ' t h a v e " r e c i p i e n t r e a d " s t a t e o r " p e r - r e c i p i e n t d e l i v e r y " s t a t e ,
// s o w e f a l l b a c k t o ` T S O u t g o i n g M e s s a g e S t a t e ` w h i c h i s n o t p e r - r e c i p i e n t a n d t h e r e f o r e
// m i g h t b e m i s l e a d i n g .
let recipientReadMap = message . recipientReadMap
let recipientReadMap = outgoingM essage. recipientReadMap
if let readTimestamp = recipientReadMap [ recipientId ] {
assert ( message . messageState = = . sentToService )
return NSLocalizedString ( " MESSAGE_STATUS_READ " , comment : " message footer for read messages " ) . rtlSafeAppend ( " " , referenceView : self . view )
. rtlSafeAppend (
DateUtil . formatPastTimestampRelativeToNow ( readTimestamp . uint64Value ) , referenceView : self . view )
assert ( outgoingMessage . messageState = = . sentToService )
let statusMessage = NSLocalizedString ( " MESSAGE_STATUS_READ " , comment : " message footer for read messages " ) . rtlSafeAppend ( " " , referenceView : self . view )
. rtlSafeAppend (
DateUtil . formatPastTimestampRelativeToNow ( readTimestamp . uint64Value ) , referenceView : self . view )
return ( . read , statusMessage )
}
let recipientDeliveryMap = message . recipientDeliveryMap
let recipientDeliveryMap = outgoingM essage. recipientDeliveryMap
if let deliveryTimestamp = recipientDeliveryMap [ recipientId ] {
assert ( message . messageState = = . sentToService )
return NSLocalizedString ( " MESSAGE_STATUS_DELIVERED " ,
assert ( outgoingM essage. messageState = = . sentToService )
let statusMessage = NSLocalizedString ( " MESSAGE_STATUS_DELIVERED " ,
comment : " message status for message delivered to their recipient. " ) . rtlSafeAppend ( " " , referenceView : self . view )
. rtlSafeAppend (
DateUtil . formatPastTimestampRelativeToNow ( deliveryTimestamp . uint64Value ) , referenceView : self . view )
return ( . delivered , statusMessage )
}
if message . wasDelivered {
return NSLocalizedString ( " MESSAGE_STATUS_DELIVERED " ,
if outgoingM essage. wasDelivered {
let statusMessage = NSLocalizedString ( " MESSAGE_STATUS_DELIVERED " ,
comment : " message status for message delivered to their recipient. " )
return ( . delivered , statusMessage )
}
if message . messageState = = . unsent {
return NSLocalizedString ( " MESSAGE_STATUS_FAILED " , comment : " message footer for failed messages " )
} else if ( message . messageState = = . sentToService ||
message . wasSent ( toRecipient : recipientId ) ) {
return
if outgoingMessage . messageState = = . unsent {
let statusMessage = NSLocalizedString ( " MESSAGE_STATUS_FAILED " , comment : " message footer for failed messages " )
return ( . failed , statusMessage )
} else if outgoingMessage . messageState = = . sentToService ||
message . wasSent ( toRecipient : recipientId ) {
let statusMessage =
NSLocalizedString ( " MESSAGE_STATUS_SENT " ,
comment : " message footer for sent messages " )
return ( . sent , statusMessage )
} else if message . hasAttachments ( ) {
assert ( m essage. messageState = = . attemptingOut )
assert ( outgoingM essage. messageState = = . attemptingOut )
return NSLocalizedString ( " MESSAGE_STATUS_UPLOADING " ,
let statusMessage = NSLocalizedString ( " MESSAGE_STATUS_UPLOADING " ,
comment : " message footer while attachment is uploading " )
return ( . uploading , statusMessage )
} else {
assert ( m essage. messageState = = . attemptingOut )
assert ( outgoingM essage. messageState = = . attemptingOut )
return NSLocalizedString ( " MESSAGE_STATUS_SENDING " ,
let statusMessage = NSLocalizedString ( " MESSAGE_STATUS_SENDING " ,
comment : " message status while message is sending. " )
return ( . sending , statusMessage )
}
}
@ -453,7 +551,6 @@ class MessageMetadataViewController: OWSViewController {
}
internal func yapDatabaseModified ( notification : NSNotification ) {
// L o g g e r . i n f o ( " \ ( T A G ) i n \ ( # f u n c t i o n ) " )
AssertIsOnMainThread ( )
let notifications = self . databaseConnection . beginLongLivedReadTransaction ( )
@ -469,4 +566,27 @@ class MessageMetadataViewController: OWSViewController {
updateContent ( )
}
private func MessageRecipientStateName ( _ value : MessageRecipientState ) -> String {
switch value {
case . uploading :
return NSLocalizedString ( " MESSAGE_METADATA_VIEW_MESSAGE_STATUS_UPLOADING " ,
comment : " Status label for messages which are uploading. " )
case . sending :
return NSLocalizedString ( " MESSAGE_METADATA_VIEW_MESSAGE_STATUS_SENDING " ,
comment : " Status label for messages which are sending. " )
case . sent :
return NSLocalizedString ( " MESSAGE_METADATA_VIEW_MESSAGE_STATUS_SENT " ,
comment : " Status label for messages which are sent. " )
case . delivered :
return NSLocalizedString ( " MESSAGE_METADATA_VIEW_MESSAGE_STATUS_DELIVERED " ,
comment : " Status label for messages which are delivered. " )
case . read :
return NSLocalizedString ( " MESSAGE_METADATA_VIEW_MESSAGE_STATUS_READ " ,
comment : " Status label for messages which are read. " )
case . failed :
return NSLocalizedString ( " MESSAGE_METADATA_VIEW_MESSAGE_STATUS_FAILED " ,
comment : " Status label for messages which are failed. " )
}
}
}