blob: 88cc45fc58b65560be367432c5c3a5336eb9c956 [file] [log] [blame]
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04001/*
Sébastien Blin029ffa82019-01-02 17:43:48 -05002 * Copyright (C) 2015-2019 Savoir-faire Linux Inc.
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04003 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
Olivier Soldanod4311552017-11-20 15:09:53 -05004 * Author: Olivier Soldano <olivier.soldano@savoirfairelinux.com>
5 * Author: Anthony Léonard <anthony.leonard@savoirfairelinux.com>
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#import "SmartViewVC.h"
23
Andreas Traczyk252a94a2018-04-20 16:36:20 -040024//std
25#import <sstream>
26
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040027//Qt
28#import <QtMacExtras/qmacfunctions.h>
29#import <QPixmap>
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040030
31//LRC
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040032#import <globalinstances.h>
Olivier Soldanod4311552017-11-20 15:09:53 -050033#import <api/newaccountmodel.h>
34#import <api/conversationmodel.h>
35#import <api/account.h>
36#import <api/contact.h>
37#import <api/contactmodel.h>
38#import <api/newcallmodel.h>
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040039
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040040#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040041#import "views/HoverTableRowView.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050042#import "views/IconButton.h"
Olivier Soldanod4311552017-11-20 15:09:53 -050043#import "views/RingTableView.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040044#import "views/ContextualTableCellView.h"
Olivier Soldanod4311552017-11-20 15:09:53 -050045#import "utils.h"
Anthony Léonardbee94cc2018-01-16 11:42:40 -050046#import "RingWindowController.h"
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040047
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -050048@interface SmartViewVC () <NSTableViewDelegate, NSTableViewDataSource, NSPopoverDelegate, ContextMenuDelegate, KeyboardShortcutDelegate> {
Alexandre Lision0f66bd32016-01-18 11:30:45 -050049
Alexandre Lision61db3552015-10-22 19:12:52 -040050 NSPopover* addToContactPopover;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040051
52 //UI elements
Olivier Soldanod4311552017-11-20 15:09:53 -050053 __unsafe_unretained IBOutlet RingTableView* smartView;
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -040054 __unsafe_unretained IBOutlet RingTableView* searchResultsView;
Alexandre Lision61db3552015-10-22 19:12:52 -040055 __unsafe_unretained IBOutlet NSSearchField* searchField;
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -040056 __unsafe_unretained IBOutlet NSTextField* searchStatus;
57 __unsafe_unretained IBOutlet NSBox* contactsHeader;
58 __unsafe_unretained IBOutlet NSBox* searchResultHeader;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -050059 __strong IBOutlet NSSegmentedControl *listTypeSelector;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040060 __strong IBOutlet NSLayoutConstraint *listTypeSelectorHeight;
Kateryna Kostiukef66f972018-11-02 17:10:37 -040061 __strong IBOutlet NSLayoutConstraint *listTypeSelectorBottom;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -050062 bool selectorIsPresent;
Alexandre Lision45c18672016-11-02 15:24:45 -040063
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -040064 QMetaObject::Connection modelSortedConnection_, modelUpdatedConnection_, filterChangedConnection_, newConversationConnection_, conversationRemovedConnection_, newInteractionConnection_, interactionStatusUpdatedConnection_, conversationClearedConnection, searchStatusChangedConnection_,
65 searchResultUpdated_;
Olivier Soldanod4311552017-11-20 15:09:53 -050066
Andreas Traczyk252a94a2018-04-20 16:36:20 -040067 lrc::api::ConversationModel* convModel_;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -040068 QString selectedUid_;
Anthony Léonard4bfd68b2018-01-15 16:23:50 -050069 lrc::api::profile::Type currentFilterType;
Anthony Léonardbee94cc2018-01-16 11:42:40 -050070
71 __unsafe_unretained IBOutlet RingWindowController *delegate;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040072}
73
74@end
75
76@implementation SmartViewVC
77
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -040078@synthesize tabbar;
79
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040080// Tags for views
Alexandre Lision57914fa2016-02-10 14:52:24 -050081NSInteger const IMAGE_TAG = 100;
82NSInteger const DISPLAYNAME_TAG = 200;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040083NSInteger const NOTIFICATONS_TAG = 300;
84NSInteger const RING_ID_LABEL = 400;
85NSInteger const PRESENCE_TAG = 500;
86NSInteger const TOTALMSGS_TAG = 600;
87NSInteger const TOTALINVITES_TAG = 700;
88NSInteger const DATE_TAG = 800;
89NSInteger const SNIPPET_TAG = 900;
90NSInteger const ADD_BUTTON_TAG = 1000;
91NSInteger const REFUSE_BUTTON_TAG = 1100;
92NSInteger const BLOCK_BUTTON_TAG = 1200;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040093
Anthony Léonarde6a01fa2018-01-12 10:29:58 -050094// Segment indices for smartlist selector
95NSInteger const CONVERSATION_SEG = 0;
96NSInteger const REQUEST_SEG = 1;
97
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040098- (void)awakeFromNib
99{
100 NSLog(@"INIT SmartView VC");
Olivier Soldanod4311552017-11-20 15:09:53 -0500101 //get selected account
102 //encapsulate conversationmodel in local version
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400103
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400104 [smartView setTarget:self];
105 [smartView setDoubleAction:@selector(placeCall:)];
106
Alexandre Lision61db3552015-10-22 19:12:52 -0400107 [smartView setContextMenuDelegate:self];
108 [smartView setShortcutsDelegate:self];
109
Olivier Soldanod4311552017-11-20 15:09:53 -0500110 [smartView setDataSource: self];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500111 currentFilterType = lrc::api::profile::Type::RING;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500112 selectorIsPresent = true;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400113
114 smartView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400115
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400116 [searchResultsView setContextMenuDelegate:self];
117 [searchResultsView setShortcutsDelegate:self];
118 [searchResultsView setDoubleAction:@selector(placeCall:)];
119 searchResultsView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400120}
121
122- (void)placeCall:(id)sender
123{
Olivier Soldanod4311552017-11-20 15:09:53 -0500124 NSInteger row;
125 if (sender != nil && [sender clickedRow] != -1)
126 row = [sender clickedRow];
127 else if ([smartView selectedRow] != -1)
128 row = [smartView selectedRow];
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400129 else if ([searchResultsView selectedRow] != -1)
130 row = [searchResultsView selectedRow];
Olivier Soldanod4311552017-11-20 15:09:53 -0500131 else
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400132 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400133 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400134 return;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400135
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400136 auto conv = sender == searchResultsView ? convModel_->searchResultForRow(row) : convModel_->filteredConversation(row);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400137 convModel_->placeCall(conv.uid);
138}
139
140-(void) reloadSelectorNotifications
141{
142 NSTextField* totalMsgsCount = [self.view viewWithTag:TOTALMSGS_TAG];
143 NSTextField* totalInvites = [self.view viewWithTag:TOTALINVITES_TAG];
144
145 if (!selectorIsPresent) {
146 [totalMsgsCount setHidden:true];
147 [totalInvites setHidden:true];
148 return;
149 }
150
151 auto ringConversations = convModel_->getFilteredConversations(lrc::api::profile::Type::RING);
152 int totalUnreadMessages = 0;
153 std::for_each(ringConversations.begin(), ringConversations.end(),
154 [&totalUnreadMessages, self] (const auto& conversation) {
155 totalUnreadMessages += convModel_->getNumberOfUnreadMessagesFor(conversation.uid);
156 });
157 [totalMsgsCount setHidden:(totalUnreadMessages == 0)];
158 [totalMsgsCount setIntValue:totalUnreadMessages];
159
160 auto totalRequests = [self chosenAccount].contactModel->pendingRequestCount();
161 [totalInvites setHidden:(totalRequests == 0)];
162 [totalInvites setIntValue:totalRequests];
Olivier Soldanod4311552017-11-20 15:09:53 -0500163}
164
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400165-(void) reloadSearchResults
166{
167 [searchResultHeader setHidden: convModel_->getAllSearchResults().size() == 0];
168 [searchResultsView reloadData];
169 [searchResultsView layoutSubtreeIfNeeded];
170}
171
172
Olivier Soldanod4311552017-11-20 15:09:53 -0500173-(void) reloadData
174{
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400175 [contactsHeader setHidden: convModel_->allFilteredConversations().empty() || searchField.stringValue.length == 0];
Olivier Soldanod4311552017-11-20 15:09:53 -0500176 [smartView deselectAll:nil];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400177 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400178 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500179
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400180 [self reloadSelectorNotifications];
181
182 if (!convModel_->owner.contactModel->hasPendingRequests()) {
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500183 if (currentFilterType == lrc::api::profile::Type::PENDING) {
184 [self selectConversationList];
185 }
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500186 if (selectorIsPresent) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400187 listTypeSelectorHeight.constant = 0.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400188 listTypeSelectorBottom.priority = 250;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400189 [listTypeSelector setHidden:YES];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500190 selectorIsPresent = false;
191 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500192 } else {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500193 if (!selectorIsPresent) {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500194 [listTypeSelector setSelected:YES forSegment:CONVERSATION_SEG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400195 listTypeSelectorHeight.constant = 18.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400196 listTypeSelectorBottom.priority = 999;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400197 [listTypeSelector setHidden:NO];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500198 selectorIsPresent = true;
199 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500200 }
201
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500202 [smartView reloadData];
Kateryna Kostiukc8035c62018-09-19 16:13:14 -0400203 [smartView layoutSubtreeIfNeeded];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500204
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400205 if (!selectedUid_.isEmpty() && convModel_ != nil) {
Kateryna Kostiuk3541ae22020-08-17 12:26:14 -0400206 auto it = getConversationFromUid(selectedUid_, *convModel_);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400207 if (it != convModel_->allFilteredConversations().end()) {
208 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Olivier Soldanod4311552017-11-20 15:09:53 -0500209 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400210 }
211 }
212
Olivier Soldanod4311552017-11-20 15:09:53 -0500213 [smartView scrollToBeginningOfDocument:nil];
214}
215
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400216-(void) reloadConversationWithUid:(NSString *)uid
217{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400218 if (convModel_ == nil) {
219 return;
220 }
221
Kateryna Kostiuk3541ae22020-08-17 12:26:14 -0400222 auto it = getConversationFromUid(QString::fromNSString(uid), *convModel_);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400223 if (it != convModel_->allFilteredConversations().end()) {
224 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400225 [smartView reloadDataForRowIndexes:indexSet
226 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
Kateryna Kostiuk3541ae22020-08-17 12:26:14 -0400227 } else {
228 it = getSearchResultFromUid(QString::fromNSString(uid), *convModel_);
229 if (it == convModel_->getAllSearchResults().end()) {
230 return;
231 }
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400232 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->getAllSearchResults().begin())];
233 [searchResultsView reloadDataForRowIndexes:indexSet
234 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400235 }
236}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400237
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400238-(void) reloadConversationWithURI:(NSString *)uri
239{
240 if (convModel_ == nil) {
241 return;
242 }
243 [smartView reloadData];
244}
245
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500246- (BOOL)setConversationModel:(lrc::api::ConversationModel *)conversationModel
Olivier Soldanod4311552017-11-20 15:09:53 -0500247{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400248 if (convModel_ == conversationModel) {
249 return false;
250 }
251
252 convModel_ = conversationModel;
253 selectedUid_.clear(); // Clear selected conversation as the selected account is being changed
254 QObject::disconnect(modelSortedConnection_);
255 QObject::disconnect(modelUpdatedConnection_);
256 QObject::disconnect(filterChangedConnection_);
257 QObject::disconnect(newConversationConnection_);
258 QObject::disconnect(conversationRemovedConnection_);
259 QObject::disconnect(conversationClearedConnection);
260 QObject::disconnect(interactionStatusUpdatedConnection_);
261 QObject::disconnect(newInteractionConnection_);
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400262 QObject::disconnect(searchStatusChangedConnection_);
263 QObject::disconnect(searchResultUpdated_);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400264 [self reloadData];
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400265 [self reloadSearchResults];
266
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400267 if (convModel_ != nil) {
268 modelSortedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::modelSorted,
Olivier Soldanod4311552017-11-20 15:09:53 -0500269 [self] (){
270 [self reloadData];
271 });
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400272 searchStatusChangedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::searchStatusChanged,
273 [self](const QString &status) {
274 [searchStatus setHidden:status.isEmpty()];
275 auto statusString = status.toNSString();
276 [searchStatus setStringValue: statusString];
277 });
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400278 modelUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationUpdated,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400279 [self] (const QString& convUid){
280 [self reloadConversationWithUid: convUid.toNSString()];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400281 });
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400282 searchResultUpdated_ = QObject::connect(convModel_, &lrc::api::ConversationModel::searchResultUpdated,
283 [self] (){
284 [self reloadSearchResults];
285 });
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400286 filterChangedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::filterChanged,
287 [self] (){
288 [self reloadData];
289 });
290 newConversationConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newConversation,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400291 [self] (const QString& convUid) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400292 [self reloadData];
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400293 [self updateConversationForNewContact:convUid.toNSString()];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400294 });
295 conversationRemovedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationRemoved,
296 [self] (){
297 [delegate listTypeChanged];
298 [self reloadData];
299 });
300 conversationClearedConnection = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationCleared,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400301 [self] (const QString& convUid){
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400302 [self deselect];
303 [delegate listTypeChanged];
304 });
305 interactionStatusUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::interactionStatusUpdated,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400306 [self] (const QString& convUid) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400307 if (convUid != selectedUid_)
308 return;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400309 [self reloadConversationWithUid: convUid.toNSString()];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400310 });
311 newInteractionConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newInteraction,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400312 [self](const QString& convUid, uint64_t interactionId, const lrc::api::interaction::Info& interaction){
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400313 if (convUid == selectedUid_) {
314 convModel_->clearUnreadInteractions(convUid);
315 }
316 });
317 convModel_->setFilter(""); // Reset the filter
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500318 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400319 [searchField setStringValue:@""];
320 return true;
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500321}
322
Kateryna Kostiuk66150982020-09-18 15:14:36 -0400323-(QString)getSelectedUID {
324 return selectedUid_;
325}
326
327
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500328-(void)selectConversation:(const lrc::api::conversation::Info&)conv model:(lrc::api::ConversationModel*)model;
329{
330 auto& uid = conv.uid;
331 if (selectedUid_ == uid)
332 return;
333
334 [self setConversationModel:model];
335
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400336 if (convModel_ == nil) {
337 return;
338 }
339
Kateryna Kostiuk3541ae22020-08-17 12:26:14 -0400340 auto it = getConversationFromUid(uid, *convModel_);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400341 if (it != convModel_->allFilteredConversations().end()) {
342 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
343 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
344 selectedUid_ = uid;
Kateryna Kostiuk3541ae22020-08-17 12:26:14 -0400345 } else {
346 it = getSearchResultFromUid(uid, *convModel_);
347 if (it == convModel_->getAllSearchResults().end()) {
348 return;
349 }
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400350 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->getAllSearchResults().begin())];
351 [searchResultsView selectRowIndexes:indexSet byExtendingSelection:NO];
352 selectedUid_ = uid;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400353 }
354}
355
Anthony Léonard8585cc02017-12-28 14:03:45 -0500356-(void)deselect
357{
358 selectedUid_.clear();
359 [smartView deselectAll:nil];
360}
361
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400362-(void) clearConversationModel {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400363 convModel_ = nil;
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400364 [self deselect];
365 [smartView reloadData];
Andreas Traczyk4fdb2d92018-04-19 15:57:46 -0400366 if (selectorIsPresent) {
367 [listTypeSelector removeFromSuperview];
368 selectorIsPresent = false;
369 }
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400370}
371
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500372- (IBAction) listTypeChanged:(id)sender
373{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400374 selectedUid_.clear();
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500375 NSInteger selectedItem = [sender selectedSegment];
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500376 switch (selectedItem) {
377 case CONVERSATION_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500378 if (currentFilterType != lrc::api::profile::Type::RING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400379 convModel_->setFilter(lrc::api::profile::Type::RING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500380 [delegate listTypeChanged];
381 currentFilterType = lrc::api::profile::Type::RING;
382 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500383 break;
384 case REQUEST_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500385 if (currentFilterType != lrc::api::profile::Type::PENDING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400386 convModel_->setFilter(lrc::api::profile::Type::PENDING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500387 [delegate listTypeChanged];
388 currentFilterType = lrc::api::profile::Type::PENDING;
389 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500390 break;
391 default:
392 NSLog(@"Invalid item selected in list selector: %d", selectedItem);
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500393 }
394}
395
396-(void) selectConversationList
397{
Anthony Léonardd00cd182018-01-17 09:21:27 -0500398 if (currentFilterType == lrc::api::profile::Type::RING)
399 return;
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500400 [listTypeSelector setSelectedSegment:CONVERSATION_SEG];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500401
402 // Do not invert order of the next two lines or stack overflow
403 // may happen on -(void) reloadData call if filter is currently set to PENDING
404 currentFilterType = lrc::api::profile::Type::RING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400405 convModel_->setFilter(lrc::api::profile::Type::RING);
406 convModel_->setFilter("");
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500407}
408
Anthony Léonardd00cd182018-01-17 09:21:27 -0500409-(void) selectPendingList
410{
411 if (currentFilterType == lrc::api::profile::Type::PENDING)
412 return;
413 [listTypeSelector setSelectedSegment:REQUEST_SEG];
414
415 currentFilterType = lrc::api::profile::Type::PENDING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400416 convModel_->setFilter(lrc::api::profile::Type::PENDING);
417 convModel_->setFilter("");
Anthony Léonardd00cd182018-01-17 09:21:27 -0500418}
419
Olivier Soldanod4311552017-11-20 15:09:53 -0500420#pragma mark - NSTableViewDelegate methods
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400421
Olivier Soldanod4311552017-11-20 15:09:53 -0500422- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400423{
424 return YES;
425}
426
Olivier Soldanod4311552017-11-20 15:09:53 -0500427- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400428{
429 return NO;
430}
431
Olivier Soldanod4311552017-11-20 15:09:53 -0500432- (void)tableViewSelectionDidChange:(NSNotification *)notification
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400433{
Olivier Soldanod4311552017-11-20 15:09:53 -0500434 NSInteger row = [notification.object selectedRow];
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400435 NSInteger rows = notification.object == smartView ? [smartView numberOfRows] : [searchResultsView numberOfRows];
Olivier Soldanod4311552017-11-20 15:09:53 -0500436
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400437 if (notification.object == smartView) {
438
439 for (int i = 0; i< rows; i++) {
440 HoverTableRowView* cellRowView = [smartView rowViewAtRow:i makeIfNecessary: NO];
441 [cellRowView drawSelection: (i == row)];
442 }
443
444 } else {
445 for (int i = 0; i< rows; i++) {
446 HoverTableRowView* cellRowView = [searchResultsView rowViewAtRow:i makeIfNecessary: NO];
447 [cellRowView drawSelection: (i == row)];
448 }
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400449 }
Olivier Soldanod4311552017-11-20 15:09:53 -0500450 if (row == -1)
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400451 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400452 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400453 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500454
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400455 auto uid = notification.object == smartView ? convModel_->filteredConversation(row).uid : convModel_->searchResultForRow(row).uid;
Olivier Soldanod4311552017-11-20 15:09:53 -0500456 if (selectedUid_ != uid) {
457 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400458 convModel_->selectConversation(uid);
459 convModel_->clearUnreadInteractions(uid);
460 [self reloadSelectorNotifications];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400461 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400462}
463
Olivier Soldanod4311552017-11-20 15:09:53 -0500464- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400465{
Olivier Soldanod4311552017-11-20 15:09:53 -0500466 return [tableView makeViewWithIdentifier:@"HoverRowView" owner:nil];
467}
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400468
Olivier Soldanod4311552017-11-20 15:09:53 -0500469- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
470{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400471 if (convModel_ == nil)
Olivier Soldanod4311552017-11-20 15:09:53 -0500472 return nil;
473
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400474 bool isSearching = tableView == searchResultsView;
475
476 auto conversation = isSearching ? convModel_->searchResultForRow(row) : convModel_->filteredConversation(row);
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400477 NSTableCellView* result;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400478
Olivier Soldanod4311552017-11-20 15:09:53 -0500479 result = [tableView makeViewWithIdentifier:@"MainCell" owner:tableView];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400480
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400481 NSTextField* unreadCount = [result viewWithTag:NOTIFICATONS_TAG];
Olivier Soldanod4311552017-11-20 15:09:53 -0500482 [unreadCount setHidden:(conversation.unreadMessages == 0)];
483 [unreadCount setIntValue:conversation.unreadMessages];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400484 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400485 NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400486 NSTextField* lastInteractionDate = [result viewWithTag:DATE_TAG];
487 NSTextField* interactionSnippet = [result viewWithTag:SNIPPET_TAG];
488 [displayName setStringValue:@""];
489 [displayRingID setStringValue:@""];
490 [lastInteractionDate setStringValue:@""];
491 [interactionSnippet setStringValue:@""];
492 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400493 NSString* displayNameString = bestNameForConversation(conversation, *convModel_);
494 NSString* displayIDString = bestIDForConversation(conversation, *convModel_);
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400495 if(displayNameString.length == 0 || [displayNameString isEqualToString:displayIDString]) {
496 [displayName setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400497 [displayRingID setHidden:YES];
Kateryna Kostiuk9d6657a2017-04-25 13:42:25 -0400498 }
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400499 else {
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400500 [displayName setStringValue:displayNameString];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400501 [displayRingID setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400502 [displayRingID setHidden:NO];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400503 }
Kateryna Kostiukdc720842020-09-10 16:44:02 -0400504 @autoreleasepool {
505 auto& imageManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
506 NSImage* image = QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(conversation, convModel_->owner)));
507 if(image) {
508 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
509 NSArray* constraints = [NSLayoutConstraint
510 constraintsWithVisualFormat:@"H:[photoView(54)]"
511 options:0
512 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
513 [NSLayoutConstraint activateConstraints:constraints];
514 } else {
515 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
516 NSArray* constraints = [NSLayoutConstraint
517 constraintsWithVisualFormat:@"H:[photoView(0)]"
518 options:0
519 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
520 [NSLayoutConstraint activateConstraints:constraints];
521 }
522 [photoView setImage: image];
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400523 }
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400524
525 NSView* presenceView = [result viewWithTag:PRESENCE_TAG];
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400526 [presenceView setHidden:YES];
527 if (!conversation.participants.empty()){
528 try {
529 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
530 if (contact.isPresent) {
531 [presenceView setHidden:NO];
532 }
533 } catch (std::out_of_range& e) {
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400534 NSLog(@"contact out of range");
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400535 }
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400536 }
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400537
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400538 NSButton* addContactButton = [result viewWithTag:ADD_BUTTON_TAG];
539 NSButton* refuseContactButton = [result viewWithTag:REFUSE_BUTTON_TAG];
540 NSButton* blockContactButton = [result viewWithTag:BLOCK_BUTTON_TAG];
541 [addContactButton setHidden:YES];
542 [refuseContactButton setHidden:YES];
543 [blockContactButton setHidden:YES];
544
545 if (profileType(conversation, *convModel_) == lrc::api::profile::Type::PENDING) {
546 [lastInteractionDate setHidden:true];
547 [interactionSnippet setHidden:true];
548 [addContactButton setHidden:NO];
549 [refuseContactButton setHidden:NO];
550 [blockContactButton setHidden:NO];
551 [addContactButton setAction:@selector(acceptInvitation:)];
552 [addContactButton setTarget:self];
553 [refuseContactButton setAction:@selector(refuseInvitation:)];
554 [refuseContactButton setTarget:self];
555 [blockContactButton setAction:@selector(blockPendingContact:)];
556 [blockContactButton setTarget:self];
557 return result;
558 }
559
560 [lastInteractionDate setHidden:false];
561
562 [interactionSnippet setHidden:false];
563
564 auto lastUid = conversation.lastMessageUid;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400565 auto callId = conversation.confId.isEmpty() ? conversation.callId : conversation.confId;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400566 NSString *callInfo = @"";
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400567 if (!callId.isEmpty()) {
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400568 if ([self chosenAccount].callModel.get()->hasCall(callId)) {
569 auto call = [self chosenAccount].callModel.get()->getCall(callId);
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400570 callInfo = (call.status == lrc::api::call::Status::IN_PROGRESS) ? @"Talking" : to_string(call.status).toNSString();
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400571 }
572 }
573
574 if (callInfo.length > 0) {
575 [lastInteractionDate setStringValue: callInfo];
576 [interactionSnippet setHidden:true];
577 return result;
578 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400579 if (conversation.interactions.find(lastUid) != conversation.interactions.end()) {
580 // last interaction snippet
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400581 auto lastInteractionSnippet = conversation.interactions[lastUid].body.trimmed().replace("\r","").replace("\n","");
582 NSString* lastInteractionSnippetFixedString = [lastInteractionSnippet.toNSString()
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400583 stringByReplacingOccurrencesOfString:@"🕽" withString:@""];
584 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString stringByReplacingOccurrencesOfString:@"📞" withString:@""];
Kateryna Kostiuk209a6302019-08-14 16:46:21 -0400585 if (conversation.interactions[lastUid].type == lrc::api::interaction::Type::DATA_TRANSFER) {
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400586 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString lastPathComponent];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400587 }
588 [interactionSnippet setStringValue:lastInteractionSnippetFixedString];
589
590 // last interaction date/time
Kateryna Kostiuk66406432019-11-09 17:20:34 -0500591 NSString *timeString = @"";
592 NSDate* msgTime = [NSDate dateWithTimeIntervalSince1970:conversation.interactions[lastUid].timestamp];
593 NSDate *today = [NSDate date];
594 NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
595 [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale currentLocale] localeIdentifier]]];
596 if ([[NSCalendar currentCalendar] compareDate:today
597 toDate:msgTime
598 toUnitGranularity:NSCalendarUnitYear]!= NSOrderedSame) {
599 timeString = [NSDateFormatter localizedStringFromDate:msgTime dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterNoStyle];
600 } else if ([[NSCalendar currentCalendar] compareDate:today
601 toDate:msgTime
602 toUnitGranularity:NSCalendarUnitDay]!= NSOrderedSame ||
603 [[NSCalendar currentCalendar] compareDate:today
604 toDate:msgTime
605 toUnitGranularity:NSCalendarUnitMonth]!= NSOrderedSame) {
606 timeString = [NSDateFormatter localizedStringFromDate:msgTime dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterNoStyle];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400607 } else {
Kateryna Kostiuk66406432019-11-09 17:20:34 -0500608 timeString = [NSDateFormatter localizedStringFromDate:msgTime dateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterShortStyle];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400609 }
Kateryna Kostiuk66406432019-11-09 17:20:34 -0500610 [lastInteractionDate setStringValue:timeString];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400611 }
612
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400613 return result;
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400614
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400615}
616
Olivier Soldanod4311552017-11-20 15:09:53 -0500617- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500618{
Olivier Soldanod4311552017-11-20 15:09:53 -0500619 return 60.0;
620}
621
622#pragma mark - NSTableDataSource methods
623
624- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
625{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400626 if (tableView == smartView && convModel_ != nullptr) {
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400627 bool hideSmartView = convModel_->getAllSearchResults().size() > 0 && convModel_->allFilteredConversations().size() == 0;
628 [[[smartView superview] superview] setHidden: hideSmartView];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400629 return convModel_->allFilteredConversations().size();
Olivier Soldanod4311552017-11-20 15:09:53 -0500630 }
631
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400632 if (tableView == searchResultsView && convModel_ != nullptr) {
633 [[[searchResultsView superview] superview] setHidden: convModel_->getAllSearchResults().size() == 0];
634 return convModel_->getAllSearchResults().size();
635 }
Olivier Soldanod4311552017-11-20 15:09:53 -0500636 return 0;
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500637}
638
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -0400639- (void)startCallForRow:(id)sender {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400640 NSInteger row = [smartView rowForView:sender];
641 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
642 [self placeCall:nil];
643}
644
645- (IBAction)hangUpClickedAtRow:(id)sender {
646 NSInteger row = [smartView rowForView:sender];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500647
Olivier Soldanod4311552017-11-20 15:09:53 -0500648 if (row == -1)
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500649 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400650 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400651 return;
Alexandre Lision45c18672016-11-02 15:24:45 -0400652
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400653 auto conv = convModel_->filteredConversation(row);
Olivier Soldanod4311552017-11-20 15:09:53 -0500654 auto& callId = conv.callId;
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500655
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400656 if (callId.isEmpty())
Olivier Soldanod4311552017-11-20 15:09:53 -0500657 return;
658
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400659 auto* callModel = convModel_->owner.callModel.get();
Olivier Soldanod4311552017-11-20 15:09:53 -0500660 callModel->hangUp(callId);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400661}
662
Alexandre Lision45c18672016-11-02 15:24:45 -0400663- (void) displayErrorModalWithTitle:(NSString*) title WithMessage:(NSString*) message
664{
665 NSAlert* alert = [NSAlert alertWithMessageText:title
666 defaultButton:@"Ok"
667 alternateButton:nil
668 otherButton:nil
669 informativeTextWithFormat:message];
670
671 [alert beginSheetModalForWindow:self.view.window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
672}
673
Olivier Soldanod4311552017-11-20 15:09:53 -0500674- (void) processSearchFieldInput
Alexandre Lision45c18672016-11-02 15:24:45 -0400675{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400676 if (convModel_ == nil) {
677 return;
678 }
679
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400680 convModel_->setFilter(QString::fromNSString([searchField stringValue]));
Alexandre Lision45c18672016-11-02 15:24:45 -0400681}
682
Olivier Soldanod4311552017-11-20 15:09:53 -0500683-(const lrc::api::account::Info&) chosenAccount
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400684{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400685 return convModel_->owner;
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400686}
687
Alexandre Lisionf909fb12016-11-03 12:35:36 -0400688- (void) clearSearchField
689{
690 [searchField setStringValue:@""];
Olivier Soldanod4311552017-11-20 15:09:53 -0500691 [self processSearchFieldInput];
Kateryna Kostiuk9dd759c2017-07-11 12:06:55 -0400692}
693
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400694-(void)updateConversationForNewContact:(NSString *)uId {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400695 if (convModel_ == nil) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400696 return;
697 }
698 [self clearSearchField];
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400699 auto uid = QString::fromNSString(uId);
Kateryna Kostiuk3541ae22020-08-17 12:26:14 -0400700 auto it = getConversationFromUid(uid, *convModel_);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400701 if (it != convModel_->allFilteredConversations().end()) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400702 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400703 auto contact = convModel_->owner.contactModel->getContact(it->participants[0]);
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400704 if (!contact.profileInfo.uri.isEmpty() && contact.profileInfo.uri.compare(selectedUid_) == 0) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400705 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400706 convModel_->selectConversation(uid);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400707 }
708 } @catch (NSException *exception) {
709 return;
710 }
711 }
712}
713
Alexandre Lision72a669e2016-09-14 17:52:16 -0400714/**
715 Copy a NSString in the general Pasteboard
716
717 @param sender the NSObject containing the represented object to copy
718 */
719- (void) copyStringToPasteboard:(id) sender
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400720{
721 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
722 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
723 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
724}
725
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400726#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400727
728- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
729{
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400730 if (commandSelector != @selector(insertNewline:) || [[searchField stringValue] isEqual:@""]) {
731 return NO;
732 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400733 if (convModel_ == nil) {
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400734 [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")];
735 return NO;
736 }
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400737 if (convModel_->getAllSearchResults().size() <= 0 && convModel_->allFilteredConversations().size() <= 0) {
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400738 return YES;
739 }
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400740 bool hasSearchResult = convModel_->getAllSearchResults().size() > 0;
741 auto model = hasSearchResult ? convModel_->searchResultForRow(0) : convModel_->filteredConversation(0);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400742 auto uid = model.uid;
743 if (selectedUid_ == uid) {
744 return YES;
745 }
746 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400747 auto contact = convModel_->owner.contactModel->getContact(model.participants[0]);
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400748 if ((contact.profileInfo.uri.isEmpty() && contact.profileInfo.type != lrc::api::profile::Type::SIP) || contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400749 return YES;
750 }
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400751 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400752 convModel_->selectConversation(uid);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400753 [self.view.window makeFirstResponder: smartView];
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400754 return YES;
755 } @catch (NSException *exception) {
756 return YES;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400757 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400758}
759
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500760- (void)controlTextDidChange:(NSNotification *) notification
761{
Olivier Soldanod4311552017-11-20 15:09:53 -0500762 [self processSearchFieldInput];
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500763}
764
Alexandre Lision61db3552015-10-22 19:12:52 -0400765#pragma mark - NSPopOverDelegate
766
767- (void)popoverDidClose:(NSNotification *)notification
768{
769 if (addToContactPopover != nullptr) {
770 [addToContactPopover performClose:self];
771 addToContactPopover = NULL;
772 }
773}
774
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400775
Alexandre Lision61db3552015-10-22 19:12:52 -0400776#pragma mark - ContactLinkedDelegate
777
778- (void)contactLinked
779{
780 if (addToContactPopover != nullptr) {
781 [addToContactPopover performClose:self];
782 addToContactPopover = NULL;
783 }
784}
785
786#pragma mark - KeyboardShortcutDelegate
787
788- (void) onAddShortcut
789{
Olivier Soldanod4311552017-11-20 15:09:53 -0500790 if ([smartView selectedRow] == -1)
Alexandre Lision61db3552015-10-22 19:12:52 -0400791 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400792 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400793 return ;
Alexandre Lision61db3552015-10-22 19:12:52 -0400794
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400795 auto uid = convModel_->filteredConversation([smartView selectedRow]).uid;
796 convModel_->makePermanent(uid);
Alexandre Lision61db3552015-10-22 19:12:52 -0400797}
798
799#pragma mark - ContextMenuDelegate
800
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400801- (NSMenu*) contextualMenuForRow:(int) index table:(NSTableView*) table
Alexandre Lision61db3552015-10-22 19:12:52 -0400802{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400803 if (convModel_ == nil)
Alexandre Lision61db3552015-10-22 19:12:52 -0400804 return nil;
805
Kateryna Kostiuk146cb9e2020-08-11 14:29:26 -0400806 auto conversation = table == smartView ? convModel_->filteredConversation(NSInteger(index)) :
807 convModel_->searchResultForRow(NSInteger(index));
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400808
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400809 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400810 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400811 if (contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
812 return nil;
813 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400814
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400815 BOOL isSIP = false;
816 BOOL isRingContact = false;
817 /* for SIP contact show only call menu options
818 * if contact does not have uri that is not RING contact
819 * for trusted Ring contact show option block contact
820 * for untrasted contact show option add contact
821 */
Alexandre Lision72a669e2016-09-14 17:52:16 -0400822
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400823 if (contact.profileInfo.type == lrc::api::profile::Type::SIP) {
824 isSIP = true;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400825 } else if (contact.profileInfo.uri.isEmpty()) {
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400826 return nil;
827 }
828
829 else if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.isTrusted == true) {
830 isRingContact = true;
831 }
832 auto conversationUD = conversation.uid;
833 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400834 NSString* conversationUID = conversationUD.toNSString();
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400835 NSMenuItem* separator = [NSMenuItem separatorItem];
836 NSMenuItem* videoCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place video call",
837 @"Contextual menu action")
838 action:@selector(videoCall:)
839 keyEquivalent:@""];
840 NSMenuItem* audioCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place audio call",
841 @"Contextual menu action")
842 action:@selector(audioCall:)
843 keyEquivalent:@""];
844 [videoCallItem setRepresentedObject: conversationUID];
845 [audioCallItem setRepresentedObject: conversationUID];
846 [theMenu addItem:videoCallItem];
847 [theMenu addItem:audioCallItem];
Kateryna Kostiukc808b902020-08-11 21:40:59 -0400848 [theMenu addItem:separator];
849 NSMenuItem* clearConversationItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Clear conversation", @"Contextual menu action")
850 action:@selector(clearConversation:)
851 keyEquivalent:@""];
852 [clearConversationItem setRepresentedObject: conversationUID];
853 [theMenu addItem:clearConversationItem];
854 NSMenuItem* removeContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Remove conversation", @"Contextual menu action")
855 action:@selector(removeContact:)
856 keyEquivalent:@""];
857 [removeContactItem setRepresentedObject: conversationUID];
858 [theMenu addItem:removeContactItem];
859 if(isRingContact) {
860 NSMenuItem* blockContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Block contact", @"Contextual menu action")
861 action:@selector(blockContact:)
862 keyEquivalent:@""];
863 [blockContactItem setRepresentedObject: conversationUID];
864 [theMenu addItem:blockContactItem];
865 } else if (isSIP == false) {
866 NSMenuItem* addContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
867 action:@selector(addContact:)
868 keyEquivalent:@"A"];
869 [addContactItem setRepresentedObject: conversationUID];
870 [theMenu addItem:addContactItem];
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400871 }
872 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400873 }
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400874 @catch (NSException *exception) {
875 return nil;
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400876 }
Alexandre Lision61db3552015-10-22 19:12:52 -0400877}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400878
879- (void) addContact: (NSMenuItem* ) item {
880 auto menuObject = item.representedObject;
881 if(menuObject == nil) {
882 return;
883 }
884 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400885 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400886 convModel_->makePermanent(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400887}
888
889- (void) blockContact: (NSMenuItem* ) item {
890 auto menuObject = item.representedObject;
891 if(menuObject == nil) {
892 return;
893 }
894 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400895 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400896 convModel_->removeConversation(conversationID, true);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400897}
898
Kateryna Kostiukc808b902020-08-11 21:40:59 -0400899- (void) removeContact: (NSMenuItem* ) item {
900 auto menuObject = item.representedObject;
901 if(menuObject == nil) {
902 return;
903 }
904 NSString * convUId = (NSString*)menuObject;
905 QString conversationID = QString::fromNSString(convUId);
906 convModel_->removeConversation(conversationID, false);
907}
908
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400909- (void) audioCall: (NSMenuItem* ) item {
910 auto menuObject = item.representedObject;
911 if(menuObject == nil) {
912 return;
913 }
914 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400915 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400916 convModel_->placeAudioOnlyCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400917
918}
919
920- (void) videoCall: (NSMenuItem* ) item {
921 auto menuObject = item.representedObject;
922 if(menuObject == nil) {
923 return;
924 }
925 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400926 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400927 convModel_->placeCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400928}
929
930- (void) clearConversation:(NSMenuItem* ) item {
931 auto menuObject = item.representedObject;
932 if(menuObject == nil) {
933 return;
934 }
935 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400936 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400937 convModel_->clearHistory(conversationID);
938}
939
940- (void)acceptInvitation:(id)sender {
941 NSInteger row = [smartView rowForView:sender];
942
943 if (row == -1)
944 return;
945 if (convModel_ == nil)
946 return;
947
948 auto conv = convModel_->filteredConversation(row);
949 auto& convID = conv.Info::uid;
950
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400951 if (convID.isEmpty())
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400952 return;
953 convModel_->makePermanent(convID);
954}
955
956- (void)refuseInvitation:(id)sender {
957 NSInteger row = [smartView rowForView:sender];
958
959 if (row == -1)
960 return;
961 if (convModel_ == nil)
962 return;
963
964 auto conv = convModel_->filteredConversation(row);
965 auto& convID = conv.Info::uid;
966
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400967 if (convID.isEmpty())
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400968 return;
969 convModel_->removeConversation(convID);
970}
971
972- (void)blockPendingContact:(id)sender {
973 NSInteger row = [smartView rowForView:sender];
974
975 if (row == -1)
976 return;
977 if (convModel_ == nil)
978 return;
979
980 auto conv = convModel_->filteredConversation(row);
981 auto& convID = conv.Info::uid;
982
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400983 if (convID.isEmpty())
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400984 return;
985 convModel_->removeConversation(convID, true);
986 [self deselect];
987 [delegate listTypeChanged];
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400988}
Alexandre Lision61db3552015-10-22 19:12:52 -0400989
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400990@end