blob: e78e1b47c31bc6f65cea029f79b1c859694e4ed4 [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
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040042#import "delegates/ImageManipulationDelegate.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040043#import "views/HoverTableRowView.h"
Alexandre Lision4baba4c2016-02-11 13:00:57 -050044#import "views/IconButton.h"
Olivier Soldanod4311552017-11-20 15:09:53 -050045#import "views/RingTableView.h"
Alexandre Lision4e280d62015-09-09 15:56:30 -040046#import "views/ContextualTableCellView.h"
Olivier Soldanod4311552017-11-20 15:09:53 -050047#import "utils.h"
Anthony Léonardbee94cc2018-01-16 11:42:40 -050048#import "RingWindowController.h"
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040049
Kateryna Kostiuk1ba5a012018-12-12 17:07:38 -050050@interface SmartViewVC () <NSTableViewDelegate, NSTableViewDataSource, NSPopoverDelegate, ContextMenuDelegate, KeyboardShortcutDelegate> {
Alexandre Lision0f66bd32016-01-18 11:30:45 -050051
Alexandre Lision61db3552015-10-22 19:12:52 -040052 NSPopover* addToContactPopover;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040053
54 //UI elements
Olivier Soldanod4311552017-11-20 15:09:53 -050055 __unsafe_unretained IBOutlet RingTableView* smartView;
Alexandre Lision61db3552015-10-22 19:12:52 -040056 __unsafe_unretained IBOutlet NSSearchField* searchField;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -050057 __strong IBOutlet NSSegmentedControl *listTypeSelector;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040058 __strong IBOutlet NSLayoutConstraint *listTypeSelectorHeight;
Kateryna Kostiukef66f972018-11-02 17:10:37 -040059 __strong IBOutlet NSLayoutConstraint *listTypeSelectorBottom;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -050060 bool selectorIsPresent;
Alexandre Lision45c18672016-11-02 15:24:45 -040061
Andreas Traczyk252a94a2018-04-20 16:36:20 -040062 QMetaObject::Connection modelSortedConnection_, modelUpdatedConnection_, filterChangedConnection_, newConversationConnection_, conversationRemovedConnection_, newInteractionConnection_, interactionStatusUpdatedConnection_, conversationClearedConnection;
Olivier Soldanod4311552017-11-20 15:09:53 -050063
Andreas Traczyk252a94a2018-04-20 16:36:20 -040064 lrc::api::ConversationModel* convModel_;
Olivier Soldanod4311552017-11-20 15:09:53 -050065 std::string selectedUid_;
Anthony Léonard4bfd68b2018-01-15 16:23:50 -050066 lrc::api::profile::Type currentFilterType;
Anthony Léonardbee94cc2018-01-16 11:42:40 -050067
68 __unsafe_unretained IBOutlet RingWindowController *delegate;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040069}
70
71@end
72
73@implementation SmartViewVC
74
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -040075@synthesize tabbar;
76
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040077// Tags for views
Alexandre Lision57914fa2016-02-10 14:52:24 -050078NSInteger const IMAGE_TAG = 100;
79NSInteger const DISPLAYNAME_TAG = 200;
Andreas Traczyk252a94a2018-04-20 16:36:20 -040080NSInteger const NOTIFICATONS_TAG = 300;
81NSInteger const RING_ID_LABEL = 400;
82NSInteger const PRESENCE_TAG = 500;
83NSInteger const TOTALMSGS_TAG = 600;
84NSInteger const TOTALINVITES_TAG = 700;
85NSInteger const DATE_TAG = 800;
86NSInteger const SNIPPET_TAG = 900;
87NSInteger const ADD_BUTTON_TAG = 1000;
88NSInteger const REFUSE_BUTTON_TAG = 1100;
89NSInteger const BLOCK_BUTTON_TAG = 1200;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040090
Anthony Léonarde6a01fa2018-01-12 10:29:58 -050091// Segment indices for smartlist selector
92NSInteger const CONVERSATION_SEG = 0;
93NSInteger const REQUEST_SEG = 1;
94
Alexandre Lision4dfcafc2015-08-20 12:43:23 -040095- (void)awakeFromNib
96{
97 NSLog(@"INIT SmartView VC");
Olivier Soldanod4311552017-11-20 15:09:53 -050098 //get selected account
99 //encapsulate conversationmodel in local version
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400100
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400101 [smartView setTarget:self];
102 [smartView setDoubleAction:@selector(placeCall:)];
103
Alexandre Lision61db3552015-10-22 19:12:52 -0400104 [smartView setContextMenuDelegate:self];
105 [smartView setShortcutsDelegate:self];
106
Olivier Soldanod4311552017-11-20 15:09:53 -0500107 [smartView setDataSource: self];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500108 currentFilterType = lrc::api::profile::Type::RING;
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500109 selectorIsPresent = true;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400110
111 smartView.selectionHighlightStyle = NSTableViewSelectionHighlightStyleNone;
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400112
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400113}
114
115- (void)placeCall:(id)sender
116{
Olivier Soldanod4311552017-11-20 15:09:53 -0500117 NSInteger row;
118 if (sender != nil && [sender clickedRow] != -1)
119 row = [sender clickedRow];
120 else if ([smartView selectedRow] != -1)
121 row = [smartView selectedRow];
122 else
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400123 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400124 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400125 return;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400126
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400127 auto conv = convModel_->filteredConversation(row);
128 convModel_->placeCall(conv.uid);
129}
130
131-(void) reloadSelectorNotifications
132{
133 NSTextField* totalMsgsCount = [self.view viewWithTag:TOTALMSGS_TAG];
134 NSTextField* totalInvites = [self.view viewWithTag:TOTALINVITES_TAG];
135
136 if (!selectorIsPresent) {
137 [totalMsgsCount setHidden:true];
138 [totalInvites setHidden:true];
139 return;
140 }
141
142 auto ringConversations = convModel_->getFilteredConversations(lrc::api::profile::Type::RING);
143 int totalUnreadMessages = 0;
144 std::for_each(ringConversations.begin(), ringConversations.end(),
145 [&totalUnreadMessages, self] (const auto& conversation) {
146 totalUnreadMessages += convModel_->getNumberOfUnreadMessagesFor(conversation.uid);
147 });
148 [totalMsgsCount setHidden:(totalUnreadMessages == 0)];
149 [totalMsgsCount setIntValue:totalUnreadMessages];
150
151 auto totalRequests = [self chosenAccount].contactModel->pendingRequestCount();
152 [totalInvites setHidden:(totalRequests == 0)];
153 [totalInvites setIntValue:totalRequests];
Olivier Soldanod4311552017-11-20 15:09:53 -0500154}
155
156-(void) reloadData
157{
158 [smartView deselectAll:nil];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400159 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400160 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500161
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400162 [self reloadSelectorNotifications];
163
164 if (!convModel_->owner.contactModel->hasPendingRequests()) {
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500165 if (currentFilterType == lrc::api::profile::Type::PENDING) {
166 [self selectConversationList];
167 }
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500168 if (selectorIsPresent) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400169 listTypeSelectorHeight.constant = 0.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400170 listTypeSelectorBottom.priority = 250;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400171 [listTypeSelector setHidden:YES];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500172 selectorIsPresent = false;
173 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500174 } else {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500175 if (!selectorIsPresent) {
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500176 [listTypeSelector setSelected:YES forSegment:CONVERSATION_SEG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400177 listTypeSelectorHeight.constant = 18.0;
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400178 listTypeSelectorBottom.priority = 999;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400179 [listTypeSelector setHidden:NO];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500180 selectorIsPresent = true;
181 }
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500182 }
183
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500184 [smartView reloadData];
Kateryna Kostiukc8035c62018-09-19 16:13:14 -0400185 [smartView layoutSubtreeIfNeeded];
Anthony Léonardcb73a2c2018-01-18 11:45:56 -0500186
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400187 if (!selectedUid_.empty() && convModel_ != nil) {
188 auto it = getConversationFromUid(selectedUid_, *convModel_);
189 if (it != convModel_->allFilteredConversations().end()) {
190 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Olivier Soldanod4311552017-11-20 15:09:53 -0500191 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400192 }
193 }
194
Olivier Soldanod4311552017-11-20 15:09:53 -0500195 [smartView scrollToBeginningOfDocument:nil];
196}
197
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400198-(void) reloadConversationWithUid:(NSString *)uid
199{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400200 if (convModel_ == nil) {
201 return;
202 }
203
204 auto it = getConversationFromUid(std::string([uid UTF8String]), *convModel_);
205 if (it != convModel_->allFilteredConversations().end()) {
206 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400207 [smartView reloadDataForRowIndexes:indexSet
208 columnIndexes:[NSIndexSet indexSetWithIndex:0]];
Andreas Traczyk3b98eb82018-03-29 17:54:42 -0400209 }
210}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400211
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500212- (BOOL)setConversationModel:(lrc::api::ConversationModel *)conversationModel
Olivier Soldanod4311552017-11-20 15:09:53 -0500213{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400214 if (convModel_ == conversationModel) {
215 return false;
216 }
217
218 convModel_ = conversationModel;
219 selectedUid_.clear(); // Clear selected conversation as the selected account is being changed
220 QObject::disconnect(modelSortedConnection_);
221 QObject::disconnect(modelUpdatedConnection_);
222 QObject::disconnect(filterChangedConnection_);
223 QObject::disconnect(newConversationConnection_);
224 QObject::disconnect(conversationRemovedConnection_);
225 QObject::disconnect(conversationClearedConnection);
226 QObject::disconnect(interactionStatusUpdatedConnection_);
227 QObject::disconnect(newInteractionConnection_);
228 [self reloadData];
229 if (convModel_ != nil) {
230 modelSortedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::modelSorted,
Olivier Soldanod4311552017-11-20 15:09:53 -0500231 [self] (){
232 [self reloadData];
233 });
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400234 modelUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationUpdated,
235 [self] (const std::string& convUid){
236 [self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
237 });
238 filterChangedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::filterChanged,
239 [self] (){
240 [self reloadData];
241 });
242 newConversationConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newConversation,
243 [self] (const std::string& convUid) {
244 [self reloadData];
245 [self updateConversationForNewContact:[NSString stringWithUTF8String:convUid.c_str()]];
246 });
247 conversationRemovedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationRemoved,
248 [self] (){
249 [delegate listTypeChanged];
250 [self reloadData];
251 });
252 conversationClearedConnection = QObject::connect(convModel_, &lrc::api::ConversationModel::conversationCleared,
253 [self] (const std::string& convUid){
254 [self deselect];
255 [delegate listTypeChanged];
256 });
257 interactionStatusUpdatedConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::interactionStatusUpdated,
258 [self] (const std::string& convUid) {
259 if (convUid != selectedUid_)
260 return;
261 [self reloadConversationWithUid: [NSString stringWithUTF8String:convUid.c_str()]];
262 });
263 newInteractionConnection_ = QObject::connect(convModel_, &lrc::api::ConversationModel::newInteraction,
264 [self](const std::string& convUid, uint64_t interactionId, const lrc::api::interaction::Info& interaction){
265 if (convUid == selectedUid_) {
266 convModel_->clearUnreadInteractions(convUid);
267 }
268 });
269 convModel_->setFilter(""); // Reset the filter
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500270 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400271 [searchField setStringValue:@""];
272 return true;
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500273}
274
275-(void)selectConversation:(const lrc::api::conversation::Info&)conv model:(lrc::api::ConversationModel*)model;
276{
277 auto& uid = conv.uid;
278 if (selectedUid_ == uid)
279 return;
280
281 [self setConversationModel:model];
282
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400283 if (convModel_ == nil) {
284 return;
285 }
286
287 auto it = getConversationFromUid(selectedUid_, *convModel_);
288 if (it != convModel_->allFilteredConversations().end()) {
289 NSIndexSet* indexSet = [NSIndexSet indexSetWithIndex:(it - convModel_->allFilteredConversations().begin())];
290 [smartView selectRowIndexes:indexSet byExtendingSelection:NO];
291 selectedUid_ = uid;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400292 }
293}
294
Anthony Léonard8585cc02017-12-28 14:03:45 -0500295-(void)deselect
296{
297 selectedUid_.clear();
298 [smartView deselectAll:nil];
299}
300
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400301-(void) clearConversationModel {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400302 convModel_ = nil;
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400303 [self deselect];
304 [smartView reloadData];
Andreas Traczyk4fdb2d92018-04-19 15:57:46 -0400305 if (selectorIsPresent) {
306 [listTypeSelector removeFromSuperview];
307 selectorIsPresent = false;
308 }
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400309}
310
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500311- (IBAction) listTypeChanged:(id)sender
312{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400313 selectedUid_.clear();
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500314 NSInteger selectedItem = [sender selectedSegment];
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500315 switch (selectedItem) {
316 case CONVERSATION_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500317 if (currentFilterType != lrc::api::profile::Type::RING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400318 convModel_->setFilter(lrc::api::profile::Type::RING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500319 [delegate listTypeChanged];
320 currentFilterType = lrc::api::profile::Type::RING;
321 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500322 break;
323 case REQUEST_SEG:
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500324 if (currentFilterType != lrc::api::profile::Type::PENDING) {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400325 convModel_->setFilter(lrc::api::profile::Type::PENDING);
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500326 [delegate listTypeChanged];
327 currentFilterType = lrc::api::profile::Type::PENDING;
328 }
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500329 break;
330 default:
331 NSLog(@"Invalid item selected in list selector: %d", selectedItem);
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500332 }
333}
334
335-(void) selectConversationList
336{
Anthony Léonardd00cd182018-01-17 09:21:27 -0500337 if (currentFilterType == lrc::api::profile::Type::RING)
338 return;
Anthony Léonarde6a01fa2018-01-12 10:29:58 -0500339 [listTypeSelector setSelectedSegment:CONVERSATION_SEG];
Anthony Léonard4bfd68b2018-01-15 16:23:50 -0500340
341 // Do not invert order of the next two lines or stack overflow
342 // may happen on -(void) reloadData call if filter is currently set to PENDING
343 currentFilterType = lrc::api::profile::Type::RING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400344 convModel_->setFilter(lrc::api::profile::Type::RING);
345 convModel_->setFilter("");
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500346}
347
Anthony Léonardd00cd182018-01-17 09:21:27 -0500348-(void) selectPendingList
349{
350 if (currentFilterType == lrc::api::profile::Type::PENDING)
351 return;
352 [listTypeSelector setSelectedSegment:REQUEST_SEG];
353
354 currentFilterType = lrc::api::profile::Type::PENDING;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400355 convModel_->setFilter(lrc::api::profile::Type::PENDING);
356 convModel_->setFilter("");
Anthony Léonardd00cd182018-01-17 09:21:27 -0500357}
358
Olivier Soldanod4311552017-11-20 15:09:53 -0500359#pragma mark - NSTableViewDelegate methods
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400360
Olivier Soldanod4311552017-11-20 15:09:53 -0500361- (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400362{
363 return YES;
364}
365
Olivier Soldanod4311552017-11-20 15:09:53 -0500366- (BOOL)tableView:(NSTableView *)tableView shouldEditTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400367{
368 return NO;
369}
370
Olivier Soldanod4311552017-11-20 15:09:53 -0500371- (void)tableViewSelectionDidChange:(NSNotification *)notification
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400372{
Olivier Soldanod4311552017-11-20 15:09:53 -0500373 NSInteger row = [notification.object selectedRow];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400374 NSInteger rows = [smartView numberOfRows];
Olivier Soldanod4311552017-11-20 15:09:53 -0500375
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400376 for (int i = 0; i< rows; i++) {
Kateryna Kostiuk394f74c2018-10-05 15:45:26 -0400377 HoverTableRowView* cellRowView = [smartView rowViewAtRow:i makeIfNecessary: NO];
378 [cellRowView drawSelection: (i == row)];
Kateryna Kostiuk82c0d532018-05-09 16:49:06 -0400379 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400380
Olivier Soldanod4311552017-11-20 15:09:53 -0500381 if (row == -1)
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400382 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400383 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400384 return;
Olivier Soldanod4311552017-11-20 15:09:53 -0500385
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400386 auto uid = convModel_->filteredConversation(row).uid;
Olivier Soldanod4311552017-11-20 15:09:53 -0500387 if (selectedUid_ != uid) {
388 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400389 convModel_->selectConversation(uid);
390 convModel_->clearUnreadInteractions(uid);
391 [self reloadSelectorNotifications];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400392 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400393}
394
Olivier Soldanod4311552017-11-20 15:09:53 -0500395- (NSTableRowView *)tableView:(NSTableView *)tableView rowViewForRow:(NSInteger)row
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400396{
Olivier Soldanod4311552017-11-20 15:09:53 -0500397 return [tableView makeViewWithIdentifier:@"HoverRowView" owner:nil];
398}
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400399
Olivier Soldanod4311552017-11-20 15:09:53 -0500400- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row
401{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400402 if (convModel_ == nil)
Olivier Soldanod4311552017-11-20 15:09:53 -0500403 return nil;
404
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400405 auto conversation = convModel_->filteredConversation(row);
Alexandre Lisiona4bb0512016-04-19 18:04:31 -0400406 NSTableCellView* result;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400407
Olivier Soldanod4311552017-11-20 15:09:53 -0500408 result = [tableView makeViewWithIdentifier:@"MainCell" owner:tableView];
Alexandre Lision4e280d62015-09-09 15:56:30 -0400409
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400410 NSTextField* unreadCount = [result viewWithTag:NOTIFICATONS_TAG];
Olivier Soldanod4311552017-11-20 15:09:53 -0500411 [unreadCount setHidden:(conversation.unreadMessages == 0)];
412 [unreadCount setIntValue:conversation.unreadMessages];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400413 NSTextField* displayName = [result viewWithTag:DISPLAYNAME_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400414 NSTextField* displayRingID = [result viewWithTag:RING_ID_LABEL];
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400415 NSTextField* lastInteractionDate = [result viewWithTag:DATE_TAG];
416 NSTextField* interactionSnippet = [result viewWithTag:SNIPPET_TAG];
417 [displayName setStringValue:@""];
418 [displayRingID setStringValue:@""];
419 [lastInteractionDate setStringValue:@""];
420 [interactionSnippet setStringValue:@""];
421 NSImageView* photoView = [result viewWithTag:IMAGE_TAG];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400422 NSString* displayNameString = bestNameForConversation(conversation, *convModel_);
423 NSString* displayIDString = bestIDForConversation(conversation, *convModel_);
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400424 if(displayNameString.length == 0 || [displayNameString isEqualToString:displayIDString]) {
425 [displayName setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400426 [displayRingID setHidden:YES];
Kateryna Kostiuk9d6657a2017-04-25 13:42:25 -0400427 }
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400428 else {
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400429 [displayName setStringValue:displayNameString];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400430 [displayRingID setStringValue:displayIDString];
Anthony Léonard4adc3f02017-07-21 10:23:04 -0400431 [displayRingID setHidden:NO];
Kateryna Kostiuk6a536362017-05-02 14:26:12 -0400432 }
Olivier Soldanod4311552017-11-20 15:09:53 -0500433 auto& imageManip = reinterpret_cast<Interfaces::ImageManipulationDelegate&>(GlobalInstances::pixmapManipulator());
Kateryna Kostiuk0bf03752018-05-09 13:57:15 -0400434 NSImage* image = QtMac::toNSImage(qvariant_cast<QPixmap>(imageManip.conversationPhoto(conversation, convModel_->owner)));
435 if(image) {
436 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
437 NSArray* constraints = [NSLayoutConstraint
438 constraintsWithVisualFormat:@"H:[photoView(54)]"
439 options:0
440 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
441 [NSLayoutConstraint activateConstraints:constraints];
442 } else {
443 [NSLayoutConstraint deactivateConstraints:[photoView constraints]];
444 NSArray* constraints = [NSLayoutConstraint
445 constraintsWithVisualFormat:@"H:[photoView(0)]"
446 options:0
447 metrics:nil views:NSDictionaryOfVariableBindings(photoView)];
448 [NSLayoutConstraint activateConstraints:constraints];
449 }
450 [photoView setImage: image];
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400451
452 NSView* presenceView = [result viewWithTag:PRESENCE_TAG];
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400453 [presenceView setHidden:YES];
454 if (!conversation.participants.empty()){
455 try {
456 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
457 if (contact.isPresent) {
458 [presenceView setHidden:NO];
459 }
460 } catch (std::out_of_range& e) {
461 NSLog(@"viewForTableColumn: getContact - out of range");
462 }
Kateryna Kostiukd9039e92017-05-24 14:29:54 -0400463 }
Andreas Traczyk4e39aa52018-05-11 17:29:26 -0400464
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400465 NSButton* addContactButton = [result viewWithTag:ADD_BUTTON_TAG];
466 NSButton* refuseContactButton = [result viewWithTag:REFUSE_BUTTON_TAG];
467 NSButton* blockContactButton = [result viewWithTag:BLOCK_BUTTON_TAG];
468 [addContactButton setHidden:YES];
469 [refuseContactButton setHidden:YES];
470 [blockContactButton setHidden:YES];
471
472 if (profileType(conversation, *convModel_) == lrc::api::profile::Type::PENDING) {
473 [lastInteractionDate setHidden:true];
474 [interactionSnippet setHidden:true];
475 [addContactButton setHidden:NO];
476 [refuseContactButton setHidden:NO];
477 [blockContactButton setHidden:NO];
478 [addContactButton setAction:@selector(acceptInvitation:)];
479 [addContactButton setTarget:self];
480 [refuseContactButton setAction:@selector(refuseInvitation:)];
481 [refuseContactButton setTarget:self];
482 [blockContactButton setAction:@selector(blockPendingContact:)];
483 [blockContactButton setTarget:self];
484 return result;
485 }
486
487 [lastInteractionDate setHidden:false];
488
489 [interactionSnippet setHidden:false];
490
491 auto lastUid = conversation.lastMessageUid;
492 if (conversation.interactions.find(lastUid) != conversation.interactions.end()) {
493 // last interaction snippet
494 std::string lastInteractionSnippet = conversation.interactions[lastUid].body;
495 std::stringstream ss(lastInteractionSnippet);
496 std::getline(ss, lastInteractionSnippet);
497 NSString* lastInteractionSnippetFixedString = [[NSString stringWithUTF8String:lastInteractionSnippet.c_str()]
498 stringByReplacingOccurrencesOfString:@"🕽" withString:@""];
499 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString stringByReplacingOccurrencesOfString:@"📞" withString:@""];
500 if (conversation.interactions[lastUid].type == lrc::api::interaction::Type::OUTGOING_DATA_TRANSFER
501 || conversation.interactions[lastUid].type == lrc::api::interaction::Type::INCOMING_DATA_TRANSFER) {
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400502 lastInteractionSnippetFixedString = [lastInteractionSnippetFixedString lastPathComponent];
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400503 }
504 [interactionSnippet setStringValue:lastInteractionSnippetFixedString];
505
506 // last interaction date/time
507 std::time_t lastInteractionTimestamp = conversation.interactions[lastUid].timestamp;
508 std::time_t now = std::time(nullptr);
509 char interactionDay[64];
510 char nowDay[64];
511 std::strftime(interactionDay, sizeof(interactionDay), "%D", std::localtime(&lastInteractionTimestamp));
512 std::strftime(nowDay, sizeof(nowDay), "%D", std::localtime(&now));
513 if (std::string(interactionDay) == std::string(nowDay)) {
514 char interactionTime[64];
515 std::strftime(interactionTime, sizeof(interactionTime), "%R", std::localtime(&lastInteractionTimestamp));
516 [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionTime]];
517 } else {
518 [lastInteractionDate setStringValue:[NSString stringWithUTF8String:interactionDay]];
519 }
520 }
521
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400522 return result;
523}
524
Olivier Soldanod4311552017-11-20 15:09:53 -0500525- (CGFloat)tableView:(NSTableView *)tableView heightOfRow:(NSInteger)row
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500526{
Olivier Soldanod4311552017-11-20 15:09:53 -0500527 return 60.0;
528}
529
530#pragma mark - NSTableDataSource methods
531
532- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView
533{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400534 if (tableView == smartView && convModel_ != nullptr) {
535 return convModel_->allFilteredConversations().size();
Olivier Soldanod4311552017-11-20 15:09:53 -0500536 }
537
538 return 0;
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500539}
540
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -0400541- (void)startCallForRow:(id)sender {
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400542 NSInteger row = [smartView rowForView:sender];
543 [smartView selectRowIndexes:[NSIndexSet indexSetWithIndex:row] byExtendingSelection:NO];
544 [self placeCall:nil];
545}
546
547- (IBAction)hangUpClickedAtRow:(id)sender {
548 NSInteger row = [smartView rowForView:sender];
Alexandre Lision57914fa2016-02-10 14:52:24 -0500549
Olivier Soldanod4311552017-11-20 15:09:53 -0500550 if (row == -1)
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500551 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400552 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400553 return;
Alexandre Lision45c18672016-11-02 15:24:45 -0400554
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400555 auto conv = convModel_->filteredConversation(row);
Olivier Soldanod4311552017-11-20 15:09:53 -0500556 auto& callId = conv.callId;
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500557
Olivier Soldanod4311552017-11-20 15:09:53 -0500558 if (callId.empty())
559 return;
560
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400561 auto* callModel = convModel_->owner.callModel.get();
Olivier Soldanod4311552017-11-20 15:09:53 -0500562 callModel->hangUp(callId);
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400563}
564
Alexandre Lision45c18672016-11-02 15:24:45 -0400565- (void) displayErrorModalWithTitle:(NSString*) title WithMessage:(NSString*) message
566{
567 NSAlert* alert = [NSAlert alertWithMessageText:title
568 defaultButton:@"Ok"
569 alternateButton:nil
570 otherButton:nil
571 informativeTextWithFormat:message];
572
573 [alert beginSheetModalForWindow:self.view.window modalDelegate:nil didEndSelector:NULL contextInfo:NULL];
574}
575
Olivier Soldanod4311552017-11-20 15:09:53 -0500576- (void) processSearchFieldInput
Alexandre Lision45c18672016-11-02 15:24:45 -0400577{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400578 if (convModel_ == nil) {
579 return;
580 }
581
582 convModel_->setFilter(std::string([[searchField stringValue] UTF8String]));
Alexandre Lision45c18672016-11-02 15:24:45 -0400583}
584
Olivier Soldanod4311552017-11-20 15:09:53 -0500585-(const lrc::api::account::Info&) chosenAccount
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400586{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400587 return convModel_->owner;
Kateryna Kostiukd2f7b5d2017-06-09 15:30:49 -0400588}
589
Alexandre Lisionf909fb12016-11-03 12:35:36 -0400590- (void) clearSearchField
591{
592 [searchField setStringValue:@""];
Olivier Soldanod4311552017-11-20 15:09:53 -0500593 [self processSearchFieldInput];
Kateryna Kostiuk9dd759c2017-07-11 12:06:55 -0400594}
595
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400596-(void)updateConversationForNewContact:(NSString *)uId {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400597 if (convModel_ == nil) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400598 return;
599 }
600 [self clearSearchField];
601 auto uid = std::string([uId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400602 auto it = getConversationFromUid(uid, *convModel_);
603 if (it != convModel_->allFilteredConversations().end()) {
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400604 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400605 auto contact = convModel_->owner.contactModel->getContact(it->participants[0]);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400606 if (!contact.profileInfo.uri.empty() && contact.profileInfo.uri.compare(selectedUid_) == 0) {
607 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400608 convModel_->selectConversation(uid);
Kateryna Kostiuk22ce9362018-04-17 13:12:50 -0400609 }
610 } @catch (NSException *exception) {
611 return;
612 }
613 }
614}
615
Alexandre Lision72a669e2016-09-14 17:52:16 -0400616/**
617 Copy a NSString in the general Pasteboard
618
619 @param sender the NSObject containing the represented object to copy
620 */
621- (void) copyStringToPasteboard:(id) sender
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400622{
623 NSPasteboard *pasteBoard = [NSPasteboard generalPasteboard];
624 [pasteBoard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
625 [pasteBoard setString:[sender representedObject] forType:NSStringPboardType];
626}
627
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400628#pragma NSTextFieldDelegate
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400629
630- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
631{
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400632 if (commandSelector != @selector(insertNewline:) || [[searchField stringValue] isEqual:@""]) {
633 return NO;
634 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400635 if (convModel_ == nil) {
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400636 [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")];
637 return NO;
638 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400639 if (convModel_->allFilteredConversations().size() <= 0) {
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400640 return YES;
641 }
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400642 auto model = convModel_->filteredConversation(0);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400643 auto uid = model.uid;
644 if (selectedUid_ == uid) {
645 return YES;
646 }
647 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400648 auto contact = convModel_->owner.contactModel->getContact(model.participants[0]);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400649 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 -0400650 return YES;
651 }
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400652 selectedUid_ = uid;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400653 convModel_->selectConversation(uid);
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400654 [self.view.window makeFirstResponder: smartView];
Kateryna Kostiuk0febcfa2018-04-04 11:14:51 -0400655 return YES;
656 } @catch (NSException *exception) {
657 return YES;
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400658 }
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400659}
660
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500661- (void)controlTextDidChange:(NSNotification *) notification
662{
Olivier Soldanod4311552017-11-20 15:09:53 -0500663 [self processSearchFieldInput];
Alexandre Lisionac4f5b62016-02-17 13:53:12 -0500664}
665
Alexandre Lision61db3552015-10-22 19:12:52 -0400666#pragma mark - NSPopOverDelegate
667
668- (void)popoverDidClose:(NSNotification *)notification
669{
670 if (addToContactPopover != nullptr) {
671 [addToContactPopover performClose:self];
672 addToContactPopover = NULL;
673 }
674}
675
Alexandre Lisionbf0385e2015-10-22 17:36:28 -0400676
Alexandre Lision61db3552015-10-22 19:12:52 -0400677#pragma mark - ContactLinkedDelegate
678
679- (void)contactLinked
680{
681 if (addToContactPopover != nullptr) {
682 [addToContactPopover performClose:self];
683 addToContactPopover = NULL;
684 }
685}
686
687#pragma mark - KeyboardShortcutDelegate
688
689- (void) onAddShortcut
690{
Olivier Soldanod4311552017-11-20 15:09:53 -0500691 if ([smartView selectedRow] == -1)
Alexandre Lision61db3552015-10-22 19:12:52 -0400692 return;
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400693 if (convModel_ == nil)
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400694 return ;
Alexandre Lision61db3552015-10-22 19:12:52 -0400695
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400696 auto uid = convModel_->filteredConversation([smartView selectedRow]).uid;
697 convModel_->makePermanent(uid);
Alexandre Lision61db3552015-10-22 19:12:52 -0400698}
699
700#pragma mark - ContextMenuDelegate
701
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400702- (NSMenu*) contextualMenuForRow:(int) index
Alexandre Lision61db3552015-10-22 19:12:52 -0400703{
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400704 if (convModel_ == nil)
Alexandre Lision61db3552015-10-22 19:12:52 -0400705 return nil;
706
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400707 auto conversation = convModel_->filteredConversation(NSInteger(index));
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400708
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400709 @try {
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400710 auto contact = convModel_->owner.contactModel->getContact(conversation.participants[0]);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400711 if (contact.profileInfo.type == lrc::api::profile::Type::INVALID) {
712 return nil;
713 }
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400714
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400715 BOOL isSIP = false;
716 BOOL isRingContact = false;
717 /* for SIP contact show only call menu options
718 * if contact does not have uri that is not RING contact
719 * for trusted Ring contact show option block contact
720 * for untrasted contact show option add contact
721 */
Alexandre Lision72a669e2016-09-14 17:52:16 -0400722
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400723 if (contact.profileInfo.type == lrc::api::profile::Type::SIP) {
724 isSIP = true;
725 } else if (contact.profileInfo.uri.empty()) {
726 return nil;
727 }
728
729 else if (contact.profileInfo.type == lrc::api::profile::Type::RING && contact.isTrusted == true) {
730 isRingContact = true;
731 }
732 auto conversationUD = conversation.uid;
733 NSMenu *theMenu = [[NSMenu alloc] initWithTitle:@""];
734 NSString* conversationUID = @(conversationUD.c_str());
735 NSMenuItem* separator = [NSMenuItem separatorItem];
736 NSMenuItem* videoCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place video call",
737 @"Contextual menu action")
738 action:@selector(videoCall:)
739 keyEquivalent:@""];
740 NSMenuItem* audioCallItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Place audio call",
741 @"Contextual menu action")
742 action:@selector(audioCall:)
743 keyEquivalent:@""];
744 [videoCallItem setRepresentedObject: conversationUID];
745 [audioCallItem setRepresentedObject: conversationUID];
746 [theMenu addItem:videoCallItem];
747 [theMenu addItem:audioCallItem];
748 if (isSIP == false) {
749 [theMenu addItem:separator];
750 NSMenuItem* clearConversationItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Clear conversation", @"Contextual menu action")
751 action:@selector(clearConversation:)
752 keyEquivalent:@""];
753 [clearConversationItem setRepresentedObject: conversationUID];
754 [theMenu addItem:clearConversationItem];
755 if(isRingContact) {
756 NSMenuItem* blockContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Block contact", @"Contextual menu action")
757 action:@selector(blockContact:)
758 keyEquivalent:@""];
759 [blockContactItem setRepresentedObject: conversationUID];
760 [theMenu addItem:blockContactItem];
761 } else {
762 NSMenuItem* addContactItem = [[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Add to contacts", @"Contextual menu action")
763 action:@selector(addContact:)
764 keyEquivalent:@"A"];
765 [addContactItem setRepresentedObject: conversationUID];
766 [theMenu addItem:addContactItem];
767 }
768 }
769 return theMenu;
Alexandre Lision61db3552015-10-22 19:12:52 -0400770 }
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400771 @catch (NSException *exception) {
772 return nil;
Alexandre Lisiond7bf2882015-10-22 22:54:32 -0400773 }
Alexandre Lision61db3552015-10-22 19:12:52 -0400774}
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400775
776- (void) addContact: (NSMenuItem* ) item {
777 auto menuObject = item.representedObject;
778 if(menuObject == nil) {
779 return;
780 }
781 NSString * convUId = (NSString*)menuObject;
782 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400783 convModel_->makePermanent(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400784}
785
786- (void) blockContact: (NSMenuItem* ) item {
787 auto menuObject = item.representedObject;
788 if(menuObject == nil) {
789 return;
790 }
791 NSString * convUId = (NSString*)menuObject;
792 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400793 //convModel_->clearHistory(conversationID);
794 convModel_->removeConversation(conversationID, true);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400795}
796
797- (void) audioCall: (NSMenuItem* ) item {
798 auto menuObject = item.representedObject;
799 if(menuObject == nil) {
800 return;
801 }
802 NSString * convUId = (NSString*)menuObject;
803 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400804 convModel_->placeAudioOnlyCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400805
806}
807
808- (void) videoCall: (NSMenuItem* ) item {
809 auto menuObject = item.representedObject;
810 if(menuObject == nil) {
811 return;
812 }
813 NSString * convUId = (NSString*)menuObject;
814 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400815 convModel_->placeCall(conversationID);
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400816}
817
818- (void) clearConversation:(NSMenuItem* ) item {
819 auto menuObject = item.representedObject;
820 if(menuObject == nil) {
821 return;
822 }
823 NSString * convUId = (NSString*)menuObject;
824 std::string conversationID = std::string([convUId UTF8String]);
Andreas Traczyk252a94a2018-04-20 16:36:20 -0400825 convModel_->clearHistory(conversationID);
826}
827
828- (void)acceptInvitation:(id)sender {
829 NSInteger row = [smartView rowForView:sender];
830
831 if (row == -1)
832 return;
833 if (convModel_ == nil)
834 return;
835
836 auto conv = convModel_->filteredConversation(row);
837 auto& convID = conv.Info::uid;
838
839 if (convID.empty())
840 return;
841 convModel_->makePermanent(convID);
842}
843
844- (void)refuseInvitation:(id)sender {
845 NSInteger row = [smartView rowForView:sender];
846
847 if (row == -1)
848 return;
849 if (convModel_ == nil)
850 return;
851
852 auto conv = convModel_->filteredConversation(row);
853 auto& convID = conv.Info::uid;
854
855 if (convID.empty())
856 return;
857 convModel_->removeConversation(convID);
858}
859
860- (void)blockPendingContact:(id)sender {
861 NSInteger row = [smartView rowForView:sender];
862
863 if (row == -1)
864 return;
865 if (convModel_ == nil)
866 return;
867
868 auto conv = convModel_->filteredConversation(row);
869 auto& convID = conv.Info::uid;
870
871 if (convID.empty())
872 return;
873 convModel_->removeConversation(convID, true);
874 [self deselect];
875 [delegate listTypeChanged];
Kateryna Kostiukd51a3252018-04-04 09:20:37 -0400876}
Alexandre Lision61db3552015-10-22 19:12:52 -0400877
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400878@end