Merge branch 'ismail/5068_design_tweaks' into ismail/5096_thread_notifications

This commit is contained in:
ismailgulek
2022-01-12 13:58:48 +03:00
165 changed files with 4485 additions and 928 deletions
@@ -73,14 +73,6 @@
return ThemeService.shared.theme.statusBarStyle;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"AttachmentsViewer"];
}
- (void)destroy
{
[super destroy];
@@ -59,7 +59,7 @@ import Foundation
case .reply:
image = Asset.Images.roomContextMenuReply.image
case .replyInThread:
image = Asset.Images.roomContextMenuReplyInThread.image
image = Asset.Images.roomContextMenuThread.image
case .edit:
image = Asset.Images.roomContextMenuEdit.image
case .more:
@@ -564,7 +564,7 @@ const CGFloat kTypingCellHeight = 24;
constant:leftMargin],
topConstraint,
[threadSummaryView.heightAnchor constraintEqualToConstant:[ThreadSummaryView contentViewHeightForThread:component.thread fitting:cellData.maxTextViewWidth]],
[threadSummaryView.trailingAnchor constraintEqualToAnchor:threadSummaryView.superview.trailingAnchor constant:-RoomBubbleCellLayout.reactionsViewRightMargin]
[threadSummaryView.trailingAnchor constraintLessThanOrEqualToAnchor:threadSummaryView.superview.trailingAnchor constant:-RoomBubbleCellLayout.reactionsViewRightMargin]
]];
}
@@ -0,0 +1,50 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// Class to build an event menu.
@objcMembers
class EventMenuBuilder: NSObject {
private var items: [EventMenuItemType: UIAlertAction] = [:]
/// Returns true if no items or only one item with the type `EventMenuItemType.cancel`.
var isEmpty: Bool {
return items.isEmpty || (items.count == 1 && items.first?.key == .cancel)
}
/// Add a menu item.
/// - Parameters:
/// - type: item type
/// - action: alert action
func addItem(withType type: EventMenuItemType,
action: UIAlertAction) {
items[type] = action
}
/// Builds the action menu items.
/// - Returns: alert actions. Sorted by item types.
func build() -> [UIAlertAction] {
items.sorted(by: { $0.key < $1.key }).map { $1 }
}
/// Reset the builder. Builder will be empty after this method call.
func reset() {
items.removeAll()
}
}
@@ -0,0 +1,49 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// Type of an event menu item. Ordering of the cases is important. See `EventMenuBuilder`.
@objc
enum EventMenuItemType: Int {
case viewInRoom
case copy
case retrySending
case cancelSending
case cancelDownloading
case saveMedia
case quote
case forward
case permalink
case share
case removePoll
case endPoll
case reactionHistory
case viewSource
case viewDecryptedSource
case viewEncryption
case report
case remove
case cancel
}
extension EventMenuItemType: Comparable {
static func < (lhs: EventMenuItemType, rhs: EventMenuItemType) -> Bool {
return lhs.rawValue < rhs.rawValue
}
}
@@ -16,6 +16,8 @@ limitations under the License.
#import "MatrixKit.h"
@class AnalyticsScreenTimer;
/**
This view controller displays the attachments of a room. Only one matrix session is handled by this view controller.
*/
@@ -23,4 +25,9 @@ limitations under the License.
@property (nonatomic) BOOL showCancelBarButtonItem;
/**
The screen timer used for analytics if they've been enabled. The default value is nil.
*/
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
@end
@@ -110,6 +110,14 @@
[UIView setAnimationsEnabled:NO];
[self roomInputToolbarView:self.inputToolbarView heightDidChanged:0 completion:nil];
[UIView setAnimationsEnabled:YES];
[self.screenTimer start];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.screenTimer stop];
}
- (void)userInterfaceThemeDidChange
@@ -104,6 +104,8 @@
@property(nonatomic, strong) UserVerificationCoordinatorBridgePresenter *userVerificationCoordinatorBridgePresenter;
@property(nonatomic) AnalyticsScreenTimer *screenTimer;
@end
@implementation RoomMemberDetailsViewController
@@ -139,6 +141,8 @@
// Keep visible the status bar by default.
isStatusBarHidden = NO;
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenUser];
}
- (void)viewDidLoad
@@ -239,9 +243,6 @@
{
[super viewWillAppear:animated];
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"RoomMemberDetails"];
[self userInterfaceThemeDidChange];
// Hide the bottom border of the navigation bar to display the expander header
@@ -264,6 +265,18 @@
self.bottomImageView.hidden = YES;
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self.screenTimer start];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.screenTimer stop];
}
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id <UIViewControllerTransitionCoordinator>)coordinator
{
[super viewWillTransitionToSize:size withTransitionCoordinator:coordinator];
@@ -92,6 +92,11 @@
*/
@property (nonatomic, weak) id<RoomParticipantsViewControllerDelegate> delegate;
/**
The screen timer used for analytics if they've been enabled. The default value is nil.
*/
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
/**
Returns the `UINib` object initialized for a `RoomParticipantsViewController`.
@@ -245,9 +245,6 @@
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"RoomParticipants"];
// Refresh display
[self refreshTableView];
@@ -268,6 +265,8 @@
[contactsPickerViewController destroy];
contactsPickerViewController = nil;
}
[self.screenTimer start];
}
- (void)viewWillDisappear:(BOOL)animated
@@ -284,6 +283,12 @@
[self searchBarCancelButtonClicked:_searchBarView];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.screenTimer stop];
}
- (void)withdrawViewControllerAnimated:(BOOL)animated completion:(void (^)(void))completion
{
// Check whether the current view controller is displayed inside a segmented view controller in order to withdraw the right item
@@ -39,9 +39,11 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
participants.enableMention = true
participants.mxRoom = self.room
participants.delegate = self
participants.screenTimer = AnalyticsScreenTimer(screen: .roomMembers)
let files = RoomFilesViewController()
files.finalizeInit()
files.screenTimer = AnalyticsScreenTimer(screen: .roomUploads)
MXKRoomDataSource.load(withRoomId: self.room.roomId, andMatrixSession: self.session) { (dataSource) in
guard let dataSource = dataSource as? MXKRoomDataSource else { return }
dataSource.filterMessagesWithURL = true
@@ -52,6 +54,7 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
let settings = RoomSettingsViewController()
settings.finalizeInit()
settings.screenTimer = AnalyticsScreenTimer(screen: .roomSettings)
settings.initWith(self.session, andRoomId: self.room.roomId)
if self.room.isDirect {
@@ -40,6 +40,7 @@ final class RoomInfoListViewController: UIViewController {
private var errorPresenter: MXKErrorPresentation!
private var activityPresenter: ActivityIndicatorPresenter!
private var isRoomDirect: Bool = false
private var screenTimer = AnalyticsScreenTimer(screen: .roomDetails)
private lazy var closeButton: CloseButton = {
let button = CloseButton()
@@ -128,12 +129,22 @@ final class RoomInfoListViewController: UIViewController {
return self.theme.statusBarStyle
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
screenTimer.start()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
mainTableView.vc_relayoutHeaderView()
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
screenTimer.stop()
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: {_ in
self.basicInfoView.updateTrimmingOnTopic()
+203 -150
View File
@@ -257,11 +257,13 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
@property (nonatomic, strong) SpaceDetailPresenter *spaceDetailPresenter;
@property (nonatomic, strong) ShareManager *shareManager;
@property (nonatomic, strong) EventMenuBuilder *eventMenuBuilder;
@property (nonatomic, strong) UserSuggestionCoordinatorBridge *userSuggestionCoordinator;
@property (nonatomic, weak) IBOutlet UIView *userSuggestionContainerView;
@property (nonatomic, readwrite) RoomDisplayConfiguration *displayConfiguration;
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
@end
@@ -347,6 +349,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager];
formattedBodyParser = [FormattedBodyParser new];
self.eventMenuBuilder = [EventMenuBuilder new];
_showMissedDiscussionsBadge = YES;
_scrollToBottomHidden = YES;
@@ -360,6 +363,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
_voiceMessageController = [[VoiceMessageController alloc] initWithThemeService:ThemeService.shared mediaServiceProvider:VoiceMessageMediaServiceProvider.sharedProvider];
self.voiceMessageController.delegate = self;
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenRoom];
}
- (void)viewDidLoad
@@ -592,9 +597,6 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
{
[super viewWillAppear:animated];
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"ChatRoom"];
// Refresh the room title view
[self refreshRoomTitle];
@@ -635,8 +637,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
[self.roomDataSource reload];
[LegacyAppDelegate theDelegate].lastNavigatedRoomIdFromPush = nil;
notificationTaskProfile = [MXSDKOptions.sharedInstance.profiler startMeasuringTaskWithName:AnalyticsNoficationsTimeToDisplayContent
category:AnalyticsNoficationsCategory];
notificationTaskProfile = [MXSDKOptions.sharedInstance.profiler startMeasuringTaskWithName:MXTaskProfileNameNotificationsOpenEvent];
}
}
@@ -733,6 +734,9 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
hasJitsiCall = NO;
[self reloadBubblesTable:YES];
}
// Screen tracking
[self.screenTimer start];
}
- (void)viewDidDisappear:(BOOL)animated
@@ -768,6 +772,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
hasJitsiCall = YES;
[self reloadBubblesTable:YES];
}
[self.screenTimer stop];
}
- (void)viewDidLayoutSubviews
@@ -1554,8 +1560,9 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
- (BadgedBarButtonItem *)threadListBarButtonItem
{
UIButton *button = [UIButton new];
UIImage *icon = [[UIImage imageNamed:@"threads_icon"] vc_resizedWith:CGSizeMake(24, 24)];
button.contentEdgeInsets = UIEdgeInsetsMake(4, 8, 4, 8);
[button setImage:[UIImage imageNamed:@"room_context_menu_reply_in_thread"]
[button setImage:icon
forState:UIControlStateNormal];
[button addTarget:self
action:@selector(onThreadListTapped:)
@@ -3272,17 +3279,19 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
currentAlert = nil;
}
[self.eventMenuBuilder reset];
MXWeakify(self);
UIAlertController *actionsMenu = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
BOOL showThreadOption = RiotSettings.shared.enableThreads
&& !self.roomDataSource.threadId
&& !selectedEvent.threadId;
&& !self.roomDataSource.threadId
&& !selectedEvent.threadId;
if (showThreadOption && [self canCopyEvent:selectedEvent andCell:cell])
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCopy]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeCopy
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCopy]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3294,9 +3303,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
// Add actions for a failed event
if (selectedEvent.sentState == MXEventSentStateFailed)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n retry]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeRetrySending
action:[UIAlertAction actionWithTitle:[VectorL10n retry]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3305,9 +3315,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
[self.roomDataSource resendEventWithEventId:selectedEvent.eventId success:nil failure:nil];
}]];
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionDelete]
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeRemove
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionDelete]
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3316,6 +3327,22 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
// View in room action
if (self.roomDataSource.threadId && [selectedEvent.eventId isEqualToString:self.roomDataSource.threadId])
{
// if in the thread and selected event is the root event
// add "View in room" action
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewInRoom
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewInRoom]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self.delegate roomViewController:self
showRoomWithId:self.roomDataSource.roomId
eventId:selectedEvent.eventId];
}]];
}
// Add actions for text message
if (!attachment)
{
@@ -3337,9 +3364,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
selectedEvent.sentState == MXEventSentStateEncrypting ||
selectedEvent.sentState == MXEventSentStateSending)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancelSending
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
self->currentAlert = nil;
@@ -3352,35 +3380,12 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
if (self.roomDataSource.threadId && [selectedEvent.eventId isEqualToString:self.roomDataSource.threadId])
{
// if in the thread and selected event is the root event
// add "View in room" action
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewInRoom]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self.delegate roomViewController:self
showRoomWithId:self.roomDataSource.roomId
eventId:selectedEvent.eventId];
}]];
}
if (selectedEvent.sentState == MXEventSentStateSent && selectedEvent.eventType != MXEventTypePollStart)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self presentEventForwardingDialogForSelectedEvent:selectedEvent];
}]];
}
if (!isJitsiCallEvent && selectedEvent.eventType != MXEventTypePollStart)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionQuote]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeQuote
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionQuote]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3393,11 +3398,23 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
if (selectedEvent.sentState == MXEventSentStateSent && selectedEvent.eventType != MXEventTypePollStart)
{
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeForward
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self presentEventForwardingDialogForSelectedEvent:selectedEvent];
}]];
}
if (!isJitsiCallEvent && BuildSettings.messageDetailsAllowShare && selectedEvent.eventType != MXEventTypePollStart)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeShare
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3438,9 +3455,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
attachment.type == MXKAttachmentTypeImage ||
attachment.type == MXKAttachmentTypeVideo ||
attachment.type == MXKAttachmentTypeVoiceMessage)) {
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeForward
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionForward]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self presentEventForwardingDialogForSelectedEvent:selectedEvent];
}]];
@@ -3450,9 +3468,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
{
if (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionSave]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeSaveMedia
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionSave]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3487,9 +3506,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
NSString *uploadId = roomBubbleTableViewCell.bubbleData.attachment.contentURL;
if ([MXMediaManager existingUploaderWithId:uploadId])
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancelSending
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelSend]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
@@ -3521,9 +3541,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
{
if (BuildSettings.messageDetailsAllowShare)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeShare
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionShare]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3568,9 +3589,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
NSString *downloadId = roomBubbleTableViewCell.bubbleData.attachment.downloadId;
if ([MXMediaManager existingDownloaderWithIdentifier:downloadId])
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelDownload]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancelDownloading
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionCancelDownload]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3586,24 +3608,92 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
}
if (BuildSettings.messageDetailsAllowPermalink)
{
[self.eventMenuBuilder addItemWithType:EventMenuItemTypePermalink
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionPermalink]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
// Create a matrix.to permalink that is common to all matrix clients
NSString *permalink = [MXTools permalinkToEvent:selectedEvent.eventId inRoom:selectedEvent.roomId];
if (permalink)
{
MXKPasteboardManager.shared.pasteboard.string = permalink;
[self.view vc_toastWithMessage:VectorL10n.roomEventCopyLinkInfo
image:[UIImage imageNamed:@"link_icon"]
duration:2.0
position:ToastPositionBottom
additionalMargin:self.roomInputToolbarContainerHeightConstraint.constant];
}
else
{
MXLogDebug(@"[RoomViewController] Contextual menu permalink action failed. Permalink is nil room id/event id: %@/%@", selectedEvent.roomId, selectedEvent.eventId);
}
}]];
}
if (BuildSettings.messageDetailsAllowViewSource)
{
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewSource
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewSource]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
// Display event details
[self showEventDetails:selectedEvent];
}]];
// Add "View Decrypted Source" for e2ee event we can decrypt
if (selectedEvent.isEncrypted && selectedEvent.clearEvent)
{
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewDecryptedSource
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewDecryptedSource]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
// Display clear event details
[self showEventDetails:selectedEvent.clearEvent];
}]];
}
}
// Do not allow to redact the event that enabled encryption (m.room.encryption)
// because it breaks everything
if (selectedEvent.eventType != MXEventTypeRoomEncryption)
{
NSString *title;
UIAlertActionStyle style;
EventMenuItemType itemType;
if (selectedEvent.eventType == MXEventTypePollStart)
{
title = [VectorL10n roomEventActionRemovePoll];
style = UIAlertActionStyleDefault;
itemType = EventMenuItemTypeRemovePoll;
}
else
{
title = [VectorL10n roomEventActionRedact];
style = UIAlertActionStyleDestructive;
itemType = EventMenuItemTypeRemove;
}
[actionsMenu addAction:[UIAlertAction actionWithTitle:title
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:itemType
action:[UIAlertAction actionWithTitle:title
style:style
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3625,11 +3715,14 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
if (selectedEvent.eventType == MXEventTypePollStart && [selectedEvent.sender isEqualToString:self.mainSession.myUserId]) {
if ([self.delegate roomViewController:self canEndPollWithEventIdentifier:selectedEvent.eventId]) {
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionEndPoll]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (selectedEvent.eventType == MXEventTypePollStart && [selectedEvent.sender isEqualToString:self.mainSession.myUserId])
{
if ([self.delegate roomViewController:self canEndPollWithEventIdentifier:selectedEvent.eventId])
{
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeEndPoll
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionEndPoll]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self.delegate roomViewController:self endPollWithEventIdentifier:selectedEvent.eventId];
@@ -3639,43 +3732,13 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}
}
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self hideContextualMenuAnimated:YES];
}]];
if (BuildSettings.messageDetailsAllowPermalink)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionPermalink]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
// Create a matrix.to permalink that is common to all matrix clients
NSString *permalink = [MXTools permalinkToEvent:selectedEvent.eventId inRoom:selectedEvent.roomId];
if (permalink)
{
MXKPasteboardManager.shared.pasteboard.string = permalink;
}
else
{
MXLogDebug(@"[RoomViewController] Contextual menu permalink action failed. Permalink is nil room id/event id: %@/%@", selectedEvent.roomId, selectedEvent.eventId);
}
}]];
}
// Add reaction history if event contains reactions
if (roomBubbleTableViewCell.bubbleData.reactions[selectedEvent.eventId].aggregatedReactionsWithNonZeroCount)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReactionHistory]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeReactionHistory
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReactionHistory]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3685,41 +3748,12 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
if (BuildSettings.messageDetailsAllowViewSource)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewSource]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
// Display event details
[self showEventDetails:selectedEvent];
}]];
// Add "View Decrypted Source" for e2ee event we can decrypt
if (selectedEvent.isEncrypted && selectedEvent.clearEvent)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewDecryptedSource]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
// Display clear event details
[self showEventDetails:selectedEvent.clearEvent];
}]];
}
}
if (![selectedEvent.sender isEqualToString:self.mainSession.myUserId] && RiotSettings.shared.roomContextualMenuShowReportContentOption)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReport]
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeReport
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionReport]
style:UIAlertActionStyleDestructive
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3809,9 +3843,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
if (!isJitsiCallEvent && self.roomDataSource.room.summary.isEncrypted)
{
[actionsMenu addAction:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewEncryption]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeViewEncryption
action:[UIAlertAction actionWithTitle:[VectorL10n roomEventActionViewEncryption]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
@@ -3821,11 +3856,29 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}]];
}
[self.eventMenuBuilder addItemWithType:EventMenuItemTypeCancel
action:[UIAlertAction actionWithTitle:[VectorL10n cancel]
style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {
MXStrongifyAndReturnIfNil(self);
[self hideContextualMenuAnimated:YES];
}]];
}
// Do not display empty action sheet
if (actionsMenu.actions.count > 1)
if (!self.eventMenuBuilder.isEmpty)
{
UIAlertController *actionsMenu = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet];
// build actions and add them to the alert
NSArray<UIAlertAction*> *actions = [self.eventMenuBuilder build];
for (UIAlertAction *action in actions)
{
[actionsMenu addAction:action];
}
NSInteger bubbleComponentIndex = [roomBubbleTableViewCell.bubbleData bubbleComponentIndexForEventId:selectedEvent.eventId];
CGRect sourceRect = [roomBubbleTableViewCell componentFrameInContentViewForIndex:bubbleComponentIndex];
@@ -6074,10 +6127,6 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
NSMutableArray<RoomContextualMenuItem*> *items = [NSMutableArray arrayWithCapacity:5];
if (!showThreadOption)
{
[items addObject:[self copyMenuItemWithEvent:event andCell:cell]];
}
[items addObject:[self replyMenuItemWithEvent:event]];
if (showThreadOption)
{
@@ -6085,6 +6134,10 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
[items addObject:[self replyInThreadMenuItemWithEvent:event]];
}
[items addObject:[self editMenuItemWithEvent:event]];
if (!showThreadOption)
{
[items addObject:[self copyMenuItemWithEvent:event andCell:cell]];
}
if (showMoreOption)
{
[items addObject:[self moreMenuItemWithEvent:event andCell:cell]];
@@ -109,9 +109,6 @@
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"RoomFilesSearch"];
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
@@ -111,9 +111,6 @@
{
[super viewWillAppear:animated];
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"RoomMessagesSearch"];
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
@@ -33,6 +33,8 @@
MXKSearchDataSource *filesSearchDataSource;
}
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
@end
@implementation RoomSearchViewController
@@ -49,6 +51,8 @@
[super finalizeInit];
// The navigation bar tint color and the rageShake Manager are handled by super (see SegmentedViewController).
self.screenTimer = [[AnalyticsScreenTimer alloc] initWithScreen:AnalyticsScreenRoomSearch];
}
- (void)viewDidLoad
@@ -106,9 +110,6 @@
[self.activityIndicator stopAnimating];
self.activityIndicator = nil;
}
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"RoomsSearch"];
// Enable the search field by default at the screen opening
if (self.searchBarHidden)
@@ -124,6 +125,8 @@
// Refresh the search results.
// Note: We wait for 'viewDidAppear' call to consider the actual view size during this update.
[self updateSearch];
[self.screenTimer start];
}
- (void)viewWillDisappear:(BOOL)animated
@@ -138,6 +141,12 @@
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.screenTimer stop];
}
- (UIStatusBarStyle)preferredStatusBarStyle
{
return ThemeService.shared.theme.statusBarStyle;
@@ -19,6 +19,8 @@
#import "MediaPickerViewController.h"
#import "TableViewCellWithCheckBoxes.h"
@class AnalyticsScreenTimer;
/**
List the settings fields. Used to preselect/edit a field
*/
@@ -52,5 +54,10 @@ typedef enum : NSUInteger {
*/
@property (nonatomic) RoomSettingsViewControllerField selectedRoomSettingsField;
/**
The screen timer used for analytics if they've been enabled. The default value is nil.
*/
@property (nonatomic) AnalyticsScreenTimer *screenTimer;
@end
@@ -311,9 +311,6 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
// Screen tracking
[[Analytics sharedInstance] trackScreen:@"RoomSettings"];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(didUpdateRules:) name:kMXNotificationCenterDidUpdateRules object:nil];
@@ -334,6 +331,8 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
{
self.selectedRoomSettingsField = _selectedRoomSettingsField;
}
[self.screenTimer start];
}
- (void)viewWillDisappear:(BOOL)animated
@@ -351,6 +350,12 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[self.screenTimer stop];
}
// Those methods are called when the viewcontroller is added or removed from a container view controller.
- (void)willMoveToParentViewController:(nullable UIViewController *)parent
{
@@ -1027,27 +1032,32 @@ NSString *const kRoomSettingsAdvancedE2eEnabledCellViewIdentifier = @"kRoomSetti
[currentAlert addAction:[UIAlertAction actionWithTitle:[VectorL10n roomDetailsCopyRoomUrl]
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// Create a matrix.to permalink to the room
NSString *permalink = [MXTools permalinkToRoom:roomAliasLabel.text];
if (permalink)
{
MXKPasteboardManager.shared.pasteboard.string = permalink;
}
else
{
MXLogDebug(@"[RoomSettingsViewController] Copy room URL failed. Room URL is nil");
}
}
}]];
if (weakSelf)
{
typeof(self) self = weakSelf;
self->currentAlert = nil;
// Create a matrix.to permalink to the room
NSString *permalink = [MXTools permalinkToRoom:roomAliasLabel.text];
if (permalink)
{
MXKPasteboardManager.shared.pasteboard.string = permalink;
[self.view vc_toastWithMessage:VectorL10n.roomEventCopyLinkInfo
image:[UIImage imageNamed:@"link_icon"]
duration:2.0
position:ToastPositionBottom
additionalMargin:0.0];
}
else
{
MXLogDebug(@"[RoomSettingsViewController] Copy room URL failed. Room URL is nil");
}
}
}]];
// The user can only delete alias they has created, even if the Admin has set it as canonical.
// So, let the server answer if it's possible to delete an alias.
@@ -27,7 +27,7 @@ protocol ThreadSummaryViewDelegate: AnyObject {
class ThreadSummaryView: UIView {
private enum Constants {
static let viewHeight: CGFloat = 32
static let viewHeight: CGFloat = 40
static let viewDefaultWidth: CGFloat = 320
static let cornerRadius: CGFloat = 4
static let lastMessageFont: UIFont = .systemFont(ofSize: 13)
@@ -38,6 +38,7 @@ class ThreadSummaryView: UIView {
@IBOutlet private weak var lastMessageAvatarView: UserAvatarView!
@IBOutlet private weak var lastMessageContentLabel: UILabel!
private var theme: Theme = ThemeService.shared().theme
private(set) var thread: MXThread!
private lazy var tapGestureRecognizer: UITapGestureRecognizer = {
@@ -74,12 +75,12 @@ class ThreadSummaryView: UIView {
} else {
lastMessageAvatarView.avatarImageView.image = nil
}
if let lastMessageText = viewModel.lastMessageText {
let mutableAttributedString = NSMutableAttributedString(attributedString: lastMessageText)
mutableAttributedString.setAttributes([
if let lastMessage = viewModel.lastMessageText {
let mutable = NSMutableAttributedString(attributedString: lastMessage)
mutable.setAttributes([
.font: Constants.lastMessageFont
], range: NSRange(location: 0, length: mutableAttributedString.length))
lastMessageContentLabel.attributedText = mutableAttributedString
], range: NSRange(location: 0, length: mutable.length))
lastMessageContentLabel.attributedText = mutable
} else {
lastMessageContentLabel.attributedText = nil
}
@@ -112,7 +113,9 @@ class ThreadSummaryView: UIView {
room.state { [weak self] roomState in
guard let self = self else { return }
let formatterError = UnsafeMutablePointer<MXKEventFormatterError>.allocate(capacity: 1)
let lastMessageText = eventFormatter.attributedString(from: lastMessage, with: roomState, error: formatterError)
let lastMessageText = eventFormatter.attributedString(from: lastMessage,
with: roomState,
error: formatterError)
let viewModel = ThreadSummaryViewModel(numberOfReplies: thread.numberOfReplies,
lastMessageSenderAvatar: avatarViewData,
@@ -137,6 +140,8 @@ extension ThreadSummaryView: NibOwnerLoadable {}
extension ThreadSummaryView: Themable {
func update(theme: Theme) {
self.theme = theme
backgroundColor = theme.colors.system
iconView.tintColor = theme.colors.secondaryContent
numberOfRepliesLabel.textColor = theme.colors.secondaryContent
@@ -18,35 +18,38 @@
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB">
<rect key="frame" x="0.0" y="0.0" width="414" height="32"/>
<rect key="frame" x="0.0" y="0.0" width="414" height="40"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="TFL-sS-eJc">
<rect key="frame" x="8" y="4" width="398" height="24"/>
<rect key="frame" x="8" y="4" width="398" height="32"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="room_context_menu_reply_in_thread" translatesAutoresizingMaskIntoConstraints="NO" id="vva-PV-3Ya">
<rect key="frame" x="1" y="3" width="18" height="18"/>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="threads_icon" translatesAutoresizingMaskIntoConstraints="NO" id="vva-PV-3Ya">
<rect key="frame" x="4" y="3" width="26" height="26"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" secondItem="vva-PV-3Ya" secondAttribute="height" multiplier="1:1" id="972-WJ-2Zq"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="252" verticalHuggingPriority="251" text="1" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GcG-W8-9LR">
<rect key="frame" x="25" y="0.0" width="6" height="24"/>
<rect key="frame" x="34" y="0.0" width="6" height="32"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="6" id="9Nt-Rk-O81"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9wW-1f-f69" customClass="UserAvatarView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="39" y="0.0" width="24" height="24"/>
<rect key="frame" x="52" y="0.0" width="32" height="32"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" secondItem="9wW-1f-f69" secondAttribute="height" multiplier="1:1" id="V4H-JA-w4O"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="DVT-JI-3kw">
<rect key="frame" x="71" y="0.0" width="319" height="24"/>
<rect key="frame" x="92" y="0.0" width="298" height="32"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
@@ -60,14 +63,14 @@
<constraint firstAttribute="bottom" secondItem="GcG-W8-9LR" secondAttribute="bottom" id="FI4-bk-goz"/>
<constraint firstItem="9wW-1f-f69" firstAttribute="top" secondItem="TFL-sS-eJc" secondAttribute="top" id="GBe-gi-Iwc"/>
<constraint firstItem="DVT-JI-3kw" firstAttribute="top" secondItem="TFL-sS-eJc" secondAttribute="top" id="MSs-PD-tov"/>
<constraint firstItem="GcG-W8-9LR" firstAttribute="leading" secondItem="vva-PV-3Ya" secondAttribute="trailing" constant="6" id="PhI-J3-Ycb"/>
<constraint firstItem="GcG-W8-9LR" firstAttribute="leading" secondItem="vva-PV-3Ya" secondAttribute="trailing" constant="4" id="PhI-J3-Ycb"/>
<constraint firstItem="GcG-W8-9LR" firstAttribute="top" secondItem="TFL-sS-eJc" secondAttribute="top" id="Twp-gS-w3u"/>
<constraint firstAttribute="bottom" secondItem="9wW-1f-f69" secondAttribute="bottom" id="VG5-XU-DAK"/>
<constraint firstAttribute="trailing" secondItem="DVT-JI-3kw" secondAttribute="trailing" constant="8" id="bX2-Ha-8bf"/>
<constraint firstItem="DVT-JI-3kw" firstAttribute="leading" secondItem="9wW-1f-f69" secondAttribute="trailing" constant="8" id="qGg-0A-C6M"/>
<constraint firstItem="9wW-1f-f69" firstAttribute="leading" secondItem="GcG-W8-9LR" secondAttribute="trailing" constant="8" id="s2V-X9-cyI"/>
<constraint firstItem="9wW-1f-f69" firstAttribute="leading" secondItem="GcG-W8-9LR" secondAttribute="trailing" constant="12" id="s2V-X9-cyI"/>
<constraint firstAttribute="bottom" secondItem="vva-PV-3Ya" secondAttribute="bottom" constant="3" id="smY-cv-CoE"/>
<constraint firstItem="vva-PV-3Ya" firstAttribute="leading" secondItem="TFL-sS-eJc" secondAttribute="leading" constant="1" id="vyh-e4-Vy3"/>
<constraint firstItem="vva-PV-3Ya" firstAttribute="leading" secondItem="TFL-sS-eJc" secondAttribute="leading" constant="4" id="vyh-e4-Vy3"/>
</constraints>
</view>
</subviews>
@@ -84,6 +87,6 @@
</view>
</objects>
<resources>
<image name="room_context_menu_reply_in_thread" width="18" height="18"/>
<image name="threads_icon" width="32" height="32"/>
</resources>
</document>
@@ -25,11 +25,6 @@ enum ThreadRoomTitleViewMode {
@objcMembers
class ThreadRoomTitleView: RoomTitleView {
private enum Constants {
static let titleLeadingConstraintOnPortrait: CGFloat = 6
static let titleLeadingConstraintOnLandscape: CGFloat = 18
}
var mode: ThreadRoomTitleViewMode = .allThreads {
didSet {
update()
@@ -37,7 +32,6 @@ class ThreadRoomTitleView: RoomTitleView {
}
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var titleLabelLeadingConstraint: NSLayoutConstraint!
@IBOutlet private weak var roomAvatarView: RoomAvatarView!
@IBOutlet private weak var roomEncryptionBadgeView: UIImageView!
@IBOutlet private weak var roomNameLabel: UILabel!
@@ -81,7 +75,7 @@ class ThreadRoomTitleView: RoomTitleView {
room.displayName))
let encrpytionBadge: UIImage?
if let summary = room.summary, room.mxSession.crypto != nil {
if let summary = room.summary, summary.isEncrypted, room.mxSession.crypto != nil {
encrpytionBadge = EncryptionTrustLevelBadgeImageHelper.roomBadgeImage(for: summary.roomEncryptionTrustLevel())
} else {
encrpytionBadge = nil
@@ -100,16 +94,6 @@ class ThreadRoomTitleView: RoomTitleView {
registerThemeServiceDidChangeThemeNotification()
}
override func updateLayout(for orientation: UIInterfaceOrientation) {
super.updateLayout(for: orientation)
if orientation.isPortrait {
titleLabelLeadingConstraint.constant = Constants.titleLeadingConstraintOnPortrait
} else {
titleLabelLeadingConstraint.constant = Constants.titleLeadingConstraintOnLandscape
}
}
// MARK: - Private
private func registerThemeServiceDidChangeThemeNotification() {
@@ -12,61 +12,90 @@
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="ThreadRoomTitleView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="243" height="64"/>
<rect key="frame" x="0.0" y="0.0" width="243" height="44"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ami-Cg-fcA">
<rect key="frame" x="0.0" y="0.0" width="243" height="64"/>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" spacing="1" translatesAutoresizingMaskIntoConstraints="NO" id="0tP-MX-JE1">
<rect key="frame" x="6" y="0.0" width="221" height="44"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Thread" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="BnG-NU-7Mg">
<rect key="frame" x="18" y="22" width="56.5" height="20.5"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Bkf-Ia-XzU">
<rect key="frame" x="0.0" y="0.0" width="221" height="0.0"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" id="sTs-mz-Sem"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" text="Thread" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.5" translatesAutoresizingMaskIntoConstraints="NO" id="BnG-NU-7Mg">
<rect key="frame" x="0.0" y="1" width="221" height="20.5"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FJB-2F-rrQ" customClass="RoomAvatarView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="82.5" y="24" width="16" height="16"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="ABf-Vz-jLY">
<rect key="frame" x="0.0" y="22.5" width="221" height="2"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="16" id="Fg7-y5-fEC"/>
<constraint firstAttribute="height" constant="16" id="Qxm-RC-uC5"/>
<constraint firstAttribute="height" constant="2" id="kec-7k-q3g"/>
</constraints>
</view>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Mli-PC-WUh">
<rect key="frame" x="92.5" y="28" width="12" height="12"/>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Ww-tc-6by">
<rect key="frame" x="0.0" y="25.5" width="221" height="17.5"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="FJB-2F-rrQ" customClass="RoomAvatarView" customModule="Riot" customModuleProvider="target">
<rect key="frame" x="0.0" y="1" width="16" height="16"/>
<color key="backgroundColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" constant="16" id="Fg7-y5-fEC"/>
<constraint firstAttribute="height" constant="16" id="Qxm-RC-uC5"/>
</constraints>
</view>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="Mli-PC-WUh">
<rect key="frame" x="10" y="6" width="12" height="12"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" secondItem="Mli-PC-WUh" secondAttribute="height" multiplier="1:1" id="Ohw-dy-qg0"/>
<constraint firstAttribute="width" constant="12" id="nKB-SN-cO0"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Room name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumScaleFactor="0.69999999999999996" translatesAutoresizingMaskIntoConstraints="NO" id="8lk-sN-3IP">
<rect key="frame" x="27" y="1.5" width="194" height="14.5"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="width" secondItem="Mli-PC-WUh" secondAttribute="height" multiplier="1:1" id="Ohw-dy-qg0"/>
<constraint firstAttribute="width" constant="12" id="nKB-SN-cO0"/>
<constraint firstItem="Mli-PC-WUh" firstAttribute="bottom" secondItem="FJB-2F-rrQ" secondAttribute="bottom" constant="1" id="1SB-L0-yS4"/>
<constraint firstItem="8lk-sN-3IP" firstAttribute="centerY" secondItem="5Ww-tc-6by" secondAttribute="centerY" id="GFB-ot-8b1"/>
<constraint firstAttribute="trailing" secondItem="8lk-sN-3IP" secondAttribute="trailing" id="HCl-gK-Xhs"/>
<constraint firstItem="FJB-2F-rrQ" firstAttribute="centerY" secondItem="5Ww-tc-6by" secondAttribute="centerY" id="NiB-In-y7x"/>
<constraint firstItem="FJB-2F-rrQ" firstAttribute="leading" secondItem="5Ww-tc-6by" secondAttribute="leading" id="Smn-wx-c0n"/>
<constraint firstItem="8lk-sN-3IP" firstAttribute="leading" secondItem="FJB-2F-rrQ" secondAttribute="trailing" constant="11" id="Y1z-dr-Y0X"/>
<constraint firstItem="Mli-PC-WUh" firstAttribute="leading" secondItem="5Ww-tc-6by" secondAttribute="leading" constant="10" id="zH1-h4-JAC"/>
</constraints>
</imageView>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Room name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="8lk-sN-3IP">
<rect key="frame" x="109.5" y="24.5" width="67" height="15"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="tf4-ZQ-a7v">
<rect key="frame" x="0.0" y="44" width="221" height="0.0"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" id="Pb4-25-yBS"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="Mli-PC-WUh" firstAttribute="centerX" secondItem="FJB-2F-rrQ" secondAttribute="trailing" id="BU4-yl-DrP"/>
<constraint firstItem="BnG-NU-7Mg" firstAttribute="leading" secondItem="Ami-Cg-fcA" secondAttribute="leading" constant="18" id="ES6-mL-Y9F"/>
<constraint firstItem="8lk-sN-3IP" firstAttribute="centerY" secondItem="Ami-Cg-fcA" secondAttribute="centerY" id="S0S-6y-Vkn"/>
<constraint firstItem="FJB-2F-rrQ" firstAttribute="leading" secondItem="BnG-NU-7Mg" secondAttribute="trailing" constant="8" id="SQk-zN-CO6"/>
<constraint firstItem="FJB-2F-rrQ" firstAttribute="centerY" secondItem="Ami-Cg-fcA" secondAttribute="centerY" id="nY0-2s-Wgo"/>
<constraint firstItem="8lk-sN-3IP" firstAttribute="leading" secondItem="FJB-2F-rrQ" secondAttribute="trailing" constant="11" id="ql2-B3-82Y"/>
<constraint firstItem="BnG-NU-7Mg" firstAttribute="centerY" secondItem="Ami-Cg-fcA" secondAttribute="centerY" id="rwC-ak-Ydb"/>
<constraint firstItem="Mli-PC-WUh" firstAttribute="bottom" secondItem="FJB-2F-rrQ" secondAttribute="bottom" id="ulL-Xh-oCC"/>
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="32" id="Ctj-V2-sxt"/>
</constraints>
</view>
</stackView>
</subviews>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="vUN-kp-3ea" firstAttribute="trailing" secondItem="Ami-Cg-fcA" secondAttribute="trailing" id="a9m-d6-0go"/>
<constraint firstAttribute="bottom" secondItem="Ami-Cg-fcA" secondAttribute="bottom" id="fEm-nj-yVF"/>
<constraint firstItem="Ami-Cg-fcA" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" id="h0M-ab-EGv"/>
<constraint firstItem="Ami-Cg-fcA" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="zAN-VI-ZYk"/>
<constraint firstItem="0tP-MX-JE1" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="30P-0k-oLG"/>
<constraint firstItem="0tP-MX-JE1" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="6" id="Ef8-9o-qNh"/>
<constraint firstAttribute="bottom" secondItem="0tP-MX-JE1" secondAttribute="bottom" id="EqL-Br-ple"/>
<constraint firstAttribute="trailing" secondItem="0tP-MX-JE1" secondAttribute="trailing" constant="16" id="JQX-7q-vCS"/>
</constraints>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
@@ -74,9 +103,8 @@
<outlet property="roomEncryptionBadgeView" destination="Mli-PC-WUh" id="MuX-Qw-DfQ"/>
<outlet property="roomNameLabel" destination="8lk-sN-3IP" id="wFm-R4-fBo"/>
<outlet property="titleLabel" destination="BnG-NU-7Mg" id="gDw-Pr-oR8"/>
<outlet property="titleLabelLeadingConstraint" destination="ES6-mL-Y9F" id="MpE-vt-KKC"/>
</connections>
<point key="canvasLocation" x="0.7246376811594204" y="-152.00892857142856"/>
<point key="canvasLocation" x="10.869565217391305" y="-256.47321428571428"/>
</view>
</objects>
<resources>