UI/UX: refactor smartlist
Change-Id: Ibfd5154757908ebd85f4b0060da00a7c608a0e56
Reviewed-by: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
diff --git a/src/ConversationVC.mm b/src/ConversationVC.mm
index 4061e66..7327881 100644
--- a/src/ConversationVC.mm
+++ b/src/ConversationVC.mm
@@ -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;
diff --git a/src/MessagesVC.h b/src/MessagesVC.h
index f88b1e9..16819ed 100644
--- a/src/MessagesVC.h
+++ b/src/MessagesVC.h
@@ -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;
diff --git a/src/MessagesVC.mm b/src/MessagesVC.mm
index a05b8c5..17b451b 100644
--- a/src/MessagesVC.mm
+++ b/src/MessagesVC.mm
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2015-2018 Savoir-faire Linux Inc.
* Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
@@ -89,6 +90,16 @@
[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 @@
} 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:
diff --git a/src/NSString+Extensions.h b/src/NSString+Extensions.h
new file mode 100644
index 0000000..7cd7506
--- /dev/null
+++ b/src/NSString+Extensions.h
@@ -0,0 +1,27 @@
+/*
+ * 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
diff --git a/src/NSString+Extensions.mm b/src/NSString+Extensions.mm
new file mode 100644
index 0000000..d843ee2
--- /dev/null
+++ b/src/NSString+Extensions.mm
@@ -0,0 +1,77 @@
+/*
+ * 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
diff --git a/src/SmartViewVC.mm b/src/SmartViewVC.mm
index 547cb18..8ee6565 100755
--- a/src/SmartViewVC.mm
+++ b/src/SmartViewVC.mm
@@ -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 @@
currentFilterType = lrc::api::profile::Type::RING;
selectorIsPresent = true;
+
+ smartView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
+
}
- (void)placeCall:(id)sender
@@ -120,57 +131,71 @@
row = [smartView selectedRow];
else
return;
- if (model_ == nil)
+ if (convModel_ == nil)
return;
- auto conv = model_->filteredConversation(row);
- model_->placeCall(conv.uid);
+ 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 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 @@
-(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 @@
[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 @@
}
-(void) clearConversationModel {
- model_ = nil;
+ convModel_ = nil;
[self deselect];
[smartView reloadData];
if (selectorIsPresent) {
@@ -281,18 +319,19 @@
- (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 @@
// 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 @@
[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 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 @@
- (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];
[displayName setStringValue:displayIDString];
[displayRingID setHidden:YES];
}
else {
- NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
[displayName setStringValue:displayNameString];
[displayRingID setStringValue:displayIDString];
[displayRingID setHidden:NO];
}
- NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
+ NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
auto& imageManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
- [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(conversation, model_->owner)))];
+ [photoView setImage:QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(conversation, convModel_->owner)))];
NSView* presenceView = [result viewWithTag:PRESENCE_TAG];
- if (model_->owner.contactModel->getContact(conversation.participants[0]).isPresent) {
+ auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
+ if (contact.isPresent) {
[presenceView setHidden:NO];
} else {
[presenceView setHidden:YES];
}
+ NSTextField* lastInteractionDate = [result viewWithTag:DATE_TAG];
+ NSTextField* interactionSnippet = [result viewWithTag:SNIPPET_TAG];
+ NSButton* addContactButton = [result viewWithTag:ADD_BUTTON_TAG];
+ NSButton* refuseContactButton = [result viewWithTag:REFUSE_BUTTON_TAG];
+ NSButton* blockContactButton = [result viewWithTag:BLOCK_BUTTON_TAG];
+ [addContactButton setHidden:YES];
+ [refuseContactButton setHidden:YES];
+ [blockContactButton setHidden:YES];
+
+ if (profileType(conversation, *convModel_) == lrc::api::profile::Type::PENDING) {
+ [lastInteractionDate setHidden:true];
+ [interactionSnippet setHidden:true];
+ [addContactButton setHidden:NO];
+ [refuseContactButton setHidden:NO];
+ [blockContactButton setHidden:NO];
+ [addContactButton setAction:@selector(acceptInvitation:)];
+ [addContactButton setTarget:self];
+ [refuseContactButton setAction:@selector(refuseInvitation:)];
+ [refuseContactButton setTarget:self];
+ [blockContactButton setAction:@selector(blockPendingContact:)];
+ [blockContactButton setTarget:self];
+ return result;
+ }
+
+ [lastInteractionDate setHidden:false];
+
+ [interactionSnippet setHidden:false];
+
+ auto lastUid = conversation.lastMessageUid;
+ if (conversation.interactions.find(lastUid) != conversation.interactions.end()) {
+ // last interaction snippet
+ std::string lastInteractionSnippet = conversation.interactions[lastUid].body;
+ std::stringstream ss(lastInteractionSnippet);
+ std::getline(ss, lastInteractionSnippet);
+ NSString* lastInteractionSnippetFixedString = [[NSString stringWithUTF8String:lastInteractionSnippet.c_str()]
+ stringByReplacingOccurrencesOfString:@"🕽" withString:@""];
+ lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString stringByReplacingOccurrencesOfString:@"📞" withString:@""];
+ if (conversation.interactions[lastUid].type == lrc::api::interaction::Type::OUTGOING_DATA_TRANSFER
+ || conversation.interactions[lastUid].type == lrc::api::interaction::Type::INCOMING_DATA_TRANSFER) {
+ if (([lastInteractionSnippetFixedString rangeOfString:@"/"].location != NSNotFound)) {
+ NSArray *listItems = [lastInteractionSnippetFixedString componentsSeparatedByString:@"/"];
+ NSString* name = listItems.lastObject;
+ lastInteractionSnippetFixedString = name;
+ }
+ }
+ [interactionSnippet setStringValue:lastInteractionSnippetFixedString];
+
+ // last interaction date/time
+ std::time_t lastInteractionTimestamp = conversation.interactions[lastUid].timestamp;
+ std::time_t now = std::time(nullptr);
+ char interactionDay[64];
+ char nowDay[64];
+ std::strftime(interactionDay, sizeof(interactionDay), "%D", std::localtime(&lastInteractionTimestamp));
+ std::strftime(nowDay, sizeof(nowDay), "%D", std::localtime(&now));
+ if (std::string(interactionDay) == std::string(nowDay)) {
+ char interactionTime[64];
+ std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(&lastInteractionTimestamp));
+ [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionTime]];
+ } else {
+ [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionDay]];
+ }
+ }
+
return result;
}
@@ -423,9 +522,8 @@
- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
{
- if (tableView == smartView) {
- if (model_ != nullptr)
- return model_->allFilteredConversations().size();
+ if (tableView == smartView && convModel_ != nullptr) {
+ return convModel_->allFilteredConversations().size();
}
return 0;
@@ -442,16 +540,16 @@
if (row == -1)
return;
- if (model_ == nil)
+ if (convModel_ == nil)
return;
- auto conv = model_->filteredConversation(row);
+ auto conv = convModel_->filteredConversation(row);
auto& callId = conv.callId;
if (callId.empty())
return;
- auto* callModel = model_->owner.callModel.get();
+ auto* callModel = convModel_->owner.callModel.get();
callModel->hangUp(callId);
}
@@ -468,14 +566,16 @@
- (void) processSearchFieldInput
{
- if (model_ == nil)
- return;
- model_->setFilter(std::string([[searchField stringValue] UTF8String]));
+ if (convModel_ == nil) {
+ return;
+ }
+
+ convModel_->setFilter(std::string([[searchField stringValue] UTF8String]));
}
-(const lrc::api::account::Info&) chosenAccount
{
- return model_->owner;
+ return convModel_->owner;
}
- (void) clearSearchField
@@ -485,18 +585,18 @@
}
-(void)updateConversationForNewContact:(NSString *)uId {
- if (model_ == nil) {
+ if (convModel_ == nil) {
return;
}
[self clearSearchField];
auto uid = std::string([uId UTF8String]);
- auto it = getConversationFromUid(uid, *model_);
- if (it != model_->allFilteredConversations().end()) {
+ auto it = getConversationFromUid(uid, *convModel_);
+ if (it != convModel_->allFilteredConversations().end()) {
@try {
- auto contact = model_->owner.contactModel->getContact(it->participants[0]);
+ auto contact = convModel_->owner.contactModel->getContact(it->participants[0]);
if (!contact.profileInfo.uri.empty() && contact.profileInfo.uri.compare(selectedUid_) == 0) {
selectedUid_ = uid;
- model_->selectConversation(uid);
+ convModel_->selectConversation(uid);
}
} @catch (NSException *exception) {
return;
@@ -523,25 +623,25 @@
if (commandSelector != @selector(insertNewline:) || [[searchField stringValue] isEqual:@""]) {
return NO;
}
- if (model_ == nil) {
+ if (convModel_ == nil) {
[self displayErrorModalWithTitle:NSLocalizedString(@"No account available", @"Displayed as RingID when no accounts are available for selection") WithMessage:NSLocalizedString(@"Navigate to preferences to create a new account", @"Allert message when no accounts are available")];
return NO;
}
- if (model_->allFilteredConversations().size() <= 0) {
+ if (convModel_->allFilteredConversations().size() <= 0) {
return YES;
}
- auto model = model_->filteredConversation(0);
+ auto model = convModel_->filteredConversation(0);
auto uid = model.uid;
if (selectedUid_ == uid) {
return YES;
}
@try {
- auto contact = model_->owner.contactModel->getContact(model.participants[0]);
+ auto contact = convModel_->owner.contactModel->getContact(model.participants[0]);
if ((contact.profileInfo.uri.empty() && contact.profileInfo.type != lrc::api::profile::Type::SIP) || contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
return YES;
}
selectedUid_ = uid;
- model_->selectConversation(uid);
+ convModel_->selectConversation(uid);
[self.view.window makeFirstResponder: smartView];
return YES;
} @catch (NSException *exception) {
@@ -581,24 +681,24 @@
{
if ([smartView selectedRow] == -1)
return;
- if (model_ == nil)
+ if (convModel_ == nil)
return ;
- auto uid = model_->filteredConversation([smartView selectedRow]).uid;
- model_->makePermanent(uid);
+ auto uid = convModel_->filteredConversation([smartView selectedRow]).uid;
+ convModel_->makePermanent(uid);
}
#pragma mark - ContextMenuDelegate
- (NSMenu*) contextualMenuForRow:(int) index
{
- if (model_ == nil)
+ if (convModel_ == nil)
return nil;
- auto conversation = model_->filteredConversation(NSInteger(index));
+ auto conversation = convModel_->filteredConversation(NSInteger(index));
@try {
- auto contact = model_->owner.contactModel->getContact(conversation.participants[0]);
+ auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
if (contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
return nil;
}
@@ -671,7 +771,7 @@
}
NSString * convUId = (NSString*)menuObject;
std::string conversationID = std::string([convUId UTF8String]);
- model_->makePermanent(conversationID);
+ convModel_->makePermanent(conversationID);
}
- (void) blockContact: (NSMenuItem* ) item {
@@ -681,8 +781,8 @@
}
NSString * convUId = (NSString*)menuObject;
std::string conversationID = std::string([convUId UTF8String]);
- model_->clearHistory(conversationID);
- model_->removeConversation(conversationID, true);
+ //convModel_->clearHistory(conversationID);
+ convModel_->removeConversation(conversationID, true);
}
- (void) audioCall: (NSMenuItem* ) item {
@@ -692,7 +792,7 @@
}
NSString * convUId = (NSString*)menuObject;
std::string conversationID = std::string([convUId UTF8String]);
- model_->placeAudioOnlyCall(conversationID);
+ convModel_->placeAudioOnlyCall(conversationID);
}
@@ -703,7 +803,7 @@
}
NSString * convUId = (NSString*)menuObject;
std::string conversationID = std::string([convUId UTF8String]);
- model_->placeCall(conversationID);
+ convModel_->placeCall(conversationID);
}
- (void) clearConversation:(NSMenuItem* ) item {
@@ -713,7 +813,57 @@
}
NSString * convUId = (NSString*)menuObject;
std::string conversationID = std::string([convUId UTF8String]);
- model_->clearHistory(conversationID);
+ convModel_->clearHistory(conversationID);
+}
+
+- (void)acceptInvitation:(id)sender {
+ NSInteger row = [smartView rowForView:sender];
+
+ if (row == -1)
+ return;
+ if (convModel_ == nil)
+ return;
+
+ auto conv = convModel_->filteredConversation(row);
+ auto& convID = conv.Info::uid;
+
+ if (convID.empty())
+ return;
+ convModel_->makePermanent(convID);
+}
+
+- (void)refuseInvitation:(id)sender {
+ NSInteger row = [smartView rowForView:sender];
+
+ if (row == -1)
+ return;
+ if (convModel_ == nil)
+ return;
+
+ auto conv = convModel_->filteredConversation(row);
+ auto& convID = conv.Info::uid;
+
+ if (convID.empty())
+ return;
+ convModel_->removeConversation(convID);
+}
+
+- (void)blockPendingContact:(id)sender {
+ NSInteger row = [smartView rowForView:sender];
+
+ if (row == -1)
+ return;
+ if (convModel_ == nil)
+ return;
+
+ auto conv = convModel_->filteredConversation(row);
+ auto& convID = conv.Info::uid;
+
+ if (convID.empty())
+ return;
+ convModel_->removeConversation(convID, true);
+ [self deselect];
+ [delegate listTypeChanged];
}
@end
diff --git a/src/delegates/ImageManipulationDelegate.mm b/src/delegates/ImageManipulationDelegate.mm
index f2014b6..9420c65 100644
--- a/src/delegates/ImageManipulationDelegate.mm
+++ b/src/delegates/ImageManipulationDelegate.mm
@@ -277,13 +277,23 @@
return pxm;
} else {
char color = contact.profileInfo.uri.at(0);
+ contact.profileInfo.alias.erase(std::remove(contact.profileInfo.alias.begin(), contact.profileInfo.alias.end(), '\n'), contact.profileInfo.alias.end());
+ contact.profileInfo.alias.erase(std::remove(contact.profileInfo.alias.begin(), contact.profileInfo.alias.end(), ' '), contact.profileInfo.alias.end());
+ contact.profileInfo.alias.erase(std::remove(contact.profileInfo.alias.begin(), contact.profileInfo.alias.end(), '\r'), contact.profileInfo.alias.end());
if (!contact.profileInfo.alias.empty()) {
return drawDefaultUserPixmap(size, color, std::toupper(contact.profileInfo.alias.at(0)));
} else if((contact.profileInfo.type == lrc::api::profile::Type::RING ||
contact.profileInfo.type == lrc::api::profile::Type::PENDING) &&
!contact.registeredName.empty()) {
- return drawDefaultUserPixmap(size, color, std::toupper(contact.registeredName.at(0)));
+ contact.registeredName.erase(std::remove(contact.registeredName.begin(), contact.registeredName.end(), '\n'), contact.registeredName.end());
+ contact.registeredName.erase(std::remove(contact.registeredName.begin(), contact.registeredName.end(), ' '), contact.registeredName.end());
+ contact.registeredName.erase(std::remove(contact.registeredName.begin(), contact.registeredName.end(), '\r'), contact.registeredName.end());
+ if(!contact.registeredName.empty()) {
+ return drawDefaultUserPixmap(size, color, std::toupper(contact.registeredName.at(0)));
+ } else {
+ return drawDefaultUserPixmapUriOnly(size, color);
+ }
} else {
return drawDefaultUserPixmapUriOnly(size, color);
}
diff --git a/src/utils.h b/src/utils.h
index e6ba40f..1c971a6 100755
--- a/src/utils.h
+++ b/src/utils.h
@@ -18,6 +18,7 @@
*/
#import <Foundation/Foundation.h>
+#import "NSString+Extensions.h"
#import <api/conversation.h>
#import <api/conversationmodel.h>
#import <api/account.h>
@@ -28,19 +29,39 @@
static inline NSString* bestIDForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
{
auto contact = model.owner.contactModel->getContact(conv.participants[0]);
- if (!contact.registeredName.empty())
- return @(contact.registeredName.c_str());
+ if (!contact.registeredName.empty()) {
+ contact.registeredName.erase(std::remove(contact.registeredName.begin(), contact.registeredName.end(), '\n'), contact.registeredName.end());
+ contact.registeredName.erase(std::remove(contact.registeredName.begin(), contact.registeredName.end(), '\r'), contact.registeredName.end());
+ return [@(contact.registeredName.c_str()) removeEmptyLinesAtBorders];
+ }
else
- return @(contact.profileInfo.uri.c_str());
+ return [@(contact.profileInfo.uri.c_str()) removeEmptyLinesAtBorders];
}
static inline NSString* bestNameForConversation(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
{
auto contact = model.owner.contactModel->getContact(conv.participants[0]);
- if (!contact.profileInfo.alias.empty())
- return @(contact.profileInfo.alias.c_str());
- else
+ if (contact.profileInfo.alias.empty()) {
return bestIDForConversation(conv, model);
+ }
+ auto alias = contact.profileInfo.alias;
+ alias.erase(std::remove(alias.begin(), alias.end(), '\n'), alias.end());
+ alias.erase(std::remove(alias.begin(), alias.end(), '\r'), alias.end());
+ if(alias.length() == 0) {
+ return bestIDForConversation(conv, model);
+ }
+ return @(alias.c_str());
+}
+
+static inline lrc::api::profile::Type profileType(const lrc::api::conversation::Info& conv, const lrc::api::ConversationModel& model)
+{
+ @try {
+ auto contact = model.owner.contactModel->getContact(conv.participants[0]);
+ return contact.profileInfo.type;
+ }
+ @catch (NSException *exception) {
+ lrc::api::profile::Type::INVALID;
+ }
}
/**
diff --git a/src/views/IconButton.h b/src/views/IconButton.h
index 66b5350..cd71176 100644
--- a/src/views/IconButton.h
+++ b/src/views/IconButton.h
@@ -52,7 +52,7 @@
/*
* Padding
- * default value : 5.0
+ * default value : 8.0
*/
@property CGFloat imageInsets;
diff --git a/src/views/IconButton.mm b/src/views/IconButton.mm
index 96c3271..a211fc1 100644
--- a/src/views/IconButton.mm
+++ b/src/views/IconButton.mm
@@ -103,12 +103,9 @@
//// Group
{
//// Oval Drawing
- NSBezierPath* ovalPath = [NSBezierPath bezierPathWithRoundedRect:
- NSMakeRect(NSMinX(group) + floor(NSWidth(group) * 0.00000 + 0.5),
- NSMinY(group) + floor(NSHeight(group) * 0.00000 + 0.5),
- floor(NSWidth(group) * 1.00000 + 0.5) - floor(NSWidth(group) * 0.00000 + 0.5),
- floor(NSHeight(group) * 1.00000 + 0.5) - floor(NSHeight(group) * 0.00000 + 0.5))
- xRadius:[self.cornerRadius floatValue] yRadius:[self.cornerRadius floatValue]];
+ NSBezierPath* ovalPath = [NSBezierPath bezierPathWithRoundedRect:dirtyRect
+ xRadius:[self.cornerRadius floatValue]
+ yRadius:[self.cornerRadius floatValue]];
[backgroundColor setFill];
[ovalPath fill];
diff --git a/src/views/RoundedTextField.h b/src/views/RoundedTextField.h
index dafbc35..4f9e4ee 100644
--- a/src/views/RoundedTextField.h
+++ b/src/views/RoundedTextField.h
@@ -33,6 +33,11 @@
@property (nonatomic, strong) NSColor* borderColor;
/*
+ * default value : [NSNumber numberWithDouble:1.0];
+ */
+@property (nonatomic, strong) NSNumber* borderThickness;
+
+/*
* default value : (self.frame) / 2;
*/
@property (nonatomic, strong) NSNumber* cornerRadius;
diff --git a/src/views/RoundedTextField.mm b/src/views/RoundedTextField.mm
index e557b6f..719d8f2 100644
--- a/src/views/RoundedTextField.mm
+++ b/src/views/RoundedTextField.mm
@@ -34,6 +34,10 @@
self.borderColor = [self.bgColor darkenColorByValue:0.1];
}
+ if(!self.borderThickness) {
+ self.borderThickness = [NSNumber numberWithDouble:1.0];
+ }
+
self.backgroundColor = [NSColor controlColor];
}
@@ -42,6 +46,7 @@
NSColor* backgroundColor = self.bgColor;
NSColor* borderColor = self.borderColor;
+ CGFloat borderThickness = [self.borderThickness floatValue];
NSRect group = NSMakeRect(NSMinX(dirtyRect) + floor(NSWidth(dirtyRect) * 0.03333) + 0.5,
NSMinY(dirtyRect) + floor(NSHeight(dirtyRect) * 0.03333) + 0.5,
@@ -57,7 +62,7 @@
[backgroundColor setFill];
[ovalPath fill];
[borderColor setStroke];
- [ovalPath setLineWidth: 1.0];
+ [ovalPath setLineWidth: borderThickness];
[ovalPath stroke];
NSDictionary *att = nil;