Room details: restore timestamp and unsent label display

This commit is contained in:
giomfo 2014-11-28 10:51:22 +01:00
parent 7752f0ddd2
commit 45a4c11eb9
8 changed files with 353 additions and 252 deletions

View file

@ -11,6 +11,7 @@
F00B5DB91A1B9BCE00EA1C8D /* CustomImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = F00B5DB81A1B9BCE00EA1C8D /* CustomImageView.m */; };
F01628C119E29C660071C473 /* default-profile.png in Resources */ = {isa = PBXBuildFile; fileRef = F01628BC19E29C660071C473 /* default-profile.png */; };
F01628C319E29C660071C473 /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = F01628BE19E29C660071C473 /* logo.png */; };
F01A0FF31A27314B009FAE2F /* RoomMessageComponent.m in Sources */ = {isa = PBXBuildFile; fileRef = F01A0FF21A27314B009FAE2F /* RoomMessageComponent.m */; };
F024098219E7D177006E741B /* tab_recents@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = F024098119E7D177006E741B /* tab_recents@2x.png */; };
F02BCE231A1A5A2B00543B47 /* play.png in Resources */ = {isa = PBXBuildFile; fileRef = F02BCE221A1A5A2B00543B47 /* play.png */; };
F02D707619F1DC9E007B47D3 /* RoomMemberTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F02D707519F1DC9E007B47D3 /* RoomMemberTableCell.m */; };
@ -59,6 +60,8 @@
F00B5DB81A1B9BCE00EA1C8D /* CustomImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CustomImageView.m; sourceTree = "<group>"; };
F01628BC19E29C660071C473 /* default-profile.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "default-profile.png"; sourceTree = "<group>"; };
F01628BE19E29C660071C473 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = "<group>"; };
F01A0FF11A27314B009FAE2F /* RoomMessageComponent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomMessageComponent.h; sourceTree = "<group>"; };
F01A0FF21A27314B009FAE2F /* RoomMessageComponent.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RoomMessageComponent.m; sourceTree = "<group>"; };
F024098119E7D177006E741B /* tab_recents@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "tab_recents@2x.png"; sourceTree = "<group>"; };
F02BCE221A1A5A2B00543B47 /* play.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = play.png; sourceTree = "<group>"; };
F02D707419F1DC9E007B47D3 /* RoomMemberTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RoomMemberTableCell.h; sourceTree = "<group>"; };
@ -203,6 +206,8 @@
children = (
F0465AF81A251F85003639F9 /* RoomMessage.h */,
F0465AF91A251F85003639F9 /* RoomMessage.m */,
F01A0FF11A27314B009FAE2F /* RoomMessageComponent.h */,
F01A0FF21A27314B009FAE2F /* RoomMessageComponent.m */,
);
path = Model;
sourceTree = "<group>";
@ -426,6 +431,7 @@
F07A80D819DD9DE700B621A1 /* main.m in Sources */,
F05B955F19DEED8A008761B0 /* MatrixHandler.m in Sources */,
F03EF5FB19F171EB00A0EE52 /* SettingsViewController.m in Sources */,
F01A0FF31A27314B009FAE2F /* RoomMessageComponent.m in Sources */,
F03EF5FA19F171EB00A0EE52 /* RoomViewController.m in Sources */,
F03EF5F819F171EB00A0EE52 /* MasterTabBarController.m in Sources */,
F03EF5F619F171EB00A0EE52 /* HomeViewController.m in Sources */,

View file

@ -4,6 +4,7 @@
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6244"/>
<capability name="Aspect ratio constraints" minToolsVersion="5.1"/>
<capability name="Constraints to layout margins" minToolsVersion="6.0"/>
</dependencies>
<scenes>
<!--Recents-->
@ -91,38 +92,34 @@
<action selector="showHideDateTime:" destination="msb-ol-2LB" eventType="touchUpInside" id="p6W-1a-kI3"/>
</connections>
</button>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Nov 13 14:44" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="yRH-kt-si3" userLabel="DateTimeLabel">
<rect key="frame" x="251" y="25" width="341" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="vdP-aa-23E"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ds0-yH-8Uu" userLabel="DateTimeView">
<rect key="frame" x="251" y="10" width="341" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="bottom" secondItem="J5R-Mh-3hV" secondAttribute="bottom" id="662-Ze-6ia"/>
<constraint firstItem="Ttt-0P-dQW" firstAttribute="leading" secondItem="egJ-aY-QVW" secondAttribute="trailing" id="6L3-Pz-zbG"/>
<constraint firstItem="ds0-yH-8Uu" firstAttribute="leading" secondItem="iJp-sA-hG6" secondAttribute="leadingMargin" constant="243" id="84m-nV-cXU"/>
<constraint firstItem="ds0-yH-8Uu" firstAttribute="top" secondItem="iJp-sA-hG6" secondAttribute="top" constant="10" id="998-YZ-TJ4"/>
<constraint firstItem="J5R-Mh-3hV" firstAttribute="leading" secondItem="mvK-ez-meg" secondAttribute="leading" constant="-8" id="Cbe-us-CeX"/>
<constraint firstAttribute="bottom" secondItem="Ttt-0P-dQW" secondAttribute="bottom" id="GAU-J5-ciT"/>
<constraint firstItem="yRH-kt-si3" firstAttribute="leading" secondItem="J5R-Mh-3hV" secondAttribute="trailing" id="JMP-AW-CDI"/>
<constraint firstItem="egJ-aY-QVW" firstAttribute="top" secondItem="iJp-sA-hG6" secondAttribute="top" constant="3" id="N8f-0n-ObR"/>
<constraint firstItem="Ttt-0P-dQW" firstAttribute="top" secondItem="iJp-sA-hG6" secondAttribute="top" id="Ptt-qa-Cg4"/>
<constraint firstItem="mvK-ez-meg" firstAttribute="centerY" secondItem="vF4-rq-4Rn" secondAttribute="centerY" id="ROj-jF-hIQ"/>
<constraint firstAttribute="trailing" secondItem="ds0-yH-8Uu" secondAttribute="trailing" constant="8" id="S8j-em-KNk"/>
<constraint firstItem="mvK-ez-meg" firstAttribute="centerX" secondItem="vF4-rq-4Rn" secondAttribute="centerX" id="VwV-of-MSN"/>
<constraint firstItem="J5R-Mh-3hV" firstAttribute="trailing" secondItem="mvK-ez-meg" secondAttribute="trailing" constant="8" id="Zdd-F0-OAU"/>
<constraint firstItem="mvK-ez-meg" firstAttribute="top" secondItem="J5R-Mh-3hV" secondAttribute="top" constant="8" id="d5n-ha-SuV"/>
<constraint firstItem="mvK-ez-meg" firstAttribute="bottom" secondItem="J5R-Mh-3hV" secondAttribute="bottom" constant="-8" id="eaa-XD-uyr"/>
<constraint firstItem="yRH-kt-si3" firstAttribute="top" secondItem="iJp-sA-hG6" secondAttribute="top" constant="25" id="epA-UD-55M"/>
<constraint firstItem="uhu-R0-9NH" firstAttribute="leading" secondItem="iJp-sA-hG6" secondAttribute="leading" constant="8" id="fNV-Tp-p31"/>
<constraint firstAttribute="trailing" secondItem="Ttt-0P-dQW" secondAttribute="trailing" id="mJg-Df-CWQ"/>
<constraint firstItem="uhu-R0-9NH" firstAttribute="top" secondItem="iJp-sA-hG6" secondAttribute="top" constant="5" id="mer-CT-cEg"/>
<constraint firstItem="J5R-Mh-3hV" firstAttribute="leading" secondItem="uhu-R0-9NH" secondAttribute="trailing" constant="3" id="qkW-ys-ckn"/>
<constraint firstItem="J5R-Mh-3hV" firstAttribute="top" secondItem="iJp-sA-hG6" secondAttribute="top" constant="10" id="rJt-w3-D8g"/>
<constraint firstAttribute="trailing" secondItem="yRH-kt-si3" secondAttribute="trailing" constant="8" id="vh8-ds-cqA"/>
<constraint firstItem="egJ-aY-QVW" firstAttribute="leading" secondItem="uhu-R0-9NH" secondAttribute="trailing" constant="3" id="wym-In-Raa"/>
<constraint firstAttribute="bottom" secondItem="ds0-yH-8Uu" secondAttribute="bottom" id="x7P-Ah-yBo"/>
</constraints>
</tableViewCellContentView>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
@ -130,8 +127,7 @@
<outlet property="attachmentView" destination="mvK-ez-meg" id="Qjc-lo-eJS"/>
<outlet property="attachmentViewBottomAlignmentConstraint" destination="eaa-XD-uyr" id="rfK-uw-FzW"/>
<outlet property="attachmentViewTopAlignmentConstraint" destination="d5n-ha-SuV" id="FaQ-jH-5C4"/>
<outlet property="dateTimeLabel" destination="yRH-kt-si3" id="udF-ai-dIs"/>
<outlet property="dateTimeLabelTopConstraint" destination="epA-UD-55M" id="QAB-r7-aT6"/>
<outlet property="dateTimeView" destination="ds0-yH-8Uu" id="9X2-Wi-VOS"/>
<outlet property="messageTextView" destination="J5R-Mh-3hV" id="d45-NI-bod"/>
<outlet property="msgTextViewBottomConstraint" destination="662-Ze-6ia" id="wfd-E3-UIV"/>
<outlet property="msgTextViewTopConstraint" destination="rJt-w3-D8g" id="6Um-o1-J08"/>
@ -177,16 +173,13 @@
<constraint firstAttribute="width" constant="40" id="tbf-QO-jKR"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Unsent" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="alD-cg-uMl">
<rect key="frame" x="286" y="14" width="58" height="21"/>
<view hidden="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="07b-tV-EBC" userLabel="UnsentView">
<rect key="frame" x="286" y="10" width="58" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="width" constant="58" id="GX0-HR-Wge"/>
<constraint firstAttribute="height" constant="21" id="q5G-LH-tGH"/>
<constraint firstAttribute="width" constant="58" id="g8G-uj-8gf"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<color key="textColor" red="1" green="0.0" blue="0.0" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fNQ-DX-U8F" userLabel="showHideDateTime">
<rect key="frame" x="0.0" y="0.0" width="349" height="50"/>
<state key="normal">
@ -196,38 +189,35 @@
<action selector="showHideDateTime:" destination="msb-ol-2LB" eventType="touchUpInside" id="eOG-oA-DQn"/>
</connections>
</button>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Nov 13 14:44" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="8" translatesAutoresizingMaskIntoConstraints="NO" id="2lD-F0-Haf" userLabel="DateTimeLabel">
<rect key="frame" x="8" y="25" width="341" height="20"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="OfQ-rH-7Bi"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<nil key="highlightedColor"/>
</label>
<view hidden="YES" userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Glo-Wx-mP6" userLabel="DateTimeView">
<rect key="frame" x="8" y="10" width="341" height="40"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="mks-jh-AiZ" secondAttribute="trailing" constant="8" id="1Fh-Wx-A41"/>
<constraint firstItem="2lD-F0-Haf" firstAttribute="top" secondItem="5tf-BC-9Ed" secondAttribute="top" constant="25" id="7Ee-H6-Wsd"/>
<constraint firstAttribute="bottom" secondItem="Glo-Wx-mP6" secondAttribute="bottom" id="23i-Iz-P2P"/>
<constraint firstItem="7qn-gi-w7s" firstAttribute="leading" secondItem="fNQ-DX-U8F" secondAttribute="trailing" id="AH0-j0-oAW"/>
<constraint firstItem="2lD-F0-Haf" firstAttribute="leading" secondItem="5tf-BC-9Ed" secondAttribute="leading" constant="8" id="CX9-TJ-kJ5"/>
<constraint firstAttribute="bottom" secondItem="07b-tV-EBC" secondAttribute="bottom" id="B5s-6H-lbt"/>
<constraint firstAttribute="trailingMargin" secondItem="07b-tV-EBC" secondAttribute="trailing" constant="248" id="G9X-vw-Q3y"/>
<constraint firstItem="Glo-Wx-mP6" firstAttribute="leading" secondItem="5tf-BC-9Ed" secondAttribute="leading" constant="8" id="HbL-hO-OE7"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="leading" secondItem="7qn-gi-w7s" secondAttribute="leading" constant="8" id="IVl-I8-Dzq"/>
<constraint firstItem="Glo-Wx-mP6" firstAttribute="top" secondItem="5tf-BC-9Ed" secondAttribute="top" constant="10" id="KAT-n3-5vl"/>
<constraint firstAttribute="bottom" secondItem="7qn-gi-w7s" secondAttribute="bottom" id="KPt-Vo-ntg"/>
<constraint firstItem="alD-cg-uMl" firstAttribute="top" secondItem="5tf-BC-9Ed" secondAttribute="top" constant="14" id="M1S-HJ-o3b"/>
<constraint firstItem="fNQ-DX-U8F" firstAttribute="leading" secondItem="5tf-BC-9Ed" secondAttribute="leading" id="MqK-3Z-lp5"/>
<constraint firstAttribute="bottom" secondItem="fNQ-DX-U8F" secondAttribute="bottom" id="NUK-Kq-ITl"/>
<constraint firstAttribute="trailingMargin" secondItem="Glo-Wx-mP6" secondAttribute="trailing" constant="243" id="NvS-It-8Cv"/>
<constraint firstItem="mks-jh-AiZ" firstAttribute="top" secondItem="5tf-BC-9Ed" secondAttribute="top" constant="5" id="SSl-4u-03L"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="top" secondItem="7qn-gi-w7s" secondAttribute="top" constant="8" id="U4f-kJ-Kgb"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="centerX" secondItem="0Bl-Sv-Q2H" secondAttribute="centerX" id="Uvh-A6-5G9"/>
<constraint firstItem="7qn-gi-w7s" firstAttribute="leading" secondItem="alD-cg-uMl" secondAttribute="trailing" constant="5" id="Vde-Q3-e8O"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="centerX" secondItem="Pq8-lB-cZM" secondAttribute="centerX" id="a0M-9u-hMC"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="centerY" secondItem="0Bl-Sv-Q2H" secondAttribute="centerY" id="aUE-Bv-XF2"/>
<constraint firstItem="mks-jh-AiZ" firstAttribute="leading" secondItem="7qn-gi-w7s" secondAttribute="trailing" constant="3" id="cCA-xk-XBe"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="centerY" secondItem="Pq8-lB-cZM" secondAttribute="centerY" id="ej0-OC-3hJ"/>
<constraint firstItem="07b-tV-EBC" firstAttribute="top" secondItem="5tf-BC-9Ed" secondAttribute="top" constant="10" id="fF0-vc-51U"/>
<constraint firstItem="7qn-gi-w7s" firstAttribute="top" secondItem="5tf-BC-9Ed" secondAttribute="top" constant="10" id="owD-KZ-snG"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="bottom" secondItem="7qn-gi-w7s" secondAttribute="bottom" constant="-8" id="q4h-EM-X4Z"/>
<constraint firstItem="7qn-gi-w7s" firstAttribute="leading" secondItem="2lD-F0-Haf" secondAttribute="trailing" id="q5r-bz-qP7"/>
<constraint firstItem="QZT-V8-yqJ" firstAttribute="trailing" secondItem="7qn-gi-w7s" secondAttribute="trailing" constant="-8" id="tgM-eJ-BEk"/>
<constraint firstItem="fNQ-DX-U8F" firstAttribute="top" secondItem="5tf-BC-9Ed" secondAttribute="top" id="wb3-8X-eQH"/>
</constraints>
@ -238,16 +228,14 @@
<outlet property="attachmentView" destination="QZT-V8-yqJ" id="Xrj-e5-Yd9"/>
<outlet property="attachmentViewBottomAlignmentConstraint" destination="q4h-EM-X4Z" id="yFu-TN-JOr"/>
<outlet property="attachmentViewTopAlignmentConstraint" destination="U4f-kJ-Kgb" id="OSr-ds-pmD"/>
<outlet property="dateTimeLabel" destination="2lD-F0-Haf" id="8Lb-qZ-9vJ"/>
<outlet property="dateTimeLabelTopConstraint" destination="7Ee-H6-Wsd" id="BFa-Sf-lzF"/>
<outlet property="dateTimeView" destination="Glo-Wx-mP6" id="12R-NA-lfr"/>
<outlet property="messageTextView" destination="7qn-gi-w7s" id="0b2-5P-dqR"/>
<outlet property="msgTextViewBottomConstraint" destination="KPt-Vo-ntg" id="5bw-yH-fYK"/>
<outlet property="msgTextViewTopConstraint" destination="owD-KZ-snG" id="oqc-0f-05O"/>
<outlet property="msgTextViewWidthConstraint" destination="a6g-WS-jjN" id="aM9-xQ-hVM"/>
<outlet property="pictureView" destination="mks-jh-AiZ" id="qL1-Kd-oRC"/>
<outlet property="playIconView" destination="0Bl-Sv-Q2H" id="VNa-J3-NuO"/>
<outlet property="unsentLabel" destination="alD-cg-uMl" id="UXm-mh-sux"/>
<outlet property="unsentLabelTopConstraint" destination="M1S-HJ-o3b" id="Geo-hs-Ils"/>
<outlet property="unsentView" destination="07b-tV-EBC" id="OET-EF-u1S"/>
</connections>
</tableViewCell>
</prototypes>

View file

@ -14,16 +14,16 @@
limitations under the License.
*/
#import <MatrixSDK/MatrixSDK.h>
#import "RoomMessageComponent.h"
#define ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH 200
#define ROOM_MESSAGE_CELL_IMAGE_MARGIN 8
extern NSString *const kLocalEchoEventIdPrefix;
extern NSString *const kFailedEventId;
#define ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH 200
#define ROOM_MESSAGE_TEXTVIEW_MARGIN 5
#define ROOM_MESSAGE_IMAGE_MARGIN 8
typedef enum : NSUInteger {
// Text type
RoomMessageTypeText,
// Attachment type
RoomMessageTypeImage,
RoomMessageTypeAudio,
RoomMessageTypeVideo,
@ -39,9 +39,11 @@ typedef enum : NSUInteger {
@property (nonatomic) NSString *senderAvatarUrl;
// The message content size depends on its type:
// - RoomMessageTypeText: returns suitable content size of a text view to display the whole text message
// - Others: returns suitable content size for an image view in order to display attachment thumbnail or icon.
// - Text (RoomMessageTypeText): returns suitable content size of a text view to display the whole text message
// - Attachment: returns suitable content size for an image view in order to display attachment thumbnail or icon.
@property (nonatomic) CGSize contentSize;
// Returns message components (Note: only one component is supported for attachment [messageType != RoomMessageTypeText])
@property (nonatomic) NSArray *components;
// The body of the message, or kind of content description in case of attachment (e.g. "image attachment")
@property (nonatomic) NSAttributedString *attributedTextMessage;

View file

@ -19,42 +19,17 @@
#import "MatrixHandler.h"
#import "AppSettings.h"
NSString *const kLocalEchoEventIdPrefix = @"localEcho-";
NSString *const kFailedEventId = @"failedEventId";
static NSDateFormatter *dateFormatter = nil;
static NSAttributedString *messageItemsSeparator = nil;
typedef enum : NSUInteger {
RoomMessageItemDisplayModeDefault,
RoomMessageItemDisplayModeHighlighted,
RoomMessageItemDisplayModeLocalEcho,
RoomMessageItemDisplayModeFailure,
RoomMessageItemDisplayModeError
} RoomMessageItemDisplayMode;
@interface RoomMessageItem : NSObject
@property (nonatomic) NSString *textMessage;
@property (nonatomic) NSString *eventId;
@property (nonatomic) NSDate *date;
@property (nonatomic) RoomMessageItemDisplayMode displayMode;
@property (nonatomic) NSUInteger height;
// True if text message starts with the sender name (see membership events, emote ...)
@property (nonatomic) BOOL startsWithSenderName;
- (id)initWithTextMessage:(NSString*)textMessage andEvent:(MXEvent*)event;
@end
#pragma mark -
static NSAttributedString *messageSeparator = nil;
@interface RoomMessage() {
// Array of RoomMessageItem
NSMutableArray *messageItems;
// Array of RoomMessageComponent
NSMutableArray *messageComponents;
// Current text message reset at each component change (see attributedTextMessage property)
NSMutableAttributedString *currentAttributedTextMsg;
}
+ (NSDateFormatter *)dateFormatter;
+ (NSAttributedString *)messageItemsSeparator;
+ (NSDictionary*)stringAttributesForDisplayMode:(RoomMessageItemDisplayMode)displayMode;
+ (NSAttributedString *)messageSeparator;
+ (NSDictionary *)stringAttributesForComponentStatus:(RoomMessageComponentStatus)status;
@end
@ -62,18 +37,15 @@ typedef enum : NSUInteger {
- (id)initWithEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState {
if (self = [super init]) {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
_senderId = event.userId;
_senderName = [roomState memberName:event.userId];
_senderAvatarUrl = [roomState memberWithUserId:event.userId].avatarUrl;
_contentSize = CGSizeZero;
currentAttributedTextMsg = nil;
// Build text message from event
NSString* textMessage = [mxHandler displayTextForEvent:event withRoomState:roomState inSubtitleMode:NO];
// Set the message type (use Text by default), and check attachment if any
// Set message type (consider text by default), and check attachment if any
_messageType = RoomMessageTypeText;
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
if ([mxHandler isSupportedAttachment:event]) {
// Note: event.eventType is equal here to MXEventTypeRoomMessage
NSString *msgtype = event.content[@"msgtype"];
@ -101,15 +73,14 @@ typedef enum : NSUInteger {
}
}
if (textMessage) {
// Create first message item
RoomMessageItem *msgItem = [[RoomMessageItem alloc] initWithTextMessage:textMessage andEvent:event];
msgItem.startsWithSenderName = ([textMessage hasPrefix:_senderName] || [mxHandler isEmote:event]);
messageItems = [NSMutableArray array];
[messageItems addObject:msgItem];
msgItem.height = self.contentSize.height;
}
else {
// Set first component of the current message
RoomMessageComponent *msgComponent = [[RoomMessageComponent alloc] initWithEvent:event andRoomState:roomState];
if (msgComponent) {
messageComponents = [NSMutableArray array];
[messageComponents addObject:msgComponent];
// Store the actual height of the text by removing textview margin from content height
msgComponent.height = self.contentSize.height - (2 * ROOM_MESSAGE_TEXTVIEW_MARGIN);
} else {
// Ignore this event
self = nil;
}
@ -118,7 +89,7 @@ typedef enum : NSUInteger {
}
- (void)dealloc {
messageItems = nil;
messageComponents = nil;
}
- (BOOL)addEvent:(MXEvent *)event withRoomState:(MXRoomState*)roomState {
@ -140,43 +111,38 @@ typedef enum : NSUInteger {
return NO;
}
NSString* textMessage = [mxHandler displayTextForEvent:event withRoomState:roomState inSubtitleMode:NO];
if (textMessage) {
// Create new message item
RoomMessageItem *addedItem = [[RoomMessageItem alloc] initWithTextMessage:textMessage andEvent:event];
addedItem.startsWithSenderName = ([textMessage hasPrefix:_senderName] || [mxHandler isEmote:event]);
// Insert the new item according to its date
NSUInteger index = messageItems.count;
NSMutableArray *savedMessageItems = [NSMutableArray arrayWithCapacity:index];
RoomMessageItem* msgItem;
if (addedItem.date) {
// Create new message component
RoomMessageComponent *addedComponent = [[RoomMessageComponent alloc] initWithEvent:event andRoomState:roomState];
if (addedComponent) {
// Insert the new component according to its date
NSUInteger index = messageComponents.count;
NSMutableArray *savedComponents = [NSMutableArray arrayWithCapacity:index];
RoomMessageComponent* msgComponent;
if (addedComponent.date) {
while (index--) {
msgItem = [messageItems lastObject];
if (!msgItem.date || [msgItem.date compare:addedItem.date] == NSOrderedDescending) {
[savedMessageItems insertObject:msgItem atIndex:0];
[messageItems removeLastObject];
msgComponent = [messageComponents lastObject];
if (!msgComponent.date || [msgComponent.date compare:addedComponent.date] == NSOrderedDescending) {
[savedComponents insertObject:msgComponent atIndex:0];
[messageComponents removeLastObject];
} else {
break;
}
}
}
// Force content size refresh
_attributedTextMessage = nil;
_contentSize = CGSizeZero;
CGFloat previousHeight = self.contentSize.height;
[messageItems addObject:addedItem];
// Force content size refresh after adding new item in order to compute its height
_attributedTextMessage = nil;
_contentSize = CGSizeZero;
addedItem.height = self.contentSize.height - previousHeight;
// Re-add existing message items (later in time than the new one)
for (msgItem in savedMessageItems) {
previousHeight = self.contentSize.height;
[messageItems addObject:msgItem];
// Force content size refresh after adding new item in order to compute its height
_attributedTextMessage = nil;
_contentSize = CGSizeZero;
msgItem.height = self.contentSize.height - previousHeight;
// Force text message refresh
self.attributedTextMessage = nil;
CGFloat previousTextViewHeight = self.contentSize.height ? self.contentSize.height : (2 * ROOM_MESSAGE_TEXTVIEW_MARGIN);
[messageComponents addObject:addedComponent];
// Force text message refresh after adding new component in order to compute its height
self.attributedTextMessage = nil;
addedComponent.height = self.contentSize.height - previousTextViewHeight;
// Re-add existing message components (later in time than the new one)
for (msgComponent in savedComponents) {
previousTextViewHeight = self.contentSize.height ? self.contentSize.height : (2 * ROOM_MESSAGE_TEXTVIEW_MARGIN);
[messageComponents addObject:msgComponent];
// Force text message refresh
self.attributedTextMessage = nil;
msgComponent.height = self.contentSize.height - previousTextViewHeight;
}
}
// else the event is ignored, we consider it as handled
@ -187,39 +153,37 @@ typedef enum : NSUInteger {
- (BOOL)removeEvent:(NSString *)eventId {
if (_messageType == RoomMessageTypeText) {
NSUInteger index = messageItems.count;
NSMutableArray *savedMessageItems = [NSMutableArray arrayWithCapacity:index];
RoomMessageItem* msgItem;
NSUInteger index = messageComponents.count;
NSMutableArray *savedComponents = [NSMutableArray arrayWithCapacity:index];
RoomMessageComponent* msgComponent;
while (index--) {
msgItem = [messageItems lastObject];
if ([msgItem.eventId isEqualToString:eventId] == NO) {
[savedMessageItems insertObject:msgItem atIndex:0];
[messageItems removeLastObject];
msgComponent = [messageComponents lastObject];
if ([msgComponent.eventId isEqualToString:eventId] == NO) {
[savedComponents insertObject:msgComponent atIndex:0];
[messageComponents removeLastObject];
} else {
[messageItems removeLastObject];
_attributedTextMessage = nil;
_contentSize = CGSizeZero;
for (msgItem in savedMessageItems) {
// Re-add message items
CGFloat previousHeight = self.contentSize.height;
[messageItems addObject:msgItem];
// Force content size refresh after adding new item in order to compute its height
_attributedTextMessage = nil;
_contentSize = CGSizeZero;
msgItem.height = self.contentSize.height - previousHeight;
[messageComponents removeLastObject];
// Force text message refresh
self.attributedTextMessage = nil;
for (msgComponent in savedComponents) {
// Re-add message components
CGFloat previousTextViewHeight = self.contentSize.height ? self.contentSize.height : (2 * ROOM_MESSAGE_TEXTVIEW_MARGIN);
[messageComponents addObject:msgComponent];
self.attributedTextMessage = nil;
msgComponent.height = self.contentSize.height - previousTextViewHeight;
}
return YES;
}
}
// here the provided eventId has not been found, restore message Items and return
messageItems = savedMessageItems;
// here the provided eventId has not been found, restore message components and return
messageComponents = savedComponents;
}
return NO;
}
- (BOOL)containsEventId:(NSString *)eventId {
for (RoomMessageItem* msgItem in messageItems) {
if ([msgItem.eventId isEqualToString:eventId]) {
for (RoomMessageComponent* msgComponent in messageComponents) {
if ([msgComponent.eventId isEqualToString:eventId]) {
return YES;
}
}
@ -233,7 +197,7 @@ typedef enum : NSUInteger {
if (_messageType == RoomMessageTypeText) {
if (self.attributedTextMessage.length) {
// Use a TextView template to compute cell height
UITextView *dummyTextView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH, MAXFLOAT)];
UITextView *dummyTextView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH, MAXFLOAT)];
dummyTextView.attributedText = self.attributedTextMessage;
_contentSize = [dummyTextView sizeThatFits:dummyTextView.frame.size];
}
@ -241,17 +205,17 @@ typedef enum : NSUInteger {
CGFloat width, height;
width = height = 40;
if (_thumbnailInfo) {
width = [_thumbnailInfo[@"w"] integerValue] + 2 * ROOM_MESSAGE_CELL_IMAGE_MARGIN;
height = [_thumbnailInfo[@"h"] integerValue] + 2 * ROOM_MESSAGE_CELL_IMAGE_MARGIN;
if (width > ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH || height > ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH) {
width = [_thumbnailInfo[@"w"] integerValue] + 2 * ROOM_MESSAGE_IMAGE_MARGIN;
height = [_thumbnailInfo[@"h"] integerValue] + 2 * ROOM_MESSAGE_IMAGE_MARGIN;
if (width > ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH || height > ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH) {
if (width > height) {
height = (height * ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH) / width;
height = (height * ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH) / width;
height = floorf(height / 2) * 2;
width = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH;
width = ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH;
} else {
width = (width * ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH) / height;
width = (width * ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH) / height;
width = floorf(width / 2) * 2;
height = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH;
height = ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH;
}
}
}
@ -263,31 +227,42 @@ typedef enum : NSUInteger {
return _contentSize;
}
- (NSArray*)components {
return [messageComponents copy];
}
- (void)setAttributedTextMessage:(NSAttributedString *)inAttributedTextMessage {
if (!inAttributedTextMessage.length) {
currentAttributedTextMsg = nil;
} else {
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:inAttributedTextMessage];
}
// Reset content size
_contentSize = CGSizeZero;
}
- (NSAttributedString*)attributedTextMessage {
if (!_attributedTextMessage && messageItems.count) {
if (!currentAttributedTextMsg && messageComponents.count) {
// Create attributed string
NSMutableAttributedString *mutableAttributedString = nil;
for (RoomMessageItem* msgItem in messageItems) {
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:msgItem.textMessage attributes:[RoomMessage stringAttributesForDisplayMode:msgItem.displayMode]];
if (!mutableAttributedString) {
mutableAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
for (RoomMessageComponent* msgComponent in messageComponents) {
NSAttributedString *attributedString = [[NSAttributedString alloc] initWithString:msgComponent.textMessage attributes:[RoomMessage stringAttributesForComponentStatus:msgComponent.status]];
if (!currentAttributedTextMsg) {
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:attributedString];
} else {
// Append attributed text
[mutableAttributedString appendAttributedString:[RoomMessage messageItemsSeparator]];
[mutableAttributedString appendAttributedString:attributedString];
[currentAttributedTextMsg appendAttributedString:[RoomMessage messageSeparator]];
[currentAttributedTextMsg appendAttributedString:attributedString];
}
}
_attributedTextMessage = mutableAttributedString;
}
return _attributedTextMessage;
return currentAttributedTextMsg;
}
- (BOOL)startsWithSenderName {
if (_messageType == RoomMessageTypeText) {
if (messageItems.count) {
RoomMessageItem *msgItem = [messageItems firstObject];
return msgItem.startsWithSenderName;
if (messageComponents.count) {
RoomMessageComponent *msgComponent = [messageComponents firstObject];
return msgComponent.startsWithSenderName;
}
}
return NO;
@ -295,9 +270,9 @@ typedef enum : NSUInteger {
- (BOOL)isUploadInProgress {
if (_messageType != RoomMessageTypeText) {
if (messageItems.count) {
RoomMessageItem *msgItem = [messageItems firstObject];
return (msgItem.displayMode == RoomMessageItemDisplayModeLocalEcho);
if (messageComponents.count) {
RoomMessageComponent *msgComponent = [messageComponents firstObject];
return (msgComponent.status == RoomMessageComponentStatusInProgress);
}
}
return NO;
@ -305,44 +280,30 @@ typedef enum : NSUInteger {
#pragma mark -
+ (NSDateFormatter *)dateFormatter {
+ (NSAttributedString *)messageSeparator {
@synchronized(self) {
if(dateFormatter == nil) {
NSString *dateFormat = @"MMM dd HH:mm";
dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:[[[NSBundle mainBundle] preferredLocalizations] objectAtIndex:0]]];
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[dateFormatter setTimeStyle:NSDateFormatterNoStyle];
[dateFormatter setDateFormat:dateFormat];
}
}
return dateFormatter;
}
+ (NSAttributedString *)messageItemsSeparator {
@synchronized(self) {
if(messageItemsSeparator == nil) {
messageItemsSeparator = [[NSAttributedString alloc] initWithString:@"\r\n\r\n" attributes:@{NSForegroundColorAttributeName : [UIColor blackColor],
if(messageSeparator == nil) {
messageSeparator = [[NSAttributedString alloc] initWithString:@"\r\n\r\n" attributes:@{NSForegroundColorAttributeName : [UIColor blackColor],
NSFontAttributeName: [UIFont systemFontOfSize:4]}];
}
}
return messageItemsSeparator;
return messageSeparator;
}
+ (NSDictionary*)stringAttributesForDisplayMode:(RoomMessageItemDisplayMode)displayMode {
+ (NSDictionary*)stringAttributesForComponentStatus:(RoomMessageComponentStatus)status {
UIColor *textColor;
switch (displayMode) {
case RoomMessageItemDisplayModeDefault:
switch (status) {
case RoomMessageComponentStatusNormal:
textColor = [UIColor blackColor];
break;
case RoomMessageItemDisplayModeHighlighted:
case RoomMessageComponentStatusHighlighted:
textColor = [UIColor blueColor];
break;
case RoomMessageItemDisplayModeLocalEcho:
case RoomMessageComponentStatusInProgress:
textColor = [UIColor lightGrayColor];
break;
case RoomMessageItemDisplayModeFailure:
case RoomMessageItemDisplayModeError:
case RoomMessageComponentStatusFailed:
case RoomMessageComponentStatusUnsupported:
textColor = [UIColor redColor];
break;
default:
@ -357,41 +318,3 @@ typedef enum : NSUInteger {
}
@end
# pragma mark -
@implementation RoomMessageItem
- (id)initWithTextMessage:(NSString*)textMessage andEvent:(MXEvent*)event {
if (self = [super init]) {
_textMessage = textMessage;
_eventId = event.eventId;
_height = 0;
// Set date time text label
if (event.originServerTs != kMXUndefinedTimestamp) {
_date = [NSDate dateWithTimeIntervalSince1970:event.originServerTs/1000];
// NSString* dateTime = [[RoomMessage dateFormatter] stringFromDate:_date];
} else {
_date = nil;
}
// Set display mode
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
BOOL isIncomingMsg = ([event.userId isEqualToString:mxHandler.userId] == NO);
if ([textMessage hasPrefix:kMatrixHandlerUnsupportedMessagePrefix]) {
_displayMode = RoomMessageItemDisplayModeError;
} else if ([_eventId hasPrefix:kFailedEventId]) {
_displayMode = RoomMessageItemDisplayModeFailure;
} else if (isIncomingMsg && ([textMessage rangeOfString:mxHandler.userDisplayName options:NSCaseInsensitiveSearch].location != NSNotFound || [textMessage rangeOfString:mxHandler.userId options:NSCaseInsensitiveSearch].location != NSNotFound)) {
_displayMode = RoomMessageItemDisplayModeHighlighted;
} else if (!isIncomingMsg && [_eventId hasPrefix:kLocalEchoEventIdPrefix]) {
_displayMode = RoomMessageItemDisplayModeLocalEcho;
} else {
_displayMode = RoomMessageItemDisplayModeDefault;
}
}
return self;
}
@end

View file

@ -0,0 +1,43 @@
/*
Copyright 2014 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 <MatrixSDK/MatrixSDK.h>
extern NSString *const kLocalEchoEventIdPrefix;
extern NSString *const kFailedEventId;
typedef enum : NSUInteger {
RoomMessageComponentStatusNormal,
RoomMessageComponentStatusHighlighted,
RoomMessageComponentStatusInProgress,
RoomMessageComponentStatusFailed,
RoomMessageComponentStatusUnsupported
} RoomMessageComponentStatus;
@interface RoomMessageComponent : NSObject
@property (nonatomic) NSString *textMessage;
@property (nonatomic) NSString *eventId;
@property (nonatomic) NSDate *date;
@property (nonatomic) RoomMessageComponentStatus status;
@property (nonatomic) CGFloat height;
// True if text message starts with the sender name (see membership events, emote ...)
@property (nonatomic) BOOL startsWithSenderName;
- (id)initWithEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState;
@end

View file

@ -0,0 +1,67 @@
/*
Copyright 2014 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 "RoomMessageComponent.h"
#import "MatrixHandler.h"
NSString *const kLocalEchoEventIdPrefix = @"localEcho-";
NSString *const kFailedEventId = @"failedEventId";
@implementation RoomMessageComponent
- (id)initWithEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState {
if (self = [super init]) {
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
// Build text component related to this event
NSString* textMessage = [mxHandler displayTextForEvent:event withRoomState:roomState inSubtitleMode:NO];
if (textMessage) {
_textMessage = textMessage;
_eventId = event.eventId;
_height = 0;
NSString *senderName = [roomState memberName:event.userId];
_startsWithSenderName = ([textMessage hasPrefix:senderName] || [mxHandler isEmote:event]);
// Set date time text label
if (event.originServerTs != kMXUndefinedTimestamp) {
_date = [NSDate dateWithTimeIntervalSince1970:event.originServerTs/1000];
} else {
_date = nil;
}
// Set display mode
BOOL isIncomingMsg = ([event.userId isEqualToString:mxHandler.userId] == NO);
if ([textMessage hasPrefix:kMatrixHandlerUnsupportedMessagePrefix]) {
_status = RoomMessageComponentStatusUnsupported;
} else if ([_eventId hasPrefix:kFailedEventId]) {
_status = RoomMessageComponentStatusFailed;
} else if (isIncomingMsg && ([textMessage rangeOfString:mxHandler.userDisplayName options:NSCaseInsensitiveSearch].location != NSNotFound || [textMessage rangeOfString:mxHandler.userId options:NSCaseInsensitiveSearch].location != NSNotFound)) {
_status = RoomMessageComponentStatusHighlighted;
} else if (!isIncomingMsg && [_eventId hasPrefix:kLocalEchoEventIdPrefix]) {
_status = RoomMessageComponentStatusInProgress;
} else {
_status = RoomMessageComponentStatusNormal;
}
} else {
// Ignore this event
self = nil;
}
}
return self;
}
@end

View file

@ -23,11 +23,10 @@
@property (weak, nonatomic) IBOutlet UITextView *messageTextView;
@property (strong, nonatomic) IBOutlet CustomImageView *attachmentView;
@property (strong, nonatomic) IBOutlet UIImageView *playIconView;
@property (weak, nonatomic) IBOutlet UILabel *dateTimeLabel;
@property (weak, nonatomic) IBOutlet UIView *dateTimeView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewWidthConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *msgTextViewBottomConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *dateTimeLabelTopConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *attachmentViewTopAlignmentConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *attachmentViewBottomAlignmentConstraint;
@end
@ -37,8 +36,7 @@
@end
@interface OutgoingMessageTableCell : RoomMessageTableCell
@property (weak, nonatomic) IBOutlet UILabel *unsentLabel;
@property (weak, nonatomic) IBOutlet UIView *unsentView;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *unsentLabelTopConstraint;
@end

View file

@ -66,7 +66,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
NSString *AVAudioSessionCategory;
MPMoviePlayerController *videoPlayer;
// Date formatter (nil if dateTimeLabel is hidden)
// Date formatter (nil if dateTime info is hidden)
NSDateFormatter *dateFormatter;
// Cache
@ -818,7 +818,14 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if ([message.senderId isEqualToString:mxHandler.userId]) {
cell = [tableView dequeueReusableCellWithIdentifier:@"OutgoingMessageCell" forIndexPath:indexPath];
[((OutgoingMessageTableCell*)cell).activityIndicator stopAnimating];
OutgoingMessageTableCell* outgoingMsgCell = (OutgoingMessageTableCell*)cell;
// Hide potential loading wheel
[outgoingMsgCell.activityIndicator stopAnimating];
// Hide unsent view by default, and remove potential unsent label(s)
outgoingMsgCell.unsentView.hidden = YES;
for (UIView *view in outgoingMsgCell.unsentView.subviews) {
[view removeFromSuperview];
}
} else {
cell = [tableView dequeueReusableCellWithIdentifier:@"IncomingMessageCell" forIndexPath:indexPath];
isIncomingMsg = YES;
@ -834,6 +841,17 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
}
cell.attachmentViewTopAlignmentConstraint.constant = 0;
cell.attachmentViewBottomAlignmentConstraint.constant = 0;
// Remove potential dateTime label(s)
if (cell.dateTimeView.constraints.count) {
if ([NSLayoutConstraint respondsToSelector:@selector(deactivateConstraints:)]) {
[NSLayoutConstraint deactivateConstraints:cell.dateTimeView.constraints];
} else {
[cell.dateTimeView removeConstraints:cell.dateTimeView.constraints];
}
for (UIView *view in cell.dateTimeView.subviews) {
[view removeFromSuperview];
}
}
// 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.
@ -868,8 +886,20 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
incomingMsgCell.userNameLabel.text = message.senderName;
} else {
OutgoingMessageTableCell* outgoingMsgCell = (OutgoingMessageTableCell*)cell;
// Hide unsent label by default
outgoingMsgCell.unsentLabel.hidden = YES; // TODO handle unsentLabel display
// Add unsent label for failed components
CGFloat yPosition = ROOM_MESSAGE_TEXTVIEW_MARGIN;
for (RoomMessageComponent *component in message.components) {
if (component.status == RoomMessageComponentStatusFailed) {
UILabel *unsentLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, yPosition, outgoingMsgCell.unsentView.frame.size.width , 20)];
unsentLabel.text = @"Unsent";
unsentLabel.textAlignment = NSTextAlignmentCenter;
unsentLabel.textColor = [UIColor redColor];
unsentLabel.font = [UIFont systemFontOfSize:14];
[outgoingMsgCell.unsentView addSubview:unsentLabel];
outgoingMsgCell.unsentView.hidden = NO;
}
yPosition += component.height;
}
}
CGSize contentSize = message.contentSize;
if (message.messageType != RoomMessageTypeText) {
@ -906,17 +936,61 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
// Adjust constraint constant
cell.msgTextViewWidthConstraint.constant = contentSize.width;
cell.attachmentViewTopAlignmentConstraint.constant = ROOM_MESSAGE_CELL_IMAGE_MARGIN;
cell.attachmentViewBottomAlignmentConstraint.constant = -ROOM_MESSAGE_CELL_IMAGE_MARGIN;
cell.attachmentViewTopAlignmentConstraint.constant = ROOM_MESSAGE_IMAGE_MARGIN;
cell.attachmentViewBottomAlignmentConstraint.constant = -ROOM_MESSAGE_IMAGE_MARGIN;
} else {
cell.messageTextView.attributedText = message.attributedTextMessage;
// Adjust textView width constraint
cell.msgTextViewWidthConstraint.constant = contentSize.width;
}
// TODO Handle timestamp display
cell.dateTimeLabel.hidden = YES;
// Handle timestamp display
if (dateFormatter) {
cell.dateTimeView.hidden = NO;
// Add datetime label for each component
CGFloat yPosition = ROOM_MESSAGE_TEXTVIEW_MARGIN;
for (RoomMessageComponent *component in message.components) {
if (component.date) {
UILabel *dateTimeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, yPosition, cell.dateTimeView.frame.size.width , 20)];
dateTimeLabel.text = [dateFormatter stringFromDate:component.date];
if (isIncomingMsg) {
dateTimeLabel.textAlignment = NSTextAlignmentRight;
} else {
dateTimeLabel.textAlignment = NSTextAlignmentLeft;
}
dateTimeLabel.textColor = [UIColor lightGrayColor];
dateTimeLabel.font = [UIFont systemFontOfSize:12];
dateTimeLabel.adjustsFontSizeToFitWidth = YES;
dateTimeLabel.minimumScaleFactor = 0.6;
[dateTimeLabel setTranslatesAutoresizingMaskIntoConstraints:NO];
[cell.dateTimeView addSubview:dateTimeLabel];
// Force dateTimeLabel in full width (to handle auto-layout in case of screen rotation)
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:dateTimeLabel
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:cell.dateTimeView
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:dateTimeLabel
attribute:NSLayoutAttributeTrailing
relatedBy:NSLayoutRelationEqual
toItem:cell.dateTimeView
attribute:NSLayoutAttributeTrailing
multiplier:1.0
constant:0];
if ([NSLayoutConstraint respondsToSelector:@selector(activateConstraints:)]) {
[NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint]];
} else {
[cell.dateTimeView addConstraint:leftConstraint];
[cell.dateTimeView addConstraint:rightConstraint];
}
}
yPosition += component.height;
}
} else {
cell.dateTimeView.hidden = YES;
}
return cell;
}
@ -1623,7 +1697,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
MXEvent *localEvent = [self addLocalEventForAttachedImage:selectedImage];
// Upload image and its thumbnail
MatrixHandler *mxHandler = [MatrixHandler sharedHandler];
NSUInteger thumbnailSize = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH - 2 * ROOM_MESSAGE_CELL_IMAGE_MARGIN;
NSUInteger thumbnailSize = ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH - 2 * ROOM_MESSAGE_IMAGE_MARGIN;
[mxHandler.mxRestClient uploadImage:selectedImage thumbnailSize:thumbnailSize timeout:30 success:^(NSDictionary *imageMessage) {
// Send image
[self postMessage:imageMessage withLocalEvent:localEvent];
@ -1644,7 +1718,7 @@ NSString *const kCmdResetUserPowerLevel = @"/deop";
if (videoThumbnail) {
// Prepare video thumbnail description
NSUInteger thumbnailSize = ROOM_MESSAGE_CELL_MAX_TEXTVIEW_WIDTH - 2 * ROOM_MESSAGE_CELL_IMAGE_MARGIN;
NSUInteger thumbnailSize = ROOM_MESSAGE_MAX_TEXTVIEW_WIDTH - 2 * ROOM_MESSAGE_IMAGE_MARGIN;
UIImage *thumbnail = [MediaManager resize:videoThumbnail toFitInSize:CGSizeMake(thumbnailSize, thumbnailSize)];
NSMutableDictionary *thumbnailInfo = [[NSMutableDictionary alloc] init];
[thumbnailInfo setValue:@"image/jpeg" forKey:@"mimetype"];