RingWindowController.mm 22.6 KB
Newer Older
1
/*
2
 *  Copyright (C) 2015-2016 Savoir-faire Linux Inc.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 *  Author: Alexandre Lision <alexandre.lision@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */
19
#import "RingWindowController.h"
20
#import <QuartzCore/QuartzCore.h>
21
#include <qrencode.h>
22
#include <memory>
23 24 25 26

//Qt
#import <QItemSelectionModel>
#import <QItemSelection>
27

28
//LRC
29 30
#import <callmodel.h>
#import <account.h>
31
#import <call.h>
32
#import <recentmodel.h>
33
#import <AvailableAccountModel.h>
34
#import <api/lrc.h>
35
#import <api/account.h>
36 37 38 39
#import <api/newaccountmodel.h>
#import <api/newcallmodel.h>
#import <api/behaviorcontroller.h>
#import <api/conversation.h>
40 41
#import <api/contactmodel.h>
#import <api/contact.h>
42
#import <api/datatransfermodel.h>
43
#import <media/recordingmodel.h>
44

45
// Ring
46
#import "AppDelegate.h"
47
#import "Constants.h"
Alexandre Lision's avatar
Alexandre Lision committed
48
#import "CurrentCallVC.h"
49
#import "MigrateRingAccountsWC.h"
50
#import "ConversationVC.h"
51
#import "PreferencesWC.h"
52
#import "SmartViewVC.h"
53
#import "views/IconButton.h"
54
#import "views/NSColor+RingTheme.h"
55
#import "views/HoverButton.h"
56
#import "utils.h"
57
#import "RingWizardWC.h"
58
#import "AccountSettingsVC.h"
59 60 61 62 63 64 65 66
#import "views/CallLayer.h"

typedef NS_ENUM(NSInteger, ViewState) {
    SHOW_WELCOME_SCREEN = 0,
    SHOW_CONVERSATION_SCREEN,
    SHOW_CALL_SCREEN,
    SHOW_SETTINGS_SCREEN,
};
67

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
68
@interface RingWindowController () <MigrateRingAccountsDelegate>
69 70

@property (retain) MigrateRingAccountsWC* migrateWC;
71
@property RingWizardWC* wizard;
72 73 74

@end

75
@implementation RingWindowController {
76

77 78
    __unsafe_unretained IBOutlet NSLayoutConstraint* centerYQRCodeConstraint;
    __unsafe_unretained IBOutlet NSLayoutConstraint* centerYWelcomeContainerConstraint;
79
    IBOutlet NSLayoutConstraint* ringLabelTrailingConstraint;
80
    __unsafe_unretained IBOutlet NSView* welcomeContainer;
81 82 83
    __unsafe_unretained IBOutlet NSView* callView;
    __unsafe_unretained IBOutlet NSTextField* ringIDLabel;
    __unsafe_unretained IBOutlet NSButton* shareButton;
84
    __unsafe_unretained IBOutlet NSImageView* qrcodeView;
Alexandre Lision's avatar
Alexandre Lision committed
85

86
    PreferencesWC* preferencesWC;
87
    IBOutlet SmartViewVC* smartViewVC;
88

89
    CurrentCallVC* currentCallVC;
90
    ConversationVC* conversationVC;
91
    AccountSettingsVC* settingsVC;
92

93
    // toolbar menu items
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
94
    IBOutlet ChooseAccountVC* chooseAccountVC;
95
}
Alexandre Lision's avatar
Alexandre Lision committed
96

97 98
static NSString* const kPreferencesIdentifier        = @"PreferencesIdentifier";
NSString* const kChangeAccountToolBarItemIdentifier  = @"ChangeAccountToolBarItemIdentifier";
99
NSString* const kOpenAccountToolBarItemIdentifier    = @"OpenAccountToolBarItemIdentifier";
100

101
@synthesize dataTransferModel, accountModel, behaviorController;
102
@synthesize wizard;
103 104 105 106 107 108 109 110 111 112 113 114

