@ -470,7 +470,14 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
documentView = nil
bodyTappableLabel = nil
// H a n d l e t h e d e l e t e d s t a t e f i r s t ( i t ' s m u c h s i m p l e r t h a n t h e o t h e r s )
// / T h e s e v a r i a n t s h a v e n o c o n t e n t s o d o n o t h i n g a f t e r c l e a n i n g u p o l d s t a t e
guard
cellViewModel . cellType != . typingIndicator &&
cellViewModel . cellType != . dateHeader &&
cellViewModel . cellType != . unreadMarker
else { return }
// / H a n d l e t h e d e l e t e d s t a t e f i r s t ( i t ' s m u c h s i m p l e r t h a n t h e o t h e r s )
guard ! cellViewModel . variant . isDeletedMessage else {
let inset : CGFloat = 12
let deletedMessageView : DeletedMessageView = DeletedMessageView (
@ -484,125 +491,223 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
return
}
// I f i t ' s a n i n c o m i n g m e d i a m e s s a g e a n d t h e t h r e a d i s n ' t t r u s t e d t h e n s h o w t h e p l a c e h o l d e r v i e w
if cellViewModel . cellType != . textOnlyMessage && cellViewModel . variant = = . standardIncoming && ! cellViewModel . threadIsTrusted {
let mediaPlaceholderView = MediaPlaceholderView ( cellViewModel : cellViewModel , textColor : bodyLabelTextColor )
bubbleView . addSubview ( mediaPlaceholderView )
mediaPlaceholderView . pin ( to : bubbleView )
snContentView . addArrangedSubview ( bubbleBackgroundView )
return
}
switch cellViewModel . cellType {
case . typingIndicator , . dateHeader , . unreadMarker : break
// / T h e ` t e x t O n l y M e s s a g e ` v a r i a n t h a s a s l i g h t l y d i f f e r e n t b e h a v i o u r ( a s i t ' s t h e o n l y v a r i a n t w h i c h s u p p o r t s l i n k p r e v i e w s )
// / s o w e h a n d l e t h a t c a s e f i r s t
// FIXME: W e s h o u l d s u p p o r t r e n d e r i n g l i n k p r e v i e w s a l o n g s i d e t h e o t h e r v a r i a n t s ( b i g g e r r e f a c t o r )
guard cellViewModel . cellType != . textOnlyMessage else {
let inset : CGFloat = 12
let maxWidth : CGFloat = ( VisibleMessageCell . getMaxWidth ( for : cellViewModel ) - 2 * inset )
case . textOnlyMessage :
let inset : CGFloat = 12
let maxWidth : CGFloat = ( VisibleMessageCell . getMaxWidth ( for : cellViewModel ) - 2 * inset )
if let linkPreview : LinkPreview = cellViewModel . linkPreview {
switch linkPreview . variant {
case . standard :
let linkPreviewView : LinkPreviewView = LinkPreviewView ( maxWidth : maxWidth )
linkPreviewView . update (
with : LinkPreview . SentState (
linkPreview : linkPreview ,
imageAttachment : cellViewModel . linkPreviewAttachment ,
using : dependencies
) ,
isOutgoing : cellViewModel . variant . isOutgoing ,
delegate : self ,
cellViewModel : cellViewModel ,
bodyLabelTextColor : bodyLabelTextColor ,
lastSearchText : lastSearchText ,
if let linkPreview : LinkPreview = cellViewModel . linkPreview {
switch linkPreview . variant {
case . standard :
let linkPreviewView : LinkPreviewView = LinkPreviewView ( maxWidth : maxWidth )
linkPreviewView . update (
with : LinkPreview . SentState (
linkPreview : linkPreview ,
imageAttachment : cellViewModel . linkPreviewAttachment ,
using : dependencies
)
self . linkPreviewView = linkPreviewView
bubbleView . addSubview ( linkPreviewView )
linkPreviewView . pin ( to : bubbleView , withInset : 0 )
snContentView . addArrangedSubview ( bubbleBackgroundView )
self . bodyTappableLabel = linkPreviewView . bodyTappableLabel
case . openGroupInvitation :
let openGroupInvitationView : OpenGroupInvitationView = OpenGroupInvitationView (
name : ( linkPreview . title ? ? " " ) ,
url : linkPreview . url ,
textColor : bodyLabelTextColor ,
isOutgoing : cellViewModel . variant . isOutgoing
)
openGroupInvitationView . isAccessibilityElement = true
openGroupInvitationView . accessibilityIdentifier = " Community invitation "
openGroupInvitationView . accessibilityLabel = cellViewModel . linkPreview ? . title
bubbleView . addSubview ( openGroupInvitationView )
bubbleView . pin ( to : openGroupInvitationView )
snContentView . addArrangedSubview ( bubbleBackgroundView )
}
}
else {
// S t a c k v i e w
let stackView = UIStackView ( arrangedSubviews : [ ] )
stackView . axis = . vertical
stackView . spacing = 2
// Q u o t e v i e w
if let quote : Quote = cellViewModel . quote {
let hInset : CGFloat = 2
let quoteView : QuoteView = QuoteView (
for : . regular ,
authorId : quote . authorId ,
quotedText : quote . body ,
threadVariant : cellViewModel . threadVariant ,
currentUserSessionId : cellViewModel . currentUserSessionId ,
currentUserBlinded15SessionId : cellViewModel . currentUserBlinded15SessionId ,
currentUserBlinded25SessionId : cellViewModel . currentUserBlinded25SessionId ,
direction : ( cellViewModel . variant . isOutgoing ? . outgoing : . incoming ) ,
attachment : cellViewModel . quoteAttachment ,
) ,
isOutgoing : cellViewModel . variant . isOutgoing ,
delegate : self ,
cellViewModel : cellViewModel ,
bodyLabelTextColor : bodyLabelTextColor ,
lastSearchText : lastSearchText ,
using : dependencies
)
self . quoteView = quoteView
let quoteViewContainer = UIView ( wrapping : quoteView , withInsets : UIEdgeInsets ( top : 0 , leading : hInset , bottom : 0 , trailing : hInset ) )
stackView . addArrangedSubview ( quoteViewContainer )
}
// B o d y t e x t v i e w
let bodyTappableLabel = VisibleMessageCell . getBodyTappableLabel (
for : cellViewModel ,
with : maxWidth ,
textColor : bodyLabelTextColor ,
searchText : lastSearchText ,
delegate : self ,
self . linkPreviewView = linkPreviewView
bubbleView . addSubview ( linkPreviewView )
linkPreviewView . pin ( to : bubbleView , withInset : 0 )
snContentView . addArrangedSubview ( bubbleBackgroundView )
self . bodyTappableLabel = linkPreviewView . bodyTappableLabel
case . openGroupInvitation :
let openGroupInvitationView : OpenGroupInvitationView = OpenGroupInvitationView (
name : ( linkPreview . title ? ? " " ) ,
url : linkPreview . url ,
textColor : bodyLabelTextColor ,
isOutgoing : cellViewModel . variant . isOutgoing
)
openGroupInvitationView . isAccessibilityElement = true
openGroupInvitationView . accessibilityIdentifier = " Community invitation "
openGroupInvitationView . accessibilityLabel = cellViewModel . linkPreview ? . title
bubbleView . addSubview ( openGroupInvitationView )
bubbleView . pin ( to : openGroupInvitationView )
snContentView . addArrangedSubview ( bubbleBackgroundView )
}
}
else {
// S t a c k v i e w
let stackView = UIStackView ( arrangedSubviews : [ ] )
stackView . axis = . vertical
stackView . spacing = 2
// Q u o t e v i e w
if let quote : Quote = cellViewModel . quote {
let hInset : CGFloat = 2
let quoteView : QuoteView = QuoteView (
for : . regular ,
authorId : quote . authorId ,
quotedText : quote . body ,
threadVariant : cellViewModel . threadVariant ,
currentUserSessionId : cellViewModel . currentUserSessionId ,
currentUserBlinded15SessionId : cellViewModel . currentUserBlinded15SessionId ,
currentUserBlinded25SessionId : cellViewModel . currentUserBlinded25SessionId ,
direction : ( cellViewModel . variant . isOutgoing ? . outgoing : . incoming ) ,
attachment : cellViewModel . quoteAttachment ,
using : dependencies
)
self . bodyTappableLabel = bodyTappableLabel
stackView . addArrangedSubview ( bodyTappableLabel )
// C o n s t r a i n t s
bubbleView . addSubview ( stackView )
stackView . pin ( to : bubbleView , withInset : inset )
stackView . widthAnchor . constraint ( lessThanOrEqualToConstant : maxWidth ) . isActive = true
snContentView . addArrangedSubview ( bubbleBackgroundView )
self . quoteView = quoteView
let quoteViewContainer = UIView ( wrapping : quoteView , withInsets : UIEdgeInsets ( top : 0 , leading : hInset , bottom : 0 , trailing : hInset ) )
stackView . addArrangedSubview ( quoteViewContainer )
}
case . mediaMessage :
// B o d y t e x t v i e w
if let body : String = cellViewModel . body , ! body . isEmpty {
let inset : CGFloat = 12
let maxWidth : CGFloat = ( VisibleMessageCell . getMaxWidth ( for : cellViewModel ) - 2 * inset )
let bodyTappableLabel = VisibleMessageCell . getBodyTappableLabel (
for : cellViewModel ,
with : maxWidth ,
textColor : bodyLabelTextColor ,
searchText : lastSearchText ,
delegate : self ,
using : dependencies
)
self . bodyTappableLabel = bodyTappableLabel
bubbleView . addSubview ( bodyTappableLabel )
bodyTappableLabel . pin ( to : bubbleView , withInset : inset )
let bodyTappableLabel = VisibleMessageCell . getBodyTappableLabel (
for : cellViewModel ,
with : maxWidth ,
textColor : bodyLabelTextColor ,
searchText : lastSearchText ,
delegate : self ,
using : dependencies
)
self . bodyTappableLabel = bodyTappableLabel
stackView . addArrangedSubview ( bodyTappableLabel )
// C o n s t r a i n t s
bubbleView . addSubview ( stackView )
stackView . pin ( to : bubbleView , withInset : inset )
stackView . widthAnchor . constraint ( lessThanOrEqualToConstant : maxWidth ) . isActive = true
snContentView . addArrangedSubview ( bubbleBackgroundView )
}
return
}
func addViewWrappingInBubbleIfNeeded ( _ targetView : UIView ) {
switch snContentView . arrangedSubviews . count {
case 0 :
bubbleView . addSubview ( targetView )
targetView . pin ( to : bubbleView )
snContentView . addArrangedSubview ( bubbleBackgroundView )
}
default :
// / S i n c e w e a l r e a d y h a v e c o n t e n t w e n e e d t o w r a p t h e ` t a r g e t V i e w ` i n i t ' s o w n
// / ` b u b b l e V i e w ` ( a s i t ' s l i k e l y t h e e x i s t i n g c o n t e n t i s q u o t e c o n t e n t )
let extraBubbleView : UIView = UIView ( )
extraBubbleView . clipsToBounds = true
extraBubbleView . themeBackgroundColor = ( cellViewModel . variant . isIncoming ?
. messageBubble_incomingBackground :
. messageBubble_outgoingBackground
)
extraBubbleView . layer . cornerRadius = VisibleMessageCell . largeCornerRadius
extraBubbleView . layer . maskedCorners = getCornerMask ( from : . allCorners )
extraBubbleView . set ( . width , greaterThanOrEqualTo : VisibleMessageCell . largeCornerRadius * 2 )
extraBubbleView . addSubview ( targetView )
targetView . pin ( to : extraBubbleView )
snContentView . addArrangedSubview ( extraBubbleView )
}
}
// / A d d a n y q u o t e & b o d y i f p r e s e n t
let inset : CGFloat = 12
let maxWidth : CGFloat = ( VisibleMessageCell . getMaxWidth ( for : cellViewModel ) - 2 * inset )
switch ( cellViewModel . quote , cellViewModel . body ) {
// / B o t h q u o t e a n d b o d y
case ( . some ( let quote ) , . some ( let body ) ) where ! body . isEmpty :
// S t a c k v i e w
let stackView = UIStackView ( arrangedSubviews : [ ] )
stackView . axis = . vertical
stackView . spacing = 2
// Q u o t e v i e w
let hInset : CGFloat = 2
let quoteView : QuoteView = QuoteView (
for : . regular ,
authorId : quote . authorId ,
quotedText : quote . body ,
threadVariant : cellViewModel . threadVariant ,
currentUserSessionId : cellViewModel . currentUserSessionId ,
currentUserBlinded15SessionId : cellViewModel . currentUserBlinded15SessionId ,
currentUserBlinded25SessionId : cellViewModel . currentUserBlinded25SessionId ,
direction : ( cellViewModel . variant . isOutgoing ? . outgoing : . incoming ) ,
attachment : cellViewModel . quoteAttachment ,
using : dependencies
)
self . quoteView = quoteView
let quoteViewContainer = UIView ( wrapping : quoteView , withInsets : UIEdgeInsets ( top : 0 , leading : hInset , bottom : 0 , trailing : hInset ) )
stackView . addArrangedSubview ( quoteViewContainer )
// B o d y
let bodyTappableLabel = VisibleMessageCell . getBodyTappableLabel (
for : cellViewModel ,
with : maxWidth ,
textColor : bodyLabelTextColor ,
searchText : lastSearchText ,
delegate : self ,
using : dependencies
)
self . bodyTappableLabel = bodyTappableLabel
stackView . addArrangedSubview ( bodyTappableLabel )
// C o n s t r a i n t s
bubbleView . addSubview ( stackView )
stackView . pin ( to : bubbleView , withInset : inset )
stackView . widthAnchor . constraint ( lessThanOrEqualToConstant : maxWidth ) . isActive = true
snContentView . addArrangedSubview ( bubbleBackgroundView )
// / J u s t b o d y
case ( _ , . some ( let body ) ) where ! body . isEmpty :
let bodyTappableLabel = VisibleMessageCell . getBodyTappableLabel (
for : cellViewModel ,
with : maxWidth ,
textColor : bodyLabelTextColor ,
searchText : lastSearchText ,
delegate : self ,
using : dependencies
)
self . bodyTappableLabel = bodyTappableLabel
bubbleView . addSubview ( bodyTappableLabel )
bodyTappableLabel . pin ( to : bubbleView , withInset : inset )
snContentView . addArrangedSubview ( bubbleBackgroundView )
// / J u s t q u o t e
case ( . some ( let quote ) , _ ) :
let quoteView : QuoteView = QuoteView (
for : . regular ,
authorId : quote . authorId ,
quotedText : quote . body ,
threadVariant : cellViewModel . threadVariant ,
currentUserSessionId : cellViewModel . currentUserSessionId ,
currentUserBlinded15SessionId : cellViewModel . currentUserBlinded15SessionId ,
currentUserBlinded25SessionId : cellViewModel . currentUserBlinded25SessionId ,
direction : ( cellViewModel . variant . isOutgoing ? . outgoing : . incoming ) ,
attachment : cellViewModel . quoteAttachment ,
using : dependencies
)
self . quoteView = quoteView
bubbleView . addSubview ( quoteView )
quoteView . pin ( to : bubbleView , withInset : inset )
snContentView . addArrangedSubview ( bubbleBackgroundView )
// / N e i t h e r q u o t e o r b o d y
default : break
}
// / I f i t ' s a n i n c o m i n g m e d i a m e s s a g e a n d t h e t h r e a d i s n ' t t r u s t e d t h e n s h o w t h e p l a c e h o l d e r v i e w
if cellViewModel . variant = = . standardIncoming && ! cellViewModel . threadIsTrusted {
let mediaPlaceholderView = MediaPlaceholderView ( cellViewModel : cellViewModel , textColor : bodyLabelTextColor )
addViewWrappingInBubbleIfNeeded ( mediaPlaceholderView )
return
}
switch cellViewModel . cellType {
case . typingIndicator , . dateHeader , . unreadMarker , . textOnlyMessage : break
case . mediaMessage :
// A l b u m v i e w
let maxMessageWidth : CGFloat = VisibleMessageCell . getMaxWidth ( for : cellViewModel )
let albumView = MediaAlbumView (
@ -637,52 +742,16 @@ final class VisibleMessageCell: MessageCell, TappableLabelDelegate {
playbackRate : ( playbackInfo ? . playbackRate ? ? 1 ) ,
oldPlaybackRate : ( playbackInfo ? . oldPlaybackRate ? ? 1 )
)
bubbleView . addSubview ( voiceMessageView )
voiceMessageView . pin ( to : bubbleView )
snContentView . addArrangedSubview ( bubbleBackgroundView )
self . voiceMessageView = voiceMessageView
addViewWrappingInBubbleIfNeeded ( voiceMessageView )
case . audio , . genericAttachment :
guard let attachment : Attachment = cellViewModel . attachments ? . first else { preconditionFailure ( ) }
let inset : CGFloat = 12
let maxWidth = ( VisibleMessageCell . getMaxWidth ( for : cellViewModel ) - 2 * inset )
// S t a c k v i e w
let stackView = UIStackView ( arrangedSubviews : [ ] )
stackView . axis = . vertical
stackView . spacing = Values . smallSpacing
// D o c u m e n t v i e w
let documentView = DocumentView ( attachment : attachment , textColor : bodyLabelTextColor )
self . documentView = documentView
stackView . addArrangedSubview ( documentView )
// B o d y t e x t v i e w
if let body : String = cellViewModel . body , ! body . isEmpty { // d e l e g a t e s h o u l d a l w a y s b e s e t a t t h i s p o i n t
let bodyContainerView : UIView = UIView ( )
let bodyTappableLabel = VisibleMessageCell . getBodyTappableLabel (
for : cellViewModel ,
with : maxWidth ,
textColor : bodyLabelTextColor ,
searchText : lastSearchText ,
delegate : self ,
using : dependencies
)
self . bodyTappableLabel = bodyTappableLabel
bodyContainerView . addSubview ( bodyTappableLabel )
bodyTappableLabel . pin ( . top , to : . top , of : bodyContainerView )
bodyTappableLabel . pin ( . leading , to : . leading , of : bodyContainerView , withInset : 12 )
bodyTappableLabel . pin ( . trailing , to : . trailing , of : bodyContainerView , withInset : - 12 )
bodyTappableLabel . pin ( . bottom , to : . bottom , of : bodyContainerView , withInset : - 12 )
stackView . addArrangedSubview ( bodyContainerView )
}
bubbleView . addSubview ( stackView )
stackView . pin ( to : bubbleView )
snContentView . addArrangedSubview ( bubbleBackgroundView )
addViewWrappingInBubbleIfNeeded ( documentView )
}
}