parent
7d1cf700be
commit
16df4f589e
|
@ -411,6 +411,7 @@
|
|||
45FBC5C81DF8575700E9B410 /* CallKitCallManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FBC59A1DF8575700E9B410 /* CallKitCallManager.swift */; };
|
||||
45FBC5D11DF8592E00E9B410 /* SignalCall.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45FBC5D01DF8592E00E9B410 /* SignalCall.swift */; };
|
||||
4AC4EA13C8A444455DAB351F /* Pods_SignalMessaging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 264242150E87D10A357DB07B /* Pods_SignalMessaging.framework */; };
|
||||
4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */; };
|
||||
4C20B2B720CA0034001BAC90 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */; };
|
||||
4C20B2B920CA10DE001BAC90 /* ConversationSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */; };
|
||||
70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; };
|
||||
|
@ -1066,6 +1067,7 @@
|
|||
45FBC59A1DF8575700E9B410 /* CallKitCallManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallKitCallManager.swift; sourceTree = "<group>"; };
|
||||
45FBC5D01DF8592E00E9B410 /* SignalCall.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignalCall.swift; sourceTree = "<group>"; };
|
||||
45FDA43420A4D22700396358 /* OWSNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSNavigationBar.swift; sourceTree = "<group>"; };
|
||||
4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerViewController.swift; sourceTree = "<group>"; };
|
||||
4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationSearchViewController.swift; sourceTree = "<group>"; };
|
||||
69349DE607F5BA6036C9AC60 /* Pods-SignalShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
70377AAA1918450100CAF501 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
|
||||
|
@ -1690,6 +1692,7 @@
|
|||
340FC897204DAC8D007AEB0F /* ThreadSettings */,
|
||||
34D1F0BE1F8EC1760066283D /* Utils */,
|
||||
452B998F20A34B6B006F2F9E /* AddContactShareToExistingContactViewController.swift */,
|
||||
4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */,
|
||||
);
|
||||
path = ViewControllers;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3272,6 +3275,7 @@
|
|||
348BB25D20A0C5530047AEC2 /* ContactShareViewHelper.swift in Sources */,
|
||||
34B3F8801E8DF1700035BE1A /* InviteFlow.swift in Sources */,
|
||||
457C87B82032645C008D52D6 /* DebugUINotifications.swift in Sources */,
|
||||
4C13C9F620E57BA30089A98B /* ColorPickerViewController.swift in Sources */,
|
||||
340FC8D0205BF2FA007AEB0F /* OWSBackupIO.m in Sources */,
|
||||
458E38371D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m in Sources */,
|
||||
4517642B1DE939FD00EDB8B9 /* ContactCell.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
let colorSwatchHeight: CGFloat = 60
|
||||
|
||||
class ColorView: UIView {
|
||||
let color: UIColor
|
||||
let swatchView: UIView
|
||||
|
||||
required init(color: UIColor) {
|
||||
self.color = color
|
||||
self.swatchView = UIView()
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
swatchView.backgroundColor = color
|
||||
|
||||
self.swatchView.layer.cornerRadius = colorSwatchHeight / 2
|
||||
|
||||
self.addSubview(swatchView)
|
||||
|
||||
swatchView.autoVCenterInSuperview()
|
||||
swatchView.autoSetDimension(.height, toSize: colorSwatchHeight)
|
||||
swatchView.autoPinEdge(toSuperviewMargin: .top, relation: .greaterThanOrEqual)
|
||||
swatchView.autoPinEdge(toSuperviewMargin: .bottom, relation: .greaterThanOrEqual)
|
||||
swatchView.autoPinLeadingToSuperviewMargin()
|
||||
swatchView.autoPinTrailingToSuperviewMargin()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
protocol ColorPickerDelegate: class {
|
||||
func colorPickerDidCancel(_ colorPicker: ColorPickerViewController)
|
||||
func colorPicker(_ colorPicker: ColorPickerViewController, didPickColorName colorName: String)
|
||||
}
|
||||
|
||||
@objc
|
||||
class ColorPickerViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
|
||||
|
||||
private let pickerView: UIPickerView
|
||||
private let thread: TSThread
|
||||
private let colors: [UIColor]
|
||||
|
||||
@objc public weak var delegate: ColorPickerDelegate?
|
||||
|
||||
@objc
|
||||
required init(thread: TSThread) {
|
||||
self.thread = thread
|
||||
self.pickerView = UIPickerView()
|
||||
self.colors = UIColor.ows_conversationColors
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
self.navigationItem.leftBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(didTapCancel))
|
||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(didTapSave))
|
||||
|
||||
pickerView.dataSource = self
|
||||
pickerView.delegate = self
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
override func loadView() {
|
||||
self.view = UIView()
|
||||
view.backgroundColor = .white
|
||||
view.addSubview(pickerView)
|
||||
|
||||
pickerView.autoVCenterInSuperview()
|
||||
pickerView.autoPinLeadingToSuperviewMargin()
|
||||
pickerView.autoPinTrailingToSuperviewMargin()
|
||||
}
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
if let colorName = thread.conversationColorName,
|
||||
let currentColor = UIColor.ows_conversationColor(colorName: colorName),
|
||||
let index = colors.index(of: currentColor) {
|
||||
pickerView.selectRow(index, inComponent: 0, animated: false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: UIPickerViewDataSource
|
||||
|
||||
public func numberOfComponents(in pickerView: UIPickerView) -> Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
public func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
|
||||
return self.colors.count
|
||||
}
|
||||
|
||||
// MARK: UIPickerViewDelegate
|
||||
|
||||
public func pickerView(_ pickerView: UIPickerView, rowHeightForComponent component: Int) -> CGFloat {
|
||||
let vMargin: CGFloat = 2
|
||||
return colorSwatchHeight + vMargin * 2
|
||||
}
|
||||
|
||||
public func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
|
||||
guard let color = colors[safe: row] else {
|
||||
owsFail("\(logTag) in \(#function) color was unexpectedly nil")
|
||||
return ColorView(color: .white)
|
||||
}
|
||||
|
||||
return ColorView(color: color)
|
||||
}
|
||||
|
||||
// MARK: Actions
|
||||
|
||||
var currentColor: UIColor {
|
||||
let index = pickerView.selectedRow(inComponent: 0)
|
||||
guard let color = self.colors[safe: index] else {
|
||||
owsFail("\(self.logTag) in \(#function) index was unexpectedly nil")
|
||||
return UIColor.white
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
@objc
|
||||
public func didTapSave() {
|
||||
guard let colorName = UIColor.ows_conversationColorName(color: self.currentColor) else {
|
||||
owsFail("\(self.logTag) in \(#function) colorName was unexpectedly nil")
|
||||
self.delegate?.colorPickerDidCancel(self)
|
||||
return
|
||||
}
|
||||
|
||||
self.delegate?.colorPicker(self, didPickColorName: colorName)
|
||||
}
|
||||
|
||||
@objc
|
||||
public func didTapCancel() {
|
||||
self.delegate?.colorPickerDidCancel(self)
|
||||
}
|
||||
}
|
|
@ -277,6 +277,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSQuotedMessageView *quotedMessageView =
|
||||
[OWSQuotedMessageView quotedMessageViewForConversation:self.viewItem.quotedReply
|
||||
displayableQuotedText:displayableQuotedText
|
||||
conversationStyle:self.conversationStyle
|
||||
isOutgoing:isOutgoing];
|
||||
quotedMessageView.delegate = self;
|
||||
|
||||
|
@ -494,7 +495,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
TSMessage *message = (TSMessage *)self.viewItem.interaction;
|
||||
return [ConversationStyle bubbleColorWithMessage:message];
|
||||
return [self.conversationStyle bubbleColorWithMessage:message];
|
||||
}
|
||||
|
||||
- (BOOL)hasBodyMediaWithThumbnail
|
||||
|
@ -1084,6 +1085,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSQuotedMessageView *quotedMessageView =
|
||||
[OWSQuotedMessageView quotedMessageViewForConversation:self.viewItem.quotedReply
|
||||
displayableQuotedText:displayableQuotedText
|
||||
conversationStyle:self.conversationStyle
|
||||
isOutgoing:isOutgoing];
|
||||
CGSize result = [quotedMessageView sizeForMaxWidth:self.conversationStyle.maxMessageWidth];
|
||||
return [NSValue valueWithCGSize:CGSizeCeil(result)];
|
||||
|
@ -1214,7 +1216,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAssert([self.viewItem.interaction isKindOfClass:[TSMessage class]]);
|
||||
|
||||
TSMessage *message = (TSMessage *)self.viewItem.interaction;
|
||||
return [ConversationStyle bubbleTextColorWithMessage:message];
|
||||
return [self.conversationStyle bubbleTextColorWithMessage:message];
|
||||
}
|
||||
|
||||
- (BOOL)isMediaBeingSent
|
||||
|
|
|
@ -296,6 +296,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
TSIncomingMessage *incomingMessage = (TSIncomingMessage *)self.viewItem.interaction;
|
||||
OWSAvatarBuilder *avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithSignalId:incomingMessage.authorId
|
||||
color:self.conversationStyle.primaryColor
|
||||
diameter:self.avatarSize
|
||||
contactsManager:contactsManager];
|
||||
self.avatarView.image = [avatarBuilder build];
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ConversationStyle;
|
||||
@class DisplayableText;
|
||||
@class OWSBubbleShapeView;
|
||||
@class OWSQuotedReplyModel;
|
||||
|
@ -33,10 +34,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
// Factory method for "message bubble" views.
|
||||
+ (OWSQuotedMessageView *)quotedMessageViewForConversation:(OWSQuotedReplyModel *)quotedMessage
|
||||
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText
|
||||
conversationStyle:(ConversationStyle *)conversationStyle
|
||||
isOutgoing:(BOOL)isOutgoing;
|
||||
|
||||
// Factory method for "message compose" views.
|
||||
+ (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage;
|
||||
+ (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage
|
||||
conversationStyle:(ConversationStyle *)conversationStyle;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@property (nonatomic, readonly) OWSQuotedReplyModel *quotedMessage;
|
||||
@property (nonatomic, nullable, readonly) DisplayableText *displayableQuotedText;
|
||||
@property (nonatomic, readonly) ConversationStyle *conversationStyle;
|
||||
|
||||
@property (nonatomic, nullable) OWSBubbleShapeView *boundsStrokeView;
|
||||
@property (nonatomic, readonly) BOOL isForPreview;
|
||||
|
@ -37,17 +38,20 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
+ (OWSQuotedMessageView *)quotedMessageViewForConversation:(OWSQuotedReplyModel *)quotedMessage
|
||||
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText
|
||||
conversationStyle:(ConversationStyle *)conversationStyle
|
||||
isOutgoing:(BOOL)isOutgoing
|
||||
{
|
||||
OWSAssert(quotedMessage);
|
||||
|
||||
return [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage
|
||||
displayableQuotedText:displayableQuotedText
|
||||
conversationStyle:conversationStyle
|
||||
isForPreview:NO
|
||||
isOutgoing:isOutgoing];
|
||||
}
|
||||
|
||||
+ (OWSQuotedMessageView *)quotedMessageViewForPreview:(OWSQuotedReplyModel *)quotedMessage
|
||||
conversationStyle:(ConversationStyle *)conversationStyle
|
||||
{
|
||||
OWSAssert(quotedMessage);
|
||||
|
||||
|
@ -58,6 +62,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
OWSQuotedMessageView *instance = [[OWSQuotedMessageView alloc] initWithQuotedMessage:quotedMessage
|
||||
displayableQuotedText:displayableQuotedText
|
||||
conversationStyle:conversationStyle
|
||||
isForPreview:YES
|
||||
isOutgoing:YES];
|
||||
[instance createContents];
|
||||
|
@ -66,6 +71,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (instancetype)initWithQuotedMessage:(OWSQuotedReplyModel *)quotedMessage
|
||||
displayableQuotedText:(nullable DisplayableText *)displayableQuotedText
|
||||
conversationStyle:(ConversationStyle *)conversationStyle
|
||||
isForPreview:(BOOL)isForPreview
|
||||
isOutgoing:(BOOL)isOutgoing
|
||||
{
|
||||
|
@ -80,6 +86,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
_quotedMessage = quotedMessage;
|
||||
_displayableQuotedText = displayableQuotedText;
|
||||
_isForPreview = isForPreview;
|
||||
_conversationStyle = conversationStyle;
|
||||
_isOutgoing = isOutgoing;
|
||||
|
||||
_quotedAuthorLabel = [UILabel new];
|
||||
|
@ -104,7 +111,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
- (UIColor *)highlightColor
|
||||
{
|
||||
BOOL isQuotingSelf = [NSObject isNullableObject:self.quotedMessage.authorId equalTo:TSAccountManager.localNumber];
|
||||
return (isQuotingSelf ? ConversationStyle.bubbleColorOutgoingSent : [UIColor colorWithRGBHex:0xB5B5B5]);
|
||||
return (isQuotingSelf ? self.conversationStyle.bubbleColorOutgoingSent : [UIColor colorWithRGBHex:0xB5B5B5]);
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
@ -120,7 +127,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
self.clipsToBounds = YES;
|
||||
|
||||
self.boundsStrokeView = [OWSBubbleShapeView new];
|
||||
self.boundsStrokeView.strokeColor = ConversationStyle.bubbleColorIncoming;
|
||||
self.boundsStrokeView.strokeColor = [self.conversationStyle primaryColor];
|
||||
self.boundsStrokeView.strokeThickness = 1.f;
|
||||
[self addSubview:self.boundsStrokeView];
|
||||
[self.boundsStrokeView autoPinToSuperviewEdges];
|
||||
|
|
|
@ -53,7 +53,7 @@ public class ConversationHeaderView: UIStackView {
|
|||
|
||||
private let titleLabel: UILabel
|
||||
private let subtitleLabel: UILabel
|
||||
private let avatarView: AvatarImageView
|
||||
private let avatarView: ConversationAvatarImageView
|
||||
|
||||
@objc
|
||||
public required init(thread: TSThread, contactsManager: OWSContactsManager) {
|
||||
|
@ -115,6 +115,11 @@ public class ConversationHeaderView: UIStackView {
|
|||
return UILayoutFittingExpandedSize
|
||||
}
|
||||
|
||||
@objc
|
||||
public func updateAvatar() {
|
||||
self.avatarView.updateImage()
|
||||
}
|
||||
|
||||
// MARK: Delegate Methods
|
||||
|
||||
@objc func didTapView(tapGesture: UITapGestureRecognizer) {
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@class ConversationStyle;
|
||||
@class OWSQuotedReplyModel;
|
||||
@class SignalAttachment;
|
||||
|
||||
|
@ -33,6 +34,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@interface ConversationInputToolbar : UIView
|
||||
|
||||
- (instancetype)initWithConversationStyle:(ConversationStyle *)conversationStyle NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
@property (nonatomic, weak) id<ConversationInputToolbarDelegate> inputToolbarDelegate;
|
||||
|
||||
- (void)beginEditingTextMessage;
|
||||
|
|
|
@ -27,6 +27,8 @@ static const CGFloat ConversationInputToolbarBorderViewHeight = 0.5;
|
|||
ConversationTextViewToolbarDelegate,
|
||||
QuotedReplyPreviewDelegate>
|
||||
|
||||
@property (nonatomic, readonly) ConversationStyle *conversationStyle;
|
||||
|
||||
@property (nonatomic, readonly) UIView *composeContainer;
|
||||
@property (nonatomic, readonly) ConversationInputTextView *inputTextView;
|
||||
@property (nonatomic, readonly) UIStackView *contentStackView;
|
||||
|
@ -66,12 +68,16 @@ static const CGFloat ConversationInputToolbarBorderViewHeight = 0.5;
|
|||
|
||||
@implementation ConversationInputToolbar
|
||||
|
||||
- (instancetype)init
|
||||
- (instancetype)initWithConversationStyle:(ConversationStyle *)conversationStyle
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_conversationStyle = conversationStyle;
|
||||
|
||||
if (self) {
|
||||
[self createContents];
|
||||
}
|
||||
|
||||
|
||||
return self;
|
||||
}
|
||||
|
@ -268,7 +274,7 @@ static const CGFloat ConversationInputToolbarBorderViewHeight = 0.5;
|
|||
return;
|
||||
}
|
||||
|
||||
self.quotedMessagePreview = [[QuotedReplyPreview alloc] initWithQuotedReply:quotedReply];
|
||||
self.quotedMessagePreview = [[QuotedReplyPreview alloc] initWithQuotedReply:quotedReply conversationStyle:self.conversationStyle];
|
||||
self.quotedMessagePreview.delegate = self;
|
||||
|
||||
// TODO animate
|
||||
|
|
|
@ -564,7 +564,7 @@ typedef enum : NSUInteger {
|
|||
|
||||
[self.collectionView applyScrollViewInsetsFix];
|
||||
|
||||
_inputToolbar = [ConversationInputToolbar new];
|
||||
_inputToolbar = [[ConversationInputToolbar alloc] initWithConversationStyle:self.conversationStyle];
|
||||
self.inputToolbar.inputToolbarDelegate = self;
|
||||
self.inputToolbar.inputTextViewDelegate = self;
|
||||
[self.collectionView autoPinToBottomLayoutGuideOfViewController:self withInset:0];
|
||||
|
@ -4326,6 +4326,13 @@ typedef enum : NSUInteger {
|
|||
}];
|
||||
}
|
||||
|
||||
- (void)conversationColorWasUpdated
|
||||
{
|
||||
[self.conversationStyle updateProperties];
|
||||
[self.headerView updateAvatar];
|
||||
[self.collectionView reloadData];
|
||||
}
|
||||
|
||||
- (void)groupWasUpdated:(TSGroupModel *)groupModel
|
||||
{
|
||||
OWSAssert(groupModel);
|
||||
|
|
|
@ -1297,6 +1297,8 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
messageState:TSOutgoingMessageStateSent
|
||||
text:@"⚠️ Outgoing Reserved Color Png ⚠️"]];
|
||||
}
|
||||
|
||||
ConversationStyle *conversationStyle = [[ConversationStyle alloc] initWithThread:thread];
|
||||
[actions addObjectsFromArray:@[
|
||||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing White Png"
|
||||
|
@ -1326,7 +1328,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Unsent' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingUnsent]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingUnsent]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateFailed
|
||||
|
@ -1334,7 +1336,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Unsent' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingUnsent]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingUnsent]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateSending
|
||||
|
@ -1342,7 +1344,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Unsent' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingUnsent]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingUnsent]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateSent
|
||||
|
@ -1351,7 +1353,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Sending' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingSending]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingSending]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateFailed
|
||||
|
@ -1359,7 +1361,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Sending' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingSending]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingSending]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateSending
|
||||
|
@ -1367,7 +1369,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Sending' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingSending]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingSending]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateSent
|
||||
|
@ -1376,7 +1378,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Sent' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingSent]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingSent]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateFailed
|
||||
|
@ -1384,7 +1386,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Sent' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingSent]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingSent]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateSending
|
||||
|
@ -1392,7 +1394,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeOutgoingPngAction:thread
|
||||
actionLabel:@"Fake Outgoing 'Outgoing Sent' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorOutgoingSent]
|
||||
backgroundColor:[conversationStyle bubbleColorOutgoingSent]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
messageState:TSOutgoingMessageStateSent
|
||||
|
@ -1560,7 +1562,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeIncomingPngAction:thread
|
||||
actionLabel:@"Fake Incoming 'Incoming' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorIncoming]
|
||||
backgroundColor:[conversationStyle primaryColor]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
isAttachmentDownloaded:YES
|
||||
|
@ -1568,7 +1570,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[self fakeIncomingPngAction:thread
|
||||
actionLabel:@"Fake Incoming 'Incoming' Png"
|
||||
imageSize:CGSizeMake(200.f, 200.f)
|
||||
backgroundColor:[ConversationStyle bubbleColorIncoming]
|
||||
backgroundColor:[conversationStyle primaryColor]
|
||||
textColor:[UIColor whiteColor]
|
||||
imageLabel:@"W"
|
||||
isAttachmentDownloaded:NO
|
||||
|
|
|
@ -37,10 +37,13 @@
|
|||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface OWSConversationSettingsViewController () <ContactEditingDelegate, ContactsViewHelperDelegate>
|
||||
@interface OWSConversationSettingsViewController () <ContactEditingDelegate,
|
||||
ContactsViewHelperDelegate,
|
||||
ColorPickerDelegate>
|
||||
|
||||
@property (nonatomic) TSThread *thread;
|
||||
@property (nonatomic) YapDatabaseConnection *uiDatabaseConnection;
|
||||
@property (nonatomic, readonly) YapDatabaseConnection *editingDatabaseConnection;
|
||||
|
||||
@property (nonatomic) NSArray<NSNumber *> *disappearingMessagesDurations;
|
||||
@property (nonatomic) OWSDisappearingMessagesConfiguration *disappearingMessagesConfiguration;
|
||||
|
@ -123,6 +126,11 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
object:nil];
|
||||
}
|
||||
|
||||
- (YapDatabaseConnection *)editingDatabaseConnection
|
||||
{
|
||||
return [OWSPrimaryStorage sharedManager].dbReadWriteConnection;
|
||||
}
|
||||
|
||||
- (NSString *)threadName
|
||||
{
|
||||
NSString *threadName = self.thread.name;
|
||||
|
@ -271,6 +279,19 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[weakSelf showMediaGallery];
|
||||
}]];
|
||||
|
||||
|
||||
[mainSection addItem:[OWSTableItem
|
||||
itemWithCustomCellBlock:^{
|
||||
NSString *colorName = self.thread.conversationColorName;
|
||||
UIColor *currentColor = [UIColor ows_conversationColorForColorName:colorName];
|
||||
NSString *title = NSLocalizedString(@"CONVERSATION_SETTINGS_CONVERSATION_COLOR",
|
||||
@"Label for table cell which leads to picking a new conversation color");
|
||||
return [weakSelf disclosureCellWithName:title iconColor:currentColor];
|
||||
}
|
||||
actionBlock:^{
|
||||
[weakSelf showColorPicker];
|
||||
}]];
|
||||
|
||||
if ([self.thread isKindOfClass:[TSContactThread class]] && self.contactsManager.supportsContactEditing
|
||||
&& !self.hasExistingContact) {
|
||||
[mainSection addItem:[OWSTableItem itemWithCustomCellBlock:^{
|
||||
|
@ -623,6 +644,39 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return 12.f;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)disclosureCellWithName:(NSString *)name iconColor:(UIColor *)iconColor
|
||||
{
|
||||
OWSAssert(name.length > 0);
|
||||
|
||||
UITableViewCell *cell = [UITableViewCell new];
|
||||
cell.preservesSuperviewLayoutMargins = YES;
|
||||
cell.contentView.preservesSuperviewLayoutMargins = YES;
|
||||
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
|
||||
|
||||
UIView *swatchView = [NeverClearView new];
|
||||
const CGFloat kSwatchWidth = 24;
|
||||
[swatchView autoSetDimension:ALDimensionWidth toSize:kSwatchWidth];
|
||||
[swatchView autoSetDimension:ALDimensionHeight toSize:kSwatchWidth];
|
||||
swatchView.layer.cornerRadius = kSwatchWidth / 2;
|
||||
swatchView.backgroundColor = iconColor;
|
||||
|
||||
[cell.contentView addSubview:swatchView];
|
||||
[swatchView autoVCenterInSuperview];
|
||||
[swatchView autoPinLeadingToSuperviewMargin];
|
||||
|
||||
UILabel *rowLabel = [UILabel new];
|
||||
rowLabel.text = name;
|
||||
rowLabel.textColor = [UIColor blackColor];
|
||||
rowLabel.font = [UIFont ows_regularFontWithSize:17.f];
|
||||
rowLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
||||
[cell.contentView addSubview:rowLabel];
|
||||
[rowLabel autoVCenterInSuperview];
|
||||
[rowLabel autoPinLeadingToTrailingEdgeOfView:swatchView offset:self.iconSpacing];
|
||||
[rowLabel autoPinTrailingToSuperviewMargin];
|
||||
|
||||
return cell;
|
||||
}
|
||||
|
||||
- (UITableViewCell *)cellWithName:(NSString *)name iconName:(NSString *)iconName
|
||||
{
|
||||
OWSAssert(name.length > 0);
|
||||
|
@ -1158,7 +1212,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (void)setThreadMutedUntilDate:(nullable NSDate *)value
|
||||
{
|
||||
[self.thread updateWithMutedUntilDate:value];
|
||||
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction * _Nonnull transaction) {
|
||||
[self.thread updateWithMutedUntilDate:value transaction:transaction];
|
||||
}];
|
||||
|
||||
[self updateTableContents];
|
||||
}
|
||||
|
||||
|
@ -1200,6 +1257,36 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - ColorPickerDelegate
|
||||
|
||||
- (void)showColorPicker
|
||||
{
|
||||
ColorPickerViewController *pickerController = [[ColorPickerViewController alloc] initWithThread:self.thread];
|
||||
pickerController.delegate = self;
|
||||
OWSNavigationController *modal = [[OWSNavigationController alloc] initWithRootViewController:pickerController];
|
||||
|
||||
[self presentViewController:modal animated:YES completion:nil];
|
||||
}
|
||||
|
||||
- (void)colorPicker:(ColorPickerViewController *)colorPicker didPickColorName:(NSString *)colorName
|
||||
{
|
||||
DDLogDebug(@"%@ in %s picked color: %@", self.logTag, __PRETTY_FUNCTION__, colorName);
|
||||
[self.editingDatabaseConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *_Nonnull transaction) {
|
||||
[self.thread updateConversationColorName:colorName transaction:transaction];
|
||||
|
||||
[self.contactsManager.avatarCache removeAllImages];
|
||||
[self updateTableContents];
|
||||
[self.conversationSettingsViewDelegate conversationColorWasUpdated];
|
||||
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)colorPickerDidCancel:(ColorPickerViewController *)colorPicker
|
||||
{
|
||||
[self dismissViewControllerAnimated:YES completion:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -8,6 +8,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
@protocol OWSConversationSettingsViewDelegate <NSObject>
|
||||
|
||||
- (void)conversationColorWasUpdated;
|
||||
- (void)groupWasUpdated:(TSGroupModel *)groupModel;
|
||||
|
||||
- (void)popAllConversationSettingsViews;
|
||||
|
|
|
@ -15,6 +15,7 @@ class QuotedReplyPreview: UIView {
|
|||
public weak var delegate: QuotedReplyPreviewDelegate?
|
||||
|
||||
private let quotedReply: OWSQuotedReplyModel
|
||||
private let conversationStyle: ConversationStyle
|
||||
private var quotedMessageView: OWSQuotedMessageView?
|
||||
private var heightConstraint: NSLayoutConstraint!
|
||||
|
||||
|
@ -24,8 +25,9 @@ class QuotedReplyPreview: UIView {
|
|||
}
|
||||
|
||||
@objc
|
||||
init(quotedReply: OWSQuotedReplyModel) {
|
||||
init(quotedReply: OWSQuotedReplyModel, conversationStyle: ConversationStyle) {
|
||||
self.quotedReply = quotedReply
|
||||
self.conversationStyle = conversationStyle
|
||||
|
||||
super.init(frame: .zero)
|
||||
|
||||
|
@ -42,7 +44,7 @@ class QuotedReplyPreview: UIView {
|
|||
// We instantiate quotedMessageView late to ensure that it is updated
|
||||
// every time contentSizeCategoryDidChange (i.e. when dynamic type
|
||||
// sizes changes).
|
||||
let quotedMessageView = OWSQuotedMessageView(forPreview: quotedReply)
|
||||
let quotedMessageView = OWSQuotedMessageView(forPreview: quotedReply, conversationStyle: conversationStyle)
|
||||
self.quotedMessageView = quotedMessageView
|
||||
|
||||
quotedMessageView.backgroundColor = .clear
|
||||
|
|
|
@ -511,6 +511,9 @@
|
|||
/* Navbar title when viewing settings for a 1-on-1 thread */
|
||||
"CONVERSATION_SETTINGS_CONTACT_INFO_TITLE" = "Contact Info";
|
||||
|
||||
/* Indicates that user's profile has been shared with a group. */
|
||||
"CONVERSATION_SETTINGS_CONVERSATION_COLOR" = "Color";
|
||||
|
||||
/* Navbar title when viewing settings for a group thread */
|
||||
"CONVERSATION_SETTINGS_GROUP_INFO_TITLE" = "Group Info";
|
||||
|
||||
|
|
|
@ -150,7 +150,7 @@ public class ConversationAvatarImageView: AvatarImageView {
|
|||
self.updateImage()
|
||||
}
|
||||
|
||||
func updateImage() {
|
||||
public func updateImage() {
|
||||
Logger.debug("\(self.logTag) in \(#function) updateImage")
|
||||
|
||||
self.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: diameter, contactsManager: contactsManager)
|
||||
|
|
|
@ -29,6 +29,7 @@ const CGFloat kContactCellAvatarTextMargin = 12;
|
|||
@property (nonatomic) UIView *accessoryViewContainer;
|
||||
|
||||
@property (nonatomic) OWSContactsManager *contactsManager;
|
||||
@property (nonatomic) TSThread *thread;
|
||||
@property (nonatomic) NSString *recipientId;
|
||||
|
||||
@end
|
||||
|
@ -139,7 +140,8 @@ const CGFloat kContactCellAvatarTextMargin = 12;
|
|||
- (void)configureWithThread:(TSThread *)thread contactsManager:(OWSContactsManager *)contactsManager
|
||||
{
|
||||
OWSAssert(thread);
|
||||
|
||||
self.thread = thread;
|
||||
|
||||
// Update fonts to reflect changes to dynamic type.
|
||||
[self configureFonts];
|
||||
|
||||
|
@ -194,7 +196,11 @@ const CGFloat kContactCellAvatarTextMargin = 12;
|
|||
return;
|
||||
}
|
||||
|
||||
NSString *colorName = self.thread.conversationColorName;
|
||||
UIColor *color = [UIColor ows_conversationColorForColorName:colorName];
|
||||
|
||||
self.avatarView.image = [[[OWSContactAvatarBuilder alloc] initWithSignalId:recipientId
|
||||
color:color
|
||||
diameter:kContactCellAvatarSize
|
||||
contactsManager:contactsManager] build];
|
||||
}
|
||||
|
@ -230,6 +236,7 @@ const CGFloat kContactCellAvatarTextMargin = 12;
|
|||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
self.thread = nil;
|
||||
self.accessoryMessage = nil;
|
||||
self.nameLabel.text = nil;
|
||||
self.subtitleLabel.text = nil;
|
||||
|
|
|
@ -24,9 +24,17 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (class, readonly, nonatomic) UIColor *ows_toolbarBackgroundColor;
|
||||
@property (class, readonly, nonatomic) UIColor *ows_messageBubbleLightGrayColor;
|
||||
|
||||
+ (UIColor *)backgroundColorForContact:(NSString *)contactIdentifier;
|
||||
+ (UIColor *)colorWithRGBHex:(unsigned long)value;
|
||||
|
||||
#pragma mark - ConversationColor
|
||||
|
||||
+ (nullable UIColor *)ows_conversationColorForColorName:(NSString *)colorName NS_SWIFT_NAME(ows_conversationColor(colorName:));
|
||||
+ (nullable NSString *)ows_conversationColorNameForColor:(UIColor *)color
|
||||
NS_SWIFT_NAME(ows_conversationColorName(color:));
|
||||
|
||||
@property (class, readonly, nonatomic) NSArray<NSString *> *ows_conversationColorNames;
|
||||
@property (class, readonly, nonatomic) NSArray<UIColor *> *ows_conversationColors;
|
||||
|
||||
- (UIColor *)blendWithColor:(UIColor *)otherColor alpha:(CGFloat)alpha;
|
||||
|
||||
#pragma mark -
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
#import "OWSMath.h"
|
||||
#import "UIColor+OWS.h"
|
||||
#import "OWSMath.h"
|
||||
#import <SignalServiceKit/Cryptography.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
@ -98,40 +98,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return [UIColor colorWithHue:240.0f / 360.0f saturation:0.02f brightness:0.92f alpha:1.0f];
|
||||
}
|
||||
|
||||
+ (UIColor *)backgroundColorForContact:(NSString *)contactIdentifier
|
||||
{
|
||||
NSArray *colors = @[
|
||||
[UIColor colorWithRed:204.f / 255.f green:148.f / 255.f blue:102.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:187.f / 255.f green:104.f / 255.f blue:62.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:145.f / 255.f green:78.f / 255.f blue:48.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:122.f / 255.f green:63.f / 255.f blue:41.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:80.f / 255.f green:46.f / 255.f blue:27.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:57.f / 255.f green:45.f / 255.f blue:19.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:37.f / 255.f green:38.f / 255.f blue:13.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:23.f / 255.f green:31.f / 255.f blue:10.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:6.f / 255.f green:19.f / 255.f blue:10.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:13.f / 255.f green:4.f / 255.f blue:16.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:27.f / 255.f green:12.f / 255.f blue:44.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:18.f / 255.f green:17.f / 255.f blue:64.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:20.f / 255.f green:42.f / 255.f blue:77.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:18.f / 255.f green:55.f / 255.f blue:68.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:18.f / 255.f green:68.f / 255.f blue:61.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:19.f / 255.f green:73.f / 255.f blue:26.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:13.f / 255.f green:48.f / 255.f blue:15.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:44.f / 255.f green:165.f / 255.f blue:137.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:137.f / 255.f green:181.f / 255.f blue:48.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:208.f / 255.f green:204.f / 255.f blue:78.f / 255.f alpha:1.f],
|
||||
[UIColor colorWithRed:227.f / 255.f green:162.f / 255.f blue:150.f / 255.f alpha:1.f]
|
||||
];
|
||||
NSData *contactData = [contactIdentifier dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSUInteger hashingLength = 8;
|
||||
unsigned long long choose;
|
||||
NSData *hashData = [Cryptography computeSHA256Digest:contactData truncatedToBytes:hashingLength];
|
||||
[hashData getBytes:&choose length:hashingLength];
|
||||
return [colors objectAtIndex:(choose % [colors count])];
|
||||
}
|
||||
|
||||
+ (UIColor *)colorWithRGBHex:(unsigned long)value
|
||||
{
|
||||
CGFloat red = ((value >> 16) & 0xff) / 255.f;
|
||||
|
@ -304,6 +270,50 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
return [UIColor colorWithRGBHex:0x757575];
|
||||
}
|
||||
|
||||
+ (NSDictionary<NSString *, UIColor *> *)ows_conversationColorMap
|
||||
{
|
||||
static NSDictionary<NSString *, UIColor *> *colorMap;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
colorMap = @{
|
||||
@"red" : self.ows_red700Color,
|
||||
@"pink": self.ows_pink600Color,
|
||||
@"purple": self.ows_purple600Color,
|
||||
@"indigo": self.ows_indigo600Color,
|
||||
@"blue": self.ows_blue700Color,
|
||||
@"cyan": self.ows_cyan800Color,
|
||||
@"teal": self.ows_teal700Color,
|
||||
@"green": self.ows_green800Color,
|
||||
@"deep_orange": self.ows_deepOrange900Color,
|
||||
@"grey": self.ows_grey600Color
|
||||
};
|
||||
});
|
||||
|
||||
return colorMap;
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)ows_conversationColorNames
|
||||
{
|
||||
return self.ows_conversationColorMap.allKeys;
|
||||
}
|
||||
|
||||
+ (NSArray<UIColor *> *)ows_conversationColors
|
||||
{
|
||||
return self.ows_conversationColorMap.allValues;
|
||||
}
|
||||
|
||||
+ (nullable UIColor *)ows_conversationColorForColorName:(NSString *)colorName
|
||||
{
|
||||
OWSAssert(colorName.length > 0);
|
||||
|
||||
return [self.ows_conversationColorMap objectForKey:colorName];
|
||||
}
|
||||
|
||||
+ (nullable NSString *)ows_conversationColorNameForColor:(UIColor *)color
|
||||
{
|
||||
return [self.ows_conversationColorMap allKeysForObject:color].firstObject;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -55,6 +55,7 @@ public class ConversationStyle: NSObject {
|
|||
|
||||
self.thread = thread
|
||||
self.isRTL = CurrentAppContext().isRTL
|
||||
self.primaryColor = ConversationStyle.primaryColor(thread: thread)
|
||||
|
||||
super.init()
|
||||
|
||||
|
@ -78,7 +79,8 @@ public class ConversationStyle: NSObject {
|
|||
|
||||
// MARK: -
|
||||
|
||||
private func updateProperties() {
|
||||
@objc
|
||||
public func updateProperties() {
|
||||
if thread.isGroupThread() {
|
||||
gutterLeading = 40
|
||||
gutterTrailing = 20
|
||||
|
@ -106,37 +108,52 @@ public class ConversationStyle: NSObject {
|
|||
textInsetHorizontal = 12
|
||||
|
||||
lastTextLineAxis = CGFloat(round(12 + messageTextFont.capHeight * 0.5))
|
||||
|
||||
self.primaryColor = ConversationStyle.primaryColor(thread: thread)
|
||||
}
|
||||
|
||||
// MARK: Colors
|
||||
|
||||
// TODO: Remove this! Incoming bubble colors are now dynamic.
|
||||
@objc
|
||||
public static let bubbleColorIncoming = UIColor.ows_messageBubbleLightGray
|
||||
private class func primaryColor(thread: TSThread) -> UIColor {
|
||||
guard let colorName = thread.conversationColorName else {
|
||||
return self.defaultBubbleColorIncoming
|
||||
}
|
||||
|
||||
guard let color = UIColor.ows_conversationColor(colorName: colorName) else {
|
||||
return self.defaultBubbleColorIncoming
|
||||
}
|
||||
|
||||
return color
|
||||
}
|
||||
|
||||
private static let defaultBubbleColorIncoming = UIColor.ows_messageBubbleLightGray
|
||||
|
||||
// TODO:
|
||||
@objc
|
||||
public static let bubbleColorOutgoingUnsent = UIColor.ows_red
|
||||
public let bubbleColorOutgoingUnsent = UIColor.ows_red
|
||||
|
||||
// TODO:
|
||||
@objc
|
||||
public static let bubbleColorOutgoingSending = UIColor.ows_light35
|
||||
public let bubbleColorOutgoingSending = UIColor.ows_light35
|
||||
|
||||
@objc
|
||||
public static let bubbleColorOutgoingSent = UIColor.ows_light10
|
||||
public let bubbleColorOutgoingSent = UIColor.ows_light10
|
||||
|
||||
@objc
|
||||
public static func bubbleColor(message: TSMessage) -> UIColor {
|
||||
public var primaryColor: UIColor
|
||||
|
||||
@objc
|
||||
public func bubbleColor(message: TSMessage) -> UIColor {
|
||||
if message is TSIncomingMessage {
|
||||
return ConversationStyle.bubbleColorIncoming
|
||||
return primaryColor
|
||||
} else if let outgoingMessage = message as? TSOutgoingMessage {
|
||||
switch outgoingMessage.messageState {
|
||||
case .failed:
|
||||
return ConversationStyle.bubbleColorOutgoingUnsent
|
||||
return self.bubbleColorOutgoingUnsent
|
||||
case .sending:
|
||||
return ConversationStyle.bubbleColorOutgoingSending
|
||||
return self.bubbleColorOutgoingSending
|
||||
default:
|
||||
return ConversationStyle.bubbleColorOutgoingSent
|
||||
return self.bubbleColorOutgoingSent
|
||||
}
|
||||
} else {
|
||||
owsFail("Unexpected message type: \(message)")
|
||||
|
@ -145,7 +162,7 @@ public class ConversationStyle: NSObject {
|
|||
}
|
||||
|
||||
@objc
|
||||
public static func bubbleTextColor(message: TSMessage) -> UIColor {
|
||||
public func bubbleTextColor(message: TSMessage) -> UIColor {
|
||||
if message is TSIncomingMessage {
|
||||
return UIColor.ows_white
|
||||
} else if let outgoingMessage = message as? TSOutgoingMessage {
|
||||
|
|
|
@ -27,7 +27,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
OWSAvatarBuilder *avatarBuilder;
|
||||
if ([thread isKindOfClass:[TSContactThread class]]) {
|
||||
TSContactThread *contactThread = (TSContactThread *)thread;
|
||||
NSString *colorName = thread.conversationColorName;
|
||||
UIColor *color = [UIColor ows_conversationColorForColorName:colorName];
|
||||
avatarBuilder = [[OWSContactAvatarBuilder alloc] initWithSignalId:contactThread.contactIdentifier
|
||||
color:color
|
||||
diameter:diameter
|
||||
contactsManager:contactsManager];
|
||||
} else if ([thread isKindOfClass:[TSGroupThread class]]) {
|
||||
|
|
|
@ -14,7 +14,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
/**
|
||||
* Build an avatar for a Signal recipient
|
||||
*/
|
||||
|
||||
- (instancetype)initWithSignalId:(NSString *)signalId
|
||||
color:(UIColor *)color
|
||||
diameter:(NSUInteger)diameter
|
||||
contactsManager:(OWSContactsManager *)contactsManager;
|
||||
|
||||
|
@ -26,7 +28,6 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
diameter:(NSUInteger)diameter
|
||||
contactsManager:(OWSContactsManager *)contactsManager;
|
||||
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
|
|
@ -22,6 +22,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@property (nonatomic, readonly) OWSContactsManager *contactsManager;
|
||||
@property (nonatomic, readonly) NSString *signalId;
|
||||
@property (nonatomic, readonly) NSString *contactName;
|
||||
@property (nonatomic, readonly) UIColor *color;
|
||||
@property (nonatomic, readonly) NSUInteger diameter;
|
||||
|
||||
@end
|
||||
|
@ -32,6 +33,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
- (instancetype)initWithContactId:(NSString *)contactId
|
||||
name:(NSString *)name
|
||||
color:(UIColor *)color
|
||||
diameter:(NSUInteger)diameter
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
{
|
||||
|
@ -42,6 +44,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
_signalId = contactId;
|
||||
_contactName = name;
|
||||
_color = color;
|
||||
_diameter = diameter;
|
||||
_contactsManager = contactsManager;
|
||||
|
||||
|
@ -49,6 +52,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
- (instancetype)initWithSignalId:(NSString *)signalId
|
||||
color:(UIColor *)color
|
||||
diameter:(NSUInteger)diameter
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
{
|
||||
|
@ -60,7 +64,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
if (name.length == 0) {
|
||||
name = signalId;
|
||||
}
|
||||
return [self initWithContactId:signalId name:name diameter:diameter contactsManager:contactsManager];
|
||||
return [self initWithContactId:signalId name:name color:color diameter:diameter contactsManager:contactsManager];
|
||||
}
|
||||
|
||||
- (instancetype)initWithNonSignalName:(NSString *)nonSignalName
|
||||
|
@ -68,7 +72,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
diameter:(NSUInteger)diameter
|
||||
contactsManager:(OWSContactsManager *)contactsManager
|
||||
{
|
||||
return [self initWithContactId:colorSeed name:nonSignalName diameter:diameter contactsManager:contactsManager];
|
||||
|
||||
NSString *colorName = [TSThread stableConversationColorNameForString:colorSeed];
|
||||
UIColor *color = [UIColor ows_conversationColorForColorName:colorName];
|
||||
OWSAssert(color);
|
||||
return [self initWithContactId:colorSeed
|
||||
name:nonSignalName
|
||||
color:color
|
||||
diameter:diameter
|
||||
contactsManager:contactsManager];
|
||||
}
|
||||
|
||||
#pragma mark - Instance methods
|
||||
|
@ -113,9 +125,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
}
|
||||
|
||||
CGFloat fontSize = (CGFloat)self.diameter / 2.8;
|
||||
UIColor *backgroundColor = [UIColor backgroundColorForContact:self.signalId];
|
||||
|
||||
UIImage *image = [[JSQMessagesAvatarImageFactory avatarImageWithUserInitials:initials
|
||||
backgroundColor:backgroundColor
|
||||
backgroundColor:self.color
|
||||
textColor:[UIColor whiteColor]
|
||||
font:[UIFont ows_boldFontWithSize:fontSize]
|
||||
diameter:self.diameter] avatarImage];
|
||||
|
|
|
@ -33,6 +33,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
*/
|
||||
- (NSString *)name;
|
||||
|
||||
@property (readonly, nullable) NSString *conversationColorName;
|
||||
- (void)updateConversationColorName:(NSString *)colorName transaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
+ (NSString *)stableConversationColorNameForString:(NSString *)colorSeed;
|
||||
|
||||
/**
|
||||
* @returns
|
||||
* Signal Id (e164) of the contact if it's a contact thread.
|
||||
|
@ -154,7 +158,7 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
|
||||
#pragma mark - Update With... Methods
|
||||
|
||||
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate;
|
||||
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate transaction:(YapDatabaseReadWriteTransaction *)transaction;
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
//
|
||||
|
||||
#import "TSThread.h"
|
||||
#import "Cryptography.h"
|
||||
#import "NSDate+OWS.h"
|
||||
#import "NSString+SSK.h"
|
||||
#import "OWSDisappearingMessagesConfiguration.h"
|
||||
|
@ -21,9 +22,10 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
@interface TSThread ()
|
||||
|
||||
@property (nonatomic) NSDate *creationDate;
|
||||
@property (nonatomic, copy) NSDate *archivalDate;
|
||||
@property (nonatomic) NSDate *lastMessageDate;
|
||||
@property (nonatomic, copy) NSString *messageDraft;
|
||||
@property (nonatomic, copy, nullable) NSDate *archivalDate;
|
||||
@property (nonatomic, nullable) NSString *conversationColorName;
|
||||
@property (nonatomic, nullable) NSDate *lastMessageDate;
|
||||
@property (nonatomic, copy, nullable) NSString *messageDraft;
|
||||
@property (atomic, nullable) NSDate *mutedUntilDate;
|
||||
|
||||
@end
|
||||
|
@ -45,11 +47,26 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
_lastMessageDate = nil;
|
||||
_creationDate = [NSDate date];
|
||||
_messageDraft = nil;
|
||||
_conversationColorName = [self.class randomConversationColorName];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (nullable instancetype)initWithCoder:(NSCoder *)coder
|
||||
{
|
||||
self = [super initWithCoder:coder];
|
||||
if (!self) {
|
||||
return self;
|
||||
}
|
||||
|
||||
if (!_conversationColorName) {
|
||||
_conversationColorName = [self.class stableConversationColorNameForString:self.uniqueId];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)removeWithTransaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[self removeAllThreadInteractionsWithTransaction:transaction];
|
||||
|
@ -395,16 +412,58 @@ NS_ASSUME_NONNULL_BEGIN
|
|||
[mutedUntilDate timeIntervalSinceDate:now] > 0);
|
||||
}
|
||||
|
||||
#pragma mark - Update With... Methods
|
||||
|
||||
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate
|
||||
- (void)updateWithMutedUntilDate:(NSDate *)mutedUntilDate transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[self.dbReadWriteConnection readWriteWithBlock:^(YapDatabaseReadWriteTransaction *transaction) {
|
||||
[self applyChangeToSelfAndLatestCopy:transaction
|
||||
changeBlock:^(TSThread *thread) {
|
||||
[thread setMutedUntilDate:mutedUntilDate];
|
||||
}];
|
||||
}];
|
||||
[self applyChangeToSelfAndLatestCopy:transaction
|
||||
changeBlock:^(TSThread *thread) {
|
||||
[thread setMutedUntilDate:mutedUntilDate];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Conversation Color
|
||||
|
||||
+ (NSString *)randomConversationColorName
|
||||
{
|
||||
NSUInteger count = self.conversationColorNames.count;
|
||||
NSUInteger index = arc4random_uniform((uint32_t)count);
|
||||
return [self.conversationColorNames objectAtIndex:index];
|
||||
}
|
||||
|
||||
+ (NSString *)stableConversationColorNameForString:(NSString *)colorSeed
|
||||
{
|
||||
NSData *contactData = [colorSeed dataUsingEncoding:NSUTF8StringEncoding];
|
||||
|
||||
NSUInteger hashingLength = sizeof(unsigned long long);
|
||||
unsigned long long choose;
|
||||
NSData *hashData = [Cryptography computeSHA256Digest:contactData truncatedToBytes:hashingLength];
|
||||
[hashData getBytes:&choose length:hashingLength];
|
||||
|
||||
NSUInteger index = (choose % [self.conversationColorNames count]);
|
||||
return [self.conversationColorNames objectAtIndex:index];
|
||||
}
|
||||
|
||||
+ (NSArray<NSString *> *)conversationColorNames
|
||||
{
|
||||
return @[
|
||||
@"red",
|
||||
@"pink",
|
||||
@"purple",
|
||||
@"indigo",
|
||||
@"blue",
|
||||
@"cyan",
|
||||
@"teal",
|
||||
@"green",
|
||||
@"deep_orange",
|
||||
@"grey"
|
||||
];
|
||||
}
|
||||
|
||||
- (void)updateConversationColorName:(NSString *)colorName transaction:(YapDatabaseReadWriteTransaction *)transaction
|
||||
{
|
||||
[self applyChangeToSelfAndLatestCopy:transaction
|
||||
changeBlock:^(TSThread *thread) {
|
||||
thread.conversationColorName = colorName;
|
||||
}];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue