Chat screen: Customize bubble cells

This commit is contained in:
giomfo
2015-08-21 20:00:39 +02:00
parent a8454f8269
commit 4cf5972bb6
12 changed files with 285 additions and 303 deletions
+8 -2
View File
@@ -15,9 +15,12 @@
*/
#import "AppDelegate.h"
#import "RoomDataSource.h"
#import "EventFormatter.h"
#import "RoomViewController.h"
#import "SettingsViewController.h"
#import "MXKContactManager.h"
#import "RageShakeManager.h"
#import "NSBundle+MatrixKit.h"
@@ -435,6 +438,9 @@
- (void)initMatrixSessions
{
// Set first RoomDataSource class used in Vector
[MXKRoomDataSourceManager registerRoomDataSourceClass:RoomDataSource.class];
// Register matrix session state observer in order to handle multi-sessions.
matrixSessionStateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif)
{
@@ -684,7 +690,7 @@
if (account.enableInAppNotifications)
{
// Build MXEvent -> NSString formatter
MXKEventFormatter *eventFormatter = [[MXKEventFormatter alloc] initWithMatrixSession:account.mxSession];
EventFormatter *eventFormatter = [[EventFormatter alloc] initWithMatrixSession:account.mxSession];
eventFormatter.isForSubtitle = YES;
[account listenToNotifications:^(MXEvent *event, MXRoomState *roomState, MXPushRule *rule) {
+5
View File
@@ -18,6 +18,8 @@
#import "AppDelegate.h"
#import "EventFormatter.h"
#import "NSBundle+MatrixKit.h"
@interface RecentListDataSource ()
@@ -54,6 +56,9 @@
self = [super init];
if (self)
{
self.eventFormatter = [[EventFormatter alloc] initWithMatrixSession:self.mxSession];
self.eventFormatter.isForSubtitle = YES;
highlightedPublicRooms = @[@"#matrix:matrix.org", @"#matrix-dev:matrix.org", @"#matrix-fr:matrix.org"]; // Add here a room name to highlight its display in public room list
}
return self;
+24
View File
@@ -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>
/**
The data source for `RoomViewController` in Vector.
*/
@interface RoomDataSource : MXKRoomDataSource
@end
+50
View File
@@ -0,0 +1,50 @@
/*
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 "RoomDataSource.h"
#import "EventFormatter.h"
#import "RoomIncomingBubbleTableViewCell.h"
#import "RoomOutgoingBubbleTableViewCell.h"
@implementation RoomDataSource
- (instancetype)initWithRoomId:(NSString *)roomId andMatrixSession:(MXSession *)matrixSession
{
self = [super initWithRoomId:roomId andMatrixSession:matrixSession];
if (self)
{
// Keep default Cell data class (MXKRoomBubbleCellDataWithAppendingMode)
// [self registerCellDataClass:MXKRoomBubbleCellDataWithAppendingMode.class forCellIdentifier:kMXKRoomBubbleCellDataIdentifier];
// Replace cell view classes
[self registerCellViewClass:RoomIncomingBubbleTableViewCell.class forCellIdentifier:kMXKRoomIncomingTextMsgBubbleTableViewCellIdentifier];
[self registerCellViewClass:RoomIncomingBubbleTableViewCell.class forCellIdentifier:kMXKRoomIncomingAttachmentBubbleTableViewCellIdentifier];
[self registerCellViewClass:RoomOutgoingBubbleTableViewCell.class forCellIdentifier:kMXKRoomOutgoingTextMsgBubbleTableViewCellIdentifier];
[self registerCellViewClass:RoomOutgoingBubbleTableViewCell.class forCellIdentifier:kMXKRoomOutgoingAttachmentBubbleTableViewCellIdentifier];
// Replace event formatter
self.eventFormatter = [[EventFormatter alloc] initWithMatrixSession:self.mxSession];
// TODO custom here self.eventsFilterForMessages according to Vector requirements
// Set bubble pagination
self.bubblesPagination = MXKRoomDataSourceBubblesPaginationPerDay;
}
return self;
}
@end
+24
View File
@@ -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>
/**
`EventFormatter` class inherits from `MXKEventFormatter` to define Vector formatting
*/
@interface EventFormatter : MXKEventFormatter
@end
+88
View File
@@ -0,0 +1,88 @@
/*
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 "EventFormatter.h"
@interface EventFormatter ()
{
/**
The calendar used to retrieve the today date.
*/
NSCalendar *calendar;
}
@end
@implementation EventFormatter
- (instancetype)initWithMatrixSession:(MXSession *)matrixSession
{
self = [super initWithMatrixSession:matrixSession];
if (self)
{
calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian];
// Note: NSDate object always shows time according to GMT, so the calendar should be in GMT too.
calendar.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"GMT"];
}
return self;
}
#pragma mark - Timestamp formatting
- (NSString*)dateStringFromDate:(NSDate *)date withTime:(BOOL)time
{
// Retrieve today date at midnight
NSDateComponents *components = [calendar components:(NSCalendarUnitYear | NSCalendarUnitMonth | NSCalendarUnitDay) fromDate:[NSDate date]];
NSDate *today = [calendar dateFromComponents:components];
NSTimeInterval interval = -[date timeIntervalSinceDate:today];
if (interval > 60*60*24*6)
{
dateFormat = @"EEE MMM dd yyyy";
return [super dateStringFromDate:date withTime:time];
}
else if (interval > 60*60*24)
{
dateFormat = @"EEEE";
return [super dateStringFromDate:date withTime:time];
}
else if (interval > 0)
{
if (time)
{
dateFormat = nil;
return [NSString stringWithFormat:@"yesterday %@", [super dateStringFromDate:date withTime:YES]];
}
return @"yesterday";
}
else if (interval > - 60*60*24)
{
if (time)
{
dateFormat = nil;
return [NSString stringWithFormat:@"today %@", [super dateStringFromDate:date withTime:YES]];
}
return @"today";
}
else
{
// Date in future
dateFormat = @"EEE MMM dd yyyy";
return [super dateStringFromDate:date withTime:time];
}
}
@end
@@ -222,6 +222,8 @@
[self dismissKeyboard];
MXKRoomMemberListDataSource *membersDataSource = [[MXKRoomMemberListDataSource alloc] initWithRoomId:self.roomDataSource.roomId andMatrixSession:self.mainSession];
[membersDataSource finalizeInitialization];
[membersController displayList:membersDataSource];
}
}
@@ -16,18 +16,16 @@
#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)dealloc
{
// [self stopTypingIndicatorAnimating];
}
- (void)render:(MXKCellData *)cellData
@@ -36,56 +34,20 @@
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;
}
}
// TODO handle here pagination display per day
// 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];
}
// TODO handle here timestamp display
// TODO handle here typing indicator
}
}
+ (CGFloat)heightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth
- (void)didEndDisplay
{
CGFloat rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth];
[super didEndDisplay];
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;
// Stop potential typing indicator
// [self stopTypingIndicatorAnimating];
}
@end
@@ -1,8 +1,8 @@
<?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">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="7706" systemVersion="14D131" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
@@ -21,15 +21,15 @@
<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"/>
<imageView hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="typing.png" translatesAutoresizingMaskIntoConstraints="NO" id="zwq-eh-8Fb" userLabel="typingBadge">
<rect key="frame" x="18" y="45" 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"/>
<rect key="frame" x="51" y="3" width="541" 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"/>
@@ -59,25 +59,6 @@
<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>
@@ -111,25 +92,19 @@
<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 firstAttribute="trailing" secondItem="q9c-0p-QyP" secondAttribute="trailing" constant="8" 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 firstItem="zwq-eh-8Fb" firstAttribute="centerX" secondItem="hgp-Z5-rAj" secondAttribute="centerX" id="Jse-fI-be0"/>
<constraint firstItem="zwq-eh-8Fb" firstAttribute="top" secondItem="hgp-Z5-rAj" secondAttribute="bottom" id="L78-IF-L1h"/>
<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 firstAttribute="trailing" relation="greaterThanOrEqual" secondItem="HTH-5n-MSU" secondAttribute="trailing" constant="8" id="Shz-6S-kGd"/>
<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"/>
@@ -141,8 +116,6 @@
<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"/>
@@ -157,4 +130,7 @@
</connections>
</tableViewCell>
</objects>
<resources>
<image name="typing.png" width="20" height="20"/>
</resources>
</document>
@@ -21,200 +21,43 @@
@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;
}
}
// TODO handle here pagination display per day
// TODO handle here timestamp display
// TODO handle here unsent
// 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}];
}
// 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];
// }
// }
}
}
@@ -1,6 +1,7 @@
<?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>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7703"/>
</dependencies>
<objects>
@@ -44,25 +45,6 @@
<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>
@@ -100,20 +82,14 @@
<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="tgO-Rv-C7R" firstAttribute="leading" relation="greaterThanOrEqual" secondItem="fCg-ju-gnG" secondAttribute="leading" constant="8" 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>
@@ -124,8 +100,6 @@
<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"/>