@ -75,18 +75,18 @@ class ColorPicker: NSObject, ColorPickerViewDelegate {
@objc
let sheetViewController : SheetViewController
private let currentConversationColor : OWSConversationColor
@objc
init ( currentConversationColor : OWSConversationColor ) {
self . currentConversationColor = currentConversationColor
init ( thread : TSThread ) {
let colorName = thread . conversationColorName
let currentConversationColor = OWSConversationColor . conversationColorOrDefault ( colorName : colorName )
sheetViewController = SheetViewController ( )
super . init ( )
let colorPickerView = ColorPickerView ( )
let colorPickerView = ColorPickerView ( thread : thread )
colorPickerView . delegate = self
colorPickerView . select ( conversationColor : currentConversationColor )
sheetViewController . contentView . addSubview ( colorPickerView )
colorPickerView . autoPinEdgesToSuperviewEdges ( )
}
@ -105,21 +105,42 @@ protocol ColorPickerViewDelegate: class {
class ColorPickerView : UIView , ColorViewDelegate {
private let colorViews : [ ColorView ]
let conversationStyle : ConversationStyle
var outgoingMessageView = OWSMessageBubbleView ( forAutoLayout : ( ) )
var incomingMessageView = OWSMessageBubbleView ( forAutoLayout : ( ) )
weak var delegate : ColorPickerViewDelegate ?
override init ( frame : CGRect ) {
// T h i s i s m o s t l y a d e v e l o p e r c o n v e n i e n c e - O W S M e s s a g e C e l l a s s e r t s a t s o m e p o i n t
// t h a t t h e a v a i l a b l e m e t h o d w i d t h i s g r e a t e r t h a n 0 .
// W e u l t i m a t e l y u s e t h e w i d t h o f t h e p i c k e r v i e w w h i c h w i l l b e l a r g e r .
let kMinimumConversationWidth : CGFloat = 300
override var bounds : CGRect {
didSet {
updateMockConversationView ( )
}
}
let mockConversationView : UIView = UIView ( )
init ( thread : TSThread ) {
let allConversationColors = OWSConversationColor . conversationColorNames . map { OWSConversationColor . conversationColorOrDefault ( colorName : $0 ) }
self . colorViews = allConversationColors . map { ColorView ( conversationColor : $0 ) }
super . init ( frame : frame )
self . conversationStyle = ConversationStyle ( thread : thread )
super . init ( frame : . zero )
colorViews . forEach { $0 . delegate = self }
let headerView = self . buildHeaderView ( )
mockConversationView . layoutMargins = UIEdgeInsets ( top : 16 , left : 16 , bottom : 16 , right : 16 )
mockConversationView . backgroundColor = Theme . backgroundColor
self . updateMockConversationView ( )
let paletteView = self . buildPaletteView ( colorViews : colorViews )
let rowsStackView = UIStackView ( arrangedSubviews : [ headerView , paletteView ] )
let rowsStackView = UIStackView ( arrangedSubviews : [ headerView , mockConversationView, paletteView] )
rowsStackView . axis = . vertical
addSubview ( rowsStackView )
rowsStackView . autoPinEdgesToSuperviewEdges ( )
@ -134,6 +155,7 @@ class ColorPickerView: UIView, ColorViewDelegate {
func colorViewWasTapped ( _ colorView : ColorView ) {
self . select ( conversationColor : colorView . conversationColor )
self . delegate ? . colorPickerView ( self , didPickConversationColor : colorView . conversationColor )
updateMockConversationView ( )
}
fileprivate func select ( conversationColor selectedConversationColor : OWSConversationColor ) {
@ -166,9 +188,59 @@ class ColorPickerView: UIView, ColorViewDelegate {
return headerView
}
private func updateMockConversationView ( ) {
conversationStyle . viewWidth = max ( bounds . size . width , kMinimumConversationWidth )
mockConversationView . subviews . forEach { $0 . removeFromSuperview ( ) }
// o u t g o i n g
outgoingMessageView = OWSMessageBubbleView ( forAutoLayout : ( ) )
let outgoingItem = MockConversationViewItem ( )
let outgoingText = NSLocalizedString ( " COLOR_PICKER_DEMO_MESSAGE_1 " , comment : " The first of two messages demonstrating the chosen conversation color, by rendering this message in an outgoing message bubble. " )
outgoingItem . interaction = MockOutgoingMessage ( messageBody : outgoingText )
outgoingItem . displayableBodyText = DisplayableText . displayableText ( outgoingText )
outgoingItem . interactionType = . outgoingMessage
outgoingMessageView . viewItem = outgoingItem
outgoingMessageView . cellMediaCache = NSCache ( )
outgoingMessageView . conversationStyle = conversationStyle
outgoingMessageView . configureViews ( )
outgoingMessageView . loadContent ( )
let outgoingCell = UIView ( )
outgoingCell . addSubview ( outgoingMessageView )
outgoingMessageView . autoPinEdgesToSuperviewEdges ( with : . zero , excludingEdge : . leading )
let outgoingSize = outgoingMessageView . measureSize ( )
outgoingMessageView . autoSetDimensions ( to : outgoingSize )
// i n c o m i n g
incomingMessageView = OWSMessageBubbleView ( forAutoLayout : ( ) )
let incomingItem = MockConversationViewItem ( )
let incomingText = NSLocalizedString ( " COLOR_PICKER_DEMO_MESSAGE_2 " , comment : " The second of two messages demonstrating the chosen conversation color, by rendering this message in an incoming message bubble. " )
incomingItem . interaction = MockIncomingMessage ( messageBody : incomingText )
incomingItem . displayableBodyText = DisplayableText . displayableText ( incomingText )
incomingItem . interactionType = . incomingMessage
incomingMessageView . viewItem = incomingItem
incomingMessageView . cellMediaCache = NSCache ( )
incomingMessageView . conversationStyle = conversationStyle
incomingMessageView . configureViews ( )
incomingMessageView . loadContent ( )
let incomingCell = UIView ( )
incomingCell . addSubview ( incomingMessageView )
incomingMessageView . autoPinEdgesToSuperviewEdges ( with : . zero , excludingEdge : . trailing )
let incomingSize = incomingMessageView . measureSize ( )
incomingMessageView . autoSetDimensions ( to : incomingSize )
let messagesStackView = UIStackView ( arrangedSubviews : [ outgoingCell , incomingCell ] )
messagesStackView . axis = . vertical
messagesStackView . spacing = 12
mockConversationView . addSubview ( messagesStackView )
messagesStackView . autoPinEdgesToSuperviewMargins ( )
}
private func buildPaletteView ( colorViews : [ ColorView ] ) -> UIView {
let paletteView = UIView ( )
paletteView . layoutMargins = UIEdgeInsets ( top : 16 , left : 16 , bottom : 16 , right : 16 )
paletteView . layoutMargins = UIEdgeInsets ( top : 16 , left : 16 , bottom : 0 , right : 16 )
let kRowLength = 4
let rows : [ UIView ] = colorViews . chunked ( by : kRowLength ) . map { colorViewsInRow in
@ -178,10 +250,210 @@ class ColorPickerView: UIView, ColorViewDelegate {
}
let rowsStackView = UIStackView ( arrangedSubviews : rows )
rowsStackView . axis = . vertical
rowsStackView . spacing = ScaleFromIPhone5To7Plus ( 1 6 , 50 )
rowsStackView . spacing = ScaleFromIPhone5To7Plus ( 1 2 , 50 )
paletteView . addSubview ( rowsStackView )
rowsStackView . ows_autoPinToSuperviewMargins ( )
// n o - o p g e s t u r e t o k e e p t a p s f r o m d i s m i s s i n g S h e e t V i e w
paletteView . addGestureRecognizer ( UITapGestureRecognizer ( target : nil , action : nil ) )
return paletteView
}
}
// MARK: M o c k C l a s s e s f o r r e n d e r i n g d e m o c o n v e r s a t i o n
@objc
private class MockConversationViewItem : NSObject , ConversationViewItem {
var interaction : TSInteraction = TSMessage ( )
var interactionType : OWSInteractionType = OWSInteractionType . unknown
var quotedReply : OWSQuotedReplyModel ?
var isGroupThread : Bool = false
var hasBodyText : Bool = true
var isQuotedReply : Bool = false
var hasQuotedAttachment : Bool = false
var hasQuotedText : Bool = false
var hasCellHeader : Bool = false
var isExpiringMessage : Bool = false
var shouldShowDate : Bool = false
var shouldShowSenderAvatar : Bool = false
var senderName : NSAttributedString ?
var shouldHideFooter : Bool = false
var isFirstInCluster : Bool = true
var isLastInCluster : Bool = true
var unreadIndicator : OWSUnreadIndicator ?
var lastAudioMessageView : OWSAudioMessageView ?
var audioDurationSeconds : CGFloat = 0
var audioProgressSeconds : CGFloat = 0
var messageCellType : OWSMessageCellType = . textMessage
var displayableBodyText : DisplayableText ?
var attachmentStream : TSAttachmentStream ?
var attachmentPointer : TSAttachmentPointer ?
var mediaSize : CGSize = . zero
var displayableQuotedText : DisplayableText ?
var quotedAttachmentMimetype : String ?
var quotedRecipientId : String ?
var didCellMediaFailToLoad : Bool = false
var contactShare : ContactShareViewModel ?
var systemMessageText : String ?
var authorConversationColorName : String ?
var hasBodyTextActionContent : Bool = false
var hasMediaActionContent : Bool = false
override init ( ) {
super . init ( )
}
func dequeueCell ( for collectionView : UICollectionView , indexPath : IndexPath ) -> ConversationViewCell {
owsFailDebug ( " unexpected invocation " )
return ConversationViewCell ( forAutoLayout : ( ) )
}
func replace ( _ interaction : TSInteraction , transaction : YapDatabaseReadTransaction ) {
owsFailDebug ( " unexpected invocation " )
return
}
func clearCachedLayoutState ( ) {
owsFailDebug ( " unexpected invocation " )
return
}
func copyMediaAction ( ) {
owsFailDebug ( " unexpected invocation " )
return
}
func copyTextAction ( ) {
owsFailDebug ( " unexpected invocation " )
return
}
func shareMediaAction ( ) {
owsFailDebug ( " unexpected invocation " )
return
}
func shareTextAction ( ) {
owsFailDebug ( " unexpected invocation " )
return
}
func saveMediaAction ( ) {
owsFailDebug ( " unexpected invocation " )
return
}
func deleteAction ( ) {
owsFailDebug ( " unexpected invocation " )
return
}
func canSaveMedia ( ) -> Bool {
owsFailDebug ( " unexpected invocation " )
return false
}
func audioPlaybackState ( ) -> AudioPlaybackState {
owsFailDebug ( " unexpected invocation " )
return AudioPlaybackState . paused
}
func setAudioPlaybackState ( _ state : AudioPlaybackState ) {
owsFailDebug ( " unexpected invocation " )
return
}
func setAudioProgress ( _ progress : CGFloat , duration : CGFloat ) {
owsFailDebug ( " unexpected invocation " )
return
}
func cellSize ( ) -> CGSize {
owsFailDebug ( " unexpected invocation " )
return CGSize . zero
}
func vSpacing ( withPreviousLayoutItem previousLayoutItem : ConversationViewLayoutItem ) -> CGFloat {
owsFailDebug ( " unexpected invocation " )
return 2
}
}
private class MockIncomingMessage : TSIncomingMessage {
init ( messageBody : String ) {
super . init ( incomingMessageWithTimestamp : NSDate . ows_millisecondTimeStamp ( ) ,
in : TSThread ( ) ,
authorId : " +fake-id " ,
sourceDeviceId : 1 ,
messageBody : messageBody ,
attachmentIds : [ ] ,
expiresInSeconds : 0 ,
quotedMessage : nil ,
contactShare : nil )
}
required init ( coder : NSCoder ) {
fatalError ( " init(coder:) has not been implemented " )
}
required init ( dictionary dictionaryValue : [ AnyHashable : Any ] ! ) throws {
fatalError ( " init(dictionary:) has not been implemented " )
}
override func save ( with transaction : YapDatabaseReadWriteTransaction ) {
// n o - o p
owsFailDebug ( " shouldn't save mock message " )
}
}
private class MockOutgoingMessage : TSOutgoingMessage {
init ( messageBody : String ) {
super . init ( outgoingMessageWithTimestamp : NSDate . ows_millisecondTimeStamp ( ) ,
in : nil ,
messageBody : messageBody ,
attachmentIds : [ ] ,
expiresInSeconds : 0 ,
expireStartedAt : 0 ,
isVoiceMessage : false ,
groupMetaMessage : . unspecified ,
quotedMessage : nil ,
contactShare : nil )
}
required init ? ( coder : NSCoder ) {
fatalError ( " init(coder:) has not been implemented " )
}
required init ( dictionary dictionaryValue : [ AnyHashable : Any ] ! ) throws {
fatalError ( " init(dictionary:) has not been implemented " )
}
override func save ( with transaction : YapDatabaseReadWriteTransaction ) {
// n o - o p
owsFailDebug ( " shouldn't save mock message " )
}
class MockOutgoingMessageRecipientState : TSOutgoingMessageRecipientState {
override var state : OWSOutgoingMessageRecipientState {
return OWSOutgoingMessageRecipientState . sent
}
override var deliveryTimestamp : NSNumber ? {
return NSNumber ( value : NSDate . ows_millisecondTimeStamp ( ) )
}
override var readTimestamp : NSNumber ? {
return NSNumber ( value : NSDate . ows_millisecondTimeStamp ( ) )
}
}
override func readRecipientIds ( ) -> [ String ] {
// m a k e s m e s s a g e a p p e a r a s r e a d
return [ " fake-non-empty-id " ]
}
override func recipientState ( forRecipientId recipientId : String ) -> TSOutgoingMessageRecipientState ? {
return MockOutgoingMessageRecipientState ( )
}
}