mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Room details: restore timestamp and unsent label display
This commit is contained in:
parent
7752f0ddd2
commit
45a4c11eb9
8 changed files with 353 additions and 252 deletions
|
@ -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 */,
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
43
matrixConsole/Model/RoomMessageComponent.h
Normal file
43
matrixConsole/Model/RoomMessageComponent.h
Normal 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
|
67
matrixConsole/Model/RoomMessageComponent.m
Normal file
67
matrixConsole/Model/RoomMessageComponent.m
Normal 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
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"];
|
||||
|
|
Loading…
Reference in a new issue