-(id) initWithWindowNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil accountModel:( lrc::api::NewAccountModel*)accountModel dataTransferModel:( lrc::api::DataTransferModel*)dataTransferModel behaviourController:( lrc::api::BehaviorController*) behaviorController
{
    if (self =  [self initWithWindowNibName:nibNameOrNil])
    {
        self.accountModel = accountModel;
        self.dataTransferModel = dataTransferModel;
        self.behaviorController = behaviorController;
    }
    return self;
}

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
115 116 117 118 119 120 121
- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
{
    return (NSApplicationPresentationFullScreen |
            NSApplicationPresentationAutoHideMenuBar |
            NSApplicationPresentationAutoHideToolbar);
}

122 123 124 125 126 127
-(void)changeViewTo:(ViewState) state  {
    switch (state) {
        case SHOW_WELCOME_SCREEN:
            [self accountSettingsShouldOpen: NO];
            [conversationVC hideWithAnimation:false];
            [currentCallVC hideWithAnimation:false];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
128
            [currentCallVC cleanUp];
129 130 131 132 133 134 135 136 137
            [currentCallVC.view removeFromSuperview];
            [welcomeContainer setHidden: NO];
            [smartViewVC.view setHidden: NO];
            [settingsVC hide];
            break;
        case SHOW_CONVERSATION_SCREEN:
            [self accountSettingsShouldOpen: NO];
            [conversationVC showWithAnimation:false];
            [currentCallVC hideWithAnimation:false];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
138
            [currentCallVC cleanUp];
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
            [currentCallVC.view removeFromSuperview];
            [welcomeContainer setHidden: YES];
            [smartViewVC.view setHidden: NO];
            [settingsVC hide];
            break;
        case SHOW_CALL_SCREEN:
            [self accountSettingsShouldOpen: NO];
            if (![currentCallVC.view superview]) {
            [callView addSubview:[currentCallVC view] positioned:NSWindowAbove relativeTo:nil];
            [currentCallVC initFrame];
            [currentCallVC showWithAnimation:false];
            [conversationVC hideWithAnimation:false];
            [welcomeContainer setHidden: YES];
            [smartViewVC.view setHidden: NO];
            [settingsVC hide];
            }
            [currentCallVC showWithAnimation:false];
            break;
        case SHOW_SETTINGS_SCREEN:
            @try {
                [self accountSettingsShouldOpen: YES];
            }
            @catch (NSException *ex) {
                return;
            }
            [welcomeContainer setHidden: YES];
            [currentCallVC hideWithAnimation:false];
            [currentCallVC.view removeFromSuperview];
            [conversationVC hideWithAnimation:false];
            [smartViewVC.view setHidden: YES];
            [settingsVC show];
            break;
        default:
            break;
    }
}

176 177
- (void)windowDidLoad {
    [super windowDidLoad];
178
    [self.window setMovableByWindowBackground:YES];
Alexandre Lision's avatar
Alexandre Lision committed
179

180
    self.window.titleVisibility = NSWindowTitleHidden;
181

182
    currentCallVC = [[CurrentCallVC alloc] initWithNibName:@"CurrentCall" bundle:nil];
183
    currentCallVC.delegate = self;
184
    conversationVC = [[ConversationVC alloc] initWithNibName:@"Conversation" bundle:nil delegate:self];
185
    // toolbar items
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
186 187
    //chooseAccountVC = [[ChooseAccountVC alloc] initWithNibName:@"ChooseAccount" bundle:nil model:self.accountModel delegate:self];
    [chooseAccountVC updateWithDelegate: self andModel:self.accountModel];
188
    settingsVC = [[AccountSettingsVC alloc] initWithNibName:@"AccountSettings" bundle:nil accountmodel:self.accountModel];
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
189
    //[self.window.contentView addSubview:[chooseAccountVC view]];
Alexandre Lision's avatar
Alexandre Lision committed
190
    [callView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
191
    [[currentCallVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
192
    [[conversationVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
193
    [[settingsVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
Alexandre Lision's avatar
Alexandre Lision committed
194

195
    [callView addSubview:[conversationVC view] positioned:NSWindowAbove relativeTo:nil];
196
    [self.window.contentView addSubview:[settingsVC view] positioned:NSWindowAbove relativeTo:nil];
Alexandre Lision's avatar
Alexandre Lision committed
197

198
    [conversationVC initFrame];
199
    [settingsVC initFrame];
200 201 202 203 204 205
    @try {
        [smartViewVC setConversationModel: [chooseAccountVC selectedAccount].conversationModel.get()];
    }
    @catch (NSException *ex) {
        NSLog(@"Caught exception %@: %@", [ex name], [ex reason]);
    }
206

207 208 209
    // Fresh run, we need to make sure RingID appears
    [shareButton sendActionOn:NSLeftMouseDownMask];

210
    [self connect];
211
    [self updateRingID];
212
    // set download folder (default - 'Documents')
213 214
    NSString* path = [[NSUserDefaults standardUserDefaults] stringForKey:Preferences::DownloadFolder];
    if (!path || path.length == 0) {
215 216
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        path = [[paths objectAtIndex:0] stringByAppendingString:@"/"];
217
    }
218
    self.dataTransferModel->downloadDirectory = std::string([path UTF8String]);
219 220
    if(appSandboxed()) {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
221
        media::RecordingModel::instance().setRecordPath(QString::fromNSString([paths objectAtIndex:0]));
222
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
223 224
    NSToolbar *tb = [[self window] toolbar];
    [tb setAllowsUserCustomization:NO];
225 226 227 228
}

- (void) connect
{
229
    QObject::connect(self.behaviorController,
230 231 232
                     &lrc::api::BehaviorController::showCallView,
                     [self](const std::string accountId,
                            const lrc::api::conversation::Info convInfo){
233
                         auto* accInfo = &self.accountModel->getAccountInfo(accountId);
234 235 236 237 238
                         if (accInfo->contactModel->getContact(convInfo.participants[0]).profileInfo.type == lrc::api::profile::Type::PENDING)
                             [smartViewVC selectPendingList];
                         else
                             [smartViewVC selectConversationList];

239 240 241
                         [currentCallVC setCurrentCall:convInfo.callId
                                          conversation:convInfo.uid
                                               account:accInfo];
242 243
                         [self changeViewTo:SHOW_CALL_SCREEN];

244
                     });
245

246
    QObject::connect(self.behaviorController,
247 248 249
                     &lrc::api::BehaviorController::showIncomingCallView,
                     [self](const std::string accountId,
                            const lrc::api::conversation::Info convInfo){
250
                         auto* accInfo = &self.accountModel->getAccountInfo(accountId);
251 252 253 254 255
                         if (accInfo->contactModel->getContact(convInfo.participants[0]).profileInfo.type == lrc::api::profile::Type::PENDING)
                             [smartViewVC selectPendingList];
                         else
                             [smartViewVC selectConversationList];

256 257 258
                         [currentCallVC setCurrentCall:convInfo.callId
                                          conversation:convInfo.uid
                                               account:accInfo];
259
                         [smartViewVC selectConversation: convInfo model:accInfo->conversationModel.get()];
260
                         [self changeViewTo:SHOW_CALL_SCREEN];
261
                     });
262

263
    QObject::connect(self.behaviorController,
264 265 266
                     &lrc::api::BehaviorController::showChatView,
                     [self](const std::string& accountId,
                            const lrc::api::conversation::Info& convInfo){
267
                         auto& accInfo = self.accountModel->getAccountInfo(accountId);
268
                         [conversationVC setConversationUid:convInfo.uid model:accInfo.conversationModel.get()];
269
                         [smartViewVC selectConversation: convInfo model:accInfo.conversationModel.get()];
270
                         [self changeViewTo:SHOW_CONVERSATION_SCREEN];
271
                     });
272 273 274 275 276 277 278 279
}

/**
 * Implement the necessary logic to choose which Ring ID to display.
 * This tries to choose the "best" ID to show
 */
- (void) updateRingID
{
280 281 282 283 284 285
    @try {
        auto& account = [chooseAccountVC selectedAccount];

        [ringIDLabel setStringValue:@""];

        if(account.profileInfo.type != lrc::api::profile::Type::RING) {
286 287
            self.notRingAccount = YES;
            self.isSIPAccount = YES;
288 289
            return;
        }
290 291 292
        self.isSIPAccount = NO;
        self.notRingAccount = NO;
        [ringLabelTrailingConstraint setActive:YES];
293 294 295 296 297 298 299 300 301 302
        auto& registeredName = account.registeredName;
        auto& ringID = account.profileInfo.uri;
        NSString* uriToDisplay = nullptr;
        if (!registeredName.empty()) {
            uriToDisplay = @(registeredName.c_str());
        } else {
            uriToDisplay = @(ringID.c_str());
        }
        [ringIDLabel setStringValue:uriToDisplay];
        [self drawQRCode:@(ringID.c_str())];
303
    }
304 305
    @catch (NSException *ex) {
        NSLog(@"Caught exception %@: %@", [ex name], [ex reason]);
306 307 308
        self.notRingAccount = YES;
        self.isSIPAccount = NO;
        [ringLabelTrailingConstraint setActive:NO];
309
        [ringIDLabel setStringValue:NSLocalizedString(@"No account available", @"Displayed as RingID when no accounts are available for selection")];
310
    }
311 312
}

313 314
- (IBAction)shareRingID:(id)sender {
    NSSharingServicePicker* sharingServicePicker = [[NSSharingServicePicker alloc] initWithItems:[NSArray arrayWithObject:[ringIDLabel stringValue]]];
315
    [sharingServicePicker setDelegate:self];
316 317 318 319 320
    [sharingServicePicker showRelativeToRect:[sender bounds]
                                      ofView:sender
                               preferredEdge:NSMinYEdge];
}

321 322 323
- (IBAction)toggleQRCode:(id)sender {
    // Toggle pressed state of QRCode button
    [sender setPressed:![sender isPressed]];
324
    [self showQRCode:[sender isPressed]];
325 326 327 328 329
}

/**
 * Draw the QRCode in the qrCodeView
 */
330
- (void)drawQRCode:(NSString*) uriToDraw
331
{
332
    auto qrCode = QRcode_encodeString(uriToDraw.UTF8String,
333 334 335 336 337 338 339 340
                                      0,
                                      QR_ECLEVEL_L, // Lowest level of error correction
                                      QR_MODE_8, // 8-bit data mode
                                      1);
    if (!qrCode) {
        return;
    }

341 342 343 344 345 346
    unsigned char *data = 0;
    int width;
    data = qrCode->data;
    width = qrCode->width;
    int qr_margin = 3;

347 348 349 350 351 352 353 354 355 356
    CGFloat size = qrcodeView.frame.size.width;

    // create context
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef ctx = CGBitmapContextCreate(0, size, size, 8, size * 4, colorSpace, kCGImageAlphaPremultipliedLast);

    CGAffineTransform translateTransform = CGAffineTransformMakeTranslation(0, -size);
    CGAffineTransform scaleTransform = CGAffineTransformMakeScale(1, -1);
    CGContextConcatCTM(ctx, CGAffineTransformConcat(translateTransform, scaleTransform));

357
    float zoom = ceil((double)size / (qrCode->width + 2.0 * qr_margin));
358 359 360 361 362 363
    CGRect rectDraw = CGRectMake(0, 0, zoom, zoom);

    int ran;
    for(int i = 0; i < width; ++i) {
        for(int j = 0; j < width; ++j) {
            if(*data & 1) {
364
                CGContextSetFillColorWithColor(ctx, [NSColor labelColor].CGColor);
365 366 367 368 369 370 371 372 373 374 375 376
                rectDraw.origin = CGPointMake((j + qr_margin) * zoom,(i + qr_margin) * zoom);
                CGContextAddRect(ctx, rectDraw);
                CGContextFillPath(ctx);
            } else {
                CGContextSetFillColorWithColor(ctx, [NSColor windowBackgroundColor].CGColor);
                rectDraw.origin = CGPointMake((j + qr_margin) * zoom,(i + qr_margin) * zoom);
                CGContextAddRect(ctx, rectDraw);
                CGContextFillPath(ctx);
            }
            ++data;
        }
    }
377 378 379 380 381 382 383 384 385 386 387 388

    // get image
    auto qrCGImage = CGBitmapContextCreateImage(ctx);
    auto qrImage = [[NSImage alloc] initWithCGImage:qrCGImage size:qrcodeView.frame.size];

    // some releases
    CGContextRelease(ctx);
    CGImageRelease(qrCGImage);
    CGColorSpaceRelease(colorSpace);
    QRcode_free(qrCode);

    [qrcodeView setImage:qrImage];
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
}

/**
 * Start the in/out animation displaying the QRCode
 * @param show should the QRCode be animated in or out
 */
- (void) showQRCode:(BOOL) show
{
    static const NSInteger offset = 110;
    [NSAnimationContext beginGrouping];
    NSAnimationContext.currentContext.duration = 0.5;
    [[NSAnimationContext currentContext] setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionDefault]];
    qrcodeView.animator.alphaValue = show ? 1.0 : 0.0;
    [centerYQRCodeConstraint.animator setConstant: show ? offset : 0];
    [centerYWelcomeContainerConstraint.animator setConstant:show ? -offset : 0];
    [NSAnimationContext endGrouping];
}

407 408
- (IBAction)openPreferences:(id)sender
{
409 410 411 412
    if (preferencesWC) {
        [preferencesWC.window orderFront:preferencesWC.window];
        return;
    }
413 414

    preferencesWC = [[PreferencesWC alloc] initWithWindowNibName: @"PreferencesWindow" bundle: nil accountModel:self.accountModel dataTransferModel:self.dataTransferModel behaviourController:self.behaviorController];
415
    [preferencesWC.window makeKeyAndOrderFront:preferencesWC.window];
416
}
417

418 419 420 421 422 423 424 425 426 427 428 429
- (IBAction)callClickedAtRow:(id)sender
{
    NSTabViewItem *selectedTab = [smartViewVC.tabbar selectedTabViewItem];
    int index = [smartViewVC.tabbar indexOfTabViewItem:selectedTab];
    switch (index) {
        case 0:
            [smartViewVC startCallForRow:sender];
            break;
        default:
            break;
    }
}
430

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447
#pragma mark - Ring account migration

- (void) migrateRingAccount:(Account*) acc
{
    self.migrateWC = [[MigrateRingAccountsWC alloc] initWithDelegate:self actionCode:1];
    self.migrateWC.account = acc;
#if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_9
    [self.window beginSheet:self.migrateWC.window completionHandler:nil];
#else
    [NSApp beginSheet: self.migrateWC.window
       modalForWindow: self.window
        modalDelegate: self
       didEndSelector: nil
          contextInfo: nil];
#endif
}

448
// TODO: Reimplement as a blocking loop when new LRC models handle migration
449 450 451 452 453 454 455 456 457 458 459
- (void)checkAccountsToMigrate
{
    auto ringList = AccountModel::instance().accountsToMigrate();
    if (ringList.length() > 0){
        Account* acc = ringList.value(0);
        [self migrateRingAccount:acc];
    } else {
        // Fresh run, we need to make sure RingID appears
        [shareButton sendActionOn:NSLeftMouseDownMask];

        [self connect];
460
        [self updateRingID];
461 462 463 464 465 466 467 468 469 470 471 472 473
    }
}

- (void)migrationDidComplete
{
    [self checkAccountsToMigrate];
}

- (void)migrationDidCompleteWithError
{
    [self checkAccountsToMigrate];
}

474
- (void) selectAccount:(const lrc::api::account::Info&)accInfo currentRemoved:(BOOL) removed
475 476
{
    // If the selected account has been changed, we close any open panel
477
    [smartViewVC setConversationModel:accInfo.conversationModel.get()];
478 479
    // Welcome view informations are also updated
    [self updateRingID];
480
    [settingsVC setSelectedAccount:accInfo.id];
481
    [self changeViewTo: ([settingsVC.view isHidden] || removed)  ?  SHOW_WELCOME_SCREEN : SHOW_SETTINGS_SCREEN];
482 483
}

484 485 486
-(void)allAccountsDeleted
{
    [smartViewVC clearConversationModel];
487
    [self changeViewTo:SHOW_WELCOME_SCREEN];
488 489 490
    [self updateRingID];
}

491 492 493
-(void)rightPanelClosed
{
    [smartViewVC deselect];
494
    [welcomeContainer setHidden:NO];
495 496
}

497 498 499 500 501
-(void)currentConversationTrusted
{
    [smartViewVC selectConversationList];
}

502
-(void) listTypeChanged {
503
    [self changeViewTo:SHOW_WELCOME_SCREEN];
504 505
}

506 507
- (IBAction)openAccountSettings:(NSButton *)sender
{
508
    [self changeViewTo: [settingsVC.view isHidden] ?  SHOW_SETTINGS_SCREEN : SHOW_WELCOME_SCREEN];
509 510
}

511
- (void) createNewAccount {
512
    [self changeViewTo:SHOW_WELCOME_SCREEN];
513 514 515
    wizard = [[RingWizardWC alloc] initWithNibName:@"RingWizard" bundle: nil accountmodel: self.accountModel];
    [wizard showChooseWithCancelButton: YES andAdvanced: YES];
    [self.window beginSheet:wizard.window completionHandler:nil];
516 517
}

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
518 519 520 521 522 523 524 525
- (NSRect)window:(NSWindow *)window willPositionSheet:(NSWindow *)sheet
       usingRect:(NSRect)rect
{
    float titleBarHeight = self.window.frame.size.height - [NSWindow contentRectForFrameRect:self.window.frame styleMask:self.window.styleMask].size.height;
    rect.origin.y = self.window.frame.size.height;
    return rect;
}

526 527
-(void) accountSettingsShouldOpen: (BOOL) open {
    if (open) {
528
        [settingsVC setSelectedAccount: [chooseAccountVC selectedAccount].id];
529
    }
530 531
}

532 533 534 535 536 537
#pragma mark - CallViewControllerDelegate

-(void) conversationInfoUpdatedFor:(const std::string&) conversationID {
    [smartViewVC reloadConversationWithUid:@(conversationID.c_str())];
}

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590
-(void) showConversation:(NSString* )conversationId forAccount:(NSString*)accountId {
    auto& accInfo = self.accountModel->getAccountInfo([accountId UTF8String]);
    [chooseAccountVC selectAccount: accountId];
    [settingsVC setSelectedAccount: [accountId UTF8String]];
    [smartViewVC setConversationModel:accInfo.conversationModel.get()];
    [smartViewVC selectConversationList];
    [self updateRingID];
    auto convInfo = getConversationFromUid([conversationId UTF8String], *accInfo.conversationModel.get());
    auto convQueue = accInfo.conversationModel.get()->allFilteredConversations();
    if (convInfo != convQueue.end()) {
        [conversationVC setConversationUid:convInfo->uid model:accInfo.conversationModel.get()];
        [smartViewVC selectConversation: *convInfo model:accInfo.conversationModel.get()];
        accInfo.conversationModel.get()->clearUnreadInteractions([conversationId UTF8String]);
    }
    [self changeViewTo:SHOW_CONVERSATION_SCREEN];
}

-(void) showCall:(NSString* )callId forAccount:(NSString*)accountId forConversation:(NSString*)conversationId {
    auto& accInfo = self.accountModel->getAccountInfo([accountId UTF8String]);
    [chooseAccountVC selectAccount: accountId];
    [settingsVC setSelectedAccount:accInfo.id];
    [smartViewVC setConversationModel:accInfo.conversationModel.get()];
    [self updateRingID];
    auto convInfo = getConversationFromUid([conversationId UTF8String], *accInfo.conversationModel.get());
    auto convQueue = accInfo.conversationModel.get()->allFilteredConversations();
    if (convInfo != convQueue.end()) {
        if (accInfo.contactModel->getContact(convInfo->participants[0]).profileInfo.type == lrc::api::profile::Type::PENDING)
            [smartViewVC selectPendingList];
        else
            [smartViewVC selectConversationList];
        [smartViewVC selectConversation: *convInfo model:accInfo.conversationModel.get()];
    }
    [currentCallVC setCurrentCall:[callId UTF8String]
                     conversation:[conversationId UTF8String]
                          account:&accInfo];
    [self changeViewTo:SHOW_CALL_SCREEN];
}

-(void) showContactRequestFor:(NSString* )accountId contactUri:(NSString*)uri {
    auto& accInfo = self.accountModel->getAccountInfo([accountId UTF8String]);
    [chooseAccountVC selectAccount: accountId];
    [settingsVC setSelectedAccount:accInfo.id];
    [smartViewVC setConversationModel:accInfo.conversationModel.get()];
    [self updateRingID];
    [smartViewVC selectPendingList];
    auto convInfo = getConversationFromURI([uri UTF8String], *accInfo.conversationModel.get());
    auto convQueue = accInfo.conversationModel.get()->allFilteredConversations();
    if (convInfo != convQueue.end()) {
        [conversationVC setConversationUid:convInfo->uid model:accInfo.conversationModel.get()];
        [smartViewVC selectConversation: *convInfo model:accInfo.conversationModel.get()];
    }
    [self changeViewTo:SHOW_CONVERSATION_SCREEN];
}
591
@end