blob: e7427147b05f6aa2c40ae31d01b93935a7d46ee5 [file] [log] [blame]
Alexandre Lision4dfcafc2015-08-20 12:43:23 -04001/*
Anthony Léonardcb73a2c2018-01-18 11:45:56 -05002 * Copyright (C) 2015-2018 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>
30#import <QIdentityProxyModel>
31#import <QItemSelectionModel>
32
33//LRC
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040034#import <globalinstances.h>
Olivier Soldanod4311552017-11-20 15:09:53 -050035#import <api/newaccountmodel.h>
36#import <api/conversationmodel.h>
37#import <api/account.h>
38#import <api/contact.h>
39#import <api/contactmodel.h>
40#import <api/newcallmodel.h>
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040041
42#import "QNSTreeController.h"
43#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040044#import "views/HoverTableRowView.h"
Alexandre Lision61db3552015-10-22 19:12:52 -040045#import "PersonLinkerVC.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050046#import "views/IconButton.h"
Olivier Soldanod4311552017-11-20 15:09:53 -050047#import "views/RingTableView.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040048#import "views/ContextualTableCellView.h"
Olivier Soldanod4311552017-11-20 15:09:53 -050049#import "utils.h"
Anthony Léonardbee94cc2018-01-16 11:42:40 -050050#import "RingWindowController.h"
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040051
Olivier Soldanod4311552017-11-20 15:09:53 -050052@interface SmartViewVC () <NSTableViewDelegate, NSTableViewDataSource, NSPopoverDelegate, ContextMenuDelegate, ContactLinkedDelegate, KeyboardShortcutDelegate> {
Alexandre Lision0f66bd32016-01-18 11:30:45 -050053
Alexandre Lision61db3552015-10-22 19:12:52 -040054 NSPopover* addToContactPopover;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040055
56 //UI elements
Olivier Soldanod4311552017-11-20 15:09:53 -050057 __unsafe_unretained IBOutlet RingTableView* smartView;
Alexandre Lision61db3552015-10-22 19:12:52 -040058 __unsafe_unretained IBOutlet NSSearchField* searchField;
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
Andreas Traczyk252a94a2018-04-20 16:36:20 -040064 QMetaObject::Connection modelSortedConnection_, modelUpdatedConnection_, filterChangedConnection_, newConversationConnection_, conversationRemovedConnection_, newInteractionConnection_, interactionStatusUpdatedConnection_, conversationClearedConnection;
Olivier Soldanod4311552017-11-20 15:09:53 -050065
Andreas Traczyk252a94a2018-04-20 16:36:20 -040066 lrc::api::ConversationModel* convModel_;
Olivier Soldanod4311552017-11-20 15:09:53 -050067 std::string selectedUid_;
Anthony Léonard4bfd68b2018-01-15 16:23:50 -050068 lrc::api::profile::Type currentFilterType;
Anthony Léonardbee94cc2018-01-16 11:42:40 -050069
70 __unsafe_unretained IBOutlet RingWindowController *delegate;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040071}
72
73@end
74
75@implementation SmartViewVC
76
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -040077@synthesize tabbar;
78
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040079// Tags for views
Alexandre Lision57914fa2016-02-10 14:52:24 -050080NSInteger const IMAGE_TAG = 100;
81NSInteger const DISPLAYNAME_TAG = 200;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040082NSInteger const NOTIFICATONS_TAG = 300;
83NSInteger const RING_ID_LABEL = 400;
84NSInteger const PRESENCE_TAG = 500;
85NSInteger const TOTALMSGS_TAG = 600;
86NSInteger const TOTALINVITES_TAG = 700;
87NSInteger const DATE_TAG = 800;
88NSInteger const SNIPPET_TAG = 900;
89NSInteger const ADD_BUTTON_TAG = 1000;
90NSInteger const REFUSE_BUTTON_TAG = 1100;
91NSInteger const BLOCK_BUTTON_TAG = 1200;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040092
Anthony Léonarde6a01fa2018-01-12 10:29:58 -050093// Segment indices for smartlist selector
94NSInteger const CONVERSATION_SEG = 0;
95NSInteger const REQUEST_SEG = 1;
96
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040097- (void)awakeFromNib
98{
99 NSLog(@"INIT SmartView VC");
Olivier Soldanod4311552017-11-20 15:09:53 -0500100 //get selected account
101 //encapsulate conversationmodel in local version
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400102
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400103 [smartView setTarget:self];
104 [smartView setDoubleAction:@selector(placeCall:)];
105
Alexandre Lision61db3552015-10-22 19:12:52 -0400106 [smartView setContextMenuDelegate:self];
107 [smartView setShortcutsDelegate:self];
108
Olivier Soldanod4311552017-11-20 15:09:53 -0500109 [smartView setDataSource: self];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500110 currentFilterType = lrc::api::profile::Type::RING;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500111 selectorIsPresent = true;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400112
113 smartView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400114
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400115}
116
117- (void)placeCall:(id)sender
118{
Olivier Soldanod4311552017-11-20 15:09:53 -0500119 NSInteger row;
120 if (sender != nil && [sender clickedRow] != -1)
121 row = [sender clickedRow];
122 else if ([smartView selectedRow] != -1)
123 row = [smartView selectedRow];
124 else
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400125 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400126 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400127 return;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400128
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400129 auto conv = convModel_->filteredConversation(row);
130 convModel_->placeCall(conv.uid);
131}
132
133-(void) reloadSelectorNotifications
134{
135 NSTextField* totalMsgsCount = [self.view viewWithTag:TOTALMSGS_TAG];
136 NSTextField* totalInvites = [self.view viewWithTag:TOTALINVITES_TAG];
137
138 if (!selectorIsPresent) {
139 [totalMsgsCount setHidden:true];
140 [totalInvites setHidden:true];
141 return;
142 }
143
144 auto ringConversations = convModel_->getFilteredConversations(lrc::api::profile::Type::RING);
145 int totalUnreadMessages = 0;
146 std::for_each(ringConversations.begin(), ringConversations.end(),
147 [&totalUnreadMessages, self] (const auto& conversation) {
148 totalUnreadMessages += convModel_->getNumberOfUnreadMessagesFor(conversation.uid);
149 });
150 [totalMsgsCount setHidden:(totalUnreadMessages == 0)];
151 [totalMsgsCount setIntValue:totalUnreadMessages];
152
153 auto totalRequests = [self chosenAccount].contactModel->pendingRequestCount();
154 [totalInvites setHidden:(totalRequests == 0)];
155 [totalInvites setIntValue:totalRequests];
Olivier Soldanod4311552017-11-20 15:09:53 -0500156}
157
158-(void) reloadData
159{
160 [smartView deselectAll:nil];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400161 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400162 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500163
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400164 [self reloadSelectorNotifications];
165
166 if (!convModel_->owner.contactModel->hasPendingRequests()) {
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500167 if (currentFilterType == lrc::api::profile::Type::PENDING) {
168 [self selectConversationList];
169 }
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500170 if (selectorIsPresent) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400171 listTypeSelectorHeight.constant = 0.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400172 listTypeSelectorBottom.priority = 250;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400173 [listTypeSelector setHidden:YES];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500174 selectorIsPresent = false;
175 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500176 } else {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500177 if (!selectorIsPresent) {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500178 [listTypeSelector setSelected:YES forSegment:CONVERSATION_SEG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400179 listTypeSelectorHeight.constant = 18.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400180 listTypeSelectorBottom.priority = 999;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400181 [listTypeSelector setHidden:NO];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500182 selectorIsPresent = true;
183 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500184 }
185
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500186 [smartView reloadData];
Kateryna Kostiukc8035c62018-09-19 16:13:14 -0400187 [smartView layoutSubtreeIfNeeded];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500188
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400189 if (!selectedUid_.empty() && convModel_ != nil) {
190 auto it = getConversationFromUid(selectedUid_, *convModel_);
191 if (it != convModel_->allFilteredConversations().end()) {
192 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Olivier Soldanod4311552017-11-20 15:09:53 -0500193 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400194 }
195 }
196
Olivier Soldanod4311552017-11-20 15:09:53 -0500197 [smartView scrollToBeginningOfDocument:nil];
198}
199
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400200-(void) reloadConversationWithUid:(NSString *)uid
201{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400202 if (convModel_ == nil) {
203 return;
204 }
205
206 auto it = getConversationFromUid(std::string([uid UTF8String]), *convModel_);
207 if (it != convModel_->allFilteredConversations().end()) {
208 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400209 [smartView reloadDataForRowIndexes:indexSet
210 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400211 }
212}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400213
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500214- (BOOL)setConversationModel:(lrc::api::ConversationModel *)conversationModel
Olivier Soldanod4311552017-11-20 15:09:53 -0500215{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400216 if (convModel_ == conversationModel) {
217 return false;
218 }
219
220 convModel_ = conversationModel;
221 selectedUid_.clear(); // Clear selected conversation as the selected account is being changed
222 QObject::disconnect(modelSortedConnection_);
223 QObject::disconnect(modelUpdatedConnection_);
224 QObject::disconnect(filterChangedConnection_);
225 QObject::disconnect(newConversationConnection_);
226 QObject::disconnect(conversationRemovedConnection_);
227 QObject::disconnect(conversationClearedConnection);
228 QObject::disconnect(interactionStatusUpdatedConnection_);
229 QObject::disconnect(newInteractionConnection_);
230 [self reloadData];
231 if (convModel_ != nil) {
232 modelSortedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::modelSorted,
Olivier Soldanod4311552017-11-20 15:09:53 -0500233 [self] (){
234 [self reloadData];
235 });
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400236 modelUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationUpdated,
237 [self] (const std::string& convUid){
238 [self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
239 });
240 filterChangedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::filterChanged,
241 [self] (){
242 [self reloadData];
243 });
244 newConversationConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newConversation,
245 [self] (const std::string& convUid) {
246 [self reloadData];
247 [self updateConversationForNewContact:[NSString stringWithUTF8String:convUid.c_str()]];
248 });
249 conversationRemovedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationRemoved,
250 [self] (){
251 [delegate listTypeChanged];
252 [self reloadData];
253 });
254 conversationClearedConnection = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationCleared,
255 [self] (const std::string& convUid){
256 [self deselect];
257 [delegate listTypeChanged];
258 });
259 interactionStatusUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::interactionStatusUpdated,
260 [self] (const std::string& convUid) {
261 if (convUid != selectedUid_)
262 return;
263 [self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
264 });
265 newInteractionConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newInteraction,
266 [self](const std::string& convUid, uint64_t interactionId, const lrc::api::interaction::Info& interaction){
267 if (convUid == selectedUid_) {
268 convModel_->clearUnreadInteractions(convUid);
269 }
270 });
271 convModel_->setFilter(""); // Reset the filter
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500272 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400273 [searchField setStringValue:@""];
274 return true;
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500275}
276
277-(void)selectConversation:(const lrc::api::conversation::Info&)conv model:(lrc::api::ConversationModel*)model;
278{
279 auto& uid = conv.uid;
280 if (selectedUid_ == uid)
281 return;
282
283 [self setConversationModel:model];
284
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400285 if (convModel_ == nil) {
286 return;
287 }
288
289 auto it = getConversationFromUid(selectedUid_, *convModel_);
290 if (it != convModel_->allFilteredConversations().end()) {
291 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
292 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
293 selectedUid_ = uid;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400294 }
295}
296
Anthony Léonard8585cc02017-12-28 14:03:45 -0500297-(void)deselect
298{
299 selectedUid_.clear();
300 [smartView deselectAll:nil];
301}
302
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400303-(void) clearConversationModel {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400304 convModel_ = nil;
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400305 [self deselect];
306 [smartView reloadData];
Andreas Traczyk4fdb2d92018-04-19 15:57:46 -0400307 if (selectorIsPresent) {
308 [listTypeSelector removeFromSuperview];
309 selectorIsPresent = false;
310 }
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400311}
312
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500313- (IBAction) listTypeChanged:(id)sender
314{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400315 selectedUid_.clear();
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500316 NSInteger selectedItem = [sender selectedSegment];
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500317 switch (selectedItem) {
318 case CONVERSATION_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500319 if (currentFilterType != lrc::api::profile::Type::RING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400320 convModel_->setFilter(lrc::api::profile::Type::RING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500321 [delegate listTypeChanged];
322 currentFilterType = lrc::api::profile::Type::RING;
323 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500324 break;
325 case REQUEST_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500326 if (currentFilterType != lrc::api::profile::Type::PENDING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400327 convModel_->setFilter(lrc::api::profile::Type::PENDING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500328 [delegate listTypeChanged];
329 currentFilterType = lrc::api::profile::Type::PENDING;
330 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500331 break;
332 default:
333 NSLog(@"Invalid item selected in list selector: %d", selectedItem);
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500334 }
335}
336
337-(void) selectConversationList
338{
Anthony Léonardd00cd182018-01-17 09:21:27 -0500339 if (currentFilterType == lrc::api::profile::Type::RING)
340 return;
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500341 [listTypeSelector setSelectedSegment:CONVERSATION_SEG];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500342
343 // Do not invert order of the next two lines or stack overflow
344 // may happen on -(void) reloadData call if filter is currently set to PENDING
345 currentFilterType = lrc::api::profile::Type::RING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400346 convModel_->setFilter(lrc::api::profile::Type::RING);
347 convModel_->setFilter("");
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500348}
349
Anthony Léonardd00cd182018-01-17 09:21:27 -0500350-(void) selectPendingList
351{
352 if (currentFilterType == lrc::api::profile::Type::PENDING)
353 return;
354 [listTypeSelector setSelectedSegment:REQUEST_SEG];
355
356 currentFilterType = lrc::api::profile::Type::PENDING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400357 convModel_->setFilter(lrc::api::profile::Type::PENDING);
358 convModel_->setFilter("");
Anthony Léonardd00cd182018-01-17 09:21:27 -0500359}
360
Olivier Soldanod4311552017-11-20 15:09:53 -0500361#pragma mark - NSTableViewDelegate methods
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400362
Olivier Soldanod4311552017-11-20 15:09:53 -0500363- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400364{
365 return YES;
366}
367
Olivier Soldanod4311552017-11-20 15:09:53 -0500368- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400369{
370 return NO;
371}
372
Olivier Soldanod4311552017-11-20 15:09:53 -0500373- (void)tableViewSelectionDidChange:(NSNotification *)notification
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400374{
Olivier Soldanod4311552017-11-20 15:09:53 -0500375 NSInteger row = [notification.object selectedRow];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400376 NSInteger rows = [smartView numberOfRows];
Olivier Soldanod4311552017-11-20 15:09:53 -0500377
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400378 for (int i = 0; i< rows; i++) {
Kateryna Kostiuk394f74c2018-10-05 15:45:26 -0400379 HoverTableRowView* cellRowView = [smartView rowViewAtRow:i makeIfNecessary: NO];
380 [cellRowView drawSelection: (i == row)];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400381 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400382
Olivier Soldanod4311552017-11-20 15:09:53 -0500383 if (row == -1)
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400384 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400385 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400386 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500387
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400388 auto uid = convModel_->filteredConversation(row).uid;
Olivier Soldanod4311552017-11-20 15:09:53 -0500389 if (selectedUid_ != uid) {
390 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400391 convModel_->selectConversation(uid);
392 convModel_->clearUnreadInteractions(uid);
393 [self reloadSelectorNotifications];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400394 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400395}
396
Olivier Soldanod4311552017-11-20 15:09:53 -0500397- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400398{
Olivier Soldanod4311552017-11-20 15:09:53 -0500399 return [tableView makeViewWithIdentifier:@"HoverRowView" owner:nil];
400}
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400401
Olivier Soldanod4311552017-11-20 15:09:53 -0500402- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
403{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400404 if (convModel_ == nil)
Olivier Soldanod4311552017-11-20 15:09:53 -0500405 return nil;
406
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400407 auto conversation = convModel_->filteredConversation(row);
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400408 NSTableCellView* result;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400409
Olivier Soldanod4311552017-11-20 15:09:53 -0500410 result = [tableView makeViewWithIdentifier:@"MainCell" owner:tableView];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400411
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400412 NSTextField* unreadCount = [result viewWithTag:NOTIFICATONS_TAG];
Olivier Soldanod4311552017-11-20 15:09:53 -0500413 [unreadCount setHidden:(conversation.unreadMessages == 0)];
414 [unreadCount setIntValue:conversation.unreadMessages];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400415 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400416 NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400417 NSTextField* lastInteractionDate = [result viewWithTag:DATE_TAG];
418 NSTextField* interactionSnippet = [result viewWithTag:SNIPPET_TAG];
419 [displayName setStringValue:@""];
420 [displayRingID setStringValue:@""];
421 [lastInteractionDate setStringValue:@""];
422 [interactionSnippet setStringValue:@""];
423 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400424 NSString* displayNameString = bestNameForConversation(conversation, *convModel_);
425 NSString* displayIDString = bestIDForConversation(conversation, *convModel_);
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400426 if(displayNameString.length == 0 || [displayNameString isEqualToString:displayIDString]) {
427 [displayName setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400428 [displayRingID setHidden:YES];
Kateryna Kostiuk9d6657a2017-04-25 13:42:25 -0400429 }
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400430 else {
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400431 [displayName setStringValue:displayNameString];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400432 [displayRingID setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400433 [displayRingID setHidden:NO];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400434 }
Olivier Soldanod4311552017-11-20 15:09:53 -0500435 auto& imageManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400436 NSImage* image = QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(conversation, convModel_->owner)));
437 if(image) {
438 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
439 NSArray* constraints = [NSLayoutConstraint
440 constraintsWithVisualFormat:@"H:[photoView(54)]"
441 options:0
442 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
443 [NSLayoutConstraint activateConstraints:constraints];
444 } else {
445 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
446 NSArray* constraints = [NSLayoutConstraint
447 constraintsWithVisualFormat:@"H:[photoView(0)]"
448 options:0
449 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
450 [NSLayoutConstraint activateConstraints:constraints];
451 }
452 [photoView setImage: image];
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400453
454 NSView* presenceView = [result viewWithTag:PRESENCE_TAG];
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400455 [presenceView setHidden:YES];
456 if (!conversation.participants.empty()){
457 try {
458 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
459 if (contact.isPresent) {
460 [presenceView setHidden:NO];
461 }
462 } catch (std::out_of_range& e) {
463 NSLog(@"viewForTableColumn: getContact - out of range");
464 }
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400465 }
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400466
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400467 NSButton* addContactButton = [result viewWithTag:ADD_BUTTON_TAG];
468 NSButton* refuseContactButton = [result viewWithTag:REFUSE_BUTTON_TAG];
469 NSButton* blockContactButton = [result viewWithTag:BLOCK_BUTTON_TAG];
470 [addContactButton setHidden:YES];
471 [refuseContactButton setHidden:YES];
472 [blockContactButton setHidden:YES];
473
474 if (profileType(conversation, *convModel_) == lrc::api::profile::Type::PENDING) {
475 [lastInteractionDate setHidden:true];
476 [interactionSnippet setHidden:true];
477 [addContactButton setHidden:NO];
478 [refuseContactButton setHidden:NO];
479 [blockContactButton setHidden:NO];
480 [addContactButton setAction:@selector(acceptInvitation:)];
481 [addContactButton setTarget:self];
482 [refuseContactButton setAction:@selector(refuseInvitation:)];
483 [refuseContactButton setTarget:self];
484 [blockContactButton setAction:@selector(blockPendingContact:)];
485 [blockContactButton setTarget:self];
486 return result;
487 }
488
489 [lastInteractionDate setHidden:false];
490
491 [interactionSnippet setHidden:false];
492
493 auto lastUid = conversation.lastMessageUid;
494 if (conversation.interactions.find(lastUid) != conversation.interactions.end()) {
495 // last interaction snippet
496 std::string lastInteractionSnippet = conversation.interactions[lastUid].body;
497 std::stringstream ss(lastInteractionSnippet);
498 std::getline(ss, lastInteractionSnippet);
499 NSString* lastInteractionSnippetFixedString = [[NSString stringWithUTF8String:lastInteractionSnippet.c_str()]
500 stringByReplacingOccurrencesOfString:@"🕽" withString:@""];
501 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString stringByReplacingOccurrencesOfString:@"📞" withString:@""];
502 if (conversation.interactions[lastUid].type == lrc::api::interaction::Type::OUTGOING_DATA_TRANSFER
503 || conversation.interactions[lastUid].type == lrc::api::interaction::Type::INCOMING_DATA_TRANSFER) {
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400504 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString lastPathComponent];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400505 }
506 [interactionSnippet setStringValue:lastInteractionSnippetFixedString];
507
508 // last interaction date/time
509 std::time_t lastInteractionTimestamp = conversation.interactions[lastUid].timestamp;
510 std::time_t now = std::time(nullptr);
511 char interactionDay[64];
512 char nowDay[64];
513 std::strftime(interactionDay, sizeof(interactionDay), "%D", std::localtime(&lastInteractionTimestamp));
514 std::strftime(nowDay, sizeof(nowDay), "%D", std::localtime(&now));
515 if (std::string(interactionDay) == std::string(nowDay)) {
516 char interactionTime[64];
517 std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(&lastInteractionTimestamp));
518 [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionTime]];
519 } else {
520 [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionDay]];
521 }
522 }
523
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400524 return result;
525}
526
Olivier Soldanod4311552017-11-20 15:09:53 -0500527- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500528{
Olivier Soldanod4311552017-11-20 15:09:53 -0500529 return 60.0;
530}
531
532#pragma mark - NSTableDataSource methods
533
534- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
535{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400536 if (tableView == smartView && convModel_ != nullptr) {
537 return convModel_->allFilteredConversations().size();
Olivier Soldanod4311552017-11-20 15:09:53 -0500538 }
539
540 return 0;
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500541}
542
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -0400543- (void)startCallForRow:(id)sender {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400544 NSInteger row = [smartView rowForView:sender];
545 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
546 [self placeCall:nil];
547}
548
549- (IBAction)hangUpClickedAtRow:(id)sender {
550 NSInteger row = [smartView rowForView:sender];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500551
Olivier Soldanod4311552017-11-20 15:09:53 -0500552 if (row == -1)
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500553 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400554 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400555 return;
Alexandre Lision45c18672016-11-02 15:24:45 -0400556
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400557 auto conv = convModel_->filteredConversation(row);
Olivier Soldanod4311552017-11-20 15:09:53 -0500558 auto& callId = conv.callId;
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500559
Olivier Soldanod4311552017-11-20 15:09:53 -0500560 if (callId.empty())
561 return;
562
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400563 auto* callModel = convModel_->owner.callModel.get();
Olivier Soldanod4311552017-11-20 15:09:53 -0500564 callModel->hangUp(callId);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400565}
566
Alexandre Lision45c18672016-11-02 15:24:45 -0400567- (void) displayErrorModalWithTitle:(NSString*) title WithMessage:(NSString*) message
568{
569 NSAlert* alert = [NSAlert alertWithMessageText:title
570 defaultButton:@"Ok"
571 alternateButton:nil
572 otherButton:nil
573 informativeTextWithFormat:message];
574
575 [alert beginSheetModalForWindow:self.view.window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
576}
577
Olivier Soldanod4311552017-11-20 15:09:53 -0500578- (void) processSearchFieldInput
Alexandre Lision45c18672016-11-02 15:24:45 -0400579{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400580 if (convModel_ == nil) {
581 return;
582 }
583
584 convModel_->setFilter(std::string([[searchField stringValue] UTF8String]));
Alexandre Lision45c18672016-11-02 15:24:45 -0400585}
586
Olivier Soldanod4311552017-11-20 15:09:53 -0500587-(const lrc::api::account::Info&) chosenAccount
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400588{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400589 return convModel_->owner;
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400590}
591
Alexandre Lisionf909fb12016-11-03 12:35:36 -0400592- (void) clearSearchField
593{
594 [searchField setStringValue:@""];
Olivier Soldanod4311552017-11-20 15:09:53 -0500595 [self processSearchFieldInput];
Kateryna Kostiuk9dd759c2017-07-11 12:06:55 -0400596}
597
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400598-(void)updateConversationForNewContact:(NSString *)uId {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400599 if (convModel_ == nil) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400600 return;
601 }
602 [self clearSearchField];
603 auto uid = std::string([uId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400604 auto it = getConversationFromUid(uid, *convModel_);
605 if (it != convModel_->allFilteredConversations().end()) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400606 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400607 auto contact = convModel_->owner.contactModel->getContact(it->participants[0]);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400608 if (!contact.profileInfo.uri.empty() && contact.profileInfo.uri.compare(selectedUid_) == 0) {
609 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400610 convModel_->selectConversation(uid);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400611 }
612 } @catch (NSException *exception) {
613 return;
614 }
615 }
616}
617
Alexandre Lision72a669e2016-09-14 17:52:16 -0400618/**
619 Copy a NSString in the general Pasteboard
620
621 @param sender the NSObject containing the represented object to copy
622 */
623- (void) copyStringToPasteboard:(id) sender
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400624{
625 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
626 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
627 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
628}
629
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400630#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400631
632- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
633{
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400634 if (commandSelector != @selector(insertNewline:) || [[searchField stringValue] isEqual:@""]) {
635 return NO;
636 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400637 if (convModel_ == nil) {
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400638 [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")];
639 return NO;
640 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400641 if (convModel_->allFilteredConversations().size() <= 0) {
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400642 return YES;
643 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400644 auto model = convModel_->filteredConversation(0);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400645 auto uid = model.uid;
646 if (selectedUid_ == uid) {
647 return YES;
648 }
649 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400650 auto contact = convModel_->owner.contactModel->getContact(model.participants[0]);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400651 if ((contact.profileInfo.uri.empty() && contact.profileInfo.type != lrc::api::profile::Type::SIP) || contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400652 return YES;
653 }
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400654 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400655 convModel_->selectConversation(uid);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400656 [self.view.window makeFirstResponder: smartView];
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400657 return YES;
658 } @catch (NSException *exception) {
659 return YES;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400660 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400661}
662
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500663- (void)controlTextDidChange:(NSNotification *) notification
664{
Olivier Soldanod4311552017-11-20 15:09:53 -0500665 [self processSearchFieldInput];
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500666}
667
Alexandre Lision61db3552015-10-22 19:12:52 -0400668#pragma mark - NSPopOverDelegate
669
670- (void)popoverDidClose:(NSNotification *)notification
671{
672 if (addToContactPopover != nullptr) {
673 [addToContactPopover performClose:self];
674 addToContactPopover = NULL;
675 }
676}
677
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400678
Alexandre Lision61db3552015-10-22 19:12:52 -0400679#pragma mark - ContactLinkedDelegate
680
681- (void)contactLinked
682{
683 if (addToContactPopover != nullptr) {
684 [addToContactPopover performClose:self];
685 addToContactPopover = NULL;
686 }
687}
688
689#pragma mark - KeyboardShortcutDelegate
690
691- (void) onAddShortcut
692{
Olivier Soldanod4311552017-11-20 15:09:53 -0500693 if ([smartView selectedRow] == -1)
Alexandre Lision61db3552015-10-22 19:12:52 -0400694 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400695 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400696 return ;
Alexandre Lision61db3552015-10-22 19:12:52 -0400697
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400698 auto uid = convModel_->filteredConversation([smartView selectedRow]).uid;
699 convModel_->makePermanent(uid);
Alexandre Lision61db3552015-10-22 19:12:52 -0400700}
701
702#pragma mark - ContextMenuDelegate
703
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400704- (NSMenu*) contextualMenuForRow:(int) index
Alexandre Lision61db3552015-10-22 19:12:52 -0400705{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400706 if (convModel_ == nil)
Alexandre Lision61db3552015-10-22 19:12:52 -0400707 return nil;
708
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400709 auto conversation = convModel_->filteredConversation(NSInteger(index));
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400710
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400711 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400712 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400713 if (contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
714 return nil;
715 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400716
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400717 BOOL isSIP = false;
718 BOOL isRingContact = false;
719 /* for SIP contact show only call menu options
720 * if contact does not have uri that is not RING contact
721 * for trusted Ring contact show option block contact
722 * for untrasted contact show option add contact
723 */
Alexandre Lision72a669e2016-09-14 17:52:16 -0400724
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400725 if (contact.profileInfo.type == lrc::api::profile::Type::SIP) {
726 isSIP = true;
727 } else if (contact.profileInfo.uri.empty()) {
728 return nil;
729 }
730
731 else if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.isTrusted == true) {
732 isRingContact = true;
733 }
734 auto conversationUD = conversation.uid;
735 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
736 NSString* conversationUID = @(conversationUD.c_str());
737 NSMenuItem* separator = [NSMenuItem separatorItem];
738 NSMenuItem* videoCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place video call",
739 @"Contextual menu action")
740 action:@selector(videoCall:)
741 keyEquivalent:@""];
742 NSMenuItem* audioCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place audio call",
743 @"Contextual menu action")
744 action:@selector(audioCall:)
745 keyEquivalent:@""];
746 [videoCallItem setRepresentedObject: conversationUID];
747 [audioCallItem setRepresentedObject: conversationUID];
748 [theMenu addItem:videoCallItem];
749 [theMenu addItem:audioCallItem];
750 if (isSIP == false) {
751 [theMenu addItem:separator];
752 NSMenuItem* clearConversationItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Clear conversation", @"Contextual menu action")
753 action:@selector(clearConversation:)
754 keyEquivalent:@""];
755 [clearConversationItem setRepresentedObject: conversationUID];
756 [theMenu addItem:clearConversationItem];
757 if(isRingContact) {
758 NSMenuItem* blockContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Block contact", @"Contextual menu action")
759 action:@selector(blockContact:)
760 keyEquivalent:@""];
761 [blockContactItem setRepresentedObject: conversationUID];
762 [theMenu addItem:blockContactItem];
763 } else {
764 NSMenuItem* addContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
765 action:@selector(addContact:)
766 keyEquivalent:@"A"];
767 [addContactItem setRepresentedObject: conversationUID];
768 [theMenu addItem:addContactItem];
769 }
770 }
771 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400772 }
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400773 @catch (NSException *exception) {
774 return nil;
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400775 }
Alexandre Lision61db3552015-10-22 19:12:52 -0400776}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400777
778- (void) addContact: (NSMenuItem* ) item {
779 auto menuObject = item.representedObject;
780 if(menuObject == nil) {
781 return;
782 }
783 NSString * convUId = (NSString*)menuObject;
784 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400785 convModel_->makePermanent(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400786}
787
788- (void) blockContact: (NSMenuItem* ) item {
789 auto menuObject = item.representedObject;
790 if(menuObject == nil) {
791 return;
792 }
793 NSString * convUId = (NSString*)menuObject;
794 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400795 //convModel_->clearHistory(conversationID);
796 convModel_->removeConversation(conversationID, true);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400797}
798
799- (void) audioCall: (NSMenuItem* ) item {
800 auto menuObject = item.representedObject;
801 if(menuObject == nil) {
802 return;
803 }
804 NSString * convUId = (NSString*)menuObject;
805 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400806 convModel_->placeAudioOnlyCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400807
808}
809
810- (void) videoCall: (NSMenuItem* ) item {
811 auto menuObject = item.representedObject;
812 if(menuObject == nil) {
813 return;
814 }
815 NSString * convUId = (NSString*)menuObject;
816 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400817 convModel_->placeCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400818}
819
820- (void) clearConversation:(NSMenuItem* ) item {
821 auto menuObject = item.representedObject;
822 if(menuObject == nil) {
823 return;
824 }
825 NSString * convUId = (NSString*)menuObject;
826 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400827 convModel_->clearHistory(conversationID);
828}
829
830- (void)acceptInvitation:(id)sender {
831 NSInteger row = [smartView rowForView:sender];
832
833 if (row == -1)
834 return;
835 if (convModel_ == nil)
836 return;
837
838 auto conv = convModel_->filteredConversation(row);
839 auto& convID = conv.Info::uid;
840
841 if (convID.empty())
842 return;
843 convModel_->makePermanent(convID);
844}
845
846- (void)refuseInvitation:(id)sender {
847 NSInteger row = [smartView rowForView:sender];
848
849 if (row == -1)
850 return;
851 if (convModel_ == nil)
852 return;
853
854 auto conv = convModel_->filteredConversation(row);
855 auto& convID = conv.Info::uid;
856
857 if (convID.empty())
858 return;
859 convModel_->removeConversation(convID);
860}
861
862- (void)blockPendingContact:(id)sender {
863 NSInteger row = [smartView rowForView:sender];
864
865 if (row == -1)
866 return;
867 if (convModel_ == nil)
868 return;
869
870 auto conv = convModel_->filteredConversation(row);
871 auto& convID = conv.Info::uid;
872
873 if (convID.empty())
874 return;
875 convModel_->removeConversation(convID, true);
876 [self deselect];
877 [delegate listTypeChanged];
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400878}
Alexandre Lision61db3552015-10-22 19:12:52 -0400879
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400880@end