blob: 816e8fcb1747fa1a61bbd70eea3708d17b099407 [file] [log] [blame]
Alexandre Lision8521baa2015-03-13 11:08:00 -04001/*
Alexandre Lision9fe374b2016-01-06 10:17:31 -05002 * Copyright (C) 2015-2016 Savoir-faire Linux Inc.
Alexandre Lision8521baa2015-03-13 11:08:00 -04003 * 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.
Alexandre Lision8521baa2015-03-13 11:08:00 -040018 */
Alexandre Lision5855b6a2015-02-03 11:31:05 -050019#import "RingWindowController.h"
Alexandre Lision0f66bd32016-01-18 11:30:45 -050020#import <QuartzCore/QuartzCore.h>
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -040021#include <qrencode.h>
Alexandre Lision0f66bd32016-01-18 11:30:45 -050022
Alexandre Lisionbb5c2462015-07-30 12:55:37 -040023//LRC
Anthony Léonard79597602017-11-13 15:47:09 -050024#import <api/lrc.h>
Anthony Léonard49cb2912017-11-13 16:15:39 -050025#import <api/account.h>
Olivier Soldano994971f2017-12-05 16:30:12 -050026#import <api/newaccountmodel.h>
27#import <api/newcallmodel.h>
28#import <api/behaviorcontroller.h>
29#import <api/conversation.h>
Anthony Léonardd00cd182018-01-17 09:21:27 -050030#import <api/contactmodel.h>
31#import <api/contact.h>
Kateryna Kostiuk67735232018-05-10 15:05:32 -040032#import <api/datatransfermodel.h>
Kateryna Kostiuk00dcbff2019-07-11 15:42:13 -040033#import <api/avmodel.h>
Alexandre Lision5855b6a2015-02-03 11:31:05 -050034
Alexandre Lision624b1a82016-09-11 19:29:01 -040035// Ring
Alexandre Lision745e4d62015-03-22 20:03:10 -040036#import "AppDelegate.h"
Alexandre Lisionc65310c2015-04-23 16:44:23 -040037#import "Constants.h"
Alexandre Lision58cab672015-06-09 15:25:40 -040038#import "CurrentCallVC.h"
Alexandre Lision624b1a82016-09-11 19:29:01 -040039#import "MigrateRingAccountsWC.h"
Alexandre Lision0f66bd32016-01-18 11:30:45 -050040#import "ConversationVC.h"
Alexandre Lisionbfa68f62015-09-10 08:38:42 -040041#import "PreferencesWC.h"
Alexandre Lisiona3a43dc2017-03-30 16:21:30 -040042#import "SmartViewVC.h"
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -040043#import "views/IconButton.h"
Alexandre Lisionbfa68f62015-09-10 08:38:42 -040044#import "views/NSColor+RingTheme.h"
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -040045#import "views/HoverButton.h"
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -040046#import "utils.h"
Kateryna Kostiukd73f9602018-07-24 13:51:28 -040047#import "RingWizardWC.h"
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -040048#import "AccountSettingsVC.h"
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -040049
50typedef NS_ENUM(NSInteger, ViewState) {
51 SHOW_WELCOME_SCREEN = 0,
52 SHOW_CONVERSATION_SCREEN,
53 SHOW_CALL_SCREEN,
54 SHOW_SETTINGS_SCREEN,
Kateryna Kostiukf6317422018-09-27 17:08:20 -040055 LEAVE_MESSAGE,
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -040056};
Alexandre Lision2db8f472015-07-22 15:05:46 -040057
Kateryna Kostiukef66f972018-11-02 17:10:37 -040058@interface RingWindowController () <MigrateRingAccountsDelegate>
Alexandre Lision624b1a82016-09-11 19:29:01 -040059
60@property (retain) MigrateRingAccountsWC* migrateWC;
Kateryna Kostiukd73f9602018-07-24 13:51:28 -040061@property RingWizardWC* wizard;
Kateryna Kostiuk4db61092019-10-17 16:57:40 -040062@property QMetaObject::Connection callState;
Alexandre Lision624b1a82016-09-11 19:29:01 -040063
64@end
65
Alexandre Lisionbfa68f62015-09-10 08:38:42 -040066@implementation RingWindowController {
Alexandre Lision5855b6a2015-02-03 11:31:05 -050067
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -040068 __unsafe_unretained IBOutlet NSLayoutConstraint* centerYQRCodeConstraint;
69 __unsafe_unretained IBOutlet NSLayoutConstraint* centerYWelcomeContainerConstraint;
Kateryna Kostiukab499f42018-04-16 12:27:33 -040070 IBOutlet NSLayoutConstraint* ringLabelTrailingConstraint;
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -040071 __unsafe_unretained IBOutlet NSView* welcomeContainer;
Alexandre Lision1abdf582016-02-09 14:21:19 -050072 __unsafe_unretained IBOutlet NSView* callView;
73 __unsafe_unretained IBOutlet NSTextField* ringIDLabel;
Kateryna Kostiuk85a334e2018-12-03 15:54:19 -050074 __unsafe_unretained IBOutlet NSTextField* explanationLabel;
Alexandre Lision1abdf582016-02-09 14:21:19 -050075 __unsafe_unretained IBOutlet NSButton* shareButton;
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -040076 __unsafe_unretained IBOutlet NSImageView* qrcodeView;
Alexandre Lision58cab672015-06-09 15:25:40 -040077
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -040078 PreferencesWC* preferencesWC;
Alexandre Lisiona3a43dc2017-03-30 16:21:30 -040079 IBOutlet SmartViewVC* smartViewVC;
Alexandre Lisione77f6f92016-04-17 23:39:39 -040080
Alexandre Lision0f66bd32016-01-18 11:30:45 -050081 CurrentCallVC* currentCallVC;
Andreas Traczykecdc4fa2018-03-22 16:11:47 -040082 ConversationVC* conversationVC;
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -040083 AccountSettingsVC* settingsVC;
Andreas Traczykecdc4fa2018-03-22 16:11:47 -040084
Kateryna Kostiukef66f972018-11-02 17:10:37 -040085 IBOutlet ChooseAccountVC* chooseAccountVC;
Alexandre Lisionbfa68f62015-09-10 08:38:42 -040086}
Alexandre Lision58cab672015-06-09 15:25:40 -040087
Kateryna Kostiukf6317422018-09-27 17:08:20 -040088@synthesize dataTransferModel, accountModel, behaviorController, avModel;
Kateryna Kostiukd73f9602018-07-24 13:51:28 -040089@synthesize wizard;
Kateryna Kostiukecaa3952018-07-13 16:00:34 -040090
Kateryna Kostiukf6317422018-09-27 17:08:20 -040091-(id) initWithWindowNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil accountModel:( lrc::api::NewAccountModel*)accountModel dataTransferModel:( lrc::api::DataTransferModel*)dataTransferModel behaviourController:( lrc::api::BehaviorController*) behaviorController avModel: (lrc::api::AVModel*)avModel
Kateryna Kostiukecaa3952018-07-13 16:00:34 -040092{
93 if (self = [self initWithWindowNibName:nibNameOrNil])
94 {
95 self.accountModel = accountModel;
96 self.dataTransferModel = dataTransferModel;
97 self.behaviorController = behaviorController;
Kateryna Kostiukf6317422018-09-27 17:08:20 -040098 self.avModel = avModel;
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -040099 self.avModel->useAVFrame(YES);
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400100 }
101 return self;
102}
103
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400104- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
105{
106 return (NSApplicationPresentationFullScreen |
107 NSApplicationPresentationAutoHideMenuBar |
108 NSApplicationPresentationAutoHideToolbar);
109}
110
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400111-(void)changeViewTo:(ViewState) state {
112 switch (state) {
113 case SHOW_WELCOME_SCREEN:
114 [self accountSettingsShouldOpen: NO];
115 [conversationVC hideWithAnimation:false];
116 [currentCallVC hideWithAnimation:false];
Kateryna Kostiuk4f37d952018-12-04 13:19:17 -0500117 [currentCallVC cleanUp];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400118 [currentCallVC.view removeFromSuperview];
119 [welcomeContainer setHidden: NO];
120 [smartViewVC.view setHidden: NO];
121 [settingsVC hide];
122 break;
123 case SHOW_CONVERSATION_SCREEN:
124 [self accountSettingsShouldOpen: NO];
125 [conversationVC showWithAnimation:false];
126 [currentCallVC hideWithAnimation:false];
Kateryna Kostiuk4f37d952018-12-04 13:19:17 -0500127 [currentCallVC cleanUp];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400128 [currentCallVC.view removeFromSuperview];
129 [welcomeContainer setHidden: YES];
130 [smartViewVC.view setHidden: NO];
131 [settingsVC hide];
132 break;
133 case SHOW_CALL_SCREEN:
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400134 self.avModel->useAVFrame(YES);
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400135 [self accountSettingsShouldOpen: NO];
136 if (![currentCallVC.view superview]) {
137 [callView addSubview:[currentCallVC view] positioned:NSWindowAbove relativeTo:nil];
138 [currentCallVC initFrame];
139 [currentCallVC showWithAnimation:false];
140 [conversationVC hideWithAnimation:false];
141 [welcomeContainer setHidden: YES];
142 [smartViewVC.view setHidden: NO];
143 [settingsVC hide];
144 }
145 [currentCallVC showWithAnimation:false];
146 break;
147 case SHOW_SETTINGS_SCREEN:
148 @try {
149 [self accountSettingsShouldOpen: YES];
150 }
151 @catch (NSException *ex) {
152 return;
153 }
154 [welcomeContainer setHidden: YES];
155 [currentCallVC hideWithAnimation:false];
156 [currentCallVC.view removeFromSuperview];
157 [conversationVC hideWithAnimation:false];
158 [smartViewVC.view setHidden: YES];
159 [settingsVC show];
160 break;
Kateryna Kostiukf6317422018-09-27 17:08:20 -0400161 case LEAVE_MESSAGE:
162 [conversationVC showWithAnimation: false];
163 [currentCallVC hideWithAnimation: false];
164 [conversationVC presentLeaveMessageView];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400165 default:
166 break;
167 }
168}
169
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500170- (void)windowDidLoad {
171 [super windowDidLoad];
Alexandre Lision4dfcafc2015-08-20 12:43:23 -0400172 [self.window setMovableByWindowBackground:YES];
Alexandre Lision58cab672015-06-09 15:25:40 -0400173
Kateryna Kostiuk13b76882017-03-30 09:18:44 -0400174 self.window.titleVisibility = NSWindowTitleHidden;
Alexandre Lisione77f6f92016-04-17 23:39:39 -0400175
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500176 currentCallVC = [[CurrentCallVC alloc] initWithNibName:@"CurrentCall" bundle:nil];
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400177 currentCallVC.delegate = self;
Kateryna Kostiukf6317422018-09-27 17:08:20 -0400178 conversationVC = [[ConversationVC alloc] initWithNibName:@"Conversation" bundle:nil delegate:self aVModel:self.avModel];
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400179 [chooseAccountVC updateWithDelegate: self andModel:self.accountModel];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400180 settingsVC = [[AccountSettingsVC alloc] initWithNibName:@"AccountSettings" bundle:nil accountmodel:self.accountModel];
Alexandre Lision58cab672015-06-09 15:25:40 -0400181 [callView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
Kateryna Kostiukc24e0f92019-10-02 13:37:16 -0400182 [[currentCallVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400183 [[conversationVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400184 [[settingsVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
Alexandre Lision58cab672015-06-09 15:25:40 -0400185
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400186 [callView addSubview:[conversationVC view] positioned:NSWindowAbove relativeTo:nil];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400187 [self.window.contentView addSubview:[settingsVC view] positioned:NSWindowAbove relativeTo:nil];
Alexandre Lision16d9c0a2015-08-10 12:05:15 -0400188
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400189 [conversationVC initFrame];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400190 [settingsVC initFrame];
Alexandre Lisionbb5c2462015-07-30 12:55:37 -0400191
Kateryna Kostiukbe7e2e42019-07-26 17:44:12 -0400192 [self checkAccountsToMigrate];
Anthony Léonard49cb2912017-11-13 16:15:39 -0500193
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -0400194 // set download folder (default - 'Documents')
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400195 NSString* path = [[NSUserDefaults standardUserDefaults] stringForKey:Preferences::DownloadFolder];
196 if (!path || path.length == 0) {
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -0400197 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
198 path = [[paths objectAtIndex:0] stringByAppendingString:@"/"];
Kateryna Kostiuk67735232018-05-10 15:05:32 -0400199 }
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400200 self.dataTransferModel->downloadDirectory = std::string([path UTF8String]);
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -0400201 if(appSandboxed()) {
202 NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
Kateryna Kostiukbe7e2e42019-07-26 17:44:12 -0400203 avModel->setRecordPath([[paths objectAtIndex:0] UTF8String]);
Kateryna Kostiuk6db52802019-08-12 17:56:38 -0400204 } else if (avModel->getRecordPath().empty()) {
205 avModel->setRecordPath([NSHomeDirectory() UTF8String]);
Kateryna Kostiuk74fe20c2018-06-14 12:05:53 -0400206 }
Kateryna Kostiukef66f972018-11-02 17:10:37 -0400207 NSToolbar *tb = [[self window] toolbar];
208 [tb setAllowsUserCustomization:NO];
Kateryna Kostiukf6317422018-09-27 17:08:20 -0400209
210 //add messages view controller to responders chain
211 NSResponder * viewNextResponder = [self nextResponder];
212 [self setNextResponder: [conversationVC getMessagesView]];
213 [[conversationVC getMessagesView] setNextResponder: viewNextResponder];
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500214}
215
216- (void) connect
217{
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400218 QObject::connect(self.behaviorController,
Olivier Soldano994971f2017-12-05 16:30:12 -0500219 &lrc::api::BehaviorController::showCallView,
220 [self](const std::string accountId,
221 const lrc::api::conversation::Info convInfo){
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400222 auto* accInfo = &self.accountModel->getAccountInfo(accountId);
Anthony Léonardd00cd182018-01-17 09:21:27 -0500223 if (accInfo->contactModel->getContact(convInfo.participants[0]).profileInfo.type == lrc::api::profile::Type::PENDING)
224 [smartViewVC selectPendingList];
225 else
226 [smartViewVC selectConversationList];
227
Olivier Soldano994971f2017-12-05 16:30:12 -0500228 [currentCallVC setCurrentCall:convInfo.callId
229 conversation:convInfo.uid
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400230 account:accInfo
231 avModel: avModel];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400232 [self changeViewTo:SHOW_CALL_SCREEN];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400233 [conversationVC setConversationUid:convInfo.uid model:accInfo->conversationModel.get()];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400234
Alexandre Lision0f66bd32016-01-18 11:30:45 -0500235 });
Alexandre Lision210fe212016-01-27 11:15:13 -0500236
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400237 QObject::connect(self.behaviorController,
Olivier Soldano994971f2017-12-05 16:30:12 -0500238 &lrc::api::BehaviorController::showIncomingCallView,
239 [self](const std::string accountId,
240 const lrc::api::conversation::Info convInfo){
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400241 auto* accInfo = &self.accountModel->getAccountInfo(accountId);
Anthony Léonardd00cd182018-01-17 09:21:27 -0500242 if (accInfo->contactModel->getContact(convInfo.participants[0]).profileInfo.type == lrc::api::profile::Type::PENDING)
243 [smartViewVC selectPendingList];
244 else
245 [smartViewVC selectConversationList];
Olivier Soldano994971f2017-12-05 16:30:12 -0500246 [currentCallVC setCurrentCall:convInfo.callId
247 conversation:convInfo.uid
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400248 account:accInfo
249 avModel: avModel];
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500250 [smartViewVC selectConversation: convInfo model:accInfo->conversationModel.get()];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400251 [self changeViewTo:SHOW_CALL_SCREEN];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400252 [conversationVC setConversationUid:convInfo.uid model:accInfo->conversationModel.get()];
Kateryna Kostiuk13b76882017-03-30 09:18:44 -0400253 });
Anthony Léonard2382b562017-12-13 15:51:28 -0500254
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400255 QObject::connect(self.behaviorController,
Anthony Léonard2382b562017-12-13 15:51:28 -0500256 &lrc::api::BehaviorController::showChatView,
257 [self](const std::string& accountId,
258 const lrc::api::conversation::Info& convInfo){
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400259 auto& accInfo = self.accountModel->getAccountInfo(accountId);
Andreas Traczykecdc4fa2018-03-22 16:11:47 -0400260 [conversationVC setConversationUid:convInfo.uid model:accInfo.conversationModel.get()];
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500261 [smartViewVC selectConversation: convInfo model:accInfo.conversationModel.get()];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400262 [self changeViewTo:SHOW_CONVERSATION_SCREEN];
Anthony Léonard2382b562017-12-13 15:51:28 -0500263 });
Kateryna Kostiukf6317422018-09-27 17:08:20 -0400264 QObject::connect(self.behaviorController,
265 &lrc::api::BehaviorController::showLeaveMessageView,
266 [self](const std::string& accountId,
267 const lrc::api::conversation::Info& convInfo){
268 auto& accInfo = self.accountModel->getAccountInfo(accountId);
269 [conversationVC setConversationUid:convInfo.uid model:accInfo.conversationModel.get()];
270 [smartViewVC selectConversation: convInfo model:accInfo.conversationModel.get()];
271 [self changeViewTo:LEAVE_MESSAGE];
272 });
Alexandre Lisionbb5c2462015-07-30 12:55:37 -0400273}
274
Kateryna Kostiukbe7e2e42019-07-26 17:44:12 -0400275#pragma mark - Ring account migration
276
277- (void) migrateRingAccount:(std::string) acc
278{
279 self.migrateWC = [[MigrateRingAccountsWC alloc] initWithDelegate:self actionCode:1];
280 self.migrateWC.accountModel = self.accountModel;
281 self.migrateWC.accountToMigrate = acc;
282 [self.window beginSheet:self.migrateWC.window completionHandler:nil];
283}
284
285- (void)checkAccountsToMigrate
286{
287 auto accounts = self.accountModel->getAccountList();
288 for (auto accountId: accounts) {
289 const lrc::api::account::Info& accountInfo = self.accountModel->getAccountInfo(accountId);
290 if (accountInfo.status == lrc::api::account::Status::ERROR_NEED_MIGRATION) {
291 [self migrateRingAccount:accountInfo.id];
292 return;
293 }
294 }
295 @try {
296 [smartViewVC setConversationModel: [chooseAccountVC selectedAccount].conversationModel.get()];
297 }
298 @catch (NSException *ex) {
299 NSLog(@"Caught exception %@: %@", [ex name], [ex reason]);
300 }
301 [shareButton sendActionOn:NSLeftMouseDownMask];
302 [self connect];
303 [self updateRingID];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400304 [self connectCallSignalsForAccount];
305}
306
307- (void)connectCallSignalsForAccount
308{
309 @try {
310 auto& account = [chooseAccountVC selectedAccount];
311 QObject::disconnect(self.callState);
312 auto *callModel = account.callModel.get();
313 self.callState = QObject::connect(callModel,
314 &lrc::api::NewCallModel::callStatusChanged,
315 [self, callModel](const std::string callId) {
316 if (callModel->hasCall(callId)) {
317 auto call = callModel->getCall(callId);
318 [smartViewVC reloadConversationWithURI: @(call.peerUri.c_str())];
319 }
320 });
321 } @catch (NSException *ex) {
322 }
Kateryna Kostiukbe7e2e42019-07-26 17:44:12 -0400323}
324
325- (void)migrationDidComplete
326{
327 [self checkAccountsToMigrate];
328}
329
330- (void)migrationDidCompleteWithError
331{
332 [self checkAccountsToMigrate];
333}
334
Alexandre Lisionbb5c2462015-07-30 12:55:37 -0400335/**
336 * Implement the necessary logic to choose which Ring ID to display.
337 * This tries to choose the "best" ID to show
338 */
339- (void) updateRingID
340{
Anthony Léonard72128c92017-12-26 16:48:39 -0500341 @try {
342 auto& account = [chooseAccountVC selectedAccount];
Alexandre Lisionbb5c2462015-07-30 12:55:37 -0400343
Anthony Léonard72128c92017-12-26 16:48:39 -0500344 [ringIDLabel setStringValue:@""];
Anthony Léonard49cb2912017-11-13 16:15:39 -0500345
Anthony Léonard72128c92017-12-26 16:48:39 -0500346 if(account.profileInfo.type != lrc::api::profile::Type::RING) {
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400347 self.notRingAccount = YES;
348 self.isSIPAccount = YES;
Anthony Léonard72128c92017-12-26 16:48:39 -0500349 return;
350 }
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400351 self.isSIPAccount = NO;
352 self.notRingAccount = NO;
353 [ringLabelTrailingConstraint setActive:YES];
Anthony Léonard72128c92017-12-26 16:48:39 -0500354 auto& registeredName = account.registeredName;
355 auto& ringID = account.profileInfo.uri;
356 NSString* uriToDisplay = nullptr;
357 if (!registeredName.empty()) {
358 uriToDisplay = @(registeredName.c_str());
Kateryna Kostiuk85a334e2018-12-03 15:54:19 -0500359 [explanationLabel setStringValue: NSLocalizedString(@"This is your Jami username. \nCopy and share it with your friends!", @"Explanation label when user have Jami username")];
Anthony Léonard72128c92017-12-26 16:48:39 -0500360 } else {
361 uriToDisplay = @(ringID.c_str());
Kateryna Kostiuk85a334e2018-12-03 15:54:19 -0500362 [explanationLabel setStringValue: NSLocalizedString(@"This is your ID. \nCopy and share it with your friends!", @"Explanation label when user have just ID")];
Anthony Léonard72128c92017-12-26 16:48:39 -0500363 }
364 [ringIDLabel setStringValue:uriToDisplay];
365 [self drawQRCode:@(ringID.c_str())];
Alexandre Lisionbb5c2462015-07-30 12:55:37 -0400366 }
Anthony Léonard72128c92017-12-26 16:48:39 -0500367 @catch (NSException *ex) {
368 NSLog(@"Caught exception %@: %@", [ex name], [ex reason]);
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400369 self.notRingAccount = YES;
370 self.isSIPAccount = NO;
371 [ringLabelTrailingConstraint setActive:NO];
Anthony Léonard70638f02018-02-05 11:10:19 -0500372 [ringIDLabel setStringValue:NSLocalizedString(@"No account available", @"Displayed as RingID when no accounts are available for selection")];
Alexandre Lision313427f2016-11-24 21:04:04 -0500373 }
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500374}
375
Alexandre Lision1abdf582016-02-09 14:21:19 -0500376- (IBAction)shareRingID:(id)sender {
377 NSSharingServicePicker* sharingServicePicker = [[NSSharingServicePicker alloc] initWithItems:[NSArray arrayWithObject:[ringIDLabel stringValue]]];
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400378 [sharingServicePicker setDelegate:self];
Alexandre Lision1abdf582016-02-09 14:21:19 -0500379 [sharingServicePicker showRelativeToRect:[sender bounds]
380 ofView:sender
381 preferredEdge:NSMinYEdge];
382}
383
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400384- (IBAction)toggleQRCode:(id)sender {
385 // Toggle pressed state of QRCode button
Kateryna Kostiuk85a334e2018-12-03 15:54:19 -0500386 // [sender setPressed:![sender isPressed]];
387 bool show = qrcodeView.animator.alphaValue == 0.0f ? YES: NO;
388 [self showQRCode: show];
Alexandre Lision313427f2016-11-24 21:04:04 -0500389}
390
391/**
392 * Draw the QRCode in the qrCodeView
393 */
Kateryna Kostiuk65ba43e2017-03-30 15:10:04 -0400394- (void)drawQRCode:(NSString*) uriToDraw
Alexandre Lision313427f2016-11-24 21:04:04 -0500395{
Kateryna Kostiuk65ba43e2017-03-30 15:10:04 -0400396 auto qrCode = QRcode_encodeString(uriToDraw.UTF8String,
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400397 0,
398 QR_ECLEVEL_L, // Lowest level of error correction
399 QR_MODE_8, // 8-bit data mode
400 1);
401 if (!qrCode) {
402 return;
403 }
404
Alexandre Lision313427f2016-11-24 21:04:04 -0500405 unsigned char *data = 0;
406 int width;
407 data = qrCode->data;
408 width = qrCode->width;
409 int qr_margin = 3;
410
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400411 CGFloat size = qrcodeView.frame.size.width;
412
413 // create context
414 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
415 CGContextRef ctx = CGBitmapContextCreate(0, size, size, 8, size * 4, colorSpace, kCGImageAlphaPremultipliedLast);
416
417 CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(0, -size);
418 CGAffineTransform scaleTransform = CGAffineTransformMakeScale(1, -1);
419 CGContextConcatCTM(ctx, CGAffineTransformConcat(translateTransform, scaleTransform));
420
Alexandre Lision313427f2016-11-24 21:04:04 -0500421 float zoom = ceil((double)size / (qrCode->width + 2.0 * qr_margin));
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400422 CGRect rectDraw = CGRectMake(0, 0, zoom, zoom);
423
424 int ran;
425 for(int i = 0; i < width; ++i) {
426 for(int j = 0; j < width; ++j) {
427 if(*data & 1) {
Kateryna Kostiukbf899502019-09-25 11:25:45 -0400428 CGContextSetFillColorWithColor(ctx, [NSColor blackColor].CGColor);
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400429 rectDraw.origin = CGPointMake((j + qr_margin) * zoom,(i + qr_margin) * zoom);
430 CGContextAddRect(ctx, rectDraw);
431 CGContextFillPath(ctx);
432 } else {
Kateryna Kostiuke28a0382019-10-25 16:29:08 -0400433 CGContextSetFillColorWithColor(ctx, [NSColor clearColor].CGColor);
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400434 rectDraw.origin = CGPointMake((j + qr_margin) * zoom,(i + qr_margin) * zoom);
435 CGContextAddRect(ctx, rectDraw);
436 CGContextFillPath(ctx);
437 }
438 ++data;
439 }
440 }
Alexandre Lision313427f2016-11-24 21:04:04 -0500441
442 // get image
443 auto qrCGImage = CGBitmapContextCreateImage(ctx);
444 auto qrImage = [[NSImage alloc] initWithCGImage:qrCGImage size:qrcodeView.frame.size];
445
446 // some releases
447 CGContextRelease(ctx);
448 CGImageRelease(qrCGImage);
449 CGColorSpaceRelease(colorSpace);
450 QRcode_free(qrCode);
451
452 [qrcodeView setImage:qrImage];
Alexandre Lisionfd0d6c82016-03-29 17:06:54 -0400453}
454
455/**
456 * Start the in/out animation displaying the QRCode
457 * @param show should the QRCode be animated in or out
458 */
459- (void) showQRCode:(BOOL) show
460{
461 static const NSInteger offset = 110;
462 [NSAnimationContext beginGrouping];
463 NSAnimationContext.currentContext.duration = 0.5;
464 [[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
465 qrcodeView.animator.alphaValue = show ? 1.0 : 0.0;
466 [centerYQRCodeConstraint.animator setConstant: show ? offset : 0];
467 [centerYWelcomeContainerConstraint.animator setConstant:show ? -offset : 0];
468 [NSAnimationContext endGrouping];
469}
470
Alexandre Lision4a7b95e2015-02-20 10:06:43 -0500471- (IBAction)openPreferences:(id)sender
472{
Kateryna Kostiukbb68a1c2018-06-12 15:16:02 -0400473 if (preferencesWC) {
474 [preferencesWC.window orderFront:preferencesWC.window];
475 return;
476 }
Kateryna Kostiukecaa3952018-07-13 16:00:34 -0400477
Kateryna Kostiukc3fc76c2019-01-06 22:12:34 -0500478 preferencesWC = [[PreferencesWC alloc] initWithWindowNibName: @"PreferencesWindow" bundle: nil accountModel:self.accountModel dataTransferModel:self.dataTransferModel behaviourController:self.behaviorController avModel: self.avModel];
Alexandre Lisionbfa68f62015-09-10 08:38:42 -0400479 [preferencesWC.window makeKeyAndOrderFront:preferencesWC.window];
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500480}
Alexandre Lision4a7b95e2015-02-20 10:06:43 -0500481
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -0400482- (IBAction)callClickedAtRow:(id)sender
483{
484 NSTabViewItem *selectedTab = [smartViewVC.tabbar selectedTabViewItem];
485 int index = [smartViewVC.tabbar indexOfTabViewItem:selectedTab];
486 switch (index) {
487 case 0:
488 [smartViewVC startCallForRow:sender];
489 break;
Kateryna Kostiuk882cbac2017-07-05 17:29:00 -0400490 default:
491 break;
492 }
493}
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500494
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400495- (void) selectAccount:(const lrc::api::account::Info&)accInfo currentRemoved:(BOOL) removed
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500496{
497 // If the selected account has been changed, we close any open panel
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400498 [smartViewVC setConversationModel:accInfo.conversationModel.get()];
Anthony Léonard61e3dcf2017-12-27 12:19:52 -0500499 // Welcome view informations are also updated
500 [self updateRingID];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400501 [settingsVC setSelectedAccount:accInfo.id];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400502 [self changeViewTo: ([settingsVC.view isHidden] || removed) ? SHOW_WELCOME_SCREEN : SHOW_SETTINGS_SCREEN];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400503 [self connectCallSignalsForAccount];
Anthony Léonard9bebf1d2017-12-21 14:33:51 -0500504}
505
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400506-(void)allAccountsDeleted
507{
508 [smartViewVC clearConversationModel];
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400509 [self changeViewTo:SHOW_WELCOME_SCREEN];
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400510 [self updateRingID];
Kateryna Kostiukb0db48e2019-01-10 16:03:55 -0500511 qrcodeView.animator.alphaValue = 0.0;
512 [centerYQRCodeConstraint.animator setConstant: 0];
513 [centerYWelcomeContainerConstraint.animator setConstant: 0];
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400514 QObject::disconnect(self.callState);
Kateryna Kostiukb0db48e2019-01-10 16:03:55 -0500515 [self close];
516 AppDelegate* delegate = (AppDelegate*)[[NSApplication sharedApplication] delegate];
517 [delegate showWizard];
Kateryna Kostiukab499f42018-04-16 12:27:33 -0400518}
519
Anthony Léonard8585cc02017-12-28 14:03:45 -0500520-(void)rightPanelClosed
521{
522 [smartViewVC deselect];
Kateryna Kostiuk394f74c2018-10-05 15:45:26 -0400523 [welcomeContainer setHidden:NO];
Anthony Léonard8585cc02017-12-28 14:03:45 -0500524}
525
Anthony Léonard0a9904c2018-01-11 16:43:47 -0500526-(void)currentConversationTrusted
527{
528 [smartViewVC selectConversationList];
529}
530
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500531-(void) listTypeChanged {
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400532 [self changeViewTo:SHOW_WELCOME_SCREEN];
Anthony Léonardbee94cc2018-01-16 11:42:40 -0500533}
534
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400535- (IBAction)openAccountSettings:(NSButton *)sender
536{
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400537 [self changeViewTo: [settingsVC.view isHidden] ? SHOW_SETTINGS_SCREEN : SHOW_WELCOME_SCREEN];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400538}
539
Kateryna Kostiukd73f9602018-07-24 13:51:28 -0400540- (void) createNewAccount {
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400541 [self changeViewTo:SHOW_WELCOME_SCREEN];
Kateryna Kostiukd73f9602018-07-24 13:51:28 -0400542 wizard = [[RingWizardWC alloc] initWithNibName:@"RingWizard" bundle: nil accountmodel: self.accountModel];
Kateryna Kostiukc7e68f32019-10-09 16:15:45 -0400543 [wizard showChooseWithCancelButton: YES];
Kateryna Kostiukd73f9602018-07-24 13:51:28 -0400544 [self.window beginSheet:wizard.window completionHandler:nil];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400545}
546
Kateryna Kostiuk23222fe2018-11-16 14:28:02 -0500547- (NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet
548 usingRect:(NSRect)rect
549{
550 float titleBarHeight = self.window.frame.size.height - [NSWindow contentRectForFrameRect:self.window.frame styleMask:self.window.styleMask].size.height;
551 rect.origin.y = self.window.frame.size.height;
552 return rect;
553}
554
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400555-(void) accountSettingsShouldOpen: (BOOL) open {
556 if (open) {
Kateryna Kostiukc17db8d2018-10-23 09:48:03 -0400557 [settingsVC setSelectedAccount: [chooseAccountVC selectedAccount].id];
Kateryna Kostiuk1f8c1252018-07-30 18:18:57 -0400558 }
Kateryna Kostiukd73f9602018-07-24 13:51:28 -0400559}
560
Kateryna Kostiuk133364b2018-10-31 11:36:08 -0400561#pragma mark - CallViewControllerDelegate
562
563-(void) conversationInfoUpdatedFor:(const std::string&) conversationID {
564 [smartViewVC reloadConversationWithUid:@(conversationID.c_str())];
565}
566
Kateryna Kostiuk4db61092019-10-17 16:57:40 -0400567-(void) callFinished {
568 [self changeViewTo:SHOW_CONVERSATION_SCREEN];
569}
570
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500571-(void) showConversation:(NSString* )conversationId forAccount:(NSString*)accountId {
572 auto& accInfo = self.accountModel->getAccountInfo([accountId UTF8String]);
573 [chooseAccountVC selectAccount: accountId];
574 [settingsVC setSelectedAccount: [accountId UTF8String]];
575 [smartViewVC setConversationModel:accInfo.conversationModel.get()];
576 [smartViewVC selectConversationList];
577 [self updateRingID];
578 auto convInfo = getConversationFromUid([conversationId UTF8String], *accInfo.conversationModel.get());
579 auto convQueue = accInfo.conversationModel.get()->allFilteredConversations();
580 if (convInfo != convQueue.end()) {
581 [conversationVC setConversationUid:convInfo->uid model:accInfo.conversationModel.get()];
582 [smartViewVC selectConversation: *convInfo model:accInfo.conversationModel.get()];
583 accInfo.conversationModel.get()->clearUnreadInteractions([conversationId UTF8String]);
584 }
585 [self changeViewTo:SHOW_CONVERSATION_SCREEN];
586}
587
588-(void) showCall:(NSString* )callId forAccount:(NSString*)accountId forConversation:(NSString*)conversationId {
589 auto& accInfo = self.accountModel->getAccountInfo([accountId UTF8String]);
590 [chooseAccountVC selectAccount: accountId];
591 [settingsVC setSelectedAccount:accInfo.id];
592 [smartViewVC setConversationModel:accInfo.conversationModel.get()];
593 [self updateRingID];
594 auto convInfo = getConversationFromUid([conversationId UTF8String], *accInfo.conversationModel.get());
595 auto convQueue = accInfo.conversationModel.get()->allFilteredConversations();
596 if (convInfo != convQueue.end()) {
597 if (accInfo.contactModel->getContact(convInfo->participants[0]).profileInfo.type == lrc::api::profile::Type::PENDING)
598 [smartViewVC selectPendingList];
599 else
600 [smartViewVC selectConversationList];
601 [smartViewVC selectConversation: *convInfo model:accInfo.conversationModel.get()];
602 }
603 [currentCallVC setCurrentCall:[callId UTF8String]
604 conversation:[conversationId UTF8String]
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400605 account:&accInfo
606 avModel:avModel];
Kateryna Kostiuke3503842018-12-12 16:39:45 -0500607 [self changeViewTo:SHOW_CALL_SCREEN];
608}
609
610-(void) showContactRequestFor:(NSString* )accountId contactUri:(NSString*)uri {
611 auto& accInfo = self.accountModel->getAccountInfo([accountId UTF8String]);
612 [chooseAccountVC selectAccount: accountId];
613 [settingsVC setSelectedAccount:accInfo.id];
614 [smartViewVC setConversationModel:accInfo.conversationModel.get()];
615 [self updateRingID];
616 [smartViewVC selectPendingList];
617 auto convInfo = getConversationFromURI([uri UTF8String], *accInfo.conversationModel.get());
618 auto convQueue = accInfo.conversationModel.get()->allFilteredConversations();
619 if (convInfo != convQueue.end()) {
620 [conversationVC setConversationUid:convInfo->uid model:accInfo.conversationModel.get()];
621 [smartViewVC selectConversation: *convInfo model:accInfo.conversationModel.get()];
622 }
623 [self changeViewTo:SHOW_CONVERSATION_SCREEN];
624}
Kateryna Kostiuk5d90c3b2019-07-18 12:03:52 -0400625
Alexandre Lision5855b6a2015-02-03 11:31:05 -0500626@end