Commit 58cab671 authored by Alexandre Lision's avatar Alexandre Lision

osx: add chat support

This commit adds a collapsible right panel for incall chatting

Refs #74037

Change-Id: I82c53174c1dbdeb1cfdb4ab8a773256aa182b70c
parent 4198197b
......@@ -68,7 +68,9 @@ SET(ringclient_CONTROLLERS
src/HistoryVC.mm
src/HistoryVC.h
src/PersonsVC.mm
src/PersonsVC.h)
src/PersonsVC.h
src/ChatVC.mm
src/ChatVC.h)
SET(ringclient_BACKENDS
src/backends/AddressBookBackend.mm
......
/*
* Copyright (C) 2015 Savoir-Faire Linux Inc.
* 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.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#import <Cocoa/Cocoa.h>
@interface ChatVC : NSViewController <NSTextFieldDelegate>
/**
* Message contained in messageField TextField.
* This is a KVO method to bind the text with the send Button
* if message.length is > 0, button is enabled, otherwise disabled
*/
@property (retain) NSString* message;
@end
/*
* Copyright (C) 2015 Savoir-Faire Linux Inc.
* 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.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this program, or any covered work, by linking or
* combining it with the OpenSSL project's OpenSSL library (or a
* modified version of that library), containing parts covered by the
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
* grants you additional permission to convey the resulting work.
* Corresponding Source for a non-source form of such a combination
* shall include the source code for the parts of OpenSSL used as well
* as that of the covered work.
*/
#import "ChatVC.h"
#import <QItemSelectionModel>
#import <qstring.h>
#import <media/media.h>
#import <media/text.h>
#import <media/textrecording.h>
#import <callmodel.h>
@interface MediaConnectionsHolder : NSObject
@property QMetaObject::Connection newMediaAdded;
@property QMetaObject::Connection newMessage;
@end
@implementation MediaConnectionsHolder
@end
@interface ChatVC ()
@property (unsafe_unretained) IBOutlet NSTextView *chatView;
@property (unsafe_unretained) IBOutlet NSTextField *messageField;
@property (unsafe_unretained) IBOutlet NSButton *sendButton;
@property MediaConnectionsHolder* mediaHolder;
@end
@implementation ChatVC
@synthesize messageField,chatView,sendButton, mediaHolder;
- (void)awakeFromNib
{
NSLog(@"Init ChatVC");
[self.view setWantsLayer:YES];
[self.view setLayer:[CALayer layer]];
[self.view.layer setBackgroundColor:[NSColor blackColor].CGColor];
mediaHolder = [[MediaConnectionsHolder alloc] init];
QObject::connect(CallModel::instance()->selectionModel(),
&QItemSelectionModel::currentChanged,
[=](const QModelIndex &current, const QModelIndex &previous) {
[self setupChat];
});
}
- (void) setupChat
{
QObject::disconnect(mediaHolder.newMediaAdded);
QObject::disconnect(mediaHolder.newMessage);
QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex();
if (!callIdx.isValid())
return;
Call* call = CallModel::instance()->getCall(callIdx);
/* check if text media is already present */
if (call->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::IN)) {
Media::Text *text = call->firstMedia<Media::Text>(Media::Media::Direction::IN);
[self parseChatModel:text->recording()->instantMessagingModel()];
} else if (call->hasMedia(Media::Media::Type::TEXT, Media::Media::Direction::OUT)) {
Media::Text *text = call->firstMedia<Media::Text>(Media::Media::Direction::OUT);
[self parseChatModel:text->recording()->instantMessagingModel()];
} else {
/* monitor media for messaging text messaging */
mediaHolder.newMediaAdded = QObject::connect(call,
&Call::mediaAdded,
[self] (Media::Media* media) {
if (media->type() == Media::Media::Type::TEXT) {
QObject::disconnect(mediaHolder.newMediaAdded);
[self parseChatModel:((Media::Text*)media)->recording()->instantMessagingModel()];
}
});
}
}
- (void) parseChatModel:(QAbstractItemModel *)model
{
QObject::disconnect(mediaHolder.newMessage);
[self.messageField setStringValue:@""];
self.message = @"";
[self.chatView.textStorage.mutableString setString:@""];
/* put all the messages in the im model into the text view */
for (int row = 0; row < model->rowCount(); ++row) {
[self appendNewMessage:model->index(row, 0)];
}
/* append new messages */
mediaHolder.newMessage = QObject::connect(model,
&QAbstractItemModel::rowsInserted,
[self, model] (const QModelIndex &parent, int first, int last) {
for (int row = first; row <= last; ++row) {
QModelIndex idx = model->index(row, 0, parent);
[self appendNewMessage:idx];
}
}
);
}
- (void) appendNewMessage:(const QModelIndex&) msgIdx
{
if (!msgIdx.isValid())
return;
QVariant message = msgIdx.data();
NSAttributedString* attr = [[NSAttributedString alloc] initWithString:
[NSString stringWithFormat:@"%@\n",message.value<QString>().toNSString()]];
[[chatView textStorage] appendAttributedString:attr];
[chatView scrollRangeToVisible:NSMakeRange([[chatView string] length], 0)];
}
- (IBAction)sendMessage:(id)sender {
QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex();
Call* call = CallModel::instance()->getCall(callIdx);
/* make sure there is text to send */
NSString* text = self.message;
if (text && text.length > 0) {
call->addOutgoingMedia<Media::Text>()->send(QString::fromNSString(text));
// Empty the text after sending it
[self.messageField setStringValue:@""];
self.message = @"";
}
}
#pragma mark - NSTextFieldDelegate
- (BOOL)control:(NSControl *)control textView:(NSTextView *)fieldEditor doCommandBySelector:(SEL)commandSelector
{
if (commandSelector == @selector(insertNewline:) && self.message.length > 0) {
[self sendMessage:nil];
return YES;
}
return NO;
}
@end
......@@ -32,14 +32,10 @@
#import <callmodel.h>
#import <QtCore/qitemselectionmodel.h>
#import "CurrentCallVC.h"
#define COLUMNID_CONVERSATIONS @"ConversationsColumn" // the single column name in our outline view
@interface ConversationsVC ()
@property CurrentCallVC* currentVC;
@property (assign) IBOutlet NSView *currentCallView;
@property QNSTreeController *treeController;
@property (assign) IBOutlet NSOutlineView *conversationsView;
......@@ -48,9 +44,6 @@
@implementation ConversationsVC
@synthesize conversationsView;
@synthesize treeController;
@synthesize currentVC;
@synthesize currentCallView;
- (void)awakeFromNib
{
NSLog(@"INIT Conversations VC");
......@@ -73,15 +66,7 @@
[conversationsView reloadDataForRowIndexes:
[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(topLeft.row(), bottomRight.row() + 1)]
columnIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(0, conversationsView.tableColumns.count)]];
});
currentVC = [[CurrentCallVC alloc] initWithNibName:@"CurrentCall" bundle:nil];
[currentCallView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[[currentVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[currentCallView addSubview:[self.currentVC view]];
[currentVC initFrame];
}
#pragma mark - NSOutlineViewDelegate methods
......
......@@ -32,9 +32,11 @@
#import <Cocoa/Cocoa.h>
#import "views/CallView.h"
class Call;
@interface CurrentCallVC : NSViewController {
@interface CurrentCallVC : NSViewController <NSSplitViewDelegate, FullScreenDelegate> {
}
......
......@@ -40,11 +40,10 @@
#import <QItemSelection>
#import <video/previewmanager.h>
#import <video/renderer.h>
#import <media/text.h>
#import "views/CallView.h"
/** FrameReceiver class - delegate for AVCaptureSession
*/
@interface RendererConnectionsHolder : NSObject
@property QMetaObject::Connection frameUpdated;
......@@ -59,21 +58,23 @@
@interface CurrentCallVC ()
@property (assign) IBOutlet NSTextField *personLabel;
@property (assign) IBOutlet NSTextField *stateLabel;
@property (assign) IBOutlet NSButton *holdOnOffButton;
@property (assign) IBOutlet NSButton *hangUpButton;
@property (assign) IBOutlet NSButton *recordOnOffButton;
@property (assign) IBOutlet NSButton *pickUpButton;
@property (assign) IBOutlet NSTextField *timeSpentLabel;
@property (assign) IBOutlet NSView *controlsPanel;
@property (unsafe_unretained) IBOutlet NSTextField *personLabel;
@property (unsafe_unretained) IBOutlet NSTextField *stateLabel;
@property (unsafe_unretained) IBOutlet NSButton *holdOnOffButton;
@property (unsafe_unretained) IBOutlet NSButton *hangUpButton;
@property (unsafe_unretained) IBOutlet NSButton *recordOnOffButton;
@property (unsafe_unretained) IBOutlet NSButton *pickUpButton;
@property (unsafe_unretained) IBOutlet NSTextField *timeSpentLabel;
@property (unsafe_unretained) IBOutlet NSView *controlsPanel;
@property (unsafe_unretained) IBOutlet NSSplitView *splitView;
@property (unsafe_unretained) IBOutlet NSButton *chatButton;
@property QHash<int, NSButton*> actionHash;
// Video
@property (assign) IBOutlet CallView *videoView;
@property (unsafe_unretained) IBOutlet CallView *videoView;
@property CALayer* videoLayer;
@property (assign) IBOutlet NSView *previewView;
@property (unsafe_unretained) IBOutlet NSView *previewView;
@property CALayer* previewLayer;
@property RendererConnectionsHolder* previewHolder;
......@@ -90,12 +91,14 @@
@synthesize hangUpButton;
@synthesize recordOnOffButton;
@synthesize pickUpButton;
@synthesize chatButton;
@synthesize timeSpentLabel;
@synthesize controlsPanel;
@synthesize videoView;
@synthesize videoLayer;
@synthesize previewLayer;
@synthesize previewView;
@synthesize splitView;
@synthesize previewHolder;
@synthesize videoHolder;
......@@ -128,10 +131,10 @@
-(void) updateCall
{
QModelIndex callIdx = CallModel::instance()->selectionModel()->currentIndex();
[personLabel setStringValue:CallModel::instance()->data(callIdx, Qt::DisplayRole).toString().toNSString()];
[timeSpentLabel setStringValue:CallModel::instance()->data(callIdx, (int)Call::Role::Length).toString().toNSString()];
[personLabel setStringValue:callIdx.data(Qt::DisplayRole).toString().toNSString()];
[timeSpentLabel setStringValue:callIdx.data((int)Call::Role::Length).toString().toNSString()];
Call::State state = CallModel::instance()->data(callIdx, (int)Call::Role::State).value<Call::State>();
Call::State state = callIdx.data((int)Call::Role::State).value<Call::State>();
switch (state) {
case Call::State::DIALING:
......@@ -221,6 +224,8 @@
previewHolder = [[RendererConnectionsHolder alloc] init];
videoHolder = [[RendererConnectionsHolder alloc] init];
[self.videoView setFullScreenDelegate:self];
[self connect];
}
......@@ -233,6 +238,7 @@
[self animateOut];
return;
}
[self collapseRightView];
[self updateCall];
[self updateAllActions];
[self animateOut];
......@@ -264,7 +270,7 @@
{
QModelIndex idx = CallModel::instance()->selectionModel()->currentIndex();
Call* call = CallModel::instance()->getCall(idx);
QObject::connect(call,
self.videoStarted = QObject::connect(call,
&Call::videoStarted,
[=](Video::Renderer* renderer) {
NSLog(@"Video started!");
......@@ -453,8 +459,37 @@
NSLog(@"frame %@ : %f %f %f %f \n\n",name ,frame.origin.x, frame.origin.y, frame.size.width, frame.size.height);
}
-(void)collapseRightView
{
NSView *right = [[splitView subviews] objectAtIndex:1];
NSView *left = [[splitView subviews] objectAtIndex:0];
NSRect leftFrame = [left frame];
[right setHidden:YES];
[splitView display];
}
-(void)uncollapseRightView
{
NSView *left = [[splitView subviews] objectAtIndex:0];
NSView *right = [[splitView subviews] objectAtIndex:1];
[right setHidden:NO];
CGFloat dividerThickness = [splitView dividerThickness];
// get the different frames
NSRect leftFrame = [left frame];
NSRect rightFrame = [right frame];
leftFrame.size.width = (leftFrame.size.width - rightFrame.size.width - dividerThickness);
rightFrame.origin.x = leftFrame.size.width + dividerThickness;
[left setFrameSize:leftFrame.size];
[right setFrame:rightFrame];
[splitView display];
}
#pragma mark - Button methods
#pragma button methods
- (IBAction)hangUp:(id)sender {
CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::REFUSE;
}
......@@ -471,4 +506,50 @@
CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex()) << Call::Action::HOLD;
}
-(IBAction)toggleChat:(id)sender;
{
BOOL rightViewCollapsed = [[self splitView] isSubviewCollapsed:[[[self splitView] subviews] objectAtIndex: 1]];
if (rightViewCollapsed) {
[self uncollapseRightView];
CallModel::instance()->getCall(CallModel::instance()->selectionModel()->currentIndex())->addOutgoingMedia<Media::Text>();
} else {
[self collapseRightView];
}
[chatButton setState:rightViewCollapsed];
}
#pragma mark - NSSplitViewDelegate
/* Return YES if the subview should be collapsed because the user has double-clicked on an adjacent divider. If a split view has a delegate, and the delegate responds to this message, it will be sent once for the subview before a divider when the user double-clicks on that divider, and again for the subview after the divider, but only if the delegate returned YES when sent -splitView:canCollapseSubview: for the subview in question. When the delegate indicates that both subviews should be collapsed NSSplitView's behavior is undefined.
*/
- (BOOL)splitView:(NSSplitView *)splitView shouldCollapseSubview:(NSView *)subview forDoubleClickOnDividerAtIndex:(NSInteger)dividerIndex;
{
NSView* rightView = [[splitView subviews] objectAtIndex:1];
return ([subview isEqual:rightView]);
}
- (BOOL)splitView:(NSSplitView *)splitView canCollapseSubview:(NSView *)subview;
{
NSView* rightView = [[splitView subviews] objectAtIndex:1];
return ([subview isEqual:rightView]);
}
# pragma mark - FullScreenDelegate
- (void) callShouldToggleFullScreen
{
if(self.splitView.isInFullScreenMode)
[self.splitView exitFullScreenModeWithOptions:nil];
else {
NSApplicationPresentationOptions options = NSApplicationPresentationDefault +NSApplicationPresentationAutoHideDock +
NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options],
NSFullScreenModeApplicationPresentationOptions, nil];
[self.splitView enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
}
}
@end
......@@ -36,15 +36,22 @@
#import "AppDelegate.h"
#import "Constants.h"
#import "CurrentCallVC.h"
@interface RingWindowController ()
@property NSSearchField* callField;
@property CurrentCallVC* currentVC;
@property (unsafe_unretained) IBOutlet NSView *callView;
@end
@implementation RingWindowController
@synthesize callField;
@synthesize currentVC;
@synthesize callView;
static NSString* const kSearchViewIdentifier = @"SearchViewIdentifier";
static NSString* const kPreferencesIdentifier = @"PreferencesIdentifier";
static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier";
......@@ -53,6 +60,13 @@ static NSString* const kCallButtonIdentifer = @"CallButtonIdentifier";
[super windowDidLoad];
[self.window setReleasedWhenClosed:FALSE];
[self displayMainToolBar];
currentVC = [[CurrentCallVC alloc] initWithNibName:@"CurrentCall" bundle:nil];
[callView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[[currentVC view] setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
[callView addSubview:[self.currentVC view]];
[currentVC initFrame];
}
- (IBAction)openPreferences:(id)sender
......
......@@ -29,6 +29,15 @@
*/
#import <Cocoa/Cocoa.h>
@protocol FullScreenDelegate;
@protocol FullScreenDelegate
@optional
-(void) callShouldToggleFullScreen;
@end
@interface CallView : NSView <NSDraggingDestination, NSOpenSavePanelDelegate>
{
//highlight the drop zone
......@@ -42,4 +51,9 @@
*/
@property BOOL shouldAcceptInteractions;
/**
* Delegate to inform about desire to move
*/
@property (nonatomic) id <FullScreenDelegate> fullScreenDelegate;
@end
......@@ -186,15 +186,8 @@
else if([theEvent clickCount] == 2)
{
[NSObject cancelPreviousPerformRequestsWithTarget:self]; // cancel showContextualMenu
if(self.isInFullScreenMode)
[self exitFullScreenModeWithOptions:nil];
else {
NSApplicationPresentationOptions options = NSApplicationPresentationDefault + NSApplicationPresentationAutoHideDock +
NSApplicationPresentationAutoHideMenuBar + NSApplicationPresentationAutoHideToolbar;
NSDictionary *opts = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithUnsignedInt:options], NSFullScreenModeApplicationPresentationOptions, nil];
[self enterFullScreenMode:[NSScreen mainScreen] withOptions:opts];
}
if(self.fullScreenDelegate)
[self.fullScreenDelegate callShouldToggleFullScreen];
}
}
......
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="6751" systemVersion="13F1066" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="7706" systemVersion="14D2134" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="6751"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="7706"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="RingWindowController">
<connections>
<outlet property="callView" destination="tSW-YT-asL" id="NRy-rM-XW4"/>
<outlet property="currentView" destination="se5-gp-TjO" id="Sae-7F-MB3"/>
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
</connections>
......@@ -32,7 +33,7 @@
<rect key="frame" x="0.0" y="0.0" width="293" height="19"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="ConversationsColumn" width="290" minWidth="40" maxWidth="1000" id="VDO-Cu-h8f">
......@@ -92,7 +93,7 @@
<rect key="frame" x="0.0" y="0.0" width="226" height="19"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="DayColumn" width="87.5703125" minWidth="40" maxWidth="1000" id="pTT-gU-NVa">
......@@ -169,7 +170,7 @@
</tabViewItem>
<tabViewItem label="Contacts" identifier="" id="Zbi-X6-DLT">
<view key="view" id="sag-tS-7Jw">
<rect key="frame" x="10" y="7" width="289" height="405"/>
<rect key="frame" x="10" y="7" width="295" height="405"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<scrollView focusRingType="none" misplaced="YES" autohidesScrollers="YES" horizontalLineScroll="48" horizontalPageScroll="10" verticalLineScroll="48" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rJv-ju-DFe">
......@@ -182,7 +183,7 @@
<rect key="frame" x="0.0" y="0.0" width="293" height="48"/>
<autoresizingMask key="autoresizingMask"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
<tableColumns>
<tableColumn identifier="NameColumn" width="289.7734375" minWidth="40" maxWidth="1000" id="8Ve-L0-o7V">
......@@ -229,8 +230,8 @@
</tabViewItem>
</tabViewItems>
</tabView>
<customView focusRingType="none" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="tSW-YT-asL">
<rect key="frame" x="323" y="0.0" width="725" height="626"/>
<customView focusRingType="none" translatesAutoresizingMaskIntoConstraints="NO" id="tSW-YT-asL">
<rect key="frame" x="323" y="0.0" width="730" height="626"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="626" id="5yo-rb-X1O"/>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="725" id="ccX-uq-zuj"/>
......@@ -263,8 +264,7 @@
<viewController id="txt-J3-pzW" customClass="ConversationsVC">
<connections>
<outlet property="conversationsView" destination="zcl-pp-rGb" id="YXp-WN-UmC"/>
<outlet property="currentCallView" destination="tSW-YT-asL" id="r2r-Q3-clc"/>
<outlet property="view" destination="tSW-YT-asL" id="fv5-ly-rk8"/>
<outlet property="view" destination="bqQ-DB-Z0g" id="nJI-EM-4z2"/>
</connections>
</viewController>
<viewController id="9RF-6W-vEW" customClass="PersonsVC">
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment