conversations: set fallback avatar's color based on known username

- Sets the conversation cells' fallback avatar colors based on
  the username known when initializing. This should avoid the
  color flickering except when a lookup is actually in progress.

- Uses the value of the username known at cell init to set
  the fallback avatar's intitial.

- Removes the use of the fallback avatar initial in the case of
  the ringId being the best name. For now, no character is shown.

Change-Id: Ib42cc1c8aa31783c77f986a6a32976fa74b48d57
diff --git a/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.swift b/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.swift
index 5ab2526..088da47 100644
--- a/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.swift
+++ b/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.swift
@@ -31,6 +31,18 @@
     @IBOutlet weak var discardButton: UIButton!
     @IBOutlet weak var banButton: UIButton!
 
+    override func setSelected(_ selected: Bool, animated: Bool) {
+        let fallbackAvatarBGColor = self.fallbackAvatar.backgroundColor
+        super.setSelected(selected, animated: animated)
+        self.fallbackAvatar.backgroundColor = fallbackAvatarBGColor
+    }
+
+    override func setHighlighted(_ highlighted: Bool, animated: Bool) {
+        let fallbackAvatarBGColor = self.fallbackAvatar.backgroundColor
+        super.setSelected(highlighted, animated: animated)
+        self.fallbackAvatar.backgroundColor = fallbackAvatarBGColor
+    }
+
     var disposeBag = DisposeBag()
 
     override func prepareForReuse() {
diff --git a/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.xib b/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.xib
index a9707de..dc88762 100644
--- a/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.xib
+++ b/Ring/Ring/Features/ContactRequests/Cells/ContactRequestCell.xib
@@ -18,7 +18,7 @@
                 <rect key="frame" x="0.0" y="0.0" width="470" height="71.5"/>
                 <autoresizingMask key="autoresizingMask"/>
                 <subviews>
-                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="R" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wjc-Nn-INi" userLabel="Fallback Avatar">
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wjc-Nn-INi" userLabel="Fallback Avatar">
                         <rect key="frame" x="16" y="16" width="40" height="40"/>
                         <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
                         <color key="backgroundColor" red="1" green="0.5" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
diff --git a/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift b/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift
index 1a427d5..705db7f 100644
--- a/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift
+++ b/Ring/Ring/Features/ContactRequests/ContactRequestsViewController.swift
@@ -74,8 +74,23 @@
                     .disposed(by: cell.disposeBag)
 
                 // Avatar placeholder initial
+                cell.fallbackAvatar.text = nil
+                let name = item.userName.value
+                let scanner = Scanner(string: name.toMD5HexString().prefixString())
+                var index: UInt64 = 0
+                if scanner.scanHexInt64(&index) {
+                    cell.fallbackAvatar.isHidden = false
+                    cell.fallbackAvatar.backgroundColor = avatarColors[Int(index)]
+                    if item.contactRequest.ringId != name {
+                        cell.fallbackAvatar.text = name.prefixString().capitalized
+                    }
+                }
+
                 item.userName.asObservable()
                     .observeOn(MainScheduler.instance)
+                    .filter({ [weak item] userName in
+                        return userName != item?.contactRequest.ringId
+                    })
                     .map { value in value.prefixString().capitalized }
                     .bind(to: cell.fallbackAvatar.rx.text)
                     .disposed(by: cell.disposeBag)
diff --git a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellReceived.xib b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellReceived.xib
index 3f5401a..69890f1 100644
--- a/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellReceived.xib
+++ b/Ring/Ring/Features/Conversations/Conversation/Cells/MessageCellReceived.xib
@@ -31,7 +31,7 @@
                             </userDefinedRuntimeAttribute>
                         </userDefinedRuntimeAttributes>
                     </imageView>
-                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="R" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="F9a-6w-Efg" userLabel="Fallback Avatar">
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="F9a-6w-Efg" userLabel="Fallback Avatar">
                         <rect key="frame" x="16" y="4" width="32" height="32"/>
                         <color key="backgroundColor" red="1" green="0.5" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
                         <constraints>
diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
index 3d5c8f1..deacaad 100644
--- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewController.swift
@@ -47,7 +47,7 @@
     var bottomOffset: CGFloat = 0
     let scrollOffsetThreshold: CGFloat = 600
 
-    fileprivate var backgroundColorObservable: Observable<UIColor>!
+    fileprivate var fallbackBGColorObservable: Observable<UIColor>!
 
     override func viewDidLoad() {
         super.viewDidLoad()
@@ -93,7 +93,7 @@
         self.viewModel.userName.asObservable().bind(to: self.navigationItem.rx.title).disposed(by: disposeBag)
 
         // UIColor that observes "best Id" prefix
-        self.backgroundColorObservable = viewModel.userName.asObservable()
+        self.fallbackBGColorObservable = viewModel.userName.asObservable()
             .observeOn(MainScheduler.instance)
             .map { name in
                 let scanner = Scanner(string: name.toMD5HexString().prefixString())
@@ -443,20 +443,36 @@
             if messageVM.sequencing == .lastOfSequence || messageVM.sequencing == .singleMessage {
                 cell.profileImage?.isHidden = false
 
+                // Set placeholder avatar
+                fallbackAvatar.text = nil
+                let name = viewModel.userName.value
+                let scanner = Scanner(string: name.toMD5HexString().prefixString())
+                var index: UInt64 = 0
+                if scanner.scanHexInt64(&index) {
+                    fallbackAvatar.isHidden = false
+                    fallbackAvatar.backgroundColor = avatarColors[Int(index)]
+                    if viewModel.conversation.recipientRingId != name {
+                        fallbackAvatar.text = name.prefixString().capitalized
+                    }
+                }
+
+                // Observe in case of a lookup
+                self.fallbackBGColorObservable
+                    .subscribe(onNext: { [weak fallbackAvatar] backgroundColor in
+                        fallbackAvatar?.backgroundColor = backgroundColor
+                    })
+                    .disposed(by: cell.disposeBag)
+
                 // Avatar placeholder initial
                 viewModel.userName.asObservable()
                     .observeOn(MainScheduler.instance)
+                    .filter({ [weak self] userName in
+                        return userName != self?.viewModel.conversation.recipientRingId
+                    })
                     .map { value in value.prefixString().capitalized }
                     .bind(to: fallbackAvatar.rx.text)
                     .disposed(by: cell.disposeBag)
 
-                // Set placeholder avatar to backgroundColorObservable
-                self.backgroundColorObservable
-                    .subscribe(onNext: { backgroundColor in
-                        fallbackAvatar.backgroundColor = backgroundColor
-                    })
-                    .disposed(by: cell.disposeBag)
-
                 // Set image if any
                 cell.profileImage?.image = nil
                 if let imageData = viewModel.profileImageData {
@@ -479,19 +495,13 @@
 
     func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
         if let messageViewModel = self.messageViewModels?[indexPath.row] {
-            if messageViewModel.bubblePosition() == .received {
-                let cell = tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellReceived.self)
-                formatCell(withCell: cell, cellForRowAt: indexPath, withMessageVM: messageViewModel)
-                return cell
-            } else if messageViewModel.bubblePosition() == .sent {
-                let cell = tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellSent.self)
-                formatCell(withCell: cell, cellForRowAt: indexPath, withMessageVM: messageViewModel)
-                return cell
-            } else if messageViewModel.bubblePosition() == .generated {
-                let cell = tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellGenerated.self)
-                formatCell(withCell: cell, cellForRowAt: indexPath, withMessageVM: messageViewModel)
-                return cell
-            }
+            let type =  messageViewModel.bubblePosition() == .received ? MessageCellReceived.self :
+                        messageViewModel.bubblePosition() == .sent ? MessageCellSent.self :
+                        messageViewModel.bubblePosition() == .generated ? MessageCellGenerated.self :
+                        MessageCellGenerated.self
+            let cell = tableView.dequeueReusableCell(for: indexPath, cellType: type)
+            formatCell(withCell: cell, cellForRowAt: indexPath, withMessageVM: messageViewModel)
+            return cell
         }
 
         return tableView.dequeueReusableCell(for: indexPath, cellType: MessageCellSent.self)
diff --git a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
index 1987d40..7db0d96 100644
--- a/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
+++ b/Ring/Ring/Features/Conversations/Conversation/ConversationViewModel.swift
@@ -112,7 +112,7 @@
                 .disposed(by: disposeBag)
 
             if let contactUserName = contact?.userName {
-                self.userName.onNext(contactUserName)
+                self.userName.value = contactUserName
             } else {
 
                 let recipientRingId = self.conversation.recipientRingId
@@ -124,10 +124,10 @@
                             lookupNameResponse.address == recipientRingId
                     }).subscribe(onNext: { [unowned self] lookupNameResponse in
                         if let name = lookupNameResponse.name, !name.isEmpty {
-                            self.userName.onNext(name)
+                            self.userName.value = name
                             contact?.userName = name
                         } else if let address = lookupNameResponse.address {
-                            self.userName.onNext(address)
+                            self.userName.value = address
                         }
                     }).disposed(by: disposeBag)
 
@@ -154,7 +154,7 @@
 
     var messages: Observable<[MessageViewModel]>!
 
-    var userName = BehaviorSubject(value: "")
+    var userName = Variable<String>("")
 
     var profileImageData: Data?
 
diff --git a/Ring/Ring/Features/Conversations/SmartList/Cells/ConversationCell.xib b/Ring/Ring/Features/Conversations/SmartList/Cells/ConversationCell.xib
index 5a700a4..827912b 100644
--- a/Ring/Ring/Features/Conversations/SmartList/Cells/ConversationCell.xib
+++ b/Ring/Ring/Features/Conversations/SmartList/Cells/ConversationCell.xib
@@ -31,7 +31,7 @@
                             </userDefinedRuntimeAttribute>
                         </userDefinedRuntimeAttributes>
                     </imageView>
-                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="R" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e0z-cM-gKq" userLabel="Fallback Avatar">
+                    <label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="e0z-cM-gKq" userLabel="Fallback Avatar">
                         <rect key="frame" x="16" y="18" width="40" height="40"/>
                         <color key="backgroundColor" red="1" green="0.5" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
                         <fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
diff --git a/Ring/Ring/Features/Conversations/SmartList/SmartlistViewController.swift b/Ring/Ring/Features/Conversations/SmartList/SmartlistViewController.swift
index c018734..2fd81bc 100644
--- a/Ring/Ring/Features/Conversations/SmartList/SmartlistViewController.swift
+++ b/Ring/Ring/Features/Conversations/SmartList/SmartlistViewController.swift
@@ -152,8 +152,23 @@
                     .disposed(by: cell.disposeBag)
 
                 // Avatar placeholder initial
+                cell.fallbackAvatar.text = nil
+                let name = item.userName.value
+                let scanner = Scanner(string: name.toMD5HexString().prefixString())
+                var index: UInt64 = 0
+                if scanner.scanHexInt64(&index) {
+                    cell.fallbackAvatar.isHidden = false
+                    cell.fallbackAvatar.backgroundColor = avatarColors[Int(index)]
+                    if item.conversation.recipientRingId != name {
+                        cell.fallbackAvatar.text = name.prefixString().capitalized
+                    }
+                }
+
                 item.userName.asObservable()
                     .observeOn(MainScheduler.instance)
+                    .filter({ [weak item] userName in
+                        return userName != item?.conversation.recipientRingId
+                    })
                     .map { value in value.prefixString().capitalized }
                     .bind(to: cell.fallbackAvatar.rx.text)
                     .disposed(by: cell.disposeBag)