blob: 895418c9c1a8d0374359fdead382e1dddaf57657 [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;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -050061 bool selectorIsPresent;
Alexandre Lision45c18672016-11-02 15:24:45 -040062
Andreas Traczyk252a94a2018-04-20 16:36:20 -040063 QMetaObject::Connection modelSortedConnection_, modelUpdatedConnection_, filterChangedConnection_, newConversationConnection_, conversationRemovedConnection_, newInteractionConnection_, interactionStatusUpdatedConnection_, conversationClearedConnection;
Olivier Soldanod4311552017-11-20 15:09:53 -050064
Andreas Traczyk252a94a2018-04-20 16:36:20 -040065 lrc::api::ConversationModel* convModel_;
Olivier Soldanod4311552017-11-20 15:09:53 -050066 std::string selectedUid_;
Anthony Léonard4bfd68b2018-01-15 16:23:50 -050067 lrc::api::profile::Type currentFilterType;
Anthony Léonardbee94cc2018-01-16 11:42:40 -050068
69 __unsafe_unretained IBOutlet RingWindowController *delegate;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040070}
71
72@end
73
74@implementation SmartViewVC
75
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -040076@synthesize tabbar;
77
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040078// Tags for views
Alexandre Lision57914fa2016-02-10 14:52:24 -050079NSInteger const IMAGE_TAG = 100;
80NSInteger const DISPLAYNAME_TAG = 200;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040081NSInteger const NOTIFICATONS_TAG = 300;
82NSInteger const RING_ID_LABEL = 400;
83NSInteger const PRESENCE_TAG = 500;
84NSInteger const TOTALMSGS_TAG = 600;
85NSInteger const TOTALINVITES_TAG = 700;
86NSInteger const DATE_TAG = 800;
87NSInteger const SNIPPET_TAG = 900;
88NSInteger const ADD_BUTTON_TAG = 1000;
89NSInteger const REFUSE_BUTTON_TAG = 1100;
90NSInteger const BLOCK_BUTTON_TAG = 1200;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040091
Anthony Léonarde6a01fa2018-01-12 10:29:58 -050092// Segment indices for smartlist selector
93NSInteger const CONVERSATION_SEG = 0;
94NSInteger const REQUEST_SEG = 1;
95
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040096- (void)awakeFromNib
97{
98 NSLog(@"INIT SmartView VC");
Olivier Soldanod4311552017-11-20 15:09:53 -050099 //get selected account
100 //encapsulate conversationmodel in local version
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400101
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400102 [smartView setTarget:self];
103 [smartView setDoubleAction:@selector(placeCall:)];
104
Alexandre Lision61db3552015-10-22 19:12:52 -0400105 [smartView setContextMenuDelegate:self];
106 [smartView setShortcutsDelegate:self];
107
Olivier Soldanod4311552017-11-20 15:09:53 -0500108 [smartView setDataSource: self];
Kateryna Kostiuk1f705ab2017-04-18 12:45:59 -0400109
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400110 [self.view setWantsLayer:YES];
111 [self.view setLayer:[CALayer layer]];
112 [self.view.layer setBackgroundColor:[NSColor whiteColor].CGColor];
113
114 [searchField setWantsLayer:YES];
115 [searchField setLayer:[CALayer layer]];
116 [searchField.layer setBackgroundColor:[NSColor colorWithCalibratedRed:0.949 green:0.949 blue:0.949 alpha:0.9].CGColor];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500117
118 currentFilterType = lrc::api::profile::Type::RING;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500119 selectorIsPresent = true;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400120
121 smartView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400122
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400123}
124
125- (void)placeCall:(id)sender
126{
Olivier Soldanod4311552017-11-20 15:09:53 -0500127 NSInteger row;
128 if (sender != nil && [sender clickedRow] != -1)
129 row = [sender clickedRow];
130 else if ([smartView selectedRow] != -1)
131 row = [smartView selectedRow];
132 else
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400133 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400134 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400135 return;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400136
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400137 auto conv = convModel_->filteredConversation(row);
138 convModel_->placeCall(conv.uid);
139}
140
141-(void) reloadSelectorNotifications
142{
143 NSTextField* totalMsgsCount = [self.view viewWithTag:TOTALMSGS_TAG];
144 NSTextField* totalInvites = [self.view viewWithTag:TOTALINVITES_TAG];
145
146 if (!selectorIsPresent) {
147 [totalMsgsCount setHidden:true];
148 [totalInvites setHidden:true];
149 return;
150 }
151
152 auto ringConversations = convModel_->getFilteredConversations(lrc::api::profile::Type::RING);
153 int totalUnreadMessages = 0;
154 std::for_each(ringConversations.begin(), ringConversations.end(),
155 [&totalUnreadMessages, self] (const auto& conversation) {
156 totalUnreadMessages += convModel_->getNumberOfUnreadMessagesFor(conversation.uid);
157 });
158 [totalMsgsCount setHidden:(totalUnreadMessages == 0)];
159 [totalMsgsCount setIntValue:totalUnreadMessages];
160
161 auto totalRequests = [self chosenAccount].contactModel->pendingRequestCount();
162 [totalInvites setHidden:(totalRequests == 0)];
163 [totalInvites setIntValue:totalRequests];
Olivier Soldanod4311552017-11-20 15:09:53 -0500164}
165
166-(void) reloadData
167{
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400168 NSLog(@"reload");
Olivier Soldanod4311552017-11-20 15:09:53 -0500169 [smartView deselectAll:nil];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400170 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400171 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500172
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400173 [self reloadSelectorNotifications];
174
175 if (!convModel_->owner.contactModel->hasPendingRequests()) {
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500176 if (currentFilterType == lrc::api::profile::Type::PENDING) {
177 [self selectConversationList];
178 }
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500179 if (selectorIsPresent) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400180 listTypeSelectorHeight.constant = 0.0;
181 [listTypeSelector setHidden:YES];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500182 selectorIsPresent = false;
183 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500184 } else {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500185 if (!selectorIsPresent) {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500186 [listTypeSelector setSelected:YES forSegment:CONVERSATION_SEG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400187 listTypeSelectorHeight.constant = 18.0;
188 [listTypeSelector setHidden:NO];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500189 selectorIsPresent = true;
190 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500191 }
192
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500193 [smartView reloadData];
Kateryna Kostiukc8035c62018-09-19 16:13:14 -0400194 [smartView layoutSubtreeIfNeeded];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500195
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400196 if (!selectedUid_.empty() && convModel_ != nil) {
197 auto it = getConversationFromUid(selectedUid_, *convModel_);
198 if (it != convModel_->allFilteredConversations().end()) {
199 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Olivier Soldanod4311552017-11-20 15:09:53 -0500200 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400201 }
202 }
203
Olivier Soldanod4311552017-11-20 15:09:53 -0500204 [smartView scrollToBeginningOfDocument:nil];
205}
206
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400207-(void) reloadConversationWithUid:(NSString *)uid
208{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400209 if (convModel_ == nil) {
210 return;
211 }
212
213 auto it = getConversationFromUid(std::string([uid UTF8String]), *convModel_);
214 if (it != convModel_->allFilteredConversations().end()) {
215 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
216 NSLog(@"reloadConversationWithUid: %@", uid);
217 [smartView reloadDataForRowIndexes:indexSet
218 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400219 }
220}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400221
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500222- (BOOL)setConversationModel:(lrc::api::ConversationModel *)conversationModel
Olivier Soldanod4311552017-11-20 15:09:53 -0500223{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400224 if (convModel_ == conversationModel) {
225 return false;
226 }
227
228 convModel_ = conversationModel;
229 selectedUid_.clear(); // Clear selected conversation as the selected account is being changed
230 QObject::disconnect(modelSortedConnection_);
231 QObject::disconnect(modelUpdatedConnection_);
232 QObject::disconnect(filterChangedConnection_);
233 QObject::disconnect(newConversationConnection_);
234 QObject::disconnect(conversationRemovedConnection_);
235 QObject::disconnect(conversationClearedConnection);
236 QObject::disconnect(interactionStatusUpdatedConnection_);
237 QObject::disconnect(newInteractionConnection_);
238 [self reloadData];
239 if (convModel_ != nil) {
240 modelSortedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::modelSorted,
Olivier Soldanod4311552017-11-20 15:09:53 -0500241 [self] (){
242 [self reloadData];
243 });
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400244 modelUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationUpdated,
245 [self] (const std::string& convUid){
246 [self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
247 });
248 filterChangedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::filterChanged,
249 [self] (){
250 [self reloadData];
251 });
252 newConversationConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newConversation,
253 [self] (const std::string& convUid) {
254 [self reloadData];
255 [self updateConversationForNewContact:[NSString stringWithUTF8String:convUid.c_str()]];
256 });
257 conversationRemovedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationRemoved,
258 [self] (){
259 [delegate listTypeChanged];
260 [self reloadData];
261 });
262 conversationClearedConnection = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationCleared,
263 [self] (const std::string& convUid){
264 [self deselect];
265 [delegate listTypeChanged];
266 });
267 interactionStatusUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::interactionStatusUpdated,
268 [self] (const std::string& convUid) {
269 if (convUid != selectedUid_)
270 return;
271 [self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
272 });
273 newInteractionConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newInteraction,
274 [self](const std::string& convUid, uint64_t interactionId, const lrc::api::interaction::Info& interaction){
275 if (convUid == selectedUid_) {
276 convModel_->clearUnreadInteractions(convUid);
277 }
278 });
279 convModel_->setFilter(""); // Reset the filter
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500280 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400281 [searchField setStringValue:@""];
282 return true;
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500283}
284
285-(void)selectConversation:(const lrc::api::conversation::Info&)conv model:(lrc::api::ConversationModel*)model;
286{
287 auto& uid = conv.uid;
288 if (selectedUid_ == uid)
289 return;
290
291 [self setConversationModel:model];
292
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400293 if (convModel_ == nil) {
294 return;
295 }
296
297 auto it = getConversationFromUid(selectedUid_, *convModel_);
298 if (it != convModel_->allFilteredConversations().end()) {
299 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
300 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
301 selectedUid_ = uid;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400302 }
303}
304
Anthony Léonard8585cc02017-12-28 14:03:45 -0500305-(void)deselect
306{
307 selectedUid_.clear();
308 [smartView deselectAll:nil];
309}
310
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400311-(void) clearConversationModel {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400312 convModel_ = nil;
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400313 [self deselect];
314 [smartView reloadData];
Andreas Traczyk4fdb2d92018-04-19 15:57:46 -0400315 if (selectorIsPresent) {
316 [listTypeSelector removeFromSuperview];
317 selectorIsPresent = false;
318 }
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400319}
320
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500321- (IBAction) listTypeChanged:(id)sender
322{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400323 selectedUid_.clear();
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500324 NSInteger selectedItem = [sender selectedSegment];
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500325 switch (selectedItem) {
326 case CONVERSATION_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500327 if (currentFilterType != lrc::api::profile::Type::RING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400328 convModel_->setFilter(lrc::api::profile::Type::RING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500329 [delegate listTypeChanged];
330 currentFilterType = lrc::api::profile::Type::RING;
331 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500332 break;
333 case REQUEST_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500334 if (currentFilterType != lrc::api::profile::Type::PENDING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400335 convModel_->setFilter(lrc::api::profile::Type::PENDING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500336 [delegate listTypeChanged];
337 currentFilterType = lrc::api::profile::Type::PENDING;
338 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500339 break;
340 default:
341 NSLog(@"Invalid item selected in list selector: %d", selectedItem);
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500342 }
343}
344
345-(void) selectConversationList
346{
Anthony Léonardd00cd182018-01-17 09:21:27 -0500347 if (currentFilterType == lrc::api::profile::Type::RING)
348 return;
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500349 [listTypeSelector setSelectedSegment:CONVERSATION_SEG];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500350
351 // Do not invert order of the next two lines or stack overflow
352 // may happen on -(void) reloadData call if filter is currently set to PENDING
353 currentFilterType = lrc::api::profile::Type::RING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400354 convModel_->setFilter(lrc::api::profile::Type::RING);
355 convModel_->setFilter("");
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500356}
357
Anthony Léonardd00cd182018-01-17 09:21:27 -0500358-(void) selectPendingList
359{
360 if (currentFilterType == lrc::api::profile::Type::PENDING)
361 return;
362 [listTypeSelector setSelectedSegment:REQUEST_SEG];
363
364 currentFilterType = lrc::api::profile::Type::PENDING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400365 convModel_->setFilter(lrc::api::profile::Type::PENDING);
366 convModel_->setFilter("");
Anthony Léonardd00cd182018-01-17 09:21:27 -0500367}
368
Olivier Soldanod4311552017-11-20 15:09:53 -0500369#pragma mark - NSTableViewDelegate methods
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400370
Olivier Soldanod4311552017-11-20 15:09:53 -0500371- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400372{
373 return YES;
374}
375
Olivier Soldanod4311552017-11-20 15:09:53 -0500376- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400377{
378 return NO;
379}
380
Olivier Soldanod4311552017-11-20 15:09:53 -0500381- (void)tableViewSelectionDidChange:(NSNotification *)notification
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400382{
Olivier Soldanod4311552017-11-20 15:09:53 -0500383 NSInteger row = [notification.object selectedRow];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400384 NSInteger rows = [smartView numberOfRows];
Olivier Soldanod4311552017-11-20 15:09:53 -0500385
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400386 for (int i = 0; i< rows; i++) {
Kateryna Kostiukc8035c62018-09-19 16:13:14 -0400387 NSTableRowView* cellRowView = [smartView rowViewAtRow:i makeIfNecessary: NO];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400388 if (i == row) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400389 cellRowView.backgroundColor = [NSColor controlColor];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400390 } else {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400391 cellRowView.backgroundColor = [NSColor whiteColor];
392 }
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400393 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400394
Olivier Soldanod4311552017-11-20 15:09:53 -0500395 if (row == -1)
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400396 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400397 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400398 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500399
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400400 auto uid = convModel_->filteredConversation(row).uid;
Olivier Soldanod4311552017-11-20 15:09:53 -0500401 if (selectedUid_ != uid) {
402 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400403 convModel_->selectConversation(uid);
404 convModel_->clearUnreadInteractions(uid);
405 [self reloadSelectorNotifications];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400406 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400407}
408
Olivier Soldanod4311552017-11-20 15:09:53 -0500409- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400410{
Olivier Soldanod4311552017-11-20 15:09:53 -0500411 return [tableView makeViewWithIdentifier:@"HoverRowView" owner:nil];
412}
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400413
Olivier Soldanod4311552017-11-20 15:09:53 -0500414- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
415{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400416 if (convModel_ == nil)
Olivier Soldanod4311552017-11-20 15:09:53 -0500417 return nil;
418
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400419 auto conversation = convModel_->filteredConversation(row);
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400420 NSTableCellView* result;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400421
Olivier Soldanod4311552017-11-20 15:09:53 -0500422 result = [tableView makeViewWithIdentifier:@"MainCell" owner:tableView];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400423
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400424 NSTextField* unreadCount = [result viewWithTag:NOTIFICATONS_TAG];
Olivier Soldanod4311552017-11-20 15:09:53 -0500425 [unreadCount setHidden:(conversation.unreadMessages == 0)];
426 [unreadCount setIntValue:conversation.unreadMessages];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400427 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400428 NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400429 NSTextField* lastInteractionDate = [result viewWithTag:DATE_TAG];
430 NSTextField* interactionSnippet = [result viewWithTag:SNIPPET_TAG];
431 [displayName setStringValue:@""];
432 [displayRingID setStringValue:@""];
433 [lastInteractionDate setStringValue:@""];
434 [interactionSnippet setStringValue:@""];
435 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400436 NSString* displayNameString = bestNameForConversation(conversation, *convModel_);
437 NSString* displayIDString = bestIDForConversation(conversation, *convModel_);
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400438 if(displayNameString.length == 0 || [displayNameString isEqualToString:displayIDString]) {
439 [displayName setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400440 [displayRingID setHidden:YES];
Kateryna Kostiuk9d6657a2017-04-25 13:42:25 -0400441 }
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400442 else {
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400443 [displayName setStringValue:displayNameString];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400444 [displayRingID setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400445 [displayRingID setHidden:NO];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400446 }
Olivier Soldanod4311552017-11-20 15:09:53 -0500447 auto& imageManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400448 NSImage* image = QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(conversation, convModel_->owner)));
449 if(image) {
450 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
451 NSArray* constraints = [NSLayoutConstraint
452 constraintsWithVisualFormat:@"H:[photoView(54)]"
453 options:0
454 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
455 [NSLayoutConstraint activateConstraints:constraints];
456 } else {
457 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
458 NSArray* constraints = [NSLayoutConstraint
459 constraintsWithVisualFormat:@"H:[photoView(0)]"
460 options:0
461 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
462 [NSLayoutConstraint activateConstraints:constraints];
463 }
464 [photoView setImage: image];
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400465
466 NSView* presenceView = [result viewWithTag:PRESENCE_TAG];
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400467 [presenceView setHidden:YES];
468 if (!conversation.participants.empty()){
469 try {
470 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
471 if (contact.isPresent) {
472 [presenceView setHidden:NO];
473 }
474 } catch (std::out_of_range& e) {
475 NSLog(@"viewForTableColumn: getContact - out of range");
476 }
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400477 }
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400478
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400479 NSButton* addContactButton = [result viewWithTag:ADD_BUTTON_TAG];
480 NSButton* refuseContactButton = [result viewWithTag:REFUSE_BUTTON_TAG];
481 NSButton* blockContactButton = [result viewWithTag:BLOCK_BUTTON_TAG];
482 [addContactButton setHidden:YES];
483 [refuseContactButton setHidden:YES];
484 [blockContactButton setHidden:YES];
485
486 if (profileType(conversation, *convModel_) == lrc::api::profile::Type::PENDING) {
487 [lastInteractionDate setHidden:true];
488 [interactionSnippet setHidden:true];
489 [addContactButton setHidden:NO];
490 [refuseContactButton setHidden:NO];
491 [blockContactButton setHidden:NO];
492 [addContactButton setAction:@selector(acceptInvitation:)];
493 [addContactButton setTarget:self];
494 [refuseContactButton setAction:@selector(refuseInvitation:)];
495 [refuseContactButton setTarget:self];
496 [blockContactButton setAction:@selector(blockPendingContact:)];
497 [blockContactButton setTarget:self];
498 return result;
499 }
500
501 [lastInteractionDate setHidden:false];
502
503 [interactionSnippet setHidden:false];
504
505 auto lastUid = conversation.lastMessageUid;
506 if (conversation.interactions.find(lastUid) != conversation.interactions.end()) {
507 // last interaction snippet
508 std::string lastInteractionSnippet = conversation.interactions[lastUid].body;
509 std::stringstream ss(lastInteractionSnippet);
510 std::getline(ss, lastInteractionSnippet);
511 NSString* lastInteractionSnippetFixedString = [[NSString stringWithUTF8String:lastInteractionSnippet.c_str()]
512 stringByReplacingOccurrencesOfString:@"🕽" withString:@""];
513 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString stringByReplacingOccurrencesOfString:@"📞" withString:@""];
514 if (conversation.interactions[lastUid].type == lrc::api::interaction::Type::OUTGOING_DATA_TRANSFER
515 || conversation.interactions[lastUid].type == lrc::api::interaction::Type::INCOMING_DATA_TRANSFER) {
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400516 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString lastPathComponent];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400517 }
518 [interactionSnippet setStringValue:lastInteractionSnippetFixedString];
519
520 // last interaction date/time
521 std::time_t lastInteractionTimestamp = conversation.interactions[lastUid].timestamp;
522 std::time_t now = std::time(nullptr);
523 char interactionDay[64];
524 char nowDay[64];
525 std::strftime(interactionDay, sizeof(interactionDay), "%D", std::localtime(&lastInteractionTimestamp));
526 std::strftime(nowDay, sizeof(nowDay), "%D", std::localtime(&now));
527 if (std::string(interactionDay) == std::string(nowDay)) {
528 char interactionTime[64];
529 std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(&lastInteractionTimestamp));
530 [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionTime]];
531 } else {
532 [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionDay]];
533 }
534 }
535
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400536 return result;
537}
538
Olivier Soldanod4311552017-11-20 15:09:53 -0500539- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500540{
Olivier Soldanod4311552017-11-20 15:09:53 -0500541 return 60.0;
542}
543
544#pragma mark - NSTableDataSource methods
545
546- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
547{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400548 if (tableView == smartView && convModel_ != nullptr) {
549 return convModel_->allFilteredConversations().size();
Olivier Soldanod4311552017-11-20 15:09:53 -0500550 }
551
552 return 0;
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500553}
554
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -0400555- (void)startCallForRow:(id)sender {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400556 NSInteger row = [smartView rowForView:sender];
557 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
558 [self placeCall:nil];
559}
560
561- (IBAction)hangUpClickedAtRow:(id)sender {
562 NSInteger row = [smartView rowForView:sender];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500563
Olivier Soldanod4311552017-11-20 15:09:53 -0500564 if (row == -1)
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500565 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400566 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400567 return;
Alexandre Lision45c18672016-11-02 15:24:45 -0400568
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400569 auto conv = convModel_->filteredConversation(row);
Olivier Soldanod4311552017-11-20 15:09:53 -0500570 auto& callId = conv.callId;
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500571
Olivier Soldanod4311552017-11-20 15:09:53 -0500572 if (callId.empty())
573 return;
574
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400575 auto* callModel = convModel_->owner.callModel.get();
Olivier Soldanod4311552017-11-20 15:09:53 -0500576 callModel->hangUp(callId);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400577}
578
Alexandre Lision45c18672016-11-02 15:24:45 -0400579- (void) displayErrorModalWithTitle:(NSString*) title WithMessage:(NSString*) message
580{
581 NSAlert* alert = [NSAlert alertWithMessageText:title
582 defaultButton:@"Ok"
583 alternateButton:nil
584 otherButton:nil
585 informativeTextWithFormat:message];
586
587 [alert beginSheetModalForWindow:self.view.window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
588}
589
Olivier Soldanod4311552017-11-20 15:09:53 -0500590- (void) processSearchFieldInput
Alexandre Lision45c18672016-11-02 15:24:45 -0400591{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400592 if (convModel_ == nil) {
593 return;
594 }
595
596 convModel_->setFilter(std::string([[searchField stringValue] UTF8String]));
Alexandre Lision45c18672016-11-02 15:24:45 -0400597}
598
Olivier Soldanod4311552017-11-20 15:09:53 -0500599-(const lrc::api::account::Info&) chosenAccount
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400600{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400601 return convModel_->owner;
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400602}
603
Alexandre Lisionf909fb12016-11-03 12:35:36 -0400604- (void) clearSearchField
605{
606 [searchField setStringValue:@""];
Olivier Soldanod4311552017-11-20 15:09:53 -0500607 [self processSearchFieldInput];
Kateryna Kostiuk9dd759c2017-07-11 12:06:55 -0400608}
609
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400610-(void)updateConversationForNewContact:(NSString *)uId {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400611 if (convModel_ == nil) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400612 return;
613 }
614 [self clearSearchField];
615 auto uid = std::string([uId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400616 auto it = getConversationFromUid(uid, *convModel_);
617 if (it != convModel_->allFilteredConversations().end()) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400618 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400619 auto contact = convModel_->owner.contactModel->getContact(it->participants[0]);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400620 if (!contact.profileInfo.uri.empty() && contact.profileInfo.uri.compare(selectedUid_) == 0) {
621 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400622 convModel_->selectConversation(uid);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400623 }
624 } @catch (NSException *exception) {
625 return;
626 }
627 }
628}
629
Alexandre Lision72a669e2016-09-14 17:52:16 -0400630/**
631 Copy a NSString in the general Pasteboard
632
633 @param sender the NSObject containing the represented object to copy
634 */
635- (void) copyStringToPasteboard:(id) sender
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400636{
637 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
638 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
639 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
640}
641
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400642#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400643
644- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
645{
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400646 if (commandSelector != @selector(insertNewline:) || [[searchField stringValue] isEqual:@""]) {
647 return NO;
648 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400649 if (convModel_ == nil) {
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400650 [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")];
651 return NO;
652 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400653 if (convModel_->allFilteredConversations().size() <= 0) {
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400654 return YES;
655 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400656 auto model = convModel_->filteredConversation(0);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400657 auto uid = model.uid;
658 if (selectedUid_ == uid) {
659 return YES;
660 }
661 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400662 auto contact = convModel_->owner.contactModel->getContact(model.participants[0]);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400663 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 -0400664 return YES;
665 }
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400666 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400667 convModel_->selectConversation(uid);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400668 [self.view.window makeFirstResponder: smartView];
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400669 return YES;
670 } @catch (NSException *exception) {
671 return YES;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400672 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400673}
674
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500675- (void)controlTextDidChange:(NSNotification *) notification
676{
Olivier Soldanod4311552017-11-20 15:09:53 -0500677 [self processSearchFieldInput];
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500678}
679
Alexandre Lision61db3552015-10-22 19:12:52 -0400680#pragma mark - NSPopOverDelegate
681
682- (void)popoverDidClose:(NSNotification *)notification
683{
684 if (addToContactPopover != nullptr) {
685 [addToContactPopover performClose:self];
686 addToContactPopover = NULL;
687 }
688}
689
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400690
Alexandre Lision61db3552015-10-22 19:12:52 -0400691#pragma mark - ContactLinkedDelegate
692
693- (void)contactLinked
694{
695 if (addToContactPopover != nullptr) {
696 [addToContactPopover performClose:self];
697 addToContactPopover = NULL;
698 }
699}
700
701#pragma mark - KeyboardShortcutDelegate
702
703- (void) onAddShortcut
704{
Olivier Soldanod4311552017-11-20 15:09:53 -0500705 if ([smartView selectedRow] == -1)
Alexandre Lision61db3552015-10-22 19:12:52 -0400706 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400707 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400708 return ;
Alexandre Lision61db3552015-10-22 19:12:52 -0400709
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400710 auto uid = convModel_->filteredConversation([smartView selectedRow]).uid;
711 convModel_->makePermanent(uid);
Alexandre Lision61db3552015-10-22 19:12:52 -0400712}
713
714#pragma mark - ContextMenuDelegate
715
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400716- (NSMenu*) contextualMenuForRow:(int) index
Alexandre Lision61db3552015-10-22 19:12:52 -0400717{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400718 if (convModel_ == nil)
Alexandre Lision61db3552015-10-22 19:12:52 -0400719 return nil;
720
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400721 auto conversation = convModel_->filteredConversation(NSInteger(index));
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400722
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400723 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400724 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400725 if (contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
726 return nil;
727 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400728
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400729 BOOL isSIP = false;
730 BOOL isRingContact = false;
731 /* for SIP contact show only call menu options
732 * if contact does not have uri that is not RING contact
733 * for trusted Ring contact show option block contact
734 * for untrasted contact show option add contact
735 */
Alexandre Lision72a669e2016-09-14 17:52:16 -0400736
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400737 if (contact.profileInfo.type == lrc::api::profile::Type::SIP) {
738 isSIP = true;
739 } else if (contact.profileInfo.uri.empty()) {
740 return nil;
741 }
742
743 else if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.isTrusted == true) {
744 isRingContact = true;
745 }
746 auto conversationUD = conversation.uid;
747 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
748 NSString* conversationUID = @(conversationUD.c_str());
749 NSMenuItem* separator = [NSMenuItem separatorItem];
750 NSMenuItem* videoCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place video call",
751 @"Contextual menu action")
752 action:@selector(videoCall:)
753 keyEquivalent:@""];
754 NSMenuItem* audioCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place audio call",
755 @"Contextual menu action")
756 action:@selector(audioCall:)
757 keyEquivalent:@""];
758 [videoCallItem setRepresentedObject: conversationUID];
759 [audioCallItem setRepresentedObject: conversationUID];
760 [theMenu addItem:videoCallItem];
761 [theMenu addItem:audioCallItem];
762 if (isSIP == false) {
763 [theMenu addItem:separator];
764 NSMenuItem* clearConversationItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Clear conversation", @"Contextual menu action")
765 action:@selector(clearConversation:)
766 keyEquivalent:@""];
767 [clearConversationItem setRepresentedObject: conversationUID];
768 [theMenu addItem:clearConversationItem];
769 if(isRingContact) {
770 NSMenuItem* blockContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Block contact", @"Contextual menu action")
771 action:@selector(blockContact:)
772 keyEquivalent:@""];
773 [blockContactItem setRepresentedObject: conversationUID];
774 [theMenu addItem:blockContactItem];
775 } else {
776 NSMenuItem* addContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
777 action:@selector(addContact:)
778 keyEquivalent:@"A"];
779 [addContactItem setRepresentedObject: conversationUID];
780 [theMenu addItem:addContactItem];
781 }
782 }
783 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400784 }
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400785 @catch (NSException *exception) {
786 return nil;
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400787 }
Alexandre Lision61db3552015-10-22 19:12:52 -0400788}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400789
790- (void) addContact: (NSMenuItem* ) item {
791 auto menuObject = item.representedObject;
792 if(menuObject == nil) {
793 return;
794 }
795 NSString * convUId = (NSString*)menuObject;
796 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400797 convModel_->makePermanent(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400798}
799
800- (void) blockContact: (NSMenuItem* ) item {
801 auto menuObject = item.representedObject;
802 if(menuObject == nil) {
803 return;
804 }
805 NSString * convUId = (NSString*)menuObject;
806 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400807 //convModel_->clearHistory(conversationID);
808 convModel_->removeConversation(conversationID, true);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400809}
810
811- (void) audioCall: (NSMenuItem* ) item {
812 auto menuObject = item.representedObject;
813 if(menuObject == nil) {
814 return;
815 }
816 NSString * convUId = (NSString*)menuObject;
817 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400818 convModel_->placeAudioOnlyCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400819
820}
821
822- (void) videoCall: (NSMenuItem* ) item {
823 auto menuObject = item.representedObject;
824 if(menuObject == nil) {
825 return;
826 }
827 NSString * convUId = (NSString*)menuObject;
828 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400829 convModel_->placeCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400830}
831
832- (void) clearConversation:(NSMenuItem* ) item {
833 auto menuObject = item.representedObject;
834 if(menuObject == nil) {
835 return;
836 }
837 NSString * convUId = (NSString*)menuObject;
838 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400839 convModel_->clearHistory(conversationID);
840}
841
842- (void)acceptInvitation:(id)sender {
843 NSInteger row = [smartView rowForView:sender];
844
845 if (row == -1)
846 return;
847 if (convModel_ == nil)
848 return;
849
850 auto conv = convModel_->filteredConversation(row);
851 auto& convID = conv.Info::uid;
852
853 if (convID.empty())
854 return;
855 convModel_->makePermanent(convID);
856}
857
858- (void)refuseInvitation:(id)sender {
859 NSInteger row = [smartView rowForView:sender];
860
861 if (row == -1)
862 return;
863 if (convModel_ == nil)
864 return;
865
866 auto conv = convModel_->filteredConversation(row);
867 auto& convID = conv.Info::uid;
868
869 if (convID.empty())
870 return;
871 convModel_->removeConversation(convID);
872}
873
874- (void)blockPendingContact:(id)sender {
875 NSInteger row = [smartView rowForView:sender];
876
877 if (row == -1)
878 return;
879 if (convModel_ == nil)
880 return;
881
882 auto conv = convModel_->filteredConversation(row);
883 auto& convID = conv.Info::uid;
884
885 if (convID.empty())
886 return;
887 convModel_->removeConversation(convID, true);
888 [self deselect];
889 [delegate listTypeChanged];
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400890}
Alexandre Lision61db3552015-10-22 19:12:52 -0400891
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400892@end