blob: 1a68a76d53aaa7c60916a28096bd7b4adb178e2c [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;
Alexandre Lision61db3552015-10-22 19:12:52 -040054 __unsafe_unretained IBOutlet NSSearchField* searchField;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -050055 __strong IBOutlet NSSegmentedControl *listTypeSelector;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040056 __strong IBOutlet NSLayoutConstraint *listTypeSelectorHeight;
Kateryna Kostiukef66f972018-11-02 17:10:37 -040057 __strong IBOutlet NSLayoutConstraint *listTypeSelectorBottom;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -050058 bool selectorIsPresent;
Alexandre Lision45c18672016-11-02 15:24:45 -040059
Andreas Traczyk252a94a2018-04-20 16:36:20 -040060 QMetaObject::Connection modelSortedConnection_, modelUpdatedConnection_, filterChangedConnection_, newConversationConnection_, conversationRemovedConnection_, newInteractionConnection_, interactionStatusUpdatedConnection_, conversationClearedConnection;
Olivier Soldanod4311552017-11-20 15:09:53 -050061
Andreas Traczyk252a94a2018-04-20 16:36:20 -040062 lrc::api::ConversationModel* convModel_;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -040063 QString selectedUid_;
Anthony Léonard4bfd68b2018-01-15 16:23:50 -050064 lrc::api::profile::Type currentFilterType;
Anthony Léonardbee94cc2018-01-16 11:42:40 -050065
66 __unsafe_unretained IBOutlet RingWindowController *delegate;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040067}
68
69@end
70
71@implementation SmartViewVC
72
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -040073@synthesize tabbar;
74
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040075// Tags for views
Alexandre Lision57914fa2016-02-10 14:52:24 -050076NSInteger const IMAGE_TAG = 100;
77NSInteger const DISPLAYNAME_TAG = 200;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040078NSInteger const NOTIFICATONS_TAG = 300;
79NSInteger const RING_ID_LABEL = 400;
80NSInteger const PRESENCE_TAG = 500;
81NSInteger const TOTALMSGS_TAG = 600;
82NSInteger const TOTALINVITES_TAG = 700;
83NSInteger const DATE_TAG = 800;
84NSInteger const SNIPPET_TAG = 900;
85NSInteger const ADD_BUTTON_TAG = 1000;
86NSInteger const REFUSE_BUTTON_TAG = 1100;
87NSInteger const BLOCK_BUTTON_TAG = 1200;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040088
Anthony Léonarde6a01fa2018-01-12 10:29:58 -050089// Segment indices for smartlist selector
90NSInteger const CONVERSATION_SEG = 0;
91NSInteger const REQUEST_SEG = 1;
92
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040093- (void)awakeFromNib
94{
95 NSLog(@"INIT SmartView VC");
Olivier Soldanod4311552017-11-20 15:09:53 -050096 //get selected account
97 //encapsulate conversationmodel in local version
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040098
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040099 [smartView setTarget:self];
100 [smartView setDoubleAction:@selector(placeCall:)];
101
Alexandre Lision61db3552015-10-22 19:12:52 -0400102 [smartView setContextMenuDelegate:self];
103 [smartView setShortcutsDelegate:self];
104
Olivier Soldanod4311552017-11-20 15:09:53 -0500105 [smartView setDataSource: self];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500106 currentFilterType = lrc::api::profile::Type::RING;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500107 selectorIsPresent = true;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400108
109 smartView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400110
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400111}
112
113- (void)placeCall:(id)sender
114{
Olivier Soldanod4311552017-11-20 15:09:53 -0500115 NSInteger row;
116 if (sender != nil && [sender clickedRow] != -1)
117 row = [sender clickedRow];
118 else if ([smartView selectedRow] != -1)
119 row = [smartView selectedRow];
120 else
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400121 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400122 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400123 return;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400124
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400125 auto conv = convModel_->filteredConversation(row);
126 convModel_->placeCall(conv.uid);
127}
128
129-(void) reloadSelectorNotifications
130{
131 NSTextField* totalMsgsCount = [self.view viewWithTag:TOTALMSGS_TAG];
132 NSTextField* totalInvites = [self.view viewWithTag:TOTALINVITES_TAG];
133
134 if (!selectorIsPresent) {
135 [totalMsgsCount setHidden:true];
136 [totalInvites setHidden:true];
137 return;
138 }
139
140 auto ringConversations = convModel_->getFilteredConversations(lrc::api::profile::Type::RING);
141 int totalUnreadMessages = 0;
142 std::for_each(ringConversations.begin(), ringConversations.end(),
143 [&totalUnreadMessages, self] (const auto& conversation) {
144 totalUnreadMessages += convModel_->getNumberOfUnreadMessagesFor(conversation.uid);
145 });
146 [totalMsgsCount setHidden:(totalUnreadMessages == 0)];
147 [totalMsgsCount setIntValue:totalUnreadMessages];
148
149 auto totalRequests = [self chosenAccount].contactModel->pendingRequestCount();
150 [totalInvites setHidden:(totalRequests == 0)];
151 [totalInvites setIntValue:totalRequests];
Olivier Soldanod4311552017-11-20 15:09:53 -0500152}
153
154-(void) reloadData
155{
156 [smartView deselectAll:nil];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400157 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400158 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500159
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400160 [self reloadSelectorNotifications];
161
162 if (!convModel_->owner.contactModel->hasPendingRequests()) {
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500163 if (currentFilterType == lrc::api::profile::Type::PENDING) {
164 [self selectConversationList];
165 }
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500166 if (selectorIsPresent) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400167 listTypeSelectorHeight.constant = 0.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400168 listTypeSelectorBottom.priority = 250;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400169 [listTypeSelector setHidden:YES];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500170 selectorIsPresent = false;
171 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500172 } else {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500173 if (!selectorIsPresent) {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500174 [listTypeSelector setSelected:YES forSegment:CONVERSATION_SEG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400175 listTypeSelectorHeight.constant = 18.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400176 listTypeSelectorBottom.priority = 999;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400177 [listTypeSelector setHidden:NO];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500178 selectorIsPresent = true;
179 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500180 }
181
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500182 [smartView reloadData];
Kateryna Kostiukc8035c62018-09-19 16:13:14 -0400183 [smartView layoutSubtreeIfNeeded];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500184
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400185 if (!selectedUid_.isEmpty() && convModel_ != nil) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400186 auto it = getConversationFromUid(selectedUid_, *convModel_);
187 if (it != convModel_->allFilteredConversations().end()) {
188 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Olivier Soldanod4311552017-11-20 15:09:53 -0500189 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400190 }
191 }
192
Olivier Soldanod4311552017-11-20 15:09:53 -0500193 [smartView scrollToBeginningOfDocument:nil];
194}
195
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400196-(void) reloadConversationWithUid:(NSString *)uid
197{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400198 if (convModel_ == nil) {
199 return;
200 }
201
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400202 auto it = getConversationFromUid(QString::fromNSString(uid), *convModel_);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400203 if (it != convModel_->allFilteredConversations().end()) {
204 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400205 [smartView reloadDataForRowIndexes:indexSet
206 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400207 }
208}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400209
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400210-(void) reloadConversationWithURI:(NSString *)uri
211{
212 if (convModel_ == nil) {
213 return;
214 }
215 [smartView reloadData];
216}
217
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500218- (BOOL)setConversationModel:(lrc::api::ConversationModel *)conversationModel
Olivier Soldanod4311552017-11-20 15:09:53 -0500219{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400220 if (convModel_ == conversationModel) {
221 return false;
222 }
223
224 convModel_ = conversationModel;
225 selectedUid_.clear(); // Clear selected conversation as the selected account is being changed
226 QObject::disconnect(modelSortedConnection_);
227 QObject::disconnect(modelUpdatedConnection_);
228 QObject::disconnect(filterChangedConnection_);
229 QObject::disconnect(newConversationConnection_);
230 QObject::disconnect(conversationRemovedConnection_);
231 QObject::disconnect(conversationClearedConnection);
232 QObject::disconnect(interactionStatusUpdatedConnection_);
233 QObject::disconnect(newInteractionConnection_);
234 [self reloadData];
235 if (convModel_ != nil) {
236 modelSortedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::modelSorted,
Olivier Soldanod4311552017-11-20 15:09:53 -0500237 [self] (){
238 [self reloadData];
239 });
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400240 modelUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationUpdated,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400241 [self] (const QString& convUid){
242 [self reloadConversationWithUid: convUid.toNSString()];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400243 });
244 filterChangedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::filterChanged,
245 [self] (){
246 [self reloadData];
247 });
248 newConversationConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newConversation,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400249 [self] (const QString& convUid) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400250 [self reloadData];
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400251 [self updateConversationForNewContact:convUid.toNSString()];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400252 });
253 conversationRemovedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationRemoved,
254 [self] (){
255 [delegate listTypeChanged];
256 [self reloadData];
257 });
258 conversationClearedConnection = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationCleared,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400259 [self] (const QString& convUid){
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400260 [self deselect];
261 [delegate listTypeChanged];
262 });
263 interactionStatusUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::interactionStatusUpdated,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400264 [self] (const QString& convUid) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400265 if (convUid != selectedUid_)
266 return;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400267 [self reloadConversationWithUid: convUid.toNSString()];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400268 });
269 newInteractionConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newInteraction,
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400270 [self](const QString& convUid, uint64_t interactionId, const lrc::api::interaction::Info& interaction){
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400271 if (convUid == selectedUid_) {
272 convModel_->clearUnreadInteractions(convUid);
273 }
274 });
275 convModel_->setFilter(""); // Reset the filter
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500276 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400277 [searchField setStringValue:@""];
278 return true;
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500279}
280
281-(void)selectConversation:(const lrc::api::conversation::Info&)conv model:(lrc::api::ConversationModel*)model;
282{
283 auto& uid = conv.uid;
284 if (selectedUid_ == uid)
285 return;
286
287 [self setConversationModel:model];
288
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400289 if (convModel_ == nil) {
290 return;
291 }
292
293 auto it = getConversationFromUid(selectedUid_, *convModel_);
294 if (it != convModel_->allFilteredConversations().end()) {
295 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
296 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
297 selectedUid_ = uid;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400298 }
299}
300
Anthony Léonard8585cc02017-12-28 14:03:45 -0500301-(void)deselect
302{
303 selectedUid_.clear();
304 [smartView deselectAll:nil];
305}
306
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400307-(void) clearConversationModel {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400308 convModel_ = nil;
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400309 [self deselect];
310 [smartView reloadData];
Andreas Traczyk4fdb2d92018-04-19 15:57:46 -0400311 if (selectorIsPresent) {
312 [listTypeSelector removeFromSuperview];
313 selectorIsPresent = false;
314 }
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400315}
316
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500317- (IBAction) listTypeChanged:(id)sender
318{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400319 selectedUid_.clear();
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500320 NSInteger selectedItem = [sender selectedSegment];
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500321 switch (selectedItem) {
322 case CONVERSATION_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500323 if (currentFilterType != lrc::api::profile::Type::RING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400324 convModel_->setFilter(lrc::api::profile::Type::RING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500325 [delegate listTypeChanged];
326 currentFilterType = lrc::api::profile::Type::RING;
327 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500328 break;
329 case REQUEST_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500330 if (currentFilterType != lrc::api::profile::Type::PENDING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400331 convModel_->setFilter(lrc::api::profile::Type::PENDING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500332 [delegate listTypeChanged];
333 currentFilterType = lrc::api::profile::Type::PENDING;
334 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500335 break;
336 default:
337 NSLog(@"Invalid item selected in list selector: %d", selectedItem);
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500338 }
339}
340
341-(void) selectConversationList
342{
Anthony Léonardd00cd182018-01-17 09:21:27 -0500343 if (currentFilterType == lrc::api::profile::Type::RING)
344 return;
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500345 [listTypeSelector setSelectedSegment:CONVERSATION_SEG];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500346
347 // Do not invert order of the next two lines or stack overflow
348 // may happen on -(void) reloadData call if filter is currently set to PENDING
349 currentFilterType = lrc::api::profile::Type::RING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400350 convModel_->setFilter(lrc::api::profile::Type::RING);
351 convModel_->setFilter("");
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500352}
353
Anthony Léonardd00cd182018-01-17 09:21:27 -0500354-(void) selectPendingList
355{
356 if (currentFilterType == lrc::api::profile::Type::PENDING)
357 return;
358 [listTypeSelector setSelectedSegment:REQUEST_SEG];
359
360 currentFilterType = lrc::api::profile::Type::PENDING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400361 convModel_->setFilter(lrc::api::profile::Type::PENDING);
362 convModel_->setFilter("");
Anthony Léonardd00cd182018-01-17 09:21:27 -0500363}
364
Olivier Soldanod4311552017-11-20 15:09:53 -0500365#pragma mark - NSTableViewDelegate methods
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400366
Olivier Soldanod4311552017-11-20 15:09:53 -0500367- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400368{
369 return YES;
370}
371
Olivier Soldanod4311552017-11-20 15:09:53 -0500372- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400373{
374 return NO;
375}
376
Olivier Soldanod4311552017-11-20 15:09:53 -0500377- (void)tableViewSelectionDidChange:(NSNotification *)notification
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400378{
Olivier Soldanod4311552017-11-20 15:09:53 -0500379 NSInteger row = [notification.object selectedRow];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400380 NSInteger rows = [smartView numberOfRows];
Olivier Soldanod4311552017-11-20 15:09:53 -0500381
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400382 for (int i = 0; i< rows; i++) {
Kateryna Kostiuk394f74c2018-10-05 15:45:26 -0400383 HoverTableRowView* cellRowView = [smartView rowViewAtRow:i makeIfNecessary: NO];
384 [cellRowView drawSelection: (i == row)];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400385 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400386
Olivier Soldanod4311552017-11-20 15:09:53 -0500387 if (row == -1)
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400388 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400389 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400390 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500391
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400392 auto uid = convModel_->filteredConversation(row).uid;
Olivier Soldanod4311552017-11-20 15:09:53 -0500393 if (selectedUid_ != uid) {
394 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400395 convModel_->selectConversation(uid);
396 convModel_->clearUnreadInteractions(uid);
397 [self reloadSelectorNotifications];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400398 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400399}
400
Olivier Soldanod4311552017-11-20 15:09:53 -0500401- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400402{
Olivier Soldanod4311552017-11-20 15:09:53 -0500403 return [tableView makeViewWithIdentifier:@"HoverRowView" owner:nil];
404}
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400405
Olivier Soldanod4311552017-11-20 15:09:53 -0500406- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
407{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400408 if (convModel_ == nil)
Olivier Soldanod4311552017-11-20 15:09:53 -0500409 return nil;
410
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400411 auto conversation = convModel_->filteredConversation(row);
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400412 NSTableCellView* result;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400413
Olivier Soldanod4311552017-11-20 15:09:53 -0500414 result = [tableView makeViewWithIdentifier:@"MainCell" owner:tableView];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400415
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400416 NSTextField* unreadCount = [result viewWithTag:NOTIFICATONS_TAG];
Olivier Soldanod4311552017-11-20 15:09:53 -0500417 [unreadCount setHidden:(conversation.unreadMessages == 0)];
418 [unreadCount setIntValue:conversation.unreadMessages];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400419 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400420 NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400421 NSTextField* lastInteractionDate = [result viewWithTag:DATE_TAG];
422 NSTextField* interactionSnippet = [result viewWithTag:SNIPPET_TAG];
423 [displayName setStringValue:@""];
424 [displayRingID setStringValue:@""];
425 [lastInteractionDate setStringValue:@""];
426 [interactionSnippet setStringValue:@""];
427 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400428 NSString* displayNameString = bestNameForConversation(conversation, *convModel_);
429 NSString* displayIDString = bestIDForConversation(conversation, *convModel_);
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400430 if(displayNameString.length == 0 || [displayNameString isEqualToString:displayIDString]) {
431 [displayName setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400432 [displayRingID setHidden:YES];
Kateryna Kostiuk9d6657a2017-04-25 13:42:25 -0400433 }
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400434 else {
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400435 [displayName setStringValue:displayNameString];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400436 [displayRingID setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400437 [displayRingID setHidden:NO];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400438 }
Olivier Soldanod4311552017-11-20 15:09:53 -0500439 auto& imageManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400440 NSImage* image = QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(conversation, convModel_->owner)));
441 if(image) {
442 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
443 NSArray* constraints = [NSLayoutConstraint
444 constraintsWithVisualFormat:@"H:[photoView(54)]"
445 options:0
446 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
447 [NSLayoutConstraint activateConstraints:constraints];
448 } else {
449 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
450 NSArray* constraints = [NSLayoutConstraint
451 constraintsWithVisualFormat:@"H:[photoView(0)]"
452 options:0
453 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
454 [NSLayoutConstraint activateConstraints:constraints];
455 }
456 [photoView setImage: image];
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400457
458 NSView* presenceView = [result viewWithTag:PRESENCE_TAG];
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400459 [presenceView setHidden:YES];
460 if (!conversation.participants.empty()){
461 try {
462 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
463 if (contact.isPresent) {
464 [presenceView setHidden:NO];
465 }
466 } catch (std::out_of_range& e) {
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400467 NSLog(@"contact out of range");
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400468 }
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400469 }
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400470
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400471 NSButton* addContactButton = [result viewWithTag:ADD_BUTTON_TAG];
472 NSButton* refuseContactButton = [result viewWithTag:REFUSE_BUTTON_TAG];
473 NSButton* blockContactButton = [result viewWithTag:BLOCK_BUTTON_TAG];
474 [addContactButton setHidden:YES];
475 [refuseContactButton setHidden:YES];
476 [blockContactButton setHidden:YES];
477
478 if (profileType(conversation, *convModel_) == lrc::api::profile::Type::PENDING) {
479 [lastInteractionDate setHidden:true];
480 [interactionSnippet setHidden:true];
481 [addContactButton setHidden:NO];
482 [refuseContactButton setHidden:NO];
483 [blockContactButton setHidden:NO];
484 [addContactButton setAction:@selector(acceptInvitation:)];
485 [addContactButton setTarget:self];
486 [refuseContactButton setAction:@selector(refuseInvitation:)];
487 [refuseContactButton setTarget:self];
488 [blockContactButton setAction:@selector(blockPendingContact:)];
489 [blockContactButton setTarget:self];
490 return result;
491 }
492
493 [lastInteractionDate setHidden:false];
494
495 [interactionSnippet setHidden:false];
496
497 auto lastUid = conversation.lastMessageUid;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400498 auto callId = conversation.confId.isEmpty() ? conversation.callId : conversation.confId;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400499 NSString *callInfo = @"";
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400500 if (!callId.isEmpty()) {
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400501 if ([self chosenAccount].callModel.get()->hasCall(callId)) {
502 auto call = [self chosenAccount].callModel.get()->getCall(callId);
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400503 callInfo = (call.status == lrc::api::call::Status::IN_PROGRESS) ? @"Talking" : to_string(call.status).toNSString();
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400504 }
505 }
506
507 if (callInfo.length > 0) {
508 [lastInteractionDate setStringValue: callInfo];
509 [interactionSnippet setHidden:true];
510 return result;
511 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400512 if (conversation.interactions.find(lastUid) != conversation.interactions.end()) {
513 // last interaction snippet
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400514 auto lastInteractionSnippet = conversation.interactions[lastUid].body.trimmed().replace("\r","").replace("\n","");
515 NSString* lastInteractionSnippetFixedString = [lastInteractionSnippet.toNSString()
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400516 stringByReplacingOccurrencesOfString:@"🕽" withString:@""];
517 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString stringByReplacingOccurrencesOfString:@"📞" withString:@""];
Kateryna Kostiuk209a6302019-08-14 16:46:21 -0400518 if (conversation.interactions[lastUid].type == lrc::api::interaction::Type::DATA_TRANSFER) {
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400519 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString lastPathComponent];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400520 }
521 [interactionSnippet setStringValue:lastInteractionSnippetFixedString];
522
523 // last interaction date/time
Kateryna Kostiuk66406432019-11-09 17:20:34 -0500524 NSString *timeString = @"";
525 NSDate* msgTime = [NSDate dateWithTimeIntervalSince1970:conversation.interactions[lastUid].timestamp];
526 NSDate *today = [NSDate date];
527 NSDateFormatter *dateFormatter=[[NSDateFormatter alloc] init];
528 [dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[NSLocale currentLocale] localeIdentifier]]];
529 if ([[NSCalendar currentCalendar] compareDate:today
530 toDate:msgTime
531 toUnitGranularity:NSCalendarUnitYear]!= NSOrderedSame) {
532 timeString = [NSDateFormatter localizedStringFromDate:msgTime dateStyle:NSDateFormatterMediumStyle timeStyle:NSDateFormatterNoStyle];
533 } else if ([[NSCalendar currentCalendar] compareDate:today
534 toDate:msgTime
535 toUnitGranularity:NSCalendarUnitDay]!= NSOrderedSame ||
536 [[NSCalendar currentCalendar] compareDate:today
537 toDate:msgTime
538 toUnitGranularity:NSCalendarUnitMonth]!= NSOrderedSame) {
539 timeString = [NSDateFormatter localizedStringFromDate:msgTime dateStyle:NSDateFormatterShortStyle timeStyle:NSDateFormatterNoStyle];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400540 } else {
Kateryna Kostiuk66406432019-11-09 17:20:34 -0500541 timeString = [NSDateFormatter localizedStringFromDate:msgTime dateStyle:NSDateFormatterNoStyle timeStyle:NSDateFormatterShortStyle];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400542 }
Kateryna Kostiuk66406432019-11-09 17:20:34 -0500543 [lastInteractionDate setStringValue:timeString];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400544 }
545
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400546 return result;
547}
548
Olivier Soldanod4311552017-11-20 15:09:53 -0500549- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500550{
Olivier Soldanod4311552017-11-20 15:09:53 -0500551 return 60.0;
552}
553
554#pragma mark - NSTableDataSource methods
555
556- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
557{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400558 if (tableView == smartView && convModel_ != nullptr) {
559 return convModel_->allFilteredConversations().size();
Olivier Soldanod4311552017-11-20 15:09:53 -0500560 }
561
562 return 0;
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500563}
564
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -0400565- (void)startCallForRow:(id)sender {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400566 NSInteger row = [smartView rowForView:sender];
567 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
568 [self placeCall:nil];
569}
570
571- (IBAction)hangUpClickedAtRow:(id)sender {
572 NSInteger row = [smartView rowForView:sender];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500573
Olivier Soldanod4311552017-11-20 15:09:53 -0500574 if (row == -1)
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500575 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400576 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400577 return;
Alexandre Lision45c18672016-11-02 15:24:45 -0400578
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400579 auto conv = convModel_->filteredConversation(row);
Olivier Soldanod4311552017-11-20 15:09:53 -0500580 auto& callId = conv.callId;
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500581
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400582 if (callId.isEmpty())
Olivier Soldanod4311552017-11-20 15:09:53 -0500583 return;
584
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400585 auto* callModel = convModel_->owner.callModel.get();
Olivier Soldanod4311552017-11-20 15:09:53 -0500586 callModel->hangUp(callId);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400587}
588
Alexandre Lision45c18672016-11-02 15:24:45 -0400589- (void) displayErrorModalWithTitle:(NSString*) title WithMessage:(NSString*) message
590{
591 NSAlert* alert = [NSAlert alertWithMessageText:title
592 defaultButton:@"Ok"
593 alternateButton:nil
594 otherButton:nil
595 informativeTextWithFormat:message];
596
597 [alert beginSheetModalForWindow:self.view.window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
598}
599
Olivier Soldanod4311552017-11-20 15:09:53 -0500600- (void) processSearchFieldInput
Alexandre Lision45c18672016-11-02 15:24:45 -0400601{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400602 if (convModel_ == nil) {
603 return;
604 }
605
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400606 convModel_->setFilter(QString::fromNSString([searchField stringValue]));
Alexandre Lision45c18672016-11-02 15:24:45 -0400607}
608
Olivier Soldanod4311552017-11-20 15:09:53 -0500609-(const lrc::api::account::Info&) chosenAccount
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400610{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400611 return convModel_->owner;
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400612}
613
Alexandre Lisionf909fb12016-11-03 12:35:36 -0400614- (void) clearSearchField
615{
616 [searchField setStringValue:@""];
Olivier Soldanod4311552017-11-20 15:09:53 -0500617 [self processSearchFieldInput];
Kateryna Kostiuk9dd759c2017-07-11 12:06:55 -0400618}
619
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400620-(void)updateConversationForNewContact:(NSString *)uId {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400621 if (convModel_ == nil) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400622 return;
623 }
624 [self clearSearchField];
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400625 auto uid = QString::fromNSString(uId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400626 auto it = getConversationFromUid(uid, *convModel_);
627 if (it != convModel_->allFilteredConversations().end()) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400628 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400629 auto contact = convModel_->owner.contactModel->getContact(it->participants[0]);
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400630 if (!contact.profileInfo.uri.isEmpty() && contact.profileInfo.uri.compare(selectedUid_) == 0) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400631 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400632 convModel_->selectConversation(uid);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400633 }
634 } @catch (NSException *exception) {
635 return;
636 }
637 }
638}
639
Alexandre Lision72a669e2016-09-14 17:52:16 -0400640/**
641 Copy a NSString in the general Pasteboard
642
643 @param sender the NSObject containing the represented object to copy
644 */
645- (void) copyStringToPasteboard:(id) sender
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400646{
647 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
648 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
649 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
650}
651
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400652#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400653
654- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
655{
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400656 if (commandSelector != @selector(insertNewline:) || [[searchField stringValue] isEqual:@""]) {
657 return NO;
658 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400659 if (convModel_ == nil) {
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400660 [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")];
661 return NO;
662 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400663 if (convModel_->allFilteredConversations().size() <= 0) {
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400664 return YES;
665 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400666 auto model = convModel_->filteredConversation(0);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400667 auto uid = model.uid;
668 if (selectedUid_ == uid) {
669 return YES;
670 }
671 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400672 auto contact = convModel_->owner.contactModel->getContact(model.participants[0]);
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400673 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 -0400674 return YES;
675 }
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400676 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400677 convModel_->selectConversation(uid);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400678 [self.view.window makeFirstResponder: smartView];
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400679 return YES;
680 } @catch (NSException *exception) {
681 return YES;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400682 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400683}
684
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500685- (void)controlTextDidChange:(NSNotification *) notification
686{
Olivier Soldanod4311552017-11-20 15:09:53 -0500687 [self processSearchFieldInput];
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500688}
689
Alexandre Lision61db3552015-10-22 19:12:52 -0400690#pragma mark - NSPopOverDelegate
691
692- (void)popoverDidClose:(NSNotification *)notification
693{
694 if (addToContactPopover != nullptr) {
695 [addToContactPopover performClose:self];
696 addToContactPopover = NULL;
697 }
698}
699
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400700
Alexandre Lision61db3552015-10-22 19:12:52 -0400701#pragma mark - ContactLinkedDelegate
702
703- (void)contactLinked
704{
705 if (addToContactPopover != nullptr) {
706 [addToContactPopover performClose:self];
707 addToContactPopover = NULL;
708 }
709}
710
711#pragma mark - KeyboardShortcutDelegate
712
713- (void) onAddShortcut
714{
Olivier Soldanod4311552017-11-20 15:09:53 -0500715 if ([smartView selectedRow] == -1)
Alexandre Lision61db3552015-10-22 19:12:52 -0400716 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400717 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400718 return ;
Alexandre Lision61db3552015-10-22 19:12:52 -0400719
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400720 auto uid = convModel_->filteredConversation([smartView selectedRow]).uid;
721 convModel_->makePermanent(uid);
Alexandre Lision61db3552015-10-22 19:12:52 -0400722}
723
724#pragma mark - ContextMenuDelegate
725
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400726- (NSMenu*) contextualMenuForRow:(int) index
Alexandre Lision61db3552015-10-22 19:12:52 -0400727{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400728 if (convModel_ == nil)
Alexandre Lision61db3552015-10-22 19:12:52 -0400729 return nil;
730
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400731 auto conversation = convModel_->filteredConversation(NSInteger(index));
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400732
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400733 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400734 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400735 if (contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
736 return nil;
737 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400738
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400739 BOOL isSIP = false;
740 BOOL isRingContact = false;
741 /* for SIP contact show only call menu options
742 * if contact does not have uri that is not RING contact
743 * for trusted Ring contact show option block contact
744 * for untrasted contact show option add contact
745 */
Alexandre Lision72a669e2016-09-14 17:52:16 -0400746
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400747 if (contact.profileInfo.type == lrc::api::profile::Type::SIP) {
748 isSIP = true;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400749 } else if (contact.profileInfo.uri.isEmpty()) {
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400750 return nil;
751 }
752
753 else if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.isTrusted == true) {
754 isRingContact = true;
755 }
756 auto conversationUD = conversation.uid;
757 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400758 NSString* conversationUID = conversationUD.toNSString();
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400759 NSMenuItem* separator = [NSMenuItem separatorItem];
760 NSMenuItem* videoCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place video call",
761 @"Contextual menu action")
762 action:@selector(videoCall:)
763 keyEquivalent:@""];
764 NSMenuItem* audioCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place audio call",
765 @"Contextual menu action")
766 action:@selector(audioCall:)
767 keyEquivalent:@""];
768 [videoCallItem setRepresentedObject: conversationUID];
769 [audioCallItem setRepresentedObject: conversationUID];
770 [theMenu addItem:videoCallItem];
771 [theMenu addItem:audioCallItem];
Kateryna Kostiukc808b902020-08-11 21:40:59 -0400772 [theMenu addItem:separator];
773 NSMenuItem* clearConversationItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Clear conversation", @"Contextual menu action")
774 action:@selector(clearConversation:)
775 keyEquivalent:@""];
776 [clearConversationItem setRepresentedObject: conversationUID];
777 [theMenu addItem:clearConversationItem];
778 NSMenuItem* removeContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Remove conversation", @"Contextual menu action")
779 action:@selector(removeContact:)
780 keyEquivalent:@""];
781 [removeContactItem setRepresentedObject: conversationUID];
782 [theMenu addItem:removeContactItem];
783 if(isRingContact) {
784 NSMenuItem* blockContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Block contact", @"Contextual menu action")
785 action:@selector(blockContact:)
786 keyEquivalent:@""];
787 [blockContactItem setRepresentedObject: conversationUID];
788 [theMenu addItem:blockContactItem];
789 } else if (isSIP == false) {
790 NSMenuItem* addContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
791 action:@selector(addContact:)
792 keyEquivalent:@"A"];
793 [addContactItem setRepresentedObject: conversationUID];
794 [theMenu addItem:addContactItem];
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400795 }
796 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400797 }
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400798 @catch (NSException *exception) {
799 return nil;
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400800 }
Alexandre Lision61db3552015-10-22 19:12:52 -0400801}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400802
803- (void) addContact: (NSMenuItem* ) item {
804 auto menuObject = item.representedObject;
805 if(menuObject == nil) {
806 return;
807 }
808 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400809 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400810 convModel_->makePermanent(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400811}
812
813- (void) blockContact: (NSMenuItem* ) item {
814 auto menuObject = item.representedObject;
815 if(menuObject == nil) {
816 return;
817 }
818 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400819 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400820 convModel_->removeConversation(conversationID, true);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400821}
822
Kateryna Kostiukc808b902020-08-11 21:40:59 -0400823- (void) removeContact: (NSMenuItem* ) item {
824 auto menuObject = item.representedObject;
825 if(menuObject == nil) {
826 return;
827 }
828 NSString * convUId = (NSString*)menuObject;
829 QString conversationID = QString::fromNSString(convUId);
830 convModel_->removeConversation(conversationID, false);
831}
832
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400833- (void) audioCall: (NSMenuItem* ) item {
834 auto menuObject = item.representedObject;
835 if(menuObject == nil) {
836 return;
837 }
838 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400839 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400840 convModel_->placeAudioOnlyCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400841
842}
843
844- (void) videoCall: (NSMenuItem* ) item {
845 auto menuObject = item.representedObject;
846 if(menuObject == nil) {
847 return;
848 }
849 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400850 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400851 convModel_->placeCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400852}
853
854- (void) clearConversation:(NSMenuItem* ) item {
855 auto menuObject = item.representedObject;
856 if(menuObject == nil) {
857 return;
858 }
859 NSString * convUId = (NSString*)menuObject;
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400860 QString conversationID = QString::fromNSString(convUId);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400861 convModel_->clearHistory(conversationID);
862}
863
864- (void)acceptInvitation:(id)sender {
865 NSInteger row = [smartView rowForView:sender];
866
867 if (row == -1)
868 return;
869 if (convModel_ == nil)
870 return;
871
872 auto conv = convModel_->filteredConversation(row);
873 auto& convID = conv.Info::uid;
874
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400875 if (convID.isEmpty())
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400876 return;
877 convModel_->makePermanent(convID);
878}
879
880- (void)refuseInvitation:(id)sender {
881 NSInteger row = [smartView rowForView:sender];
882
883 if (row == -1)
884 return;
885 if (convModel_ == nil)
886 return;
887
888 auto conv = convModel_->filteredConversation(row);
889 auto& convID = conv.Info::uid;
890
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400891 if (convID.isEmpty())
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400892 return;
893 convModel_->removeConversation(convID);
894}
895
896- (void)blockPendingContact:(id)sender {
897 NSInteger row = [smartView rowForView:sender];
898
899 if (row == -1)
900 return;
901 if (convModel_ == nil)
902 return;
903
904 auto conv = convModel_->filteredConversation(row);
905 auto& convID = conv.Info::uid;
906
Kateryna Kostiukc867eb92020-03-08 13:15:17 -0400907 if (convID.isEmpty())
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400908 return;
909 convModel_->removeConversation(convID, true);
910 [self deselect];
911 [delegate listTypeChanged];
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400912}
Alexandre Lision61db3552015-10-22 19:12:52 -0400913
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400914@end