Prepare chat screen.

This commit is contained in:
giomfo
2015-08-18 10:04:30 +02:00
parent d1c2f61d58
commit e30bb8aad3
16 changed files with 1519 additions and 5 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -0,0 +1,64 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
@class MediaPickerViewController;
/**
`MediaPickerViewController` delegate.
*/
@protocol MediaPickerViewControllerDelegate <NSObject>
/**
Tells the delegate.
@param mediaPickerController the `MediaPickerViewController` instance.
@param .
*/
- (void)todo:(MediaPickerViewController *)mediaPickerController;
@end
/**
*/
@interface MediaPickerViewController : MXKViewController
/**
* Returns the `UINib` object initialized for a `MediaPickerViewController`.
*
* @return The initialized `UINib` object or `nil` if there were errors during initialization
* or the nib file could not be located.
*/
+ (UINib *)nib;
/**
* Creates and returns a new `MediaPickerViewController` object.
*
* @discussion This is the designated initializer for programmatic instantiation.
*
* @return An initialized `MediaPickerViewController` object if successful, `nil` otherwise.
*/
+ (instancetype)mediaPickerViewController;
/**
The delegate for the view controller.
*/
@property (nonatomic) id<MediaPickerViewControllerDelegate> delegate;
@end
@@ -0,0 +1,79 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "MediaPickerViewController.h"
@interface MediaPickerViewController ()
{
}
@end
@implementation MediaPickerViewController
#pragma mark - Class methods
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([MediaPickerViewController class])
bundle:[NSBundle bundleForClass:[MediaPickerViewController class]]];
}
+ (instancetype)mediaPickerViewController
{
return [[[self class] alloc] initWithNibName:NSStringFromClass([MediaPickerViewController class])
bundle:[NSBundle bundleForClass:[MediaPickerViewController class]]];
}
#pragma mark -
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)dealloc
{
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
#pragma mark - Override MXKViewController
- (void)destroy
{
[super destroy];
}
@end
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="MediaPickerViewController">
<connections>
<outlet property="view" destination="iN0-l3-epB" id="AXL-FN-kh5"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</objects>
</document>
+4 -5
View File
@@ -16,12 +16,11 @@
#import "RoomViewController.h"
#import "MXKRoomBubbleTableViewCell.h"
#import "AppDelegate.h"
#import "RageShakeManager.h"
#import "RoomInputToolbarView.h"
@interface RoomViewController ()
{
// Members list
@@ -52,8 +51,8 @@
// Set room title view
[self setRoomTitleViewClass:MXKRoomTitleViewWithTopic.class];
// Replace the default input toolbar view with the one based on `HPGrowingTextView`.
[self setRoomInputToolbarViewClass:MXKRoomInputToolbarViewWithHPGrowingText.class];
// Replace the default input toolbar view.
[self setRoomInputToolbarViewClass:RoomInputToolbarView.class];
// Set rageShake handler
self.rageShakeManager = [RageShakeManager sharedManager];
@@ -0,0 +1,24 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
/**
`RoomIncomingBubbleTableViewCell` displays incoming message bubbles.
*/
@interface RoomIncomingBubbleTableViewCell : MXKRoomIncomingBubbleTableViewCell
@end
@@ -0,0 +1,91 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RoomIncomingBubbleTableViewCell.h"
#import "NSBundle+MatrixKit.h"
#pragma mark - UI Constant definitions
#define MXKROOMBUBBLETABLEVIEWCELL_INCOMING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN -10
@implementation RoomIncomingBubbleTableViewCell
- (void)awakeFromNib
{
[super awakeFromNib];
self.typingBadge.image = [NSBundle mxk_imageFromMXKAssetsBundleWithName:@"icon_keyboard"];
}
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];
if (self.bubbleData)
{
// Check whether the previous message has been sent by the same user.
// The user's picture and name are displayed only for the first message.
// Handle sender's picture and adjust view's constraints
if (self.bubbleData.isSameSenderAsPreviousBubble)
{
self.pictureView.hidden = YES;
self.msgTextViewTopConstraint.constant = self.class.cellWithOriginalXib.msgTextViewTopConstraint.constant + MXKROOMBUBBLETABLEVIEWCELL_INCOMING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
self.attachViewTopConstraint.constant = self.class.cellWithOriginalXib.attachViewTopConstraint.constant + MXKROOMBUBBLETABLEVIEWCELL_INCOMING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
if (!self.dateTimeLabelContainer.hidden)
{
self.dateTimeLabelContainerTopConstraint.constant += MXKROOMBUBBLETABLEVIEWCELL_INCOMING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
}
}
// Display user's display name except if the name appears in the displayed text (see emote and membership event)
self.userNameLabel.hidden = (self.bubbleData.isSameSenderAsPreviousBubble || self.bubbleData.startsWithSenderName);
self.userNameLabel.text = self.bubbleData.senderDisplayName;
// Set typing badge visibility
self.typingBadge.hidden = (self.pictureView.hidden || !self.bubbleData.isTyping);
if (!self.typingBadge.hidden)
{
[self.typingBadge.superview bringSubviewToFront:self.typingBadge];
}
}
}
+ (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
{
CGFloat rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth];
MXKRoomBubbleCellData *bubbleData = (MXKRoomBubbleCellData*)cellData;
// Check whether the previous message has been sent by the same user.
// The user's picture and name are displayed only for the first message.
if (bubbleData.isSameSenderAsPreviousBubble)
{
// Reduce top margin -> row height reduction
rowHeight += MXKROOMBUBBLETABLEVIEWCELL_INCOMING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
}
else
{
// We consider a minimun cell height in order to display correctly user's picture
if (rowHeight < self.cellWithOriginalXib.frame.size.height)
{
rowHeight = self.cellWithOriginalXib.frame.size.height;
}
}
return rowHeight;
}
@end
@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6751" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="WmY-Jw-mqv" customClass="MXKRoomIncomingBubbleTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="WmY-Jw-mqv" id="ef1-Tq-U3Z">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="hgp-Z5-rAj" userLabel="Picture View" customClass="MXKImageView">
<rect key="frame" x="8" y="5" width="40" height="40"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="NQk-ck-Lo8"/>
<constraint firstAttribute="height" constant="40" id="dNT-QU-CUG"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="zwq-eh-8Fb" userLabel="typingBadge">
<rect key="frame" x="5" y="0.0" width="20" height="20"/>
<constraints>
<constraint firstAttribute="width" constant="20" id="7ni-rb-ovL"/>
<constraint firstAttribute="height" constant="20" id="mcu-rQ-hnj"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="User name:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="q9c-0p-QyP">
<rect key="frame" x="51" y="3" width="480" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" constant="20" placeholder="YES" id="5ZO-W1-tS2"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="13"/>
<color key="textColor" white="0.33333333333333331" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="HTH-5n-MSU">
<rect key="frame" x="51" y="10" width="97" height="39"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="5IE-JS-uf3" userLabel="Attachment View" customClass="MXKImageView">
<rect key="frame" x="51" y="18" width="192" height="31"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="192" id="9zO-jU-qTb"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Cot-3X-2cU">
<rect key="frame" x="131" y="18" width="32" height="32"/>
<constraints>
<constraint firstAttribute="height" constant="32" id="8io-Wk-GzF"/>
<constraint firstAttribute="width" constant="32" id="aeJ-j3-rfX"/>
</constraints>
</imageView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="kwd-hP-feC" userLabel="showHideDateTime">
<rect key="frame" x="531" y="0.0" width="69" height="49"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="9vA-4g-EE5"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="showHideDateTime:" destination="WmY-Jw-mqv" eventType="touchUpInside" id="jYV-nj-p60"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="IOg-Kt-8vW" userLabel="DateTimeLabelContainer">
<rect key="frame" x="531" y="10" width="61" height="39"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="tLr-6k-ArA"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fdx-qs-8en" userLabel="ProgressView">
<rect key="frame" x="487" y="-1" width="100" height="70"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="rate" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="4" preferredMaxLayoutWidth="100" translatesAutoresizingMaskIntoConstraints="NO" id="eU5-iK-u8i" userLabel="Progress stats">
<rect key="frame" x="0.0" y="60" width="100" height="10"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hJj-TC-pxK" customClass="MXKPieChartView">
<rect key="frame" x="30" y="0.0" width="40" height="40"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="Cpt-s4-tlK"/>
<constraint firstAttribute="height" constant="40" id="Jb4-9E-tG0"/>
</constraints>
</view>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstAttribute="height" constant="70" id="5w2-Hm-hZx"/>
<constraint firstAttribute="centerX" secondItem="eU5-iK-u8i" secondAttribute="centerX" id="APi-aE-mLc"/>
<constraint firstItem="eU5-iK-u8i" firstAttribute="leading" secondItem="fdx-qs-8en" secondAttribute="leading" id="Njw-3a-E9Y"/>
<constraint firstAttribute="bottom" secondItem="eU5-iK-u8i" secondAttribute="bottom" id="QMO-g9-QVE"/>
<constraint firstAttribute="centerX" secondItem="hJj-TC-pxK" secondAttribute="centerX" id="laR-Vg-ol3"/>
<constraint firstItem="hJj-TC-pxK" firstAttribute="top" secondItem="fdx-qs-8en" secondAttribute="top" id="ovD-8p-4dP"/>
<constraint firstAttribute="width" constant="100" id="ryE-fW-SgG"/>
<constraint firstAttribute="trailing" secondItem="eU5-iK-u8i" secondAttribute="trailing" id="teG-8q-BOX"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="hgp-Z5-rAj" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="5" id="2Ih-ga-N9s"/>
<constraint firstItem="kwd-hP-feC" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="5bB-HV-WAA"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="leading" secondItem="hgp-Z5-rAj" secondAttribute="trailing" constant="3" id="6mM-Ag-m0K"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="18" id="96U-67-5TP"/>
<constraint firstAttribute="trailing" secondItem="q9c-0p-QyP" secondAttribute="trailing" constant="69" id="Bkh-h2-JOQ"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerY" secondItem="Cot-3X-2cU" secondAttribute="centerY" id="H5t-l6-fL1"/>
<constraint firstItem="q9c-0p-QyP" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="3" id="Ixr-7h-f8j"/>
<constraint firstAttribute="bottom" secondItem="kwd-hP-feC" secondAttribute="bottom" id="LQg-cI-Nkn"/>
<constraint firstAttribute="bottom" secondItem="5IE-JS-uf3" secondAttribute="bottom" id="SHN-tC-zsJ"/>
<constraint firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="HTH-5n-MSU" secondAttribute="trailing" constant="69" id="Shz-6S-kGd"/>
<constraint firstAttribute="bottom" secondItem="IOg-Kt-8vW" secondAttribute="bottom" id="TPw-iE-nii"/>
<constraint firstItem="IOg-Kt-8vW" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="10" id="XSL-TG-m62"/>
<constraint firstItem="q9c-0p-QyP" firstAttribute="leading" secondItem="hgp-Z5-rAj" secondAttribute="trailing" constant="3" id="YWK-C2-15w"/>
<constraint firstItem="zwq-eh-8Fb" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="5" id="dgK-4e-UuE"/>
<constraint firstAttribute="trailing" secondItem="kwd-hP-feC" secondAttribute="trailing" id="f2V-Ka-kU3"/>
<constraint firstAttribute="trailing" secondItem="IOg-Kt-8vW" secondAttribute="trailing" constant="8" id="hQV-lO-7aQ"/>
<constraint firstItem="HTH-5n-MSU" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" constant="10" id="mkw-3s-H8B"/>
<constraint firstAttribute="bottom" secondItem="HTH-5n-MSU" secondAttribute="bottom" id="oTk-3F-SEC"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerX" secondItem="Cot-3X-2cU" secondAttribute="centerX" id="sF7-QL-vdj"/>
<constraint firstItem="zwq-eh-8Fb" firstAttribute="top" secondItem="ef1-Tq-U3Z" secondAttribute="top" id="tUd-UR-lzA"/>
<constraint firstItem="hgp-Z5-rAj" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="8" id="tuw-aU-ncu"/>
<constraint firstItem="HTH-5n-MSU" firstAttribute="leading" secondItem="ef1-Tq-U3Z" secondAttribute="leading" constant="51" id="uig-Xh-7m6"/>
<constraint firstItem="5IE-JS-uf3" firstAttribute="centerY" secondItem="fdx-qs-8en" secondAttribute="centerY" id="v0F-Ts-14P"/>
<constraint firstAttribute="trailing" secondItem="fdx-qs-8en" secondAttribute="trailing" constant="13" id="xKk-Gz-moE"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<connections>
<outlet property="attachViewTopConstraint" destination="96U-67-5TP" id="Ugm-cH-32E"/>
<outlet property="attachViewWidthConstraint" destination="9zO-jU-qTb" id="fOO-VW-fe1"/>
<outlet property="attachmentView" destination="5IE-JS-uf3" id="imT-1z-hR1"/>
<outlet property="dateTimeLabelContainer" destination="IOg-Kt-8vW" id="TAw-QY-Y9e"/>
<outlet property="dateTimeLabelContainerTopConstraint" destination="XSL-TG-m62" id="qVf-vJ-4aP"/>
<outlet property="messageTextView" destination="HTH-5n-MSU" id="YN4-iK-gNc"/>
<outlet property="msgTextViewLeadingConstraint" destination="uig-Xh-7m6" id="kgj-3v-ECW"/>
<outlet property="msgTextViewTopConstraint" destination="mkw-3s-H8B" id="lON-oG-Xx9"/>
<outlet property="msgTextViewTrailingConstraint" destination="Shz-6S-kGd" id="5ib-m6-Lna"/>
<outlet property="pictureView" destination="hgp-Z5-rAj" id="rKM-QG-RJN"/>
<outlet property="playIconView" destination="Cot-3X-2cU" id="KEF-KK-Og1"/>
<outlet property="progressChartView" destination="hJj-TC-pxK" id="Zz3-s5-Qqr"/>
<outlet property="progressView" destination="fdx-qs-8en" id="V7E-pn-Xze"/>
<outlet property="statsLabel" destination="eU5-iK-u8i" id="MSm-kU-RSY"/>
<outlet property="typingBadge" destination="zwq-eh-8Fb" id="4vs-Dk-vWt"/>
<outlet property="userNameLabel" destination="q9c-0p-QyP" id="JId-R7-LoM"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,24 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
/**
`RoomOutgoingBubbleTableViewCell` displays outgoing message bubbles.
*/
@interface RoomOutgoingBubbleTableViewCell : MXKRoomOutgoingBubbleTableViewCell
@end
@@ -0,0 +1,221 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RoomOutgoingBubbleTableViewCell.h"
#pragma mark - UI Constant definitions
#define MXKROOMBUBBLETABLEVIEWCELL_OUTGOING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN -10
@implementation RoomOutgoingBubbleTableViewCell
- (void)dealloc
{
[self stopAnimating];
}
- (void)render:(MXKCellData *)cellData
{
[super render:cellData];
if (self.bubbleData)
{
// Check whether the previous message has been sent by the same user.
// The user's picture and name are displayed only for the first message.
// Handle sender's picture and adjust view's constraints
if (self.bubbleData.isSameSenderAsPreviousBubble)
{
self.pictureView.hidden = YES;
self.msgTextViewTopConstraint.constant = self.class.cellWithOriginalXib.msgTextViewTopConstraint.constant + MXKROOMBUBBLETABLEVIEWCELL_OUTGOING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
self.attachViewTopConstraint.constant = self.class.cellWithOriginalXib.attachViewTopConstraint.constant + MXKROOMBUBBLETABLEVIEWCELL_OUTGOING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
if (!self.dateTimeLabelContainer.hidden)
{
self.dateTimeLabelContainerTopConstraint.constant += MXKROOMBUBBLETABLEVIEWCELL_OUTGOING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
}
}
// Add unsent label for failed components
for (MXKRoomBubbleComponent *component in self.bubbleData.bubbleComponents)
{
if (component.event.mxkState == MXKEventStateSendingFailed)
{
UIButton *unsentButton = [[UIButton alloc] initWithFrame:CGRectMake(0, component.position.y, 58 , 20)];
[unsentButton setTitle:[NSBundle mxk_localizedStringForKey:@"unsent"] forState:UIControlStateNormal];
[unsentButton setTitle:[NSBundle mxk_localizedStringForKey:@"unsent"] forState:UIControlStateSelected];
[unsentButton setTitleColor:[UIColor redColor] forState:UIControlStateNormal];
[unsentButton setTitleColor:[UIColor redColor] forState:UIControlStateSelected];
unsentButton.backgroundColor = [UIColor whiteColor];
unsentButton.titleLabel.font = [UIFont systemFontOfSize:14];
[unsentButton addTarget:self action:@selector(onResendToggle:) forControlEvents:UIControlEventTouchUpInside];
[self.dateTimeLabelContainer addSubview:unsentButton];
self.dateTimeLabelContainer.hidden = NO;
self.dateTimeLabelContainer.userInteractionEnabled = YES;
// ensure that dateTimeLabelContainer is at front to catch the tap event
[self.dateTimeLabelContainer.superview bringSubviewToFront:self.dateTimeLabelContainer];
}
}
if (self.attachmentView)
{
// Check if the image is uploading
MXKRoomBubbleComponent *component = self.bubbleData.bubbleComponents.firstObject;
if (MXKEventStateUploading == component.event.mxkState)
{
// Retrieve the uploadId embedded in the fake url
self.bubbleData.uploadId = component.event.content[@"url"];
// And start showing upload progress
[self startUploadAnimating];
self.attachmentView.hideActivityIndicator = YES;
}
else
{
self.attachmentView.hideActivityIndicator = NO;
}
}
}
}
+ (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
{
CGFloat rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth];
MXKRoomBubbleCellData *bubbleData = (MXKRoomBubbleCellData*)cellData;
// Check whether the previous message has been sent by the same user.
// The user's picture and name are displayed only for the first message.
if (bubbleData.isSameSenderAsPreviousBubble)
{
// Reduce top margin -> row height reduction
rowHeight += MXKROOMBUBBLETABLEVIEWCELL_OUTGOING_HEIGHT_REDUCTION_WHEN_SENDER_INFO_IS_HIDDEN;
}
else
{
// We consider a minimun cell height in order to display correctly user's picture
if (rowHeight < self.cellWithOriginalXib.frame.size.height)
{
rowHeight = self.cellWithOriginalXib.frame.size.height;
}
}
return rowHeight;
}
- (void)didEndDisplay
{
[super didEndDisplay];
// Hide potential loading wheel
[self stopAnimating];
self.dateTimeLabelContainer.userInteractionEnabled = NO;
}
-(void)startUploadAnimating
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKMediaUploadProgressNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onUploadProgress:) name:kMXKMediaUploadProgressNotification object:nil];
self.activityIndicator.hidden = NO;
[self.activityIndicator startAnimating];
MXKMediaLoader *uploader = [MXKMediaManager existingUploaderWithId:self.bubbleData.uploadId];
if (uploader && uploader.statisticsDict)
{
[self.activityIndicator stopAnimating];
[self updateProgressUI:uploader.statisticsDict];
// Check whether the upload is ended
if (self.progressChartView.progress == 1.0)
{
self.progressView.hidden = YES;
}
}
else
{
self.progressView.hidden = YES;
}
}
-(void)stopAnimating
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:kMXKMediaUploadProgressNotification object:nil];
[self.activityIndicator stopAnimating];
}
- (void)onUploadProgress:(NSNotification *)notif
{
// sanity check
if ([notif.object isKindOfClass:[NSString class]])
{
NSString *uploadId = notif.object;
if ([uploadId isEqualToString:self.bubbleData.uploadId])
{
[self.activityIndicator stopAnimating];
[self updateProgressUI:notif.userInfo];
// the upload is ended
if (self.progressChartView.progress == 1.0)
{
self.progressView.hidden = YES;
}
}
}
}
#pragma mark - User actions
- (IBAction)onResendToggle:(id)sender
{
if ([sender isKindOfClass:[UIButton class]] && self.delegate)
{
MXEvent *selectedEvent = nil;
if (self.bubbleData.bubbleComponents.count == 1)
{
MXKRoomBubbleComponent *component = [self.bubbleData.bubbleComponents firstObject];
selectedEvent = component.event;
}
else if (self.bubbleData.bubbleComponents.count)
{
// Here the selected view is a textView (attachment has no more than one component)
// Look for the selected component
UIButton *unsentButton = (UIButton *)sender;
for (MXKRoomBubbleComponent *component in self.bubbleData.bubbleComponents)
{
if (unsentButton.frame.origin.y == component.position.y)
{
selectedEvent = component.event;
break;
}
}
}
if (selectedEvent)
{
[self.delegate cell:self didRecognizeAction:kMXKRoomBubbleCellUnsentButtonPressed userInfo:@{kMXKRoomBubbleCellEventKey:selectedEvent}];
}
}
}
@end
@@ -0,0 +1,141 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="none" indentationWidth="10" id="pad-g3-2YJ" customClass="RoomOutgoingBubbleTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="600" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="pad-g3-2YJ" id="fCg-ju-gnG">
<autoresizingMask key="autoresizingMask"/>
<subviews>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="ezT-Dl-ESR" userLabel="Picture View" customClass="MXKImageView">
<rect key="frame" x="552" y="5" width="40" height="40"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="40" id="JHc-dD-geb"/>
<constraint firstAttribute="height" constant="40" id="lFb-Jo-C7R"/>
</constraints>
</view>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" scrollEnabled="NO" editable="NO" text="text message" translatesAutoresizingMaskIntoConstraints="NO" id="tgO-Rv-C7R">
<rect key="frame" x="452" y="10" width="97" height="39"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
<dataDetectorType key="dataDetectorTypes" link="YES"/>
</textView>
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="SIW-l4-PfI" userLabel="Attachment View" customClass="MXKImageView">
<rect key="frame" x="357" y="18" width="192" height="31"/>
<color key="backgroundColor" red="0.93725490196078431" green="0.93725490196078431" blue="0.95686274509803926" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="width" constant="192" id="iGr-e7-bde"/>
</constraints>
</view>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="ZC1-hT-7fs">
<rect key="frame" x="437" y="18" width="32" height="32"/>
<constraints>
<constraint firstAttribute="width" constant="32" id="MOQ-Xi-18w"/>
<constraint firstAttribute="height" constant="32" id="pGM-pf-SiP"/>
</constraints>
</imageView>
<activityIndicatorView hidden="YES" opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Mqf-7a-bsm">
<rect key="frame" x="443" y="24" width="20" height="20"/>
</activityIndicatorView>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="LVJ-Av-zVs" userLabel="showHideDateTime">
<rect key="frame" x="0.0" y="0.0" width="69" height="49"/>
<constraints>
<constraint firstAttribute="width" constant="69" id="ghQ-Qb-BBg"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="showHideDateTime:" destination="pad-g3-2YJ" eventType="touchUpInside" id="Ztw-z5-zlU"/>
</connections>
</button>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a51-cR-7FE" userLabel="DateTimeLabelContainer">
<rect key="frame" x="8" y="10" width="61" height="39"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="61" id="fDy-iL-hrD"/>
</constraints>
</view>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="apW-hM-veR" userLabel="ProgressView">
<rect key="frame" x="18" y="-1" width="100" height="70"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="text" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="3" baselineAdjustment="alignBaselines" minimumFontSize="4" preferredMaxLayoutWidth="100" translatesAutoresizingMaskIntoConstraints="NO" id="DYj-Mb-me4" userLabel="Progress stats">
<rect key="frame" x="0.0" y="60" width="100" height="10"/>
<fontDescription key="fontDescription" type="system" pointSize="8"/>
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Oec-kb-Tsz" customClass="MXKPieChartView">
<rect key="frame" x="30" y="0.0" width="40" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="6tZ-ag-7TT"/>
<constraint firstAttribute="width" constant="40" id="duC-38-cLC"/>
</constraints>
</view>
</subviews>
<gestureRecognizers/>
<constraints>
<constraint firstItem="Oec-kb-Tsz" firstAttribute="top" secondItem="apW-hM-veR" secondAttribute="top" id="49H-ch-qkC"/>
<constraint firstAttribute="height" constant="70" id="GhG-zB-k3A"/>
<constraint firstAttribute="centerX" secondItem="Oec-kb-Tsz" secondAttribute="centerX" id="KZy-2p-KaW"/>
<constraint firstItem="DYj-Mb-me4" firstAttribute="leading" secondItem="apW-hM-veR" secondAttribute="leading" id="UxL-bV-5Ca"/>
<constraint firstAttribute="bottom" secondItem="DYj-Mb-me4" secondAttribute="bottom" id="YFp-gQ-NFz"/>
<constraint firstAttribute="width" constant="100" id="Zr4-5T-h9g"/>
<constraint firstAttribute="trailing" secondItem="DYj-Mb-me4" secondAttribute="trailing" id="ehN-ME-1U1"/>
<constraint firstAttribute="centerX" secondItem="DYj-Mb-me4" secondAttribute="centerX" id="nuq-lO-8pT"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="Mqf-7a-bsm" secondAttribute="centerX" id="1b6-1l-hg0"/>
<constraint firstAttribute="trailing" secondItem="ezT-Dl-ESR" secondAttribute="trailing" constant="8" id="3Pd-Qy-Xva"/>
<constraint firstAttribute="bottom" secondItem="tgO-Rv-C7R" secondAttribute="bottom" id="7C3-Tl-mMq"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="10" id="8Sy-eu-tYs"/>
<constraint firstItem="ezT-Dl-ESR" firstAttribute="leading" secondItem="SIW-l4-PfI" secondAttribute="trailing" constant="3" id="9z3-D2-2SS"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="8" id="E3x-h8-GPF"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" id="Hve-E3-z5N"/>
<constraint firstAttribute="bottom" secondItem="LVJ-Av-zVs" secondAttribute="bottom" id="IKr-Dc-HKz"/>
<constraint firstItem="apW-hM-veR" firstAttribute="leading" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="18" id="LFn-vp-m0v"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="18" id="QDm-tP-KWa"/>
<constraint firstItem="ezT-Dl-ESR" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="5" id="aHl-KA-68x"/>
<constraint firstAttribute="bottom" secondItem="SIW-l4-PfI" secondAttribute="bottom" id="fMN-Th-SOT"/>
<constraint firstItem="a51-cR-7FE" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" constant="10" id="fQv-07-Pgx"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="ZC1-hT-7fs" secondAttribute="centerY" id="ffI-vh-SW3"/>
<constraint firstItem="tgO-Rv-C7R" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="69" id="hwr-aa-TB4"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerX" secondItem="ZC1-hT-7fs" secondAttribute="centerX" id="jah-TA-P0P"/>
<constraint firstItem="LVJ-Av-zVs" firstAttribute="top" secondItem="fCg-ju-gnG" secondAttribute="top" id="pYO-hi-P72"/>
<constraint firstItem="SIW-l4-PfI" firstAttribute="centerY" secondItem="Mqf-7a-bsm" secondAttribute="centerY" id="sKJ-ny-LjM"/>
<constraint firstAttribute="bottom" secondItem="a51-cR-7FE" secondAttribute="bottom" id="viZ-Sx-3RW"/>
<constraint firstItem="apW-hM-veR" firstAttribute="centerY" secondItem="SIW-l4-PfI" secondAttribute="centerY" id="wmh-x1-U32"/>
<constraint firstAttribute="trailing" secondItem="tgO-Rv-C7R" secondAttribute="trailing" constant="51" id="xYz-lp-c7K"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<connections>
<outlet property="activityIndicator" destination="Mqf-7a-bsm" id="toi-nL-eJa"/>
<outlet property="attachViewTopConstraint" destination="QDm-tP-KWa" id="Ku6-FC-jqo"/>
<outlet property="attachViewWidthConstraint" destination="iGr-e7-bde" id="uD4-aU-1Ru"/>
<outlet property="attachmentView" destination="SIW-l4-PfI" id="L4H-ub-pPI"/>
<outlet property="dateTimeLabelContainer" destination="a51-cR-7FE" id="wrR-cU-DVm"/>
<outlet property="dateTimeLabelContainerTopConstraint" destination="fQv-07-Pgx" id="82c-KH-Wop"/>
<outlet property="messageTextView" destination="tgO-Rv-C7R" id="LZ5-hQ-AbQ"/>
<outlet property="msgTextViewLeadingConstraint" destination="hwr-aa-TB4" id="c9j-8p-cpx"/>
<outlet property="msgTextViewTopConstraint" destination="8Sy-eu-tYs" id="7yx-oJ-KBP"/>
<outlet property="msgTextViewTrailingConstraint" destination="xYz-lp-c7K" id="ubt-P4-J2o"/>
<outlet property="pictureView" destination="ezT-Dl-ESR" id="YsO-Kp-xaD"/>
<outlet property="playIconView" destination="ZC1-hT-7fs" id="zeH-8B-gxw"/>
<outlet property="progressChartView" destination="Oec-kb-Tsz" id="uoq-6v-6Tm"/>
<outlet property="progressView" destination="apW-hM-veR" id="Iy8-ca-hMi"/>
<outlet property="statsLabel" destination="DYj-Mb-me4" id="CbG-5V-unO"/>
</connections>
</tableViewCell>
</objects>
</document>
@@ -0,0 +1,28 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import <MatrixKit/MatrixKit.h>
#import "MediaPickerViewController.h"
/**
`RoomInputToolbarView` instance is a view used to handle all kinds of available inputs
for a room (message composer, attachments selection...).
*/
@interface RoomInputToolbarView : MXKRoomInputToolbarViewWithHPGrowingText <MediaPickerViewControllerDelegate>
@end
@@ -0,0 +1,511 @@
/*
Copyright 2015 OpenMarket Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#import "RoomInputToolbarView.h"
//#import <MediaPlayer/MediaPlayer.h>
//#import <MobileCoreServices/MobileCoreServices.h>
//
//#import <AssetsLibrary/ALAsset.h>
//#import <AssetsLibrary/ALAssetRepresentation.h>
#define MXKROOM_INPUT_TOOLBAR_VIEW_LARGE_IMAGE_SIZE 1024
#define MXKROOM_INPUT_TOOLBAR_VIEW_MEDIUM_IMAGE_SIZE 768
#define MXKROOM_INPUT_TOOLBAR_VIEW_SMALL_IMAGE_SIZE 512
@interface RoomInputToolbarView()
{
MediaPickerViewController *mediaPicker;
}
@end
@implementation RoomInputToolbarView
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([RoomInputToolbarView class])
bundle:[NSBundle bundleForClass:[RoomInputToolbarView class]]];
}
+ (instancetype)roomInputToolbarView
{
if ([[self class] nib])
{
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
else
{
return [[self alloc] init];
}
}
- (void)awakeFromNib
{
[super awakeFromNib];
self.leftInputToolbarButton.backgroundColor = [UIColor clearColor];
[self.leftInputToolbarButton setImage:[UIImage imageNamed:@"attach_media"] forState:UIControlStateNormal];
[self.leftInputToolbarButton setImage:[UIImage imageNamed:@"attach_media"] forState:UIControlStateHighlighted];
self.rightInputToolbarButton.backgroundColor = [UIColor clearColor];
[self.rightInputToolbarButton setImage:[UIImage imageNamed:@"send"] forState:UIControlStateNormal];
[self.rightInputToolbarButton setImage:[UIImage imageNamed:@"send"] forState:UIControlStateHighlighted];
[self.rightInputToolbarButton setTitle:nil forState:UIControlStateNormal];
[self.rightInputToolbarButton setTitle:nil forState:UIControlStateHighlighted];
self.rightInputToolbarButton.enabled = YES;
}
- (void)setTextMessage:(NSString *)textMessage
{
[super setTextMessage:textMessage];
// Force right button to be enabled (even if the text input is empty)
self.rightInputToolbarButton.enabled = YES;
}
#pragma mark - HPGrowingTextView delegate
- (void)growingTextViewDidChange:(HPGrowingTextView *)sender
{
[super growingTextViewDidChange:sender];
// Force right button to be enabled (even if the text input is empty)
self.rightInputToolbarButton.enabled = YES;
}
- (BOOL)growingTextViewShouldReturn:(HPGrowingTextView *)growingTextView
{
NSString *message = self.textMessage;
// Reset message
self.textMessage = nil;
// Send button has been pressed
if (message.length && [self.delegate respondsToSelector:@selector(roomInputToolbarView:sendTextMessage:)])
{
[self.delegate roomInputToolbarView:self sendTextMessage:message];
}
return YES;
}
#pragma mark - Override MXKRoomInputToolbarView
- (IBAction)onTouchUpInside:(UIButton*)button
{
if (button == self.leftInputToolbarButton)
{
// Check whether media attachment is supported
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:presentViewController:)])
{
mediaPicker = [MediaPickerViewController mediaPickerViewController];
mediaPicker.delegate = self;
UINavigationController *navigationController = [UINavigationController new];
[navigationController pushViewController:mediaPicker animated:NO];
[self.delegate roomInputToolbarView:self presentViewController:mediaPicker];
}
else
{
NSLog(@"[RoomInputToolbarView] Attach media is not supported");
}
}
else if (button == self.rightInputToolbarButton)
{
// Check whether media attachment is supported
if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:presentViewController:)])
{
mediaPicker = [MediaPickerViewController mediaPickerViewController];
mediaPicker.delegate = self;
UINavigationController *navigationController = [UINavigationController new];
[navigationController pushViewController:mediaPicker animated:NO];
[self.delegate roomInputToolbarView:self presentViewController:mediaPicker];
}
else
{
NSLog(@"[RoomInputToolbarView] Attach media is not supported");
}
}
// else if (button == self.rightInputToolbarButton)
// {
//
// NSString *message = self.textMessage;
//
// // Reset message
// self.textMessage = nil;
//
// // Send button has been pressed
// if (message.length && [self.delegate respondsToSelector:@selector(roomInputToolbarView:sendTextMessage:)])
// {
// [self.delegate roomInputToolbarView:self sendTextMessage:message];
// }
// }
}
- (void)destroy
{
[super destroy];
}
//#pragma mark - UIImagePickerControllerDelegate
//
//- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
//{
// NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];
// if ([mediaType isEqualToString:(NSString *)kUTTypeImage])
// {
//
// /*
// NSData *dataOfGif = [NSData dataWithContentsOfFile: [info objectForKey:UIImagePickerControllerReferenceURL]];
//
// NSLog(@"%d", dataOfGif.length);
//
// ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
// [library assetForURL:[info objectForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset)
// {
//
// NSLog(@"%@", asset.defaultRepresentation.metadata);
//
//
// NSLog(@"%@", asset.defaultRepresentation.url);
//
// NSData *dataOfGif = [NSData dataWithContentsOfURL: asset.defaultRepresentation.url];
//
// NSLog(@"%d", dataOfGif.length);
// ;
//
// } failureBlock:^(NSError *error)
// {
//
// }];
//
// */
//
// if (![self.delegate respondsToSelector:@selector(roomInputToolbarView:sendImage:)])
// {
// NSLog(@"[RoomInputToolbarView] Attach image is not supported");
// }
// else
// {
// UIImage *selectedImage = [info objectForKey:UIImagePickerControllerOriginalImage];
// if (selectedImage)
// {
// // media picker does not offer a preview
// // so add a preview to let the user validates his selection
// if (picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary)
// {
// __weak typeof(self) weakSelf = self;
//
// imageValidationView = [[MXKImageView alloc] initWithFrame:CGRectZero];
// imageValidationView.stretchable = YES;
//
// // the user validates the image
// [imageValidationView setRightButtonTitle:[NSBundle mxk_localizedStringForKey:@"ok"] handler:^(MXKImageView* imageView, NSString* buttonTitle)
// {
// __strong __typeof(weakSelf)strongSelf = weakSelf;
//
// // Dismiss the image view
// [strongSelf dismissImageValidationView];
//
// // prompt user about image compression
// [strongSelf promptCompressionForSelectedImage:info];
// }];
//
// // the user wants to use an other image
// [imageValidationView setLeftButtonTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] handler:^(MXKImageView* imageView, NSString* buttonTitle)
// {
// __strong __typeof(weakSelf)strongSelf = weakSelf;
//
// // dismiss the image view
// [strongSelf dismissImageValidationView];
//
// // Open again media gallery
// strongSelf->mediaPicker = [[UIImagePickerController alloc] init];
// strongSelf->mediaPicker.delegate = strongSelf;
// strongSelf->mediaPicker.sourceType = picker.sourceType;
// strongSelf->mediaPicker.allowsEditing = NO;
// strongSelf->mediaPicker.mediaTypes = picker.mediaTypes;
// [strongSelf.delegate roomInputToolbarView:strongSelf presentMediaPicker:strongSelf->mediaPicker];
// }];
//
// imageValidationView.image = selectedImage;
// [imageValidationView showFullScreen];
// }
// else
// {
// // Save the original image in user's photos library and suggest compression before sending image
// [MXKMediaManager saveImageToPhotosLibrary:selectedImage success:nil failure:nil];
// [self promptCompressionForSelectedImage:info];
// }
// }
// }
// }
// else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
// {
// NSURL* selectedVideo = [info objectForKey:UIImagePickerControllerMediaURL];
//
// // Check the selected video, and ignore multiple calls (observed when user pressed several time Choose button)
// if (selectedVideo && !tmpVideoPlayer)
// {
// if (picker.sourceType == UIImagePickerControllerSourceTypePhotoLibrary)
// {
// [MXKMediaManager saveMediaToPhotosLibrary:selectedVideo isImage:NO success:nil failure:nil];
// }
//
// // Create video thumbnail
// tmpVideoPlayer = [[MPMoviePlayerController alloc] initWithContentURL:selectedVideo];
// if (tmpVideoPlayer)
// {
// [tmpVideoPlayer setShouldAutoplay:NO];
// [[NSNotificationCenter defaultCenter] addObserver:self
// selector:@selector(moviePlayerThumbnailImageRequestDidFinishNotification:)
// name:MPMoviePlayerThumbnailImageRequestDidFinishNotification
// object:nil];
// [tmpVideoPlayer requestThumbnailImagesAtTimes:@[@1.0f] timeOption:MPMovieTimeOptionNearestKeyFrame];
// // We will finalize video attachment when thumbnail will be available (see movie player callback)
// return;
// }
// }
// }
//
// [self dismissMediaPicker];
//}
//
//- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
//{
// [self dismissMediaPicker];
//}
//
//- (void)dismissImageValidationView
//{
// if (imageValidationView)
// {
// [imageValidationView dismissSelection];
// [imageValidationView removeFromSuperview];
// imageValidationView = nil;
// }
//}
//
//- (void)promptCompressionForSelectedImage:(NSDictionary*)selectedImageInfo
//{
// if (currentAlert)
// {
// [currentAlert dismiss:NO];
// currentAlert = nil;
// }
//
// UIImage *selectedImage = [selectedImageInfo objectForKey:UIImagePickerControllerOriginalImage];
// CGSize originalSize = selectedImage.size;
// NSLog(@"Selected image size : %f %f", originalSize.width, originalSize.height);
//
// [self getSelectedImageFileData:selectedImageInfo success:^(NSData *selectedImageFileData) {
//
// long long smallFilesize = 0;
// long long mediumFilesize = 0;
// long long largeFilesize = 0;
//
// // succeed to get the file size (provided by the photo library)
// long long originalFileSize = selectedImageFileData.length;
// NSLog(@"- use the photo library file size: %tu", originalFileSize);
//
// CGFloat maxSize = MAX(originalSize.width, originalSize.height);
// if (maxSize >= MXKROOM_INPUT_TOOLBAR_VIEW_SMALL_IMAGE_SIZE)
// {
// CGFloat factor = MXKROOM_INPUT_TOOLBAR_VIEW_SMALL_IMAGE_SIZE / maxSize;
// smallFilesize = factor * factor * originalFileSize;
// }
// else
// {
// NSLog(@"- too small to fit in %d", MXKROOM_INPUT_TOOLBAR_VIEW_SMALL_IMAGE_SIZE);
// }
//
// if (maxSize >= MXKROOM_INPUT_TOOLBAR_VIEW_MEDIUM_IMAGE_SIZE)
// {
// CGFloat factor = MXKROOM_INPUT_TOOLBAR_VIEW_MEDIUM_IMAGE_SIZE / maxSize;
// mediumFilesize = factor * factor * originalFileSize;
// }
// else
// {
// NSLog(@"- too small to fit in %d", MXKROOM_INPUT_TOOLBAR_VIEW_MEDIUM_IMAGE_SIZE);
// }
//
// if (maxSize >= MXKROOM_INPUT_TOOLBAR_VIEW_LARGE_IMAGE_SIZE)
// {
// CGFloat factor = MXKROOM_INPUT_TOOLBAR_VIEW_LARGE_IMAGE_SIZE / maxSize;
// largeFilesize = factor * factor * originalFileSize;
// }
// else
// {
// NSLog(@"- too small to fit in %d", MXKROOM_INPUT_TOOLBAR_VIEW_LARGE_IMAGE_SIZE);
// }
//
// if (smallFilesize || mediumFilesize || largeFilesize)
// {
// currentAlert = [[MXKAlert alloc] initWithTitle:[NSBundle mxk_localizedStringForKey:@"attachment_size_prompt"] message:nil style:MXKAlertStyleActionSheet];
// __weak typeof(self) weakSelf = self;
//
// if (smallFilesize)
// {
// NSString *title = [NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"attachment_small"], [MXKTools fileSizeToString: (int)smallFilesize]];
// [currentAlert addActionWithTitle:title style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
// __strong __typeof(weakSelf)strongSelf = weakSelf;
// strongSelf->currentAlert = nil;
//
// // Send the small image
// UIImage *smallImage = [MXKTools resize:selectedImage toFitInSize:CGSizeMake(MXKROOM_INPUT_TOOLBAR_VIEW_SMALL_IMAGE_SIZE, MXKROOM_INPUT_TOOLBAR_VIEW_SMALL_IMAGE_SIZE)];
// [strongSelf.delegate roomInputToolbarView:weakSelf sendImage:smallImage];
// }];
// }
//
// if (mediumFilesize)
// {
// NSString *title = [NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"attachment_medium"], [MXKTools fileSizeToString: (int)mediumFilesize]];
// [currentAlert addActionWithTitle:title style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
// __strong __typeof(weakSelf)strongSelf = weakSelf;
// strongSelf->currentAlert = nil;
//
// // Send the medium image
// UIImage *mediumImage = [MXKTools resize:selectedImage toFitInSize:CGSizeMake(MXKROOM_INPUT_TOOLBAR_VIEW_MEDIUM_IMAGE_SIZE, MXKROOM_INPUT_TOOLBAR_VIEW_MEDIUM_IMAGE_SIZE)];
// [strongSelf.delegate roomInputToolbarView:weakSelf sendImage:mediumImage];
// }];
// }
//
// if (largeFilesize)
// {
// NSString *title = [NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"attachment_large"], [MXKTools fileSizeToString: (int)largeFilesize]];
// [currentAlert addActionWithTitle:title style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
// __strong __typeof(weakSelf)strongSelf = weakSelf;
// strongSelf->currentAlert = nil;
//
// // Send the large image
// UIImage *largeImage = [MXKTools resize:selectedImage toFitInSize:CGSizeMake(MXKROOM_INPUT_TOOLBAR_VIEW_LARGE_IMAGE_SIZE, MXKROOM_INPUT_TOOLBAR_VIEW_LARGE_IMAGE_SIZE)];
// [strongSelf.delegate roomInputToolbarView:weakSelf sendImage:largeImage];
// }];
// }
//
// NSString *title = [NSString stringWithFormat:[NSBundle mxk_localizedStringForKey:@"attachment_original"], [MXKTools fileSizeToString: (int)originalFileSize]];
// [currentAlert addActionWithTitle:title style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
// __strong __typeof(weakSelf)strongSelf = weakSelf;
// strongSelf->currentAlert = nil;
//
// // Send the original image
// [strongSelf.delegate roomInputToolbarView:weakSelf sendImage:selectedImage];
// }];
//
// currentAlert.cancelButtonIndex = [currentAlert addActionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:MXKAlertActionStyleDefault handler:^(MXKAlert *alert) {
// __strong __typeof(weakSelf)strongSelf = weakSelf;
// strongSelf->currentAlert = nil;
// }];
//
// currentAlert.sourceView = self;
//
// [self.delegate roomInputToolbarView:self presentMXKAlert:currentAlert];
// }
// else
// {
// // Send the original image
// [self.delegate roomInputToolbarView:self sendImage:selectedImage];
// }
// } failure:^(NSError *error) {
//
// // Send the original image
// [self.delegate roomInputToolbarView:self sendImage:selectedImage];
// }];
//}
//- (void)getSelectedImageFileData:(NSDictionary*)selectedImageInfo success:(void (^)(NSData *selectedImageFileData))success failure:(void (^)(NSError *error))failure
//{
// ALAssetsLibrary *assetLibrary=[[ALAssetsLibrary alloc] init];
// [assetLibrary assetForURL:[selectedImageInfo valueForKey:UIImagePickerControllerReferenceURL] resultBlock:^(ALAsset *asset) {
//
// NSData *selectedImageFileData;
//
// // asset may be nil if the image is not saved in photos library
// if (asset)
// {
// ALAssetRepresentation* assetRepresentation = [asset defaultRepresentation];
//
// // Check whether the user select an image with a cropping
// if ([[assetRepresentation metadata] objectForKey:@"AdjustmentXMP"])
// {
// // In case of crop we have to consider the original image
// selectedImageFileData = UIImageJPEGRepresentation([selectedImageInfo objectForKey:UIImagePickerControllerOriginalImage], 0.9);
// }
// else
// {
// // cannot use assetRepresentation size to get the image size
// // it gives wrong result with panorama picture
// unsigned long imageDataSize = (unsigned long)[assetRepresentation size];
// uint8_t* imageDataBytes = malloc(imageDataSize);
// [assetRepresentation getBytes:imageDataBytes fromOffset:0 length:imageDataSize error:nil];
//
// selectedImageFileData = [NSData dataWithBytesNoCopy:imageDataBytes length:imageDataSize freeWhenDone:YES];
// }
// }
// else
// {
// selectedImageFileData = UIImageJPEGRepresentation([selectedImageInfo objectForKey:UIImagePickerControllerOriginalImage], 0.9);
// }
//
// if (success)
// {
// success (selectedImageFileData);
// }
// } failureBlock:^(NSError *err) {
//
// if (failure)
// {
// failure (err);
// }
// }];
//}
#pragma mark - Media Picker handling
//- (void)dismissMediaPicker
//{
// mediaPicker.delegate = nil;
//
// if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:dismissMediaPicker:)])
// {
// [self.delegate roomInputToolbarView:self dismissMediaPicker:mediaPicker];
// }
//}
//
//- (void)moviePlayerThumbnailImageRequestDidFinishNotification:(NSNotification *)notification
//{
// // Finalize video attachment
// UIImage* videoThumbnail = [[notification userInfo] objectForKey:MPMoviePlayerThumbnailImageKey];
// NSURL* selectedVideo = [tmpVideoPlayer contentURL];
// [tmpVideoPlayer stop];
// tmpVideoPlayer = nil;
//
// if ([self.delegate respondsToSelector:@selector(roomInputToolbarView:sendVideo:withThumbnail:)])
// {
// [self.delegate roomInputToolbarView:self sendVideo:selectedVideo withThumbnail:videoThumbnail];
// }
// else
// {
// NSLog(@"[RoomInputToolbarView] Attach video is not supported");
// }
//
// [self dismissMediaPicker];
//}
@end
@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="RoomInputToolbarView">
<rect key="frame" x="0.0" y="0.0" width="600" height="41"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QWp-NV-uh5" userLabel="Message Composer Container">
<rect key="frame" x="8" y="4" width="497" height="33"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="wgb-ON-N29" customClass="HPGrowingTextView">
<rect key="frame" x="0.0" y="0.0" width="497" height="33"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="wgb-ON-N29" firstAttribute="top" secondItem="QWp-NV-uh5" secondAttribute="top" id="0jt-Ye-2DW"/>
<constraint firstAttribute="trailing" secondItem="wgb-ON-N29" secondAttribute="trailing" id="30f-rE-CKj"/>
<constraint firstItem="wgb-ON-N29" firstAttribute="leading" secondItem="QWp-NV-uh5" secondAttribute="leading" id="N7q-ch-iRz"/>
<constraint firstAttribute="bottom" secondItem="wgb-ON-N29" secondAttribute="bottom" id="fFG-SH-Hjh"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Hga-l8-Wua" userLabel="left Button">
<rect key="frame" x="513" y="0.0" width="41" height="41"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="Hga-l8-Wua" secondAttribute="height" multiplier="1:1" id="f0T-3f-BJu"/>
</constraints>
<connections>
<action selector="onTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="WbU-WH-gwL"/>
</connections>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Owf-M8-qJi" userLabel="right Button">
<rect key="frame" x="555" y="0.0" width="41" height="41"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" secondItem="Owf-M8-qJi" secondAttribute="height" multiplier="1:1" id="1Ni-y7-Nsa"/>
<constraint firstAttribute="width" constant="41" id="9FZ-CI-diT"/>
</constraints>
<state key="normal">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
</state>
<connections>
<action selector="onTouchUpInside:" destination="iN0-l3-epB" eventType="touchUpInside" id="Cxg-BO-TfK"/>
</connections>
</button>
</subviews>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="Owf-M8-qJi" secondAttribute="trailing" constant="4" id="Gjp-VM-BFw"/>
<constraint firstAttribute="bottom" secondItem="QWp-NV-uh5" secondAttribute="bottom" constant="4" id="Gsc-LN-yec"/>
<constraint firstItem="Hga-l8-Wua" firstAttribute="leading" secondItem="QWp-NV-uh5" secondAttribute="trailing" constant="8" id="QiS-Qt-q6t"/>
<constraint firstAttribute="centerY" secondItem="Owf-M8-qJi" secondAttribute="centerY" id="Tnh-rh-LJx"/>
<constraint firstAttribute="centerY" secondItem="Hga-l8-Wua" secondAttribute="centerY" id="U29-yX-mV5"/>
<constraint firstItem="Owf-M8-qJi" firstAttribute="leading" secondItem="Hga-l8-Wua" secondAttribute="trailing" constant="1" id="VBF-Cx-fhG"/>
<constraint firstItem="QWp-NV-uh5" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" constant="4" id="W1p-U9-VVV"/>
<constraint firstItem="Owf-M8-qJi" firstAttribute="width" secondItem="Hga-l8-Wua" secondAttribute="width" id="sWE-Of-hto"/>
<constraint firstItem="QWp-NV-uh5" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="8" id="v1f-6D-q3u"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="growingTextView" destination="wgb-ON-N29" id="nwF-uV-Ng9"/>
<outlet property="leftInputToolbarButton" destination="Hga-l8-Wua" id="jaa-D6-e6X"/>
<outlet property="messageComposerContainer" destination="QWp-NV-uh5" id="APR-B5-ogC"/>
<outlet property="messageComposerContainerBottomConstraint" destination="Gsc-LN-yec" id="tnh-uf-g5s"/>
<outlet property="messageComposerContainerTopConstraint" destination="W1p-U9-VVV" id="ETg-Iu-GEf"/>
<outlet property="rightInputToolbarButton" destination="Owf-M8-qJi" id="UQH-fw-chf"/>
</connections>
</view>
</objects>
</document>