Commit 252a94ab authored by Andreas Traczyk's avatar Andreas Traczyk Committed by Kateryna Kostiuk

UI/UX: refactor smartlist

Change-Id: Ibfd5154757908ebd85f4b0060da00a7c608a0e56
Reviewed-by: Kateryna Kostiuk's avatarKateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
parent 9d8b7923
......@@ -227,7 +227,9 @@ SET(ringclient_OTHERS
src/delegates/ImageManipulationDelegate.h
src/AccountSelectionManager.h
src/AccountSelectionManager.mm
src/utils.h)
src/utils.h
src/NSString+Extensions.h
src/NSString+Extensions.mm)
SET(ringclient_XIBS
......@@ -275,6 +277,7 @@ SET_SOURCE_FILES_PROPERTIES(${myApp_ICON} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
SET(ring_ICONS
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_action_block.png
${CMAKE_CURRENT_SOURCE_DIR}/data/dark/ic_attachment.png
${CMAKE_CURRENT_SOURCE_DIR}/data/default_avatar_overlay.png
${CMAKE_CURRENT_SOURCE_DIR}/data/symbol_name.png
......
......@@ -88,6 +88,18 @@
return self;
}
-(void) clearData {
cachedConv_ = nil;
convUid_ = "";
convModel_ = nil;
[messagesViewVC clearData];
QObject::disconnect(modelSortedConnection_);
QObject::disconnect(filterChangedConnection_);
QObject::disconnect(newConversationConnection_);
QObject::disconnect(conversationRemovedConnection_);
}
-(const lrc::api::conversation::Info*) getCurrentConversation
{
if (convModel_ == nil || convUid_.empty())
......@@ -282,6 +294,8 @@
return;
}
[self clearData];
if (!animate) {
[self.view setHidden:YES];
return;
......
......@@ -30,6 +30,7 @@
@interface MessagesVC : NSViewController
-(void)setConversationUid:(const std::string)convUid model:(lrc::api::ConversationModel*)model;
-(void)clearData;
@property (retain, nonatomic) id <MessagesVCDelegate> delegate;
......
/*
* Copyright (C) 2015-2018 Savoir-faire Linux Inc.
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
......@@ -89,6 +90,16 @@ typedef NS_ENUM(NSInteger, MessageSequencing) {
[conversationView registerNib:cellNib forIdentifier:@"RightOngoingFileView"];
[conversationView registerNib:cellNib forIdentifier:@"RightFinishedFileView"];
}
-(void) clearData {
cachedConv_ = nil;
convUid_ = "";
convModel_ = nil;
QObject::disconnect(modelSortedSignal_);
QObject::disconnect(filterChangedSignal_);
QObject::disconnect(interactionStatusUpdatedSignal_);
QObject::disconnect(newInteractionSignal_);
}
-(const lrc::api::conversation::Info*) getCurrentConversation
{
......@@ -390,8 +401,6 @@ typedef NS_ENUM(NSInteger, MessageSequencing) {
} else {
result = [tableView makeViewWithIdentifier:@"LeftMessageView" owner:self];
}
if (interaction.status == lrc::api::interaction::Status::UNREAD)
convModel_->setInteractionRead(convUid_, it->first);
break;
case lrc::api::interaction::Type::INCOMING_DATA_TRANSFER:
case lrc::api::interaction::Type::OUTGOING_DATA_TRANSFER:
......
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import <Foundation/Foundation.h>
@interface NSString (Extensions)
- (NSString *) removeAllNewLinesAtTheEnd;
- (NSString *) removeEmptyLinesAtBorders;
@end
/*
* Copyright (C) 2018 Savoir-faire Linux Inc.
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#import "NSString+Extensions.h"
@implementation NSString (Extensions)
- (NSString *) removeAllNewLinesAtTheEnd {
NSString *result = self;
while ([result endedByEmptyLine]) {
result = [result removeLastWhiteSpaceAndNewLineCharacter];
}
return result;
}
- (NSString *) removeAllNewLinesAtBegining {
NSString *result = self;
while ([result startByEmptyLine]) {
result = [result removeFirstWhiteSpaceAndNewLineCharacter];
}
return result;
}
- (NSString *) removeEmptyLinesAtBorders {
NSString *result = self;
result = [result removeAllNewLinesAtBegining];
result = [result removeAllNewLinesAtTheEnd];
return result;
}
-(bool)endedByEmptyLine {
if ([self length] < 1) {
return false;
}
unichar last = [self characterAtIndex:[self length] - 1];
return [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:last];
}
- (bool)startByEmptyLine {
if ([self length] < 1) {
return false;
}
unichar first = [self characterAtIndex:0];
return [[NSCharacterSet whitespaceAndNewlineCharacterSet] characterIsMember:first];
}
- (NSString *) removeLastWhiteSpaceAndNewLineCharacter {
if ([self endedByEmptyLine]) {
return [self substringToIndex:[self length]-1];
}
return self;
}
- (NSString *) removeFirstWhiteSpaceAndNewLineCharacter {
if ([self startByEmptyLine]) {
return [self substringFromIndex:1];
}
return self;
}
@end
......@@ -21,6 +21,9 @@
#import "SmartViewVC.h"
//std
#import <sstream>
//Qt
#import <QtMacExtras/qmacfunctions.h>
#import <QPixmap>
......@@ -54,11 +57,12 @@
__unsafe_unretained IBOutlet RingTableView* smartView;
__unsafe_unretained IBOutlet NSSearchField* searchField;
__strong IBOutlet NSSegmentedControl *listTypeSelector;
__strong IBOutlet NSLayoutConstraint *listTypeSelectorHeight;
bool selectorIsPresent;
QMetaObject::Connection modelSortedConnection_, modelUpdatedConnection_, filterChangedConnection_, newConversationConnection_, conversationRemovedConnection_, interactionStatusUpdatedConnection_, conversationClearedConnection;
QMetaObject::Connection modelSortedConnection_, modelUpdatedConnection_, filterChangedConnection_, newConversationConnection_, conversationRemovedConnection_, newInteractionConnection_, interactionStatusUpdatedConnection_, conversationClearedConnection;
lrc::api::ConversationModel* model_;
lrc::api::ConversationModel* convModel_;
std::string selectedUid_;
lrc::api::profile::Type currentFilterType;
......@@ -74,12 +78,16 @@
// Tags for views
NSInteger const IMAGE_TAG = 100;
NSInteger const DISPLAYNAME_TAG = 200;
NSInteger const DETAILS_TAG = 300;
NSInteger const CALL_BUTTON_TAG = 400;
NSInteger const TXT_BUTTON_TAG = 500;
NSInteger const CANCEL_BUTTON_TAG = 600;
NSInteger const RING_ID_LABEL = 700;
NSInteger const PRESENCE_TAG = 800;
NSInteger const NOTIFICATONS_TAG = 300;
NSInteger const RING_ID_LABEL = 400;
NSInteger const PRESENCE_TAG = 500;
NSInteger const TOTALMSGS_TAG = 600;
NSInteger const TOTALINVITES_TAG = 700;
NSInteger const DATE_TAG = 800;
NSInteger const SNIPPET_TAG = 900;
NSInteger const ADD_BUTTON_TAG = 1000;
NSInteger const REFUSE_BUTTON_TAG = 1100;
NSInteger const BLOCK_BUTTON_TAG = 1200;
// Segment indices for smartlist selector
NSInteger const CONVERSATION_SEG = 0;
......@@ -109,6 +117,9 @@ NSInteger const REQUEST_SEG = 1;
currentFilterType = lrc::api::profile::Type::RING;
selectorIsPresent = true;
smartView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
}
- (void)placeCall:(id)sender
......@@ -120,57 +131,71 @@ NSInteger const REQUEST_SEG = 1;
row = [smartView selectedRow];
else
return;
if (model_ == nil)
if (convModel_ == nil)
return;
auto conv = convModel_->filteredConversation(row);
convModel_->placeCall(conv.uid);
}
-(void) reloadSelectorNotifications
{
NSTextField* totalMsgsCount = [self.view viewWithTag:TOTALMSGS_TAG];
NSTextField* totalInvites = [self.view viewWithTag:TOTALINVITES_TAG];
if (!selectorIsPresent) {
[totalMsgsCount setHidden:true];
[totalInvites setHidden:true];
return;
}
auto conv = model_->filteredConversation(row);
model_->placeCall(conv.uid);
auto ringConversations = convModel_->getFilteredConversations(lrc::api::profile::Type::RING);
int totalUnreadMessages = 0;
std::for_each(ringConversations.begin(), ringConversations.end(),
[&totalUnreadMessages, self] (const auto& conversation) {
totalUnreadMessages += convModel_->getNumberOfUnreadMessagesFor(conversation.uid);
});
[totalMsgsCount setHidden:(totalUnreadMessages == 0)];
[totalMsgsCount setIntValue:totalUnreadMessages];
auto totalRequests = [self chosenAccount].contactModel->pendingRequestCount();
[totalInvites setHidden:(totalRequests == 0)];
[totalInvites setIntValue:totalRequests];
}
-(void) reloadData
{
NSLog(@"reload");
[smartView deselectAll:nil];
if (model_ == nil)
if (convModel_ == nil)
return;
if (!model_->owner.contactModel->hasPendingRequests()) {
[self reloadSelectorNotifications];
if (!convModel_->owner.contactModel->hasPendingRequests()) {
if (currentFilterType == lrc::api::profile::Type::PENDING) {
[self selectConversationList];
}
if (selectorIsPresent) {
[listTypeSelector removeFromSuperview];
listTypeSelectorHeight.constant = 0.0;
[listTypeSelector setHidden:YES];
selectorIsPresent = false;
}
} else {
if (!selectorIsPresent) {
// First we restore the selector with selection on "Conversations"
[self.view addSubview:listTypeSelector];
[listTypeSelector setSelected:YES forSegment:CONVERSATION_SEG];
// Then constraints are recreated (as these are lost when calling removeFromSuperview)
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[searchField]-8-[listTypeSelector]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(searchField, listTypeSelector)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[listTypeSelector]-8-[tabbar]"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(listTypeSelector, tabbar)]];
[self.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|-20-[listTypeSelector]-20-|"
options:0
metrics:nil
views:NSDictionaryOfVariableBindings(listTypeSelector)]];
listTypeSelectorHeight.constant = 18.0;
[listTypeSelector setHidden:NO];
selectorIsPresent = true;
}
}
[smartView reloadData];
if (!selectedUid_.empty() && model_ != nil) {
auto it = getConversationFromUid(selectedUid_, *model_);
if (it != model_->allFilteredConversations().end()) {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - model_->allFilteredConversations().begin())];
if (!selectedUid_.empty() && convModel_ != nil) {
auto it = getConversationFromUid(selectedUid_, *convModel_);
if (it != convModel_->allFilteredConversations().end()) {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
[smartView selectRowIndexes:indexSet byExtendingSelection:NO];
}
}
......@@ -180,69 +205,80 @@ NSInteger const REQUEST_SEG = 1;
-(void) reloadConversationWithUid:(NSString *)uid
{
if (model_ != nil) {
auto it = getConversationFromUid(std::string([uid UTF8String]), *model_);
if (it != model_->allFilteredConversations().end()) {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - model_->allFilteredConversations().begin())];
NSLog(@"reloadConversationWithUid: %@", uid);
[smartView reloadDataForRowIndexes:indexSet
columnIndexes:[NSIndexSet indexSetWithIndex:0]];
}
if (convModel_ == nil) {
return;
}
auto it = getConversationFromUid(std::string([uid UTF8String]), *convModel_);
if (it != convModel_->allFilteredConversations().end()) {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
NSLog(@"reloadConversationWithUid: %@", uid);
[smartView reloadDataForRowIndexes:indexSet
columnIndexes:[NSIndexSet indexSetWithIndex:0]];
}
}
- (BOOL)setConversationModel:(lrc::api::ConversationModel *)conversationModel
{
if (model_ != conversationModel) {
model_ = conversationModel;
selectedUid_.clear(); // Clear selected conversation as the selected account is being changed
QObject::disconnect(modelSortedConnection_);
QObject::disconnect(modelUpdatedConnection_);
QObject::disconnect(filterChangedConnection_);
QObject::disconnect(newConversationConnection_);
QObject::disconnect(conversationRemovedConnection_);
QObject::disconnect(interactionStatusUpdatedConnection_);
QObject::disconnect(conversationClearedConnection);
[self reloadData];
if (model_ != nil) {
modelSortedConnection_ = QObject::connect(model_, &lrc::api::ConversationModel::modelSorted,
[self] (){
[self reloadData];
});
modelUpdatedConnection_ = QObject::connect(model_, &lrc::api::ConversationModel::conversationUpdated,
[self] (const std::string& uid){
[self reloadConversationWithUid: [NSString stringWithUTF8String:uid.c_str()]];
});
filterChangedConnection_ = QObject::connect(model_, &lrc::api::ConversationModel::filterChanged,
if (convModel_ == conversationModel) {
return false;
}
convModel_ = conversationModel;
selectedUid_.clear(); // Clear selected conversation as the selected account is being changed
QObject::disconnect(modelSortedConnection_);
QObject::disconnect(modelUpdatedConnection_);
QObject::disconnect(filterChangedConnection_);
QObject::disconnect(newConversationConnection_);
QObject::disconnect(conversationRemovedConnection_);
QObject::disconnect(conversationClearedConnection);
QObject::disconnect(interactionStatusUpdatedConnection_);
QObject::disconnect(newInteractionConnection_);
[self reloadData];
if (convModel_ != nil) {
modelSortedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::modelSorted,
[self] (){
[self reloadData];
});
newConversationConnection_ = QObject::connect(model_, &lrc::api::ConversationModel::newConversation,
[self] (const std::string& uid) {
[self reloadData];
[self updateConversationForNewContact:[NSString stringWithUTF8String:uid.c_str()]];
});
conversationRemovedConnection_ = QObject::connect(model_, &lrc::api::ConversationModel::conversationRemoved,
[self] (){
[self reloadData];
});
conversationClearedConnection = QObject::connect(model_, &lrc::api::ConversationModel::conversationCleared,
[self] (const std::string& id){
[self deselect];
[delegate listTypeChanged];
});
interactionStatusUpdatedConnection_ = QObject::connect(model_, &lrc::api::ConversationModel::interactionStatusUpdated,
[self] (const std::string& convUid) {
if (convUid != selectedUid_)
return;
[self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
});
model_->setFilter(""); // Reset the filter
}
[searchField setStringValue:@""];
return YES;
modelUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationUpdated,
[self] (const std::string& convUid){
[self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
});
filterChangedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::filterChanged,
[self] (){
[self reloadData];
});
newConversationConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newConversation,
[self] (const std::string& convUid) {
[self reloadData];
[self updateConversationForNewContact:[NSString stringWithUTF8String:convUid.c_str()]];
});
conversationRemovedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationRemoved,
[self] (){
[delegate listTypeChanged];
[self reloadData];
});
conversationClearedConnection = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationCleared,
[self] (const std::string& convUid){
[self deselect];
[delegate listTypeChanged];
});
interactionStatusUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::interactionStatusUpdated,
[self] (const std::string& convUid) {
if (convUid != selectedUid_)
return;
[self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
});
newInteractionConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newInteraction,
[self](const std::string& convUid, uint64_t interactionId, const lrc::api::interaction::Info& interaction){
if (convUid == selectedUid_) {
convModel_->clearUnreadInteractions(convUid);
}
});
convModel_->setFilter(""); // Reset the filter
}
return NO;
[searchField setStringValue:@""];
return true;
}
-(void)selectConversation:(const lrc::api::conversation::Info&)conv model:(lrc::api::ConversationModel*)model;
......@@ -253,13 +289,15 @@ NSInteger const REQUEST_SEG = 1;
[self setConversationModel:model];
if (model_ != nil) {
auto it = getConversationFromUid(selectedUid_, *model_);
if (it != model_->allFilteredConversations().end()) {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - model_->allFilteredConversations().begin())];
[smartView selectRowIndexes:indexSet byExtendingSelection:NO];
selectedUid_ = uid;
}
if (convModel_ == nil) {
return;
}
auto it = getConversationFromUid(selectedUid_, *convModel_);
if (it != convModel_->allFilteredConversations().end()) {
NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
[smartView selectRowIndexes:indexSet byExtendingSelection:NO];
selectedUid_ = uid;
}
}
......@@ -270,7 +308,7 @@ NSInteger const REQUEST_SEG = 1;
}
-(void) clearConversationModel {
model_ = nil;
convModel_ = nil;
[self deselect];
[smartView reloadData];
if (selectorIsPresent) {
......@@ -281,18 +319,19 @@ NSInteger const REQUEST_SEG = 1;
- (IBAction) listTypeChanged:(id)sender
{
selectedUid_.clear();
NSInteger selectedItem = [sender selectedSegment];
switch (selectedItem) {
case CONVERSATION_SEG:
if (currentFilterType != lrc::api::profile::Type::RING) {
model_->setFilter(lrc::api::profile::Type::RING);
convModel_->setFilter(lrc::api::profile::Type::RING);
[delegate listTypeChanged];
currentFilterType = lrc::api::profile::Type::RING;
}
break;
case REQUEST_SEG:
if (currentFilterType != lrc::api::profile::Type::PENDING) {
model_->setFilter(lrc::api::profile::Type::PENDING);
convModel_->setFilter(lrc::api::profile::Type::PENDING);
[delegate listTypeChanged];
currentFilterType = lrc::api::profile::Type::PENDING;
}
......@@ -311,8 +350,8 @@ NSInteger const REQUEST_SEG = 1;
// Do not invert order of the next two lines or stack overflow
// may happen on -(void) reloadData call if filter is currently set to PENDING
currentFilterType = lrc::api::profile::Type::RING;
model_->setFilter(lrc::api::profile::Type::RING);
model_->setFilter("");
convModel_->setFilter(lrc::api::profile::Type::RING);
convModel_->setFilter("");
}
-(void) selectPendingList
......@@ -322,8 +361,8 @@ NSInteger const REQUEST_SEG = 1;
[listTypeSelector setSelectedSegment:REQUEST_SEG];
currentFilterType = lrc::api::profile::Type::PENDING;
model_->setFilter(lrc::api::profile::Type::PENDING);
model_->setFilter("");
convModel_->setFilter(lrc::api::profile::Type::PENDING);
convModel_->setFilter("");
}
#pragma mark - NSTableViewDelegate methods
......@@ -342,15 +381,26 @@ NSInteger const REQUEST_SEG = 1;
{
NSInteger row = [notification.object selectedRow];
[smartView enumerateAvailableRowViewsUsingBlock:^(NSTableRowView *rowView, NSInteger row){
NSTableRowView* cellRowView = [smartView rowViewAtRow:row makeIfNecessary:NO];
if(rowView.selected){
cellRowView.backgroundColor = [NSColor controlColor];
}else{
cellRowView.backgroundColor = [NSColor whiteColor];
}
}];
if (row == -1)
return;
if (model_ == nil)
if (convModel_ == nil)
return;
auto uid = model_->filteredConversation(row).uid;
auto uid = convModel_->filteredConversation(row).uid;
if (selectedUid_ != uid) {
selectedUid_ = uid;
model_->selectConversation(uid);
convModel_->selectConversation(uid);
convModel_->clearUnreadInteractions(uid);
[self reloadSelectorNotifications];
}
}
......@@ -361,56 +411,105 @@ NSInteger const REQUEST_SEG = 1;
- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
{
if (model_ == nil)
if (convModel_ == nil)
return nil;
auto conversation = model_->filteredConversation(row);
auto conversation = convModel_->filteredConversation(row);
NSTableCellView* result;
result = [tableView makeViewWithIdentifier:@"MainCell" owner:tableView];
// NSTextField* details = [result viewWithTag:DETAILS_TAG];
NSMutableArray* controls = [NSMutableArray arrayWithObject:[result viewWithTag:CALL_BUTTON_TAG]];
[((ContextualTableCellView*) result) setContextualsControls:controls];
[((ContextualTableCellView*) result) setShouldBlurParentView:YES];
// if (auto call = RecentModel::instance().getActiveCall(qIdx)) {
// [details setStringValue:call->roleData((int)Ring::Role::FormattedState).toString().toNSString()];
// [((ContextualTableCellView*) result) setActiveState:YES];
// } else {
// [details setStringValue:qIdx.data((int)Ring::Role::FormattedLastUsed).toString().toNSString()];
// [((ContextualTableCellView*) result) setActiveState:NO];
// }
NSTextField* unreadCount = [result viewWithTag:TXT_BUTTON_TAG];
NSTextField* unreadCount = [result viewWithTag:NOTIFICATONS_TAG];
[unreadCount setHidden:(conversation.unreadMessages == 0)];
[unreadCount setIntValue:conversation.unreadMessages];
NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
NSString* displayNameString = bestNameForConversation(conversation, *model_);
NSString* displayIDString = bestIDForConversation(conversation, *model_);
NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
NSString* displayNameString = bestNameForConversation(conversation, *convModel_);
NSString* displayIDString = bestIDForConversation(conversation, *convModel_);
if(displayNameString.length == 0 || [displayNameString isEqualToString:displayIDString]) {
NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];