blob: cb3e6b1a1908aaa509165c0568fd7c0f8751eea3 [file] [log] [blame]
Alexandre Lision0f66bd32016-01-18 11:30:45 -05001/*
2 * Copyright (C) 2016 Savoir-faire Linux Inc.
3 * Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#import "ConversationVC.h"
21
22#import <QItemSelectionModel>
23#import <qstring.h>
24#import <QPixmap>
25#import <QtMacExtras/qmacfunctions.h>
26
27#import <media/media.h>
28#import <recentmodel.h>
29#import <person.h>
30#import <contactmethod.h>
31#import <media/text.h>
32#import <media/textrecording.h>
33#import <callmodel.h>
34#import <globalinstances.h>
35
36#import "views/IconButton.h"
37#import "views/IMTableCellView.h"
38#import "views/NSColor+RingTheme.h"
39#import "QNSTreeController.h"
Alexandre Lision83180df2016-01-18 11:32:20 -050040#import "INDSequentialTextSelectionManager.h"
Alexandre Lision0f66bd32016-01-18 11:30:45 -050041#import "delegates/ImageManipulationDelegate.h"
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -040042#import "SendContactRequestWC.h"
43#import "PhoneDirectoryModel.h"
44#import "account.h"
45#import "AvailableAccountModel.h"
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -040046#import "MessagesVC.h"
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -040047
Alexandre Lision0f66bd32016-01-18 11:30:45 -050048
49#import <QuartzCore/QuartzCore.h>
50
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -040051@interface ConversationVC () <NSOutlineViewDelegate, MessagesVCDelegate> {
Alexandre Lision0f66bd32016-01-18 11:30:45 -050052
Alexandre Lision0f66bd32016-01-18 11:30:45 -050053 __unsafe_unretained IBOutlet NSTextField* messageField;
54 QVector<ContactMethod*> contactMethods;
55 NSMutableString* textSelection;
56
Alexandre Lision05ca83c2016-11-24 19:31:08 -050057 QMetaObject::Connection contactMethodChanged;
58 ContactMethod* selectedContactMethod;
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -040059 SendContactRequestWC* sendRequestWC;
Alexandre Lision0f66bd32016-01-18 11:30:45 -050060
61 __unsafe_unretained IBOutlet NSView* sendPanel;
62 __unsafe_unretained IBOutlet NSTextField* conversationTitle;
63 __unsafe_unretained IBOutlet NSTextField* emptyConversationPlaceHolder;
64 __unsafe_unretained IBOutlet IconButton* sendButton;
Alexandre Lision0f66bd32016-01-18 11:30:45 -050065 __unsafe_unretained IBOutlet NSPopUpButton* contactMethodsPopupButton;
Kateryna Kostiuk78e1f122017-06-14 14:52:34 -040066 __unsafe_unretained IBOutlet NSLayoutConstraint* sentContactRequestWidth;
67 __unsafe_unretained IBOutlet NSButton* sentContactRequestButton;
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -040068 IBOutlet MessagesVC* messagesViewVC;
Alexandre Lision0f66bd32016-01-18 11:30:45 -050069}
70
Alexandre Lision83180df2016-01-18 11:32:20 -050071
Alexandre Lision0f66bd32016-01-18 11:30:45 -050072@end
73
74@implementation ConversationVC
75
Alexandre Lision4baba4c2016-02-11 13:00:57 -050076- (void)loadView {
77 [super loadView];
Alexandre Lision0f66bd32016-01-18 11:30:45 -050078 // Do view setup here.
79 [self.view setWantsLayer:YES];
80 [self.view setLayer:[CALayer layer]];
81 [self.view.layer setBackgroundColor:[NSColor ringGreyHighlight].CGColor];
82 [self.view.layer setCornerRadius:5.0f];
83
84 [sendPanel setWantsLayer:YES];
85 [sendPanel setLayer:[CALayer layer]];
86
87 [self setupChat];
88
89}
90
Kateryna Kostiuk78e1f122017-06-14 14:52:34 -040091-(Account* ) chosenAccount
92{
93 QModelIndex index = AvailableAccountModel::instance().selectionModel()->currentIndex();
94 if(!index.isValid()) {
95 return nullptr;
96 }
97 Account* account = index.data(static_cast<int>(Account::Role::Object)).value<Account*>();
98 return account;
99}
100
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500101- (void) initFrame
102{
103 [self.view setFrame:self.view.superview.bounds];
104 [self.view setHidden:YES];
105 self.view.layer.position = self.view.frame.origin;
106}
107
108- (void) setupChat
109{
110 QObject::connect(RecentModel::instance().selectionModel(),
111 &QItemSelectionModel::currentChanged,
112 [=](const QModelIndex &current, const QModelIndex &previous) {
113
114 contactMethods = RecentModel::instance().getContactMethods(current);
115 if (contactMethods.isEmpty()) {
116 return ;
117 }
118
119 [contactMethodsPopupButton removeAllItems];
120 for (auto cm : contactMethods) {
121 [contactMethodsPopupButton addItemWithTitle:cm->uri().toNSString()];
122 }
123
124 [contactMethodsPopupButton setEnabled:(contactMethods.length() > 1)];
125
126 [emptyConversationPlaceHolder setHidden:NO];
127 // Select first cm
128 [contactMethodsPopupButton selectItemAtIndex:0];
129 [self itemChanged:contactMethodsPopupButton];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500130 });
131}
132
133- (IBAction)sendMessage:(id)sender
134{
135 /* make sure there is text to send */
136 NSString* text = self.message;
137 if (text && text.length > 0) {
138 QMap<QString, QString> messages;
139 messages["text/plain"] = QString::fromNSString(text);
140 contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])->sendOfflineTextMessage(messages);
141 self.message = @"";
142 }
143}
144
145- (IBAction)placeCall:(id)sender
146{
147 if(auto cm = contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])) {
148 auto c = CallModel::instance().dialingCall();
149 c->setPeerContactMethod(cm);
150 c << Call::Action::ACCEPT;
151 CallModel::instance().selectCall(c);
152 }
153}
154
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500155- (IBAction)backPressed:(id)sender {
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500156 RecentModel::instance().selectionModel()->clearCurrentIndex();
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -0400157 messagesViewVC.delegate = nil;
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500158}
159
Kateryna Kostiuk0ba1baf2017-05-12 16:51:27 -0400160- (IBAction)openSendContactRequestWindow:(id)sender
161{
162 if(auto cm = contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])) {
163 sendRequestWC = [[SendContactRequestWC alloc] initWithWindowNibName:@"SendContactRequest"];
164 sendRequestWC.contactMethod = cm;
165 [sendRequestWC.window makeKeyAndOrderFront:sendRequestWC.window];
166 }
167}
168
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500169# pragma mark private IN/OUT animations
170
171-(void) animateIn
172{
173 CGRect frame = CGRectOffset(self.view.superview.bounds, -self.view.superview.bounds.size.width, 0);
174 [self.view setHidden:NO];
175
176 [CATransaction begin];
177 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
178 [animation setFromValue:[NSValue valueWithPoint:frame.origin]];
179 [animation setToValue:[NSValue valueWithPoint:self.view.superview.bounds.origin]];
180 [animation setDuration:0.2f];
181 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn]];
182 [self.view.layer addAnimation:animation forKey:animation.keyPath];
183
184 [CATransaction commit];
185}
186
Alexandre Lision01cf5e32016-01-21 15:54:30 -0500187-(void) animateOut
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500188{
189 if(self.view.frame.origin.x < 0) {
190 return;
191 }
192
193 CGRect frame = CGRectOffset(self.view.frame, -self.view.frame.size.width, 0);
194 [CATransaction begin];
195 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
196 [animation setFromValue:[NSValue valueWithPoint:self.view.frame.origin]];
197 [animation setToValue:[NSValue valueWithPoint:frame.origin]];
198 [animation setDuration:0.2f];
199 [animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
200
201 [CATransaction setCompletionBlock:^{
202 [self.view setHidden:YES];
203 }];
204 [self.view.layer addAnimation:animation forKey:animation.keyPath];
205
206 [self.view.layer setPosition:frame.origin];
207 [CATransaction commit];
208}
209
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500210#pragma mark - NSTextFieldDelegate
211
212- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
213{
214 if (commandSelector == @selector(insertNewline:) && self.message.length > 0) {
215 [self sendMessage:nil];
216 return YES;
217 }
218 return NO;
219}
220
221#pragma mark - NSPopUpButton item selection
222
223- (IBAction)itemChanged:(id)sender {
224 NSInteger index = [(NSPopUpButton *)sender indexOfSelectedItem];
225
Alexandre Lision05ca83c2016-11-24 19:31:08 -0500226 selectedContactMethod = contactMethods.at(index);
Kateryna Kostiuk78e1f122017-06-14 14:52:34 -0400227
228 /*to send contact request we need to meet two condition:
229 1)contact method has RING protocol
230 2)accound is used to send request is also RING*/
231
232 Boolean hideSendTrustRequestButton = NO;
233 if(selectedContactMethod->protocolHint() != URI::ProtocolHint::RING) {
234 hideSendTrustRequestButton = YES;
235 }
236 else if(selectedContactMethod->account()) {
237 hideSendTrustRequestButton = selectedContactMethod->account()->protocol() != Account::Protocol::RING;
238 }
239 else if([self chosenAccount]) {
240 Boolean hideSendTrustRequestButton = [self chosenAccount]->protocol() != Account::Protocol::RING;
241 }
242
243 [sentContactRequestButton setHidden:hideSendTrustRequestButton];
244 sentContactRequestWidth.priority = hideSendTrustRequestButton ? 999: 250;
245
Alexandre Lision05ca83c2016-11-24 19:31:08 -0500246 [conversationTitle setStringValue:selectedContactMethod->primaryName().toNSString()];
247 QObject::disconnect(contactMethodChanged);
248 contactMethodChanged = QObject::connect(selectedContactMethod,
249 &ContactMethod::changed,
250 [self] {
251 [conversationTitle setStringValue:selectedContactMethod->primaryName().toNSString()];
252 });
253
254 if (auto txtRecording = selectedContactMethod->textRecording()) {
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -0400255 messagesViewVC.delegate = self;
256 [messagesViewVC setUpViewWithModel:txtRecording->instantMessagingModel()];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500257 }
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500258}
259
Kateryna Kostiuk58276bc2017-06-07 08:50:48 -0400260#pragma mark - MessagesVC delegate
261
262-(void) newMessageAdded {
263
264 if (auto txtRecording = contactMethods.at([contactMethodsPopupButton indexOfSelectedItem])->textRecording()) {
265 [emptyConversationPlaceHolder setHidden:txtRecording->instantMessagingModel()->rowCount() > 0];
266 txtRecording->setAllRead();
267 }
268}
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500269
270@end