Merge pull request #1781 from vector-im/riot_1775

Room: Inform user when they cannot post to a room because of low power level
This commit is contained in:
manuroe
2018-02-22 10:36:32 +01:00
committed by GitHub
6 changed files with 281 additions and 22 deletions
+10
View File
@@ -66,6 +66,8 @@
327382C21F276AED00356143 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382BC1F276AED00356143 /* InfoPlist.strings */; };
327382C31F276AED00356143 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382BE1F276AED00356143 /* Localizable.strings */; };
327382C41F276AED00356143 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 327382C01F276AED00356143 /* Vector.strings */; };
32795C0B203DA4C4002420E2 /* DisabledRoomInputToolbarView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */; };
32795C0C203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */; };
3284D7FF1FBB34B70090AA80 /* RoomKeyRequestViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 3284D7FE1FBB34B70090AA80 /* RoomKeyRequestViewController.m */; };
32918EA91F473BDB0076CA16 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32918EA51F473BDB0076CA16 /* Localizable.strings */; };
32918EAA1F473BDB0076CA16 /* Vector.strings in Resources */ = {isa = PBXBuildFile; fileRef = 32918EA71F473BDB0076CA16 /* Vector.strings */; };
@@ -717,6 +719,9 @@
327382BD1F276AED00356143 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = InfoPlist.strings; sourceTree = "<group>"; };
327382BF1F276AED00356143 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Localizable.strings; sourceTree = "<group>"; };
327382C11F276AED00356143 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = Vector.strings; sourceTree = "<group>"; };
32795C08203DA4C3002420E2 /* DisabledRoomInputToolbarView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DisabledRoomInputToolbarView.h; sourceTree = "<group>"; };
32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DisabledRoomInputToolbarView.m; sourceTree = "<group>"; };
32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = DisabledRoomInputToolbarView.xib; sourceTree = "<group>"; };
3284D7FD1FBB34B70090AA80 /* RoomKeyRequestViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomKeyRequestViewController.h; sourceTree = "<group>"; };
3284D7FE1FBB34B70090AA80 /* RoomKeyRequestViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomKeyRequestViewController.m; sourceTree = "<group>"; };
32918EA61F473BDB0076CA16 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Localizable.strings; sourceTree = "<group>"; };
@@ -2470,6 +2475,9 @@
F083BCD81E7009EC00A9B29C /* RoomInputToolbar */ = {
isa = PBXGroup;
children = (
32795C08203DA4C3002420E2 /* DisabledRoomInputToolbarView.h */,
32795C09203DA4C3002420E2 /* DisabledRoomInputToolbarView.m */,
32795C0A203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib */,
F083BCD91E7009EC00A9B29C /* RoomInputToolbarView.h */,
F083BCDA1E7009EC00A9B29C /* RoomInputToolbarView.m */,
F083BCDB1E7009EC00A9B29C /* RoomInputToolbarView.xib */,
@@ -3156,6 +3164,7 @@
F083BE0D1E7009ED00A9B29C /* ContactDetailsViewController.xib in Resources */,
F083BDCB1E7009ED00A9B29C /* selection_tick.png in Resources */,
F083BD6C1E7009ED00A9B29C /* disclosure_icon@2x.png in Resources */,
32795C0C203DA4C4002420E2 /* DisabledRoomInputToolbarView.xib in Resources */,
F083BD4A1E7009ED00A9B29C /* call_video_mute_on_icon@3x.png in Resources */,
F083BE8F1E7009ED00A9B29C /* RoomAvatarTitleView.xib in Resources */,
F083BD9E1E7009ED00A9B29C /* mod_icon.png in Resources */,
@@ -3638,6 +3647,7 @@
F083BE191E7009ED00A9B29C /* RecentsViewController.m in Sources */,
F0B4CBA51F418D0B008E99C5 /* WebViewViewController.m in Sources */,
F0E5D90E1FF6DDCF00560D7F /* GroupRoomsViewController.m in Sources */,
32795C0B203DA4C4002420E2 /* DisabledRoomInputToolbarView.m in Sources */,
F083BE351E7009ED00A9B29C /* MediaAlbumTableCell.m in Sources */,
F083BE4C1E7009ED00A9B29C /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */,
F075BEDB1EBB26F100A7B68A /* TableViewCellWithCollectionView.m in Sources */,
+1
View File
@@ -233,6 +233,7 @@
"room_two_users_are_typing" = "%@ & %@ are typing…";
"room_many_users_are_typing" = "%@, %@ & others are typing…";
"room_message_placeholder" = "Send a message (unencrypted)…";
"room_do_not_have_permission_to_post" = "You do not have permission to post to this room";
"encrypted_room_message_placeholder" = "Send an encrypted message…";
"room_message_short_placeholder" = "Send a message…";
"room_offline_notification" = "Connectivity to the server has been lost.";
+70 -22
View File
@@ -23,6 +23,7 @@
#import "AppDelegate.h"
#import "RoomInputToolbarView.h"
#import "DisabledRoomInputToolbarView.h"
#import "RoomActivitiesView.h"
@@ -348,10 +349,10 @@
// Replace the default input toolbar view.
// Note: this operation will force the layout of subviews. That is why cell view classes must be registered before.
[self setRoomInputToolbarViewClass:RoomInputToolbarView.class];
[self setRoomInputToolbarViewClass];
// Update the inputToolBar height.
CGFloat height = (self.inputToolbarView ? ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant : 0);
CGFloat height = [self inputToolbarHeight];
// Disable animation during the update
[UIView setAnimationsEnabled:NO];
[self roomInputToolbarView:self.inputToolbarView heightDidChanged:height completion:nil];
@@ -867,10 +868,10 @@
// Restore tool bar view and room activities view if none
if (!self.inputToolbarView)
{
[self setRoomInputToolbarViewClass:RoomInputToolbarView.class];
[self setRoomInputToolbarViewClass];
// Update the inputToolBar height.
CGFloat height = (self.inputToolbarView ? ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant : 0);
CGFloat height = [self inputToolbarHeight];
// Disable animation during the update
[UIView setAnimationsEnabled:NO];
[self roomInputToolbarView:self.inputToolbarView heightDidChanged:height completion:nil];
@@ -905,8 +906,24 @@
[super leaveRoomOnEvent:event];
}
- (void)setRoomInputToolbarViewClass:(Class)roomInputToolbarViewClass
// Set the input toolbar according to the current display
- (void)setRoomInputToolbarViewClass
{
Class roomInputToolbarViewClass = RoomInputToolbarView.class;
// Check the user has enough power to post message
if (self.roomDataSource.room.state)
{
MXRoomPowerLevels *powerLevels = self.roomDataSource.room.state.powerLevels;
NSInteger userPowerLevel = [powerLevels powerLevelOfUserWithUserID:self.mainSession.myUser.userId];
BOOL canSend = (userPowerLevel >= [powerLevels minimumPowerLevelForSendingEventAsMessage:kMXEventTypeStringRoomMessage]);
if (!canSend)
{
roomInputToolbarViewClass = DisabledRoomInputToolbarView.class;
}
}
// Do not show toolbar in case of preview
if (self.isRoomPreview)
{
@@ -916,6 +933,23 @@
[super setRoomInputToolbarViewClass:roomInputToolbarViewClass];
}
// Get the height of the current room input toolbar
- (CGFloat)inputToolbarHeight
{
CGFloat height = 0;
if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
{
height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant;
}
else if ([self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class])
{
height = ((DisabledRoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant;
}
return height;
}
- (void)setRoomActivitiesViewClass:(Class)roomActivitiesViewClass
{
// Do not show room activities in case of preview (FIXME: show it when live events will be supported during peeking)
@@ -1271,6 +1305,8 @@
- (void)refreshRoomInputToolbar
{
MXKImageView *userPictureView;
// Check whether the input toolbar is ready before updating it.
if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
{
@@ -1279,22 +1315,8 @@
// Check whether the call option is supported
roomInputToolbarView.supportCallOption = self.roomDataSource.mxSession.callManager && self.roomDataSource.room.state.joinedMembers.count >= 2;
// Set user picture in input toolbar
MXKImageView *userPictureView = roomInputToolbarView.pictureView;
if (userPictureView)
{
UIImage *preview = [AvatarGenerator generateAvatarForMatrixItem:self.mainSession.myUser.userId withDisplayName:self.mainSession.myUser.displayname];
NSString *avatarThumbURL = nil;
if (self.mainSession.myUser.avatarUrl)
{
// Suppose this url is a matrix content uri, we use SDK to get the well adapted thumbnail from server
avatarThumbURL = [self.mainSession.matrixRestClient urlOfContentThumbnail:self.mainSession.myUser.avatarUrl toFitViewSize:userPictureView.frame.size withMethod:MXThumbnailingMethodCrop];
}
userPictureView.enableInMemoryCache = YES;
[userPictureView setImageURL:avatarThumbURL withType:nil andImageOrientation:UIImageOrientationUp previewImage:preview];
[userPictureView.layer setCornerRadius:userPictureView.frame.size.width / 2];
userPictureView.clipsToBounds = YES;
}
// Get user picture view in input toolbar
userPictureView = roomInputToolbarView.pictureView;
// Show the hangup button if there is an active call or an active jitsi
// conference call in the current room
@@ -1319,6 +1341,32 @@
roomInputToolbarView.isEncryptionEnabled = (self.mainSession.crypto != nil);
}
}
else if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class])
{
DisabledRoomInputToolbarView *roomInputToolbarView = (DisabledRoomInputToolbarView*)self.inputToolbarView;
// Get user picture view in input toolbar
userPictureView = roomInputToolbarView.pictureView;
// For the moment, there is only one reason to use `DisabledRoomInputToolbarView`
[roomInputToolbarView setDisabledReason:NSLocalizedStringFromTable(@"room_do_not_have_permission_to_post", @"Vector", nil)];
}
// Set user picture in input toolbar
if (userPictureView)
{
UIImage *preview = [AvatarGenerator generateAvatarForMatrixItem:self.mainSession.myUser.userId withDisplayName:self.mainSession.myUser.displayname];
NSString *avatarThumbURL = nil;
if (self.mainSession.myUser.avatarUrl)
{
// Suppose this url is a matrix content uri, we use SDK to get the well adapted thumbnail from server
avatarThumbURL = [self.mainSession.matrixRestClient urlOfContentThumbnail:self.mainSession.myUser.avatarUrl toFitViewSize:userPictureView.frame.size withMethod:MXThumbnailingMethodCrop];
}
userPictureView.enableInMemoryCache = YES;
[userPictureView setImageURL:avatarThumbURL withType:nil andImageOrientation:UIImageOrientationUp previewImage:preview];
[userPictureView.layer setCornerRadius:userPictureView.frame.size.width / 2];
userPictureView.clipsToBounds = YES;
}
}
- (void)onSwipeGesture:(UISwipeGestureRecognizer*)swipeGestureRecognizer
@@ -3265,7 +3313,7 @@
[self setRoomInputToolbarViewClass:RoomInputToolbarView.class];
// Update the inputToolBar height.
CGFloat height = (self.inputToolbarView ? ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant : 0);
CGFloat height = [self inputToolbarHeight];
// Disable animation during the update
[UIView setAnimationsEnabled:NO];
[self roomInputToolbarView:self.inputToolbarView heightDidChanged:height completion:nil];
@@ -0,0 +1,40 @@
/*
Copyright 2018 New Vector 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>
/**
`DisabledRoomInputToolbarView` instance is an input toolbar to show to the end user
that they have limited permission to interact with the room.
*/
@interface DisabledRoomInputToolbarView : MXKRoomInputToolbarView
@property (weak, nonatomic) IBOutlet UIView *mainToolbarView;
@property (weak, nonatomic) IBOutlet UIView *separatorView;
@property (strong, nonatomic) IBOutlet MXKImageView *pictureView;
@property (weak, nonatomic) IBOutlet UITextView *disabledReasonTextView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarMinHeightConstraint;
/**
Display the limitation reason message.
@param reason the reason why the user cannot interact with the room.
*/
- (void)setDisabledReason:(NSString*)reason;
@end
@@ -0,0 +1,66 @@
/*
Copyright 2018 New Vector 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 "DisabledRoomInputToolbarView.h"
#import "RiotDesignValues.h"
@implementation DisabledRoomInputToolbarView
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([DisabledRoomInputToolbarView class])
bundle:[NSBundle bundleForClass:[DisabledRoomInputToolbarView class]]];
}
+ (instancetype)roomInputToolbarView
{
if ([[self class] nib])
{
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
else
{
return [[self alloc] init];
}
}
#pragma mark - Override MXKView
-(void)customizeViewRendering
{
[super customizeViewRendering];
// Remove default toolbar background color
self.backgroundColor = [UIColor clearColor];
self.separatorView.backgroundColor = kRiotAuxiliaryColor;
self.disabledReasonTextView.font = [UIFont systemFontOfSize:15];
self.disabledReasonTextView.textColor = kRiotPrimaryTextColor;
self.disabledReasonTextView.tintColor = kRiotColorGreen;
self.disabledReasonTextView.editable = NO;
self.disabledReasonTextView.scrollEnabled = NO;
}
#pragma mark -
- (void)setDisabledReason:(NSString *)reason
{
self.disabledReasonTextView.text = reason;
}
@end
@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="13771" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
<device id="retina4_7" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13771"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</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="DisabledRoomInputToolbarView">
<rect key="frame" x="0.0" y="0.0" width="600" height="46"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="a84-Vc-6ud" userLabel="MainToolBar View">
<rect key="frame" x="0.0" y="0.0" width="600" height="46"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="3ln-yI-ef9" userLabel="Separator View">
<rect key="frame" x="10" y="0.0" width="580" height="1"/>
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="WBM-ts-zPX"/>
</constraints>
</view>
<view clipsSubviews="YES" contentMode="scaleAspectFill" translatesAutoresizingMaskIntoConstraints="NO" id="dd4-pE-Es1" userLabel="Picture View" customClass="MXKImageView">
<rect key="frame" x="13" y="8" width="30" height="30"/>
<color key="backgroundColor" red="0.66666666666666663" green="0.66666666666666663" blue="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="PictureView"/>
<constraints>
<constraint firstAttribute="width" constant="30" id="gwh-Qs-gY1"/>
<constraint firstAttribute="height" constant="30" id="wMB-Ec-D9s"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QWp-NV-uh5" userLabel="Message Composer Container">
<rect key="frame" x="52" y="4" width="533" height="38"/>
<subviews>
<view contentMode="scaleToFill" verticalCompressionResistancePriority="1000" translatesAutoresizingMaskIntoConstraints="NO" id="wgb-ON-N29" customClass="UITextView">
<rect key="frame" x="0.0" y="0.0" width="533" height="38"/>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="GrowingTextView"/>
</view>
</subviews>
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="wgb-ON-N29" firstAttribute="centerY" secondItem="QWp-NV-uh5" secondAttribute="centerY" id="PZl-Tb-mDv"/>
<constraint firstAttribute="trailing" secondItem="wgb-ON-N29" secondAttribute="trailing" id="ms6-mg-0xf"/>
<constraint firstItem="wgb-ON-N29" firstAttribute="height" secondItem="QWp-NV-uh5" secondAttribute="height" priority="750" id="pOn-UI-aTZ"/>
<constraint firstItem="wgb-ON-N29" firstAttribute="leading" secondItem="QWp-NV-uh5" secondAttribute="leading" id="yvw-X3-WtY"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="46" id="1FO-iu-urG"/>
<constraint firstItem="3ln-yI-ef9" firstAttribute="leading" secondItem="a84-Vc-6ud" secondAttribute="leading" constant="10" id="BwP-sF-qE1"/>
<constraint firstItem="QWp-NV-uh5" firstAttribute="leading" secondItem="a84-Vc-6ud" secondAttribute="leading" constant="52" id="ErD-eB-stF"/>
<constraint firstItem="dd4-pE-Es1" firstAttribute="leading" secondItem="a84-Vc-6ud" secondAttribute="leading" constant="13" id="Jem-U2-oEq"/>
<constraint firstAttribute="bottom" secondItem="QWp-NV-uh5" secondAttribute="bottom" constant="4" id="NGr-2o-sOP"/>
<constraint firstAttribute="trailing" secondItem="3ln-yI-ef9" secondAttribute="trailing" constant="10" id="RXB-PN-3n8"/>
<constraint firstItem="QWp-NV-uh5" firstAttribute="top" secondItem="a84-Vc-6ud" secondAttribute="top" constant="4" id="WyZ-3i-OHi"/>
<constraint firstAttribute="height" constant="46" id="Yjj-ua-rbe"/>
<constraint firstAttribute="trailing" secondItem="QWp-NV-uh5" secondAttribute="trailing" constant="15" id="hXO-cY-Jgz"/>
<constraint firstItem="dd4-pE-Es1" firstAttribute="top" secondItem="a84-Vc-6ud" secondAttribute="top" constant="8" id="xjT-If-1cd"/>
<constraint firstItem="3ln-yI-ef9" firstAttribute="top" secondItem="a84-Vc-6ud" secondAttribute="top" id="yUF-5t-x03"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<accessibility key="accessibilityConfiguration" identifier="DisabledRoomInputToolbarView"/>
<constraints>
<constraint firstItem="a84-Vc-6ud" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="VBS-kE-WiP"/>
<constraint firstAttribute="trailing" secondItem="a84-Vc-6ud" secondAttribute="trailing" id="cNb-nJ-iAI"/>
<constraint firstAttribute="bottom" secondItem="a84-Vc-6ud" secondAttribute="bottom" id="jFc-lW-hpZ"/>
</constraints>
<nil key="simulatedStatusBarMetrics"/>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="disabledReasonTextView" destination="wgb-ON-N29" id="JnB-sr-s3I"/>
<outlet property="mainToolbarMinHeightConstraint" destination="1FO-iu-urG" id="2U6-h2-0zQ"/>
<outlet property="mainToolbarView" destination="a84-Vc-6ud" id="5p3-Ft-0gv"/>
<outlet property="messageComposerContainer" destination="QWp-NV-uh5" id="APR-B5-ogC"/>
<outlet property="messageComposerContainerBottomConstraint" destination="NGr-2o-sOP" id="oez-6D-IKA"/>
<outlet property="messageComposerContainerTopConstraint" destination="WyZ-3i-OHi" id="OcO-1f-bNA"/>
<outlet property="pictureView" destination="dd4-pE-Es1" id="rb6-z2-pJ9"/>
<outlet property="separatorView" destination="3ln-yI-ef9" id="HQ3-B4-Goj"/>
</connections>
</view>
</objects>
</document>