Merge branch 'develop' into phlpro/4722_objc_headers

This commit is contained in:
Phl-Pro
2021-11-05 14:41:36 +01:00
committed by GitHub
50 changed files with 587 additions and 317 deletions

View File

@@ -118,9 +118,9 @@ final class BuildSettings: NSObject {
"https://element.io/help"
// MARk: - Matrix permalinks
// Paths for URLs that will considered as Matrix permalinks. Those permalinks are opened within the app
static let matrixPermalinkPaths: [String: [String]] = [
// MARK: - Permalinks
// Hosts/Paths for URLs that will considered as valid permalinks. Those permalinks are opened within the app.
static let permalinkSupportedHosts: [String: [String]] = [
"app.element.io": [],
"staging.element.io": [],
"develop.element.io": [],
@@ -133,8 +133,16 @@ final class BuildSettings: NSObject {
// Official Matrix ones
"matrix.to": ["/"],
"www.matrix.to": ["/"],
// Client Permalinks (for use with `BuildSettings.clientPermalinkBaseUrl`)
// "example.com": ["/"],
// "www.example.com": ["/"],
]
// For use in clients that use a custom base url for permalinks rather than matrix.to.
// This baseURL is used to generate permalinks within the app (E.g. timeline message permalinks).
// Optional String that when set is used as permalink base, when nil matrix.to format is used.
// Example value would be "https://www.example.com", note there is no trailing '/'.
static let clientPermalinkBaseUrl: String? = nil
// MARK: - VoIP
static var allowVoIPUsage: Bool {

View File

@@ -75,6 +75,7 @@ class CommonConfiguration: NSObject, Configurable {
// Disable key backup on common
sdkOptions.enableKeyBackupWhenStartingMXCrypto = false
sdkOptions.clientPermalinkBaseUrl = BuildSettings.clientPermalinkBaseUrl
// Configure key provider delegate
MXKeyProvider.sharedInstance().delegate = EncryptionKeyManager.shared
}

View File

@@ -73,7 +73,6 @@ abstract_target 'RiotPods' do
pod 'SideMenu', '~> 6.5'
pod 'DSWaveformImage', '~> 6.1.1'
pod 'ffmpeg-kit-ios-audio', '~> 4.5'
pod 'GrowingTextView', '~> 0.7.2'
pod 'FLEX', '~> 4.5.0', :configurations => ['Debug']

View File

@@ -1016,6 +1016,9 @@ Tap the + to start adding people.";
// Share extension
"share_extension_auth_prompt" = "Login in the main app to share content";
"share_extension_failed_to_encrypt" = "Failed to send. Check in the main app the encryption settings for this room";
"share_extension_low_quality_video_title" = "Video will be sent in low quality";
"share_extension_low_quality_video_message" = "Send in %@ for better quality, or send in low quality below.";
"share_extension_send_now" = "Send now";
// Room key request dialog
"e2e_room_key_request_title" = "Encryption key request";

View File

@@ -4775,6 +4775,18 @@ public class VectorL10n: NSObject {
public static var shareExtensionFailedToEncrypt: String {
return VectorL10n.tr("Vector", "share_extension_failed_to_encrypt")
}
/// Send in %@ for better quality, or send in low quality below.
public static func shareExtensionLowQualityVideoMessage(_ p1: String) -> String {
return VectorL10n.tr("Vector", "share_extension_low_quality_video_message", p1)
}
/// Video will be sent in low quality
public static var shareExtensionLowQualityVideoTitle: String {
return VectorL10n.tr("Vector", "share_extension_low_quality_video_title")
}
/// Send now
public static var shareExtensionSendNow: String {
return VectorL10n.tr("Vector", "share_extension_send_now")
}
/// Feedback
public static var sideMenuActionFeedback: String {
return VectorL10n.tr("Vector", "side_menu_action_feedback")

View File

@@ -32,7 +32,7 @@
/**
The delegate object to receive analytics events.
*/
@property (nonatomic) id<MXAnalyticsDelegate> delegate;
@property (nonatomic, weak) id<MXAnalyticsDelegate> delegate;
/**
Report an event unable to decrypt.

View File

@@ -40,7 +40,7 @@ FOUNDATION_EXPORT NSString *const RecentsViewControllerDataReadyNotification;
/**
Current alert (if any).
*/
UIAlertController *currentAlert;
__weak UIAlertController *currentAlert;
/**
The list of the section headers currently displayed in the recents table.

View File

@@ -50,13 +50,13 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
NSIndexPath* lastPotentialCellPath;
// Observe UIApplicationDidEnterBackgroundNotification to cancel editing mode when app leaves the foreground state.
id UIApplicationDidEnterBackgroundNotificationObserver;
__weak id UIApplicationDidEnterBackgroundNotificationObserver;
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
id kAppDelegateDidTapStatusBarNotificationObserver;
__weak id kAppDelegateDidTapStatusBarNotificationObserver;
// Observe kMXNotificationCenterDidUpdateRules to update missed messages counts.
id kMXNotificationCenterDidUpdateRulesObserver;
__weak id kMXNotificationCenterDidUpdateRulesObserver;
MXHTTPOperation *currentRequest;
@@ -65,7 +65,7 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
UISearchBar *tableSearchBar;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
__weak id kThemeServiceDidChangeThemeNotificationObserver;
}
@property (nonatomic, strong) CreateRoomCoordinatorBridgePresenter *createRoomCoordinatorBridgePresenter;
@@ -156,11 +156,15 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
// Apply dragging settings
self.enableDragging = _enableDragging;
MXWeakify(self);
// Observe UIApplicationDidEnterBackgroundNotification to refresh bubbles when app leaves the foreground state.
UIApplicationDidEnterBackgroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
// Leave potential editing mode
[self cancelEditionMode:isRefreshPending];
[self cancelEditionMode:self->isRefreshPending];
}];
@@ -170,6 +174,8 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
@@ -268,9 +274,13 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
[self.recentsTableView deselectRowAtIndexPath:indexPath animated:NO];
}
MXWeakify(self);
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self scrollToTop:YES];
}];
@@ -278,6 +288,8 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
// Observe kMXNotificationCenterDidUpdateRules to refresh missed messages counts
kMXNotificationCenterDidUpdateRulesObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXNotificationCenterDidUpdateRules object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
MXStrongifyAndReturnIfNil(self);
[self refreshRecentsTable];
}];

View File

@@ -46,7 +46,7 @@
NSLayoutConstraint *leftMarkerViewConstraint;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
__weak id kThemeServiceDidChangeThemeNotificationObserver;
}
@end
@@ -179,9 +179,13 @@
[self createSegmentedViews];
MXWeakify(self);
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];

View File

@@ -27,10 +27,10 @@
BOOL isRefreshPending;
// Observe UIApplicationDidEnterBackgroundNotification to cancel editing mode when app leaves the foreground state.
id UIApplicationDidEnterBackgroundNotificationObserver;
__weak id UIApplicationDidEnterBackgroundNotificationObserver;
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
id kAppDelegateDidTapStatusBarNotificationObserver;
__weak id kAppDelegateDidTapStatusBarNotificationObserver;
MXHTTPOperation *currentRequest;
@@ -39,7 +39,7 @@
UISearchBar *tableSearchBar;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
__weak id kThemeServiceDidChangeThemeNotificationObserver;
}
@end
@@ -98,11 +98,15 @@
self.groupsTableView.estimatedSectionHeaderHeight = 30;
self.groupsTableView.estimatedSectionFooterHeight = 0;
MXWeakify(self);
// Observe UIApplicationDidEnterBackgroundNotification to refresh bubbles when app leaves the foreground state.
UIApplicationDidEnterBackgroundNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidEnterBackgroundNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
// Leave potential editing mode
[self cancelEditionMode:isRefreshPending];
[self cancelEditionMode:self->isRefreshPending];
}];
@@ -117,6 +121,8 @@
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
@@ -207,9 +213,13 @@
[self.groupsTableView deselectRowAtIndexPath:indexPath animated:NO];
}
MXWeakify(self);
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self scrollToTop:YES];
}];

View File

@@ -43,6 +43,10 @@ final class GroupDetailsCoordinator: GroupDetailsCoordinatorProtocol {
self.groupDetailsViewController = groupDetailsViewController
}
deinit {
groupDetailsViewController.destroy()
}
// MARK: - Public
func start() {

View File

@@ -122,7 +122,7 @@
/**
The delegate for the view controller.
*/
@property (nonatomic) id<ContactsTableViewControllerDelegate> contactsTableViewControllerDelegate;
@property (nonatomic, weak) id<ContactsTableViewControllerDelegate> contactsTableViewControllerDelegate;
@end

View File

@@ -33,12 +33,12 @@
/**
Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
*/
id kAppDelegateDidTapStatusBarNotificationObserver;
__weak id kAppDelegateDidTapStatusBarNotificationObserver;
/**
Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
*/
id kThemeServiceDidChangeThemeNotificationObserver;
__weak id kThemeServiceDidChangeThemeNotificationObserver;
}
@property (nonatomic, strong) FindYourContactsFooterView *findYourContactsFooterView;
@@ -102,9 +102,13 @@
self.contactsTableView.tableFooterView = [[UIView alloc] init];
self.contactsAreFilteredWithSearch = NO;
MXWeakify(self);
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
@@ -158,10 +162,14 @@
// Screen tracking
[[Analytics sharedInstance] trackScreen:_screenName];
MXWeakify(self);
// Observe kAppDelegateDidTapStatusBarNotification.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self.contactsTableView setContentOffset:CGPointMake(-self.contactsTableView.adjustedContentInset.left, -self.contactsTableView.adjustedContentInset.top) animated:YES];
}];

View File

@@ -47,12 +47,12 @@
/**
Observe UIApplicationWillChangeStatusBarOrientationNotification to hide/show bubbles bg.
*/
id UIApplicationWillChangeStatusBarOrientationNotificationObserver;
__weak id UIApplicationWillChangeStatusBarOrientationNotificationObserver;
/**
The observer of the presence for matrix user.
*/
id mxPresenceObserver;
__weak id mxPresenceObserver;
/**
List of the basic actions on this contact.
@@ -79,7 +79,7 @@
/**
Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
*/
id kThemeServiceDidChangeThemeNotificationObserver;
__weak id kThemeServiceDidChangeThemeNotificationObserver;
/**
The current visibility of the status bar in this view controller.
@@ -182,9 +182,13 @@
self.bottomImageView.hidden = (orientation.integerValue == UIInterfaceOrientationLandscapeLeft || orientation.integerValue == UIInterfaceOrientationLandscapeRight);
}];
MXWeakify(self);
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
@@ -379,9 +383,13 @@
// Be warned when the thumbnail is updated
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onThumbnailUpdate:) name:kMXKContactThumbnailUpdateNotification object:nil];
MXWeakify(self);
// Observe contact presence change
mxPresenceObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKContactManagerMatrixUserPresenceChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
NSString* matrixId = self.firstMatrixId;
if (matrixId && [matrixId isEqualToString:notif.object])

View File

@@ -105,8 +105,8 @@ final class EnterNewRoomDetailsViewController: UIViewController {
var section3: Section?
if RiotSettings.shared.roomCreationScreenAllowEncryptionConfiguration {
let row_3_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.isEncrypted, onValueChanged: { (theSwitch) in
self.viewModel.roomCreationParameters.isEncrypted = theSwitch.isOn
let row_3_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.isEncrypted, onValueChanged: { [weak self] (theSwitch) in
self?.viewModel.roomCreationParameters.isEncrypted = theSwitch.isOn
}), text: VectorL10n.createRoomEnableEncryption, accessoryType: .none) {
// no-op
}
@@ -117,11 +117,20 @@ final class EnterNewRoomDetailsViewController: UIViewController {
var section4: Section?
if RiotSettings.shared.roomCreationScreenAllowRoomTypeConfiguration {
let row_4_0 = Row(type: .default, text: VectorL10n.createRoomTypePrivate, accessoryType: viewModel.roomCreationParameters.isPublic ? .none : .checkmark) {
let row_4_0 = Row(type: .default, text: VectorL10n.createRoomTypePrivate, accessoryType: viewModel.roomCreationParameters.isPublic ? .none : .checkmark) { [weak self] in
guard let self = self else {
return
}
self.viewModel.roomCreationParameters.isPublic = false
self.updateSections()
}
let row_4_1 = Row(type: .default, text: VectorL10n.createRoomTypePublic, accessoryType: viewModel.roomCreationParameters.isPublic ? .checkmark : .none) {
let row_4_1 = Row(type: .default, text: VectorL10n.createRoomTypePublic, accessoryType: viewModel.roomCreationParameters.isPublic ? .checkmark : .none) { [weak self] in
guard let self = self else {
return
}
self.viewModel.roomCreationParameters.isPublic = true
self.updateSections()
// scroll bottom to show user new fields
@@ -149,8 +158,8 @@ final class EnterNewRoomDetailsViewController: UIViewController {
}
if viewModel.roomCreationParameters.isPublic {
let row_5_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.showInDirectory, onValueChanged: { (theSwitch) in
self.viewModel.roomCreationParameters.showInDirectory = theSwitch.isOn
let row_5_0 = Row(type: .withSwitch(isOn: viewModel.roomCreationParameters.showInDirectory, onValueChanged: { [weak self] (theSwitch) in
self?.viewModel.roomCreationParameters.showInDirectory = theSwitch.isOn
}), text: VectorL10n.createRoomShowInDirectory, accessoryType: .none) {
// no-op
}
@@ -389,7 +398,10 @@ extension EnterNewRoomDetailsViewController: UITableViewDataSource {
cell.mxkLabel.text = row.text
cell.mxkSwitch.isOn = isOn
cell.mxkSwitch.removeTarget(nil, action: nil, for: .valueChanged)
cell.mxkSwitch.vc_addAction(for: .valueChanged) {
cell.mxkSwitch.vc_addAction(for: .valueChanged) { [weak cell] in
guard let cell = cell else {
return
}
onValueChanged?(cell.mxkSwitch)
}
cell.mxkLabelLeadingConstraint.constant = cell.vc_separatorInset.left

View File

@@ -81,7 +81,7 @@
/**
The delegate for the view controller.
*/
@property (nonatomic) id<JitsiViewControllerDelegate> delegate;
@property (nonatomic, weak) id<JitsiViewControllerDelegate> delegate;
@end

View File

@@ -44,7 +44,7 @@ const CGFloat kTypingCellHeight = 24;
// Timer used to debounce cells refresh
@property (nonatomic, strong) NSTimer *refreshCellsTimer;
@property (nonatomic, readonly) id<RoomDataSourceDelegate> roomDataSourceDelegate;
@property (nonatomic, weak, readonly) id<RoomDataSourceDelegate> roomDataSourceDelegate;
@property(nonatomic, readwrite) RoomEncryptionTrustLevel encryptionTrustLevel;

View File

@@ -54,6 +54,6 @@
/**
The delegate.
*/
@property (nonatomic) id<RoomMemberTitleViewDelegate> delegate;
@property (nonatomic, weak) id<RoomMemberTitleViewDelegate> delegate;
@end

View File

@@ -90,7 +90,7 @@
/**
The delegate for the view controller.
*/
@property (nonatomic) id<RoomParticipantsViewControllerDelegate> delegate;
@property (nonatomic, weak) id<RoomParticipantsViewControllerDelegate> delegate;
/**
Returns the `UINib` object initialized for a `RoomParticipantsViewController`.

View File

@@ -77,6 +77,10 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
super.init()
}
deinit {
roomViewController.destroy()
}
// MARK: - Public
@@ -90,7 +94,8 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
self.roomViewController.delegate = self
// Detect when view controller has been dismissed by gesture when presented modally (not in full screen).
self.roomViewController.presentationController?.delegate = self
// FIXME: Find a better way to manage modal dismiss. This makes the `roomViewController` to never be released
// self.roomViewController.presentationController?.delegate = self
if let eventId = self.selectedEventId {
self.loadRoom(withId: self.parameters.roomId, and: eventId, completion: completion)

View File

@@ -152,11 +152,10 @@ final class RoomInfoCoordinator: NSObject, RoomInfoCoordinatorType {
case .search:
MXKRoomDataSourceManager.sharedManager(forMatrixSession: session)?.roomDataSource(forRoom: self.room.roomId, create: false, onComplete: { (roomDataSource) in
guard let dataSource = roomDataSource else { return }
let storyboard = UIStoryboard(name: "Main", bundle: nil)
if let search = storyboard.instantiateViewController(withIdentifier: "RoomSearch") as? RoomSearchViewController {
search.roomDataSource = dataSource
self.navigationRouter.push(search, animated: animated, popCompletion: nil)
}
let roomSearchViewController: RoomSearchViewController = RoomSearchViewController.instantiate()
roomSearchViewController.loadViewIfNeeded()
roomSearchViewController.roomDataSource = dataSource
self.navigationRouter.push(roomSearchViewController, animated: animated, popCompletion: nil)
})
case .notifications:
let coordinator = createRoomNotificationSettingsCoordinator()

View File

@@ -50,8 +50,8 @@ final class RoomInfoListViewController: UIViewController {
private lazy var basicInfoView: RoomInfoBasicView = {
let view = RoomInfoBasicView.loadFromNib()
view.onTopicSizeChange = { _ in
self.view.setNeedsLayout()
view.onTopicSizeChange = { [weak self] _ in
self?.view.setNeedsLayout()
}
return view
}()

View File

@@ -144,7 +144,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
{
// The preview header
PreviewRoomTitleView *previewHeader;
__weak PreviewRoomTitleView *previewHeader;
// The customized room data source for Vector
RoomDataSource *customizedRoomDataSource;
@@ -156,7 +156,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
NSArray *currentTypingUsers;
// Typing notifications listener.
id typingNotifListener;
__weak id typingNotifListener;
// The position of the first touch down event stored in case of scrolling when the expanded header is visible.
CGPoint startScrollingPoint;
@@ -168,33 +168,33 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
UIView *missedDiscussionsDotView;
// Potential encryption details view.
EncryptionInfoView *encryptionInfoView;
__weak EncryptionInfoView *encryptionInfoView;
// The list of unknown devices that prevent outgoing messages from being sent
MXUsersDevicesMap<MXDeviceInfo*> *unknownDevices;
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
id kAppDelegateDidTapStatusBarNotificationObserver;
__weak id kAppDelegateDidTapStatusBarNotificationObserver;
// Observe kAppDelegateNetworkStatusDidChangeNotification to handle network status change.
id kAppDelegateNetworkStatusDidChangeNotificationObserver;
__weak id kAppDelegateNetworkStatusDidChangeNotificationObserver;
// Observers to manage MXSession state (and sync errors)
id kMXSessionStateDidChangeObserver;
__weak id kMXSessionStateDidChangeObserver;
// Observers to manage ongoing conference call banner
id kMXCallStateDidChangeObserver;
id kMXCallManagerConferenceStartedObserver;
id kMXCallManagerConferenceFinishedObserver;
__weak id kMXCallStateDidChangeObserver;
__weak id kMXCallManagerConferenceStartedObserver;
__weak id kMXCallManagerConferenceFinishedObserver;
// Observers to manage widgets
id kMXKWidgetManagerDidUpdateWidgetObserver;
__weak id kMXKWidgetManagerDidUpdateWidgetObserver;
// Observer kMXRoomSummaryDidChangeNotification to keep updated the missed discussion count
id mxRoomSummaryDidChangeObserver;
__weak id mxRoomSummaryDidChangeObserver;
// Observer for removing the re-request explanation/waiting dialog
id mxEventDidDecryptNotificationObserver;
__weak id mxEventDidDecryptNotificationObserver;
// The table view cell in which the read marker is displayed (nil by default).
MXKRoomBubbleTableViewCell *readMarkerTableViewCell;
@@ -209,13 +209,13 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
NSArray<UIBarButtonItem *> *rightBarButtonItems;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
__weak id kThemeServiceDidChangeThemeNotificationObserver;
// Observe URL preview updates to refresh cells.
id URLPreviewDidUpdateNotificationObserver;
__weak id URLPreviewDidUpdateNotificationObserver;
// Listener for `m.room.tombstone` event type
id tombstoneEventNotificationsListener;
__weak id tombstoneEventNotificationsListener;
// Homeserver notices
MXServerNotices *serverNotices;
@@ -454,9 +454,13 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
self.jumpToLastUnreadLabel.text = [VectorL10n roomJumpToFirstUnread];
MXWeakify(self);
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
@@ -587,9 +591,13 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
[self listenTombstoneEventNotifications];
[self listenMXSessionStateChangeNotifications];
MXWeakify(self);
// Observe kAppDelegateDidTapStatusBarNotification.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self setBubbleTableViewContentOffset:CGPointMake(-self.bubblesTableView.adjustedContentInset.left, -self.bubblesTableView.adjustedContentInset.top) animated:YES];
}];
@@ -661,9 +669,13 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
[AppDelegate theDelegate].visibleRoomId = self.roomDataSource.roomId;
}
MXWeakify(self);
// Observe network reachability
kAppDelegateNetworkStatusDidChangeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateNetworkStatusDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self refreshActivitiesViewDisplay];
}];
@@ -673,6 +685,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
// Observe missed notifications
mxRoomSummaryDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXRoomSummaryDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
MXRoomSummary *roomSummary = notif.object;
if ([roomSummary.roomId isEqualToString:self.roomDataSource.roomId])
@@ -1398,8 +1412,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}
if (URLPreviewDidUpdateNotificationObserver)
{
[NSNotificationCenter.defaultCenter removeObserver:URLPreviewDidUpdateNotificationObserver];
URLPreviewDidUpdateNotificationObserver = nil;
[NSNotificationCenter.defaultCenter removeObserver:URLPreviewDidUpdateNotificationObserver];
}
[self removeCallNotificationsListeners];
@@ -1555,8 +1568,12 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
- (void)registerURLPreviewNotifications
{
MXWeakify(self);
URLPreviewDidUpdateNotificationObserver = [NSNotificationCenter.defaultCenter addObserverForName:URLPreviewDidUpdateNotification object:nil queue:NSOperationQueue.mainQueue usingBlock:^(NSNotification * _Nonnull notification) {
MXStrongifyAndReturnIfNil(self);
// Ensure this is the correct room
if (![(NSString*)notification.userInfo[@"roomId"] isEqualToString:self.roomDataSource.roomId])
{
@@ -2013,6 +2030,11 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
roomInputView.actionsBar.actionItems = actionItems;
}
- (NSString *)textInputContextIdentifier
{
return self.roomDataSource.roomId;
}
- (void)roomInputToolbarViewPresentStickerPicker
{
// Search for the sticker picker widget in the user account
@@ -2240,6 +2262,12 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}
UIViewController *suggestionsViewController = self.userSuggestionCoordinator.toPresentable;
if (!suggestionsViewController)
{
return;
}
[suggestionsViewController.view setTranslatesAutoresizingMaskIntoConstraints:NO];
[self addChildViewController:suggestionsViewController];
@@ -2336,11 +2364,14 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
if (isVisible)
{
previewHeader = [PreviewRoomTitleView roomTitleView];
PreviewRoomTitleView *previewHeader = [PreviewRoomTitleView roomTitleView];
previewHeader.delegate = self;
previewHeader.tapGestureDelegate = self;
previewHeader.translatesAutoresizingMaskIntoConstraints = NO;
[self.previewHeaderContainer addSubview:previewHeader];
self->previewHeader = previewHeader;
// Force preview header in full width
NSLayoutConstraint *leftConstraint = [NSLayoutConstraint constraintWithItem:previewHeader
attribute:NSLayoutAttributeLeading
@@ -4768,10 +4799,14 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
- (void)listenCallNotifications
{
MXWeakify(self);
kMXCallStateDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXCallStateDidChange object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
MXCall *call = notif.object;
if ([call.room.roomId isEqualToString:customizedRoomDataSource.roomId])
if ([call.room.roomId isEqualToString:self->customizedRoomDataSource.roomId])
{
[self refreshActivitiesViewDisplay];
[self refreshRoomInputToolbar];
@@ -4779,16 +4814,20 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
}];
kMXCallManagerConferenceStartedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXCallManagerConferenceStarted object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
NSString *roomId = notif.object;
if ([roomId isEqualToString:customizedRoomDataSource.roomId])
if ([roomId isEqualToString:self->customizedRoomDataSource.roomId])
{
[self refreshActivitiesViewDisplay];
}
}];
kMXCallManagerConferenceFinishedObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXCallManagerConferenceFinished object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
NSString *roomId = notif.object;
if ([roomId isEqualToString:customizedRoomDataSource.roomId])
if ([roomId isEqualToString:self->customizedRoomDataSource.roomId])
{
[self refreshActivitiesViewDisplay];
[self refreshRoomInputToolbar];
@@ -5373,7 +5412,7 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
// Remove potential existing subviews
[self dismissTemporarySubViews];
encryptionInfoView = [[EncryptionInfoView alloc] initWithEvent:event andMatrixSession:self.roomDataSource.mxSession];
EncryptionInfoView *encryptionInfoView = [[EncryptionInfoView alloc] initWithEvent:event andMatrixSession:self.roomDataSource.mxSession];
// Add shadow on added view
encryptionInfoView.layer.cornerRadius = 5;
@@ -5383,6 +5422,8 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
// Add the view and define edge constraints
[self.view addSubview:encryptionInfoView];
self->encryptionInfoView = encryptionInfoView;
[self.view addConstraint:[NSLayoutConstraint constraintWithItem:encryptionInfoView
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
@@ -5839,8 +5880,12 @@ const NSTimeInterval kResizeComposerAnimationDuration = .05;
- (void)listenMXSessionStateChangeNotifications
{
MXWeakify(self);
kMXSessionStateDidChangeObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXSessionStateDidChangeNotification object:self.roomDataSource.mxSession queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
if (self.roomDataSource.mxSession.state == MXSessionStateSyncError
|| self.roomDataSource.mxSession.state == MXSessionStateRunning)
{

View File

@@ -25,4 +25,6 @@
*/
@property (nonatomic) MXKRoomDataSource *roomDataSource;
+ (instancetype)instantiate;
@end

View File

@@ -37,6 +37,13 @@
@implementation RoomSearchViewController
+ (instancetype)instantiate
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:[NSBundle mainBundle]];
RoomSearchViewController *viewController = [storyboard instantiateViewControllerWithIdentifier:@"RoomSearch"];
return viewController;
}
- (void)finalizeInit
{
[super finalizeInit];

View File

@@ -14,26 +14,100 @@
// limitations under the License.
//
import GrowingTextView
@objc protocol RoomInputToolbarTextViewDelegate: AnyObject {
func textView(_ textView: RoomInputToolbarTextView, didChangeHeight height: CGFloat)
func textView(_ textView: RoomInputToolbarTextView, didReceivePasteForMediaFromSender sender: Any?)
}
class RoomInputToolbarTextView: GrowingTextView {
@objcMembers
class RoomInputToolbarTextView: UITextView {
@objc weak var toolbarDelegate: RoomInputToolbarTextViewDelegate?
override var keyCommands: [UIKeyCommand]? {
return [UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(keyCommandSelector(_:)))]
private var heightConstraint: NSLayoutConstraint!
weak var toolbarDelegate: RoomInputToolbarTextViewDelegate?
var placeholder: String? {
didSet {
setNeedsDisplay()
}
}
@objc private func keyCommandSelector(_ keyCommand: UIKeyCommand) {
guard keyCommand.input == "\r", let delegate = (self.delegate as? RoomInputToolbarView) else {
var placeholderColor: UIColor = UIColor(white: 0.8, alpha: 1.0) {
didSet {
setNeedsDisplay()
}
}
var minHeight: CGFloat = 30.0 {
didSet {
updateUI()
}
}
var maxHeight: CGFloat = 0.0 {
didSet {
updateUI()
}
}
override var text: String! {
didSet {
updateUI()
}
}
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
commonInit()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
commonInit()
}
private func commonInit() {
contentMode = .redraw
NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: UITextView.textDidChangeNotification, object: self)
if let heightConstraint = constraints.filter({ $0.firstAttribute == .height && $0.relation == .equal }).first {
self.heightConstraint = heightConstraint
} else {
heightConstraint = self.heightAnchor.constraint(equalToConstant: minHeight)
addConstraint(heightConstraint)
}
}
// MARK: - Overrides
override func layoutSubviews() {
super.layoutSubviews()
updateUI()
}
override func draw(_ rect: CGRect) {
super.draw(rect)
guard text.isEmpty, let placeholder = placeholder else {
return
}
delegate.onTouchUp(inside: delegate.rightInputToolbarButton)
var attributes: [NSAttributedString.Key: Any] = [.foregroundColor: placeholderColor]
if let font = font {
attributes[.font] = font
}
let frame = rect.inset(by: .init(top: textContainerInset.top,
left: textContainerInset.left + textContainer.lineFragmentPadding,
bottom: textContainerInset.bottom,
right: textContainerInset.right))
placeholder.draw(in: frame, withAttributes: attributes)
}
override var keyCommands: [UIKeyCommand]? {
return [UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(keyCommandSelector(_:)))]
}
/// Overrides paste to handle images pasted from Safari, passing them up to the input toolbar.
@@ -49,4 +123,36 @@ class RoomInputToolbarTextView: GrowingTextView {
super.paste(sender)
}
}
// MARK: - Private
@objc private func textDidChange(notification: Notification) {
if let sender = notification.object as? RoomInputToolbarTextView, sender == self {
updateUI()
}
}
private func updateUI() {
var height = sizeThatFits(CGSize(width: bounds.size.width, height: CGFloat.greatestFiniteMagnitude)).height
height = minHeight > 0 ? max(height, minHeight) : height
height = maxHeight > 0 ? min(height, maxHeight) : height
// Update placeholder
self.setNeedsDisplay()
guard height != heightConstraint.constant else {
return
}
heightConstraint.constant = height
toolbarDelegate?.textView(self, didChangeHeight: height)
}
@objc private func keyCommandSelector(_ keyCommand: UIKeyCommand) {
guard keyCommand.input == "\r", let delegate = (self.delegate as? RoomInputToolbarView) else {
return
}
delegate.onTouchUp(inside: delegate.rightInputToolbarButton)
}
}

View File

@@ -60,28 +60,10 @@ typedef enum : NSUInteger
*/
@property (nonatomic, weak) id<RoomInputToolbarViewDelegate> delegate;
@property (weak, nonatomic) IBOutlet UIView *mainToolbarView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarMinHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *mainToolbarHeightConstraint;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *messageComposerContainerTrailingConstraint;
@property (weak, nonatomic) IBOutlet UIButton *attachMediaButton;
@property (weak, nonatomic) IBOutlet UIImageView *inputTextBackgroundView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *inputContextViewHeightConstraint;
@property (weak, nonatomic) IBOutlet UIImageView *inputContextImageView;
@property (weak, nonatomic) IBOutlet UILabel *inputContextLabel;
@property (weak, nonatomic) IBOutlet UIButton *inputContextButton;
@property (weak, nonatomic) IBOutlet RoomActionsBar *actionsBar;
@property (weak, nonatomic) UIView *voiceMessageToolbarView;
/**
Tell whether the filled data will be sent encrypted. NO by default.
*/
@property (nonatomic) BOOL isEncryptionEnabled;
@property (nonatomic, assign) BOOL isEncryptionEnabled;
/**
Sender of the event being edited / replied.
@@ -91,11 +73,31 @@ typedef enum : NSUInteger
/**
Destination of the message in the composer.
*/
@property (nonatomic) RoomInputToolbarViewSendMode sendMode;
@property (nonatomic, assign) RoomInputToolbarViewSendMode sendMode;
/**
YES if action menu is opened. NO otherwise
*/
@property (nonatomic, getter=isActionMenuOpened) BOOL actionMenuOpened;
@property (nonatomic, assign) BOOL actionMenuOpened;
/**
The input toolbar's main height constraint
*/
@property (nonatomic, weak, readonly) NSLayoutConstraint *mainToolbarHeightConstraint;
/**
The input toolbar's action bar
*/
@property (nonatomic, weak, readonly) RoomActionsBar *actionsBar;
/**
The attach media button
*/
@property (nonatomic, weak, readonly) UIButton *attachMediaButton;
/**
Adds a voice message toolbar view to be displayed inside this input toolbar
*/
- (void)setVoiceMessageToolbarView:(UIView *)toolbarView;
@end

View File

@@ -19,32 +19,39 @@
#import "ThemeService.h"
#import "GeneratedInterface-Swift.h"
#import "GBDeviceInfo_iOS.h"
#import "UINavigationController+Riot.h"
static const CGFloat kContextBarHeight = 24;
static const CGFloat kActionMenuAttachButtonSpringVelocity = 7;
static const CGFloat kActionMenuAttachButtonSpringDamping = .45;
#import "WidgetManager.h"
#import "IntegrationManagerViewController.h"
static const NSTimeInterval kSendModeAnimationDuration = .15;
static const NSTimeInterval kActionMenuAttachButtonAnimationDuration = .4;
static const NSTimeInterval kActionMenuContentAlphaAnimationDuration = .2;
static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3;
@import GrowingTextView;
@interface RoomInputToolbarView() <UITextViewDelegate, RoomInputToolbarTextViewDelegate>
const double kContextBarHeight = 24;
const NSTimeInterval kSendModeAnimationDuration = .15;
const NSTimeInterval kActionMenuAttachButtonAnimationDuration = .4;
const CGFloat kActionMenuAttachButtonSpringVelocity = 7;
const CGFloat kActionMenuAttachButtonSpringDamping = .45;
const NSTimeInterval kActionMenuContentAlphaAnimationDuration = .2;
const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3;
const CGFloat kComposerContainerTrailingPadding = 12;
@property (nonatomic, weak) IBOutlet UIView *mainToolbarView;
@interface RoomInputToolbarView() <GrowingTextViewDelegate, RoomInputToolbarTextViewDelegate>
{
// The intermediate action sheet
UIAlertController *actionSheet;
}
@property (nonatomic, weak) IBOutlet UIButton *attachMediaButton;
@property (nonatomic, weak) IBOutlet RoomInputToolbarTextView *textView;
@property (nonatomic, weak) IBOutlet UIImageView *inputTextBackgroundView;
@property (nonatomic, weak) IBOutlet UIImageView *inputContextImageView;
@property (nonatomic, weak) IBOutlet UILabel *inputContextLabel;
@property (nonatomic, weak) IBOutlet UIButton *inputContextButton;
@property (nonatomic, weak) IBOutlet RoomActionsBar *actionsBar;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *mainToolbarMinHeightConstraint;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *mainToolbarHeightConstraint;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *messageComposerContainerTrailingConstraint;
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *inputContextViewHeightConstraint;
@property (nonatomic, weak) UIView *voiceMessageToolbarView;
@property (nonatomic, assign) CGFloat expandedMainToolbarHeight;
@end
@@ -52,22 +59,10 @@ const CGFloat kComposerContainerTrailingPadding = 12;
@implementation RoomInputToolbarView
@dynamic delegate;
+ (UINib *)nib
{
return [UINib nibWithNibName:NSStringFromClass([RoomInputToolbarView class])
bundle:[NSBundle bundleForClass:[RoomInputToolbarView class]]];
}
+ (instancetype)roomInputToolbarView
{
if ([[self class] nib])
{
return [[[self class] nib] instantiateWithOwner:nil options:nil].firstObject;
}
else
{
return [[self alloc] init];
}
UINib *nib = [UINib nibWithNibName:NSStringFromClass([RoomInputToolbarView class]) bundle:nil];
return [nib instantiateWithOwner:nil options:nil].firstObject;
}
- (void)awakeFromNib
@@ -315,6 +310,11 @@ const CGFloat kComposerContainerTrailingPadding = 12;
self.textView.placeholder = inPlaceholder;
}
- (void)pasteText:(NSString *)text
{
self.textMessage = [self.textView.text stringByReplacingCharactersInRange:self.textView.selectedRange withString:text];
}
#pragma mark - Actions
- (IBAction)cancelAction:(id)sender
@@ -325,7 +325,7 @@ const CGFloat kComposerContainerTrailingPadding = 12;
}
}
#pragma mark - GrowingTextViewDelegate
#pragma mark - UITextViewDelegate
- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text
{
@@ -351,7 +351,9 @@ const CGFloat kComposerContainerTrailingPadding = 12;
[self.delegate roomInputToolbarViewDidChangeTextMessage:self];
}
- (void)textViewDidChangeHeight:(GrowingTextView *)textView height:(CGFloat)height
#pragma mark - RoomInputToolbarTextViewDelegate
- (void)textView:(RoomInputToolbarTextView *)textView didChangeHeight:(CGFloat)height
{
// Update height of the main toolbar (message composer)
CGFloat updatedHeight = height + (self.messageComposerContainerTopConstraint.constant + self.messageComposerContainerBottomConstraint.constant) + self.inputContextViewHeightConstraint.constant;
@@ -376,13 +378,18 @@ const CGFloat kComposerContainerTrailingPadding = 12;
}
}
- (void)textView:(RoomInputToolbarTextView *)textView didReceivePasteForMediaFromSender:(id)sender
{
[self paste:sender];
}
#pragma mark - Override MXKRoomInputToolbarView
- (IBAction)onTouchUpInside:(UIButton*)button
{
if (button == self.attachMediaButton)
{
self.actionMenuOpened = !self.isActionMenuOpened;
self.actionMenuOpened = !self.actionMenuOpened;
}
[super onTouchUpInside:button];
@@ -400,12 +407,6 @@ const CGFloat kComposerContainerTrailingPadding = 12;
- (void)destroy
{
if (actionSheet)
{
[actionSheet dismissViewControllerAnimated:NO completion:nil];
actionSheet = nil;
}
[super destroy];
}
@@ -462,20 +463,6 @@ const CGFloat kComposerContainerTrailingPadding = 12;
}
}
#pragma mark - Clipboard - Handle image/data paste from general pasteboard
- (void)paste:(id)sender
{
// TODO Custom here the validation screen for each available item
[super paste:sender];
}
- (void)textView:(GrowingTextView *)textView didReceivePasteForMediaFromSender:(id)sender
{
[self paste:sender];
}
#pragma mark - Private
- (void)updateUIWithTextMessage:(NSString *)textMessage animated:(BOOL)animated

View File

@@ -178,16 +178,16 @@ ServiceTermsModalCoordinatorBridgePresenterDelegate,
TableViewSectionsDelegate>
{
// Current alert (if any).
UIAlertController *currentAlert;
__weak UIAlertController *currentAlert;
// listener
id removedAccountObserver;
id accountUserInfoObserver;
id pushInfoUpdateObserver;
__weak id removedAccountObserver;
__weak id accountUserInfoObserver;
__weak id pushInfoUpdateObserver;
id notificationCenterWillUpdateObserver;
id notificationCenterDidUpdateObserver;
id notificationCenterDidFailObserver;
__weak id notificationCenterWillUpdateObserver;
__weak id notificationCenterDidUpdateObserver;
__weak id notificationCenterDidFailObserver;
// profile updates
// avatar
@@ -216,10 +216,10 @@ TableViewSectionsDelegate>
GroupsDataSource *groupsDataSource;
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar.
id kAppDelegateDidTapStatusBarNotificationObserver;
__weak id kAppDelegateDidTapStatusBarNotificationObserver;
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change.
id kThemeServiceDidChangeThemeNotificationObserver;
__weak id kThemeServiceDidChangeThemeNotificationObserver;
// Postpone destroy operation when saving, pwd reset or email binding is in progress
BOOL isSavingInProgress;
@@ -612,9 +612,13 @@ TableViewSectionsDelegate>
self.tableView.sectionFooterHeight = UITableViewAutomaticDimension;
self.tableView.estimatedSectionFooterHeight = 50;
MXWeakify(self);
// Add observer to handle removed accounts
removedAccountObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountManagerDidRemoveAccountNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
if ([MXKAccountManager sharedManager].accounts.count)
{
// Refresh table to remove this account
@@ -626,6 +630,8 @@ TableViewSectionsDelegate>
// Add observer to handle accounts update
accountUserInfoObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountUserInfoDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
[self refreshSettings];
@@ -635,6 +641,8 @@ TableViewSectionsDelegate>
// Add observer to push settings
pushInfoUpdateObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kMXKAccountAPNSActivityDidChangeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
[self refreshSettings];
@@ -663,6 +671,8 @@ TableViewSectionsDelegate>
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self userInterfaceThemeDidChange];
}];
@@ -777,10 +787,14 @@ TableViewSectionsDelegate>
// Refresh linked emails and phone numbers in parallel
[self loadAccount3PIDs];
MXWeakify(self);
// Observe kAppDelegateDidTapStatusBarNotificationObserver.
kAppDelegateDidTapStatusBarNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kAppDelegateDidTapStatusBarNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
MXStrongifyAndReturnIfNil(self);
[self.tableView setContentOffset:CGPointMake(-self.tableView.adjustedContentInset.left, -self.tableView.adjustedContentInset.top) animated:YES];
}];

View File

@@ -41,7 +41,8 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
private let masterNavigationController: UINavigationController
private var currentSpaceId: String?
private var homeViewControllerWrapperViewController: HomeViewControllerWithBannerWrapperViewController?
private weak var versionCheckCoordinator: VersionCheckCoordinator?
private var currentMatrixSession: MXSession? {
return parameters.userSessionsService.mainUserSession?.matrixSession
@@ -77,8 +78,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
}
func start(with spaceId: String?) {
self.currentSpaceId = spaceId
// If start has been done once do not setup view controllers again
if self.hasStartedOnce == false {
let masterTabBarController = self.createMasterTabBarController()
@@ -98,16 +98,12 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
self.registerUserSessionsServiceNotifications()
self.registerSessionChange()
if let homeViewController = homeViewControllerWrapperViewController {
let versionCheckCoordinator = VersionCheckCoordinator(rootViewController: masterTabBarController,
bannerPresenter: homeViewController,
themeService: ThemeService.shared())
versionCheckCoordinator.start()
add(childCoordinator: versionCheckCoordinator)
}
self.updateMasterTabBarController(with: spaceId, forceReload: true)
} else {
self.updateMasterTabBarController(with: spaceId)
}
self.updateMasterTabBarController(with: spaceId)
self.currentSpaceId = spaceId
}
func toPresentable() -> UIViewController {
@@ -211,21 +207,25 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
}
searchBarButtonItem.accessibilityLabel = VectorL10n.searchDefaultPlaceholder
tabBarController.navigationItem.rightBarButtonItem = searchBarButtonItem
self.updateTabControllers(for: tabBarController, showCommunities: true)
tabBarController.navigationItem.rightBarButtonItem = searchBarButtonItem
return tabBarController
}
private func createHomeViewController() -> UIViewController {
private func createVersionCheckCoordinator(withRootViewController rootViewController: UIViewController, bannerPresentrer: BannerPresentationProtocol) -> VersionCheckCoordinator {
let versionCheckCoordinator = VersionCheckCoordinator(rootViewController: rootViewController,
bannerPresenter: bannerPresentrer,
themeService: ThemeService.shared())
return versionCheckCoordinator
}
private func createHomeViewController() -> HomeViewControllerWithBannerWrapperViewController {
let homeViewController: HomeViewController = HomeViewController.instantiate()
homeViewController.tabBarItem.tag = Int(TABBAR_HOME_INDEX)
homeViewController.tabBarItem.image = homeViewController.tabBarItem.image
homeViewController.accessibilityLabel = VectorL10n.titleHome
let wrapperViewController = HomeViewControllerWithBannerWrapperViewController(viewController: homeViewController)
homeViewControllerWrapperViewController = wrapperViewController
let wrapperViewController = HomeViewControllerWithBannerWrapperViewController(viewController: homeViewController)
return wrapperViewController
}
@@ -280,18 +280,35 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType {
gesture.delegate = self
}
private func updateMasterTabBarController(with spaceId: String?) {
private func updateMasterTabBarController(with spaceId: String?, forceReload: Bool = false) {
guard forceReload || spaceId != self.currentSpaceId else { return }
self.updateTabControllers(for: self.masterTabBarController, showCommunities: spaceId == nil)
self.masterTabBarController.filterRooms(withParentId: spaceId, inMatrixSession: self.currentMatrixSession)
}
// TODO: Avoid to reinstantiate controllers everytime
private func updateTabControllers(for tabBarController: MasterTabBarController, showCommunities: Bool) {
var viewControllers: [UIViewController] = []
let homeViewController = self.createHomeViewController()
viewControllers.append(homeViewController)
if let existingVersionCheckCoordinator = self.versionCheckCoordinator {
self.remove(childCoordinator: existingVersionCheckCoordinator)
}
if let masterTabBarController = self.masterTabBarController {
let versionCheckCoordinator = self.createVersionCheckCoordinator(withRootViewController: masterTabBarController, bannerPresentrer: homeViewController)
versionCheckCoordinator.start()
self.add(childCoordinator: versionCheckCoordinator)
self.versionCheckCoordinator = versionCheckCoordinator
}
if RiotSettings.shared.homeScreenShowFavouritesTab {
let favouritesViewController = self.createFavouritesViewController()
viewControllers.append(favouritesViewController)

View File

@@ -299,13 +299,12 @@ final class NavigationRouter: NSObject, NavigationRouterType {
self.postNotification(withName: NavigationRouter.willPopModule, for: viewController)
}
private func didPopViewController(_ viewController: UIViewController) {
private func didPopViewController(_ viewController: UIViewController) {
self.postNotification(withName: NavigationRouter.didPopModule, for: viewController)
// Call completion closure associated to the view controller
// So associated coordinator can be deallocated
runCompletion(for: viewController)
self.postNotification(withName: NavigationRouter.didPopModule, for: viewController)
self.removeModule(for: viewController)
}

View File

@@ -66,11 +66,11 @@
{
BOOL isUniversalLink = NO;
for (NSString *matrixPermalinkHost in BuildSettings.matrixPermalinkPaths)
for (NSString *matrixPermalinkHost in BuildSettings.permalinkSupportedHosts)
{
if ([url.host isEqualToString:matrixPermalinkHost])
{
NSArray<NSString*> *hostPaths = BuildSettings.matrixPermalinkPaths[matrixPermalinkHost];
NSArray<NSString*> *hostPaths = BuildSettings.permalinkSupportedHosts[matrixPermalinkHost];
if (hostPaths.count)
{
// iOS Patch: fix urls before using it

View File

@@ -77,10 +77,7 @@ targets:
- path: Assets/ca.lproj/Localizable.strings
- path: Assets/ca.lproj/Vector.strings
- path: Assets/cy.lproj/InfoPlist.strings
- path: Assets/cy.lproj/InfoPlist.strings
- path: Assets/cy.lproj/Localizable.strings
- path: Assets/cy.lproj/Localizable.strings
- path: Assets/cy.lproj/Vector.strings
- path: Assets/cy.lproj/Vector.strings
- path: Assets/de.lproj/InfoPlist.strings
- path: Assets/de.lproj/Localizable.strings
@@ -113,10 +110,7 @@ targets:
- path: Assets/it.lproj/Localizable.strings
- path: Assets/it.lproj/Vector.strings
- path: Assets/ja.lproj/InfoPlist.strings
- path: Assets/ja.lproj/InfoPlist.strings
- path: Assets/ja.lproj/Localizable.strings
- path: Assets/ja.lproj/Localizable.strings
- path: Assets/ja.lproj/Vector.strings
- path: Assets/ja.lproj/Vector.strings
- path: Assets/kab.lproj/InfoPlist.strings
- path: Assets/kab.lproj/Localizable.strings

View File

@@ -86,7 +86,7 @@
for (MXRoomSummary *roomSummary in roomsSummaries)
{
if (!roomSummary.hiddenFromUser)
if (!roomSummary.hiddenFromUser && roomSummary.roomType == MXRoomTypeRoom)
{
[roomSummary setMatrixSession:session];

View File

@@ -28,6 +28,8 @@
static const CGFloat kLargeImageSizeMaxDimension = 2048.0;
static const CGSize kThumbnailSize = {800.0, 600.0};
/// A safe maximum file size for an image to send the original.
static const NSUInteger kImageMaxFileSize = 20 * 1024 * 1024;
typedef NS_ENUM(NSInteger, ImageCompressionMode)
{
@@ -310,30 +312,43 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
if ([self.shareItemProvider areAllItemsImages])
{
// When all items are images, they're processed together from the
// pending list, immediately after the final image has been loaded.
[self.pendingImages addObject:imageData];
}
else
{
CGSize imageSize = [self imageSizeFromImageData:imageData];
// Otherwise, the image is sent as is, without prompting for a resize
// as that wouldn't make much sense with multiple content types.
self.imageCompressionMode = ImageCompressionModeNone;
self.actualLargeSize = MAX(imageSize.width, imageSize.height);
[self sendImageData:imageData toRooms:rooms success:requestSuccess failure:requestFailure];
}
// Only prompt for image resize if all items are images
// Ignore showMediaCompressionPrompt setting due to memory constraints with full size images.
// When there are multiple content types the image will have been sent above.
// Otherwise, if we have loaded all of the images we can send them all together.
if ([self.shareItemProvider areAllItemsImages])
{
if ([self.shareItemProvider areAllItemsLoaded])
{
UIAlertController *compressionPrompt = [self compressionPromptForPendingImagesWithShareBlock:^{
MXWeakify(self);
void (^sendPendingImages)(void) = ^void() {
MXStrongifyAndReturnIfNil(self);
[self sendImageDatas:self.pendingImages.copy toRooms:rooms success:requestSuccess failure:requestFailure];
}];
};
if (compressionPrompt)
if (RiotSettings.shared.showMediaCompressionPrompt)
{
[self presentCompressionPrompt:compressionPrompt];
// Create a compression prompt which will be nil when the sizes can't be determined or if there are no pending images.
UIAlertController *compressionPrompt = [self compressionPromptForPendingImagesWithShareBlock:sendPendingImages];
if (compressionPrompt)
{
[self presentCompressionPrompt:compressionPrompt];
}
}
else
{
self.imageCompressionMode = ImageCompressionModeNone;
sendPendingImages();
}
}
else
@@ -406,26 +421,26 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
}
}
- (void)resetPendingData
- (BOOL)roomsContainEncryptedRoom:(NSArray<MXRoom *> *)rooms
{
[self.pendingImages removeAllObjects];
[self.imageUploadProgresses removeAllObjects];
}
- (BOOL)isAPendingImageNotOrientedUp
{
BOOL isAPendingImageNotOrientedUp = NO;
BOOL foundEncryptedRoom = NO;
for (NSData *imageData in self.pendingImages)
for (MXRoom *room in rooms)
{
if ([self isImageOrientationNotUpOrUndeterminedForImageData:imageData])
if (room.summary.isEncrypted)
{
isAPendingImageNotOrientedUp = YES;
foundEncryptedRoom = YES;
break;
}
}
return isAPendingImageNotOrientedUp;
return foundEncryptedRoom;
}
- (void)resetPendingData
{
[self.pendingImages removeAllObjects];
[self.imageUploadProgresses removeAllObjects];
}
// TODO: When select multiple images:
@@ -438,8 +453,6 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
return nil;
}
BOOL isAPendingImageNotOrientedUp = [self isAPendingImageNotOrientedUp];
NSData *firstImageData = self.pendingImages.firstObject;
UIImage *firstImage = [UIImage imageWithData:firstImageData];
@@ -447,16 +460,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
if (compressionSizes.small.fileSize == 0 && compressionSizes.medium.fileSize == 0 && compressionSizes.large.fileSize == 0)
{
if (isAPendingImageNotOrientedUp && self.pendingImages.count > 1)
{
self.imageCompressionMode = ImageCompressionModeSmall;
}
else
{
self.imageCompressionMode = ImageCompressionModeNone;
}
MXLogDebug(@"[ShareManager] Send %lu image(s) without compression prompt using compression mode: %ld", (unsigned long)self.pendingImages.count, (long)self.imageCompressionMode);
self.imageCompressionMode = ImageCompressionModeNone;
MXLogDebug(@"[ShareManager] Bypass compression prompt and send originals for %lu image(s) due to undetermined file sizes", (unsigned long)self.pendingImages.count);
shareBlock();
@@ -476,7 +481,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
MXStrongifyAndReturnIfNil(self);
self.imageCompressionMode = ImageCompressionModeSmall;
[self logCompressionSizeChoice:compressionSizes.large];
[self logCompressionSizeChoice:compressionSizes.small];
shareBlock();
}]];
@@ -491,7 +496,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
MXStrongifyAndReturnIfNil(self);
self.imageCompressionMode = ImageCompressionModeMedium;
[self logCompressionSizeChoice:compressionSizes.large];
[self logCompressionSizeChoice:compressionSizes.medium];
shareBlock();
}]];
@@ -516,8 +521,8 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
}]];
}
// To limit memory consumption, we suggest the original resolution only if the image orientation is up, or if the image size is moderate
if (!isAPendingImageNotOrientedUp || !compressionSizes.large.fileSize)
// To limit memory consumption when encrypting, we suggest the original resolution only if the image size is moderate
if (compressionSizes.original.fileSize < kImageMaxFileSize)
{
NSString *fileSizeString = [MXTools fileSizeToString:compressionSizes.original.fileSize];
@@ -528,7 +533,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
MXStrongifyAndReturnIfNil(self);
self.imageCompressionMode = ImageCompressionModeNone;
[self logCompressionSizeChoice:compressionSizes.large];
[self logCompressionSizeChoice:compressionSizes.original];
shareBlock();
}]];
@@ -622,46 +627,6 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
return CGSizeMake(width, height);
}
- (NSNumber*)cgImageimageOrientationNumberFromImageData:(NSData*)imageData
{
NSNumber *orientationNumber;
CGImageSourceRef imageSource = CGImageSourceCreateWithData((CFDataRef)imageData, NULL);
CFDictionaryRef imageProperties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, NULL);
CFRelease(imageSource);
if (imageProperties != NULL)
{
CFNumberRef orientationNum = CFDictionaryGetValue(imageProperties, kCGImagePropertyOrientation);
// Check orientation and flip size if required
if (orientationNum != NULL)
{
orientationNumber = (__bridge NSNumber *)orientationNum;
}
CFRelease(imageProperties);
}
return orientationNumber;
}
- (BOOL)isImageOrientationNotUpOrUndeterminedForImageData:(NSData*)imageData
{
BOOL isImageNotOrientedUp = YES;
NSNumber *cgImageOrientationNumber = [self cgImageimageOrientationNumberFromImageData:imageData];
if (cgImageOrientationNumber && cgImageOrientationNumber.unsignedIntegerValue == (NSUInteger)kCGImagePropertyOrientationUp)
{
isImageNotOrientedUp = NO;
}
return isImageNotOrientedUp;
}
- (void)logCompressionSizeChoice:(MXKImageCompressionSize)compressionSize
{
NSString *fileSize = [MXTools fileSizeToString:compressionSize.fileSize round:NO];
@@ -822,19 +787,9 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
MXWeakify(self);
// Ignore showMediaCompressionPrompt setting due to memory constraints when encrypting large videos.
UIAlertController *compressionPrompt = [MXKTools videoConversionPromptForVideoAsset:videoAsset withCompletion:^(NSString *presetName) {
void (^sendVideo)(void) = ^void() {
MXStrongifyAndReturnIfNil(self);
// If the preset name is nil, the user cancelled.
if (!presetName)
{
return;
}
// Set the chosen video conversion preset.
[MXSDKOptions sharedInstance].videoConversionPresetName = presetName;
[self didStartSending];
if (!videoLocalUrl)
{
@@ -872,9 +827,57 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
success();
}
});
}];
};
[self presentCompressionPrompt:compressionPrompt];
BOOL allRoomsAreUnencrypted = ![self roomsContainEncryptedRoom:rooms];
// When rooms are unencrypted convert the video according to the user's normal preferences
if (allRoomsAreUnencrypted)
{
if (!RiotSettings.shared.showMediaCompressionPrompt)
{
[MXSDKOptions sharedInstance].videoConversionPresetName = AVCaptureSessionPreset1920x1080;
sendVideo();
}
else
{
UIAlertController *compressionPrompt = [MXKTools videoConversionPromptForVideoAsset:videoAsset withCompletion:^(NSString *presetName) {
// If the preset name is nil, the user cancelled.
if (!presetName)
{
return;
}
// Set the chosen video conversion preset.
[MXSDKOptions sharedInstance].videoConversionPresetName = presetName;
sendVideo();
}];
[self presentCompressionPrompt:compressionPrompt];
}
}
else
{
// When rooms are encrypted we quickly run out of memory encrypting the video
// Prompt the user if they're happy to send a low quality video (320p).
UIAlertController *lowQualityPrompt = [UIAlertController alertControllerWithTitle:VectorL10n.shareExtensionLowQualityVideoTitle
message:[VectorL10n shareExtensionLowQualityVideoMessage:AppInfo.current.displayName]
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:MatrixKitL10n.cancel style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
// Do nothing
}];
UIAlertAction *sendAction = [UIAlertAction actionWithTitle:VectorL10n.shareExtensionSendNow style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
[MXSDKOptions sharedInstance].videoConversionPresetName = AVAssetExportPresetMediumQuality;
sendVideo();
}];
[lowQualityPrompt addAction:cancelAction];
[lowQualityPrompt addAction:sendAction];
[lowQualityPrompt setPreferredAction:sendAction];
[self presentCompressionPrompt:lowQualityPrompt];
}
}
- (void)sendVoiceMessage:(NSURL *)fileUrl
@@ -1017,17 +1020,7 @@ typedef NS_ENUM(NSInteger, ImageCompressionMode)
break;
}
if (CGSizeEqualToSize(newImageSize, CGSizeZero))
{
// No resize to make
// Make sure the uploaded image orientation is up
if ([self isImageOrientationNotUpOrUndeterminedForImageData:imageData])
{
UIImage *image = [UIImage imageWithData:imageData];
convertedImage = [MXKTools forceImageOrientationUp:image];
}
}
else
if (!CGSizeEqualToSize(newImageSize, CGSizeZero))
{
// Resize the image and set image in right orientation too
convertedImage = [MXKTools resizeImageWithData:imageData toFitInSize:newImageSize];

View File

@@ -82,7 +82,7 @@
[self.avatarImageView vc_setRoomAvatarImageWith:roomCellData.avatarUrl
roomId:roomCellData.roomIdentifier
displayName:roomCellData.roomDisplayname
mediaManager:roomCellData.mxSession.mediaManager];
mediaManager:roomCellData.roomSummary.mxSession.mediaManager];
self.roomTitleLabel.text = roomCellData.roomDisplayname;
if (!self.roomTitleLabel.text.length)

View File

@@ -28,18 +28,18 @@ private class ShareExtensionItem: ShareItemProtocol {
}
var type: ShareItemType {
if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.text.rawValue) {
return .text
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.url.rawValue) {
return .URL
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.fileUrl.rawValue) {
return .fileURL
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.image.rawValue) {
if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.image.rawValue) {
return .image
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.video.rawValue) {
return .video
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.movie.rawValue) {
return .movie
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.fileUrl.rawValue) {
return .fileURL
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.url.rawValue) {
return .URL
} else if itemProvider.hasItemConformingToTypeIdentifier(MXKUTI.text.rawValue) {
return .text
}
return .unknown

View File

@@ -34,11 +34,10 @@ final class UserSuggestionCoordinator: Coordinator {
private let parameters: UserSuggestionCoordinatorParameters
private var userSuggestionHostingController: UIViewController!
private var userSuggestionService: UserSuggestionServiceProtocol!
private var userSuggestionViewModel: UserSuggestionViewModelProtocol!
private var roomMembers: [MXRoomMember] = []
private var userSuggestionHostingController: UIViewController
private var userSuggestionService: UserSuggestionServiceProtocol
private var userSuggestionViewModel: UserSuggestionViewModelProtocol
private var roomMemberProvider: UserSuggestionCoordinatorRoomMemberProvider
// MARK: Public
@@ -54,9 +53,10 @@ final class UserSuggestionCoordinator: Coordinator {
init(parameters: UserSuggestionCoordinatorParameters) {
self.parameters = parameters
userSuggestionService = UserSuggestionService(roomMembersProvider: self)
roomMemberProvider = UserSuggestionCoordinatorRoomMemberProvider(room: parameters.room)
userSuggestionService = UserSuggestionService(roomMemberProvider: roomMemberProvider)
userSuggestionViewModel = UserSuggestionViewModel.makeUserSuggestionViewModel(userSuggestionService: userSuggestionService)
let view = UserSuggestionList(viewModel: userSuggestionViewModel.context)
.addDependency(AvatarService.instantiate(mediaManager: parameters.mediaManager))
@@ -69,7 +69,7 @@ final class UserSuggestionCoordinator: Coordinator {
switch result {
case .selectedItemWithIdentifier(let identifier):
guard let member = self.roomMembers.filter({ $0.userId == identifier }).first else {
guard let member = self.roomMemberProvider.roomMembers.filter({ $0.userId == identifier }).first else {
return
}
@@ -93,9 +93,18 @@ final class UserSuggestionCoordinator: Coordinator {
}
@available(iOS 14.0, *)
extension UserSuggestionCoordinator: RoomMembersProviderProtocol {
private class UserSuggestionCoordinatorRoomMemberProvider: RoomMembersProviderProtocol {
private let room: MXRoom
var roomMembers: [MXRoomMember] = []
init(room: MXRoom) {
self.room = room;
}
func fetchMembers(_ members: @escaping ([RoomMembersProviderMember]) -> Void) {
parameters.room.members({ [weak self] roomMembers in
room.members({ [weak self] roomMembers in
guard let self = self, let joinedMembers = roomMembers?.joinedMembers else {
return
}
@@ -108,7 +117,7 @@ extension UserSuggestionCoordinator: RoomMembersProviderProtocol {
self.roomMembers = joinedMembers
members(self.roomMembersToProviderMembers(joinedMembers))
}, failure: { error in
MXLog.error("[UserSuggestionCoordinator] Failed loading room with error: \(String(describing: error))")
MXLog.error("[UserSuggestionCoordinatorRoomMemberProvider] Failed loading room with error: \(String(describing: error))")
})
}

View File

@@ -30,7 +30,7 @@ enum MockUserSuggestionScreenState: MockScreenState, CaseIterable {
}
var screenView: ([Any], AnyView) {
let service = UserSuggestionService(roomMembersProvider: self)
let service = UserSuggestionService(roomMemberProvider: self)
let listViewModel = UserSuggestionViewModel.makeUserSuggestionViewModel(userSuggestionService: service)
let viewModel = UserSuggestionListWithInputViewModel(listViewModel: listViewModel) { textMessage in

View File

@@ -45,7 +45,7 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
// MARK: Private
private let roomMembersProvider: RoomMembersProviderProtocol
private let roomMemberProvider: RoomMembersProviderProtocol
private var suggestionItems: [UserSuggestionItemProtocol] = []
private let currentTextTriggerSubject = CurrentValueSubject<String?, Never>(nil)
@@ -61,13 +61,13 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
// MARK: - Setup
init(roomMembersProvider: RoomMembersProviderProtocol) {
self.roomMembersProvider = roomMembersProvider
init(roomMemberProvider: RoomMembersProviderProtocol) {
self.roomMemberProvider = roomMemberProvider
currentTextTriggerSubject
.debounce(for: 0.5, scheduler: RunLoop.main)
.removeDuplicates()
.sink { self.fetchAndFilterMembersForTextTrigger($0) }
.sink { [weak self] in self?.fetchAndFilterMembersForTextTrigger($0) }
.store(in: &cancellables)
}
@@ -96,7 +96,7 @@ class UserSuggestionService: UserSuggestionServiceProtocol {
partialName.removeFirst() // remove the '@' prefix
roomMembersProvider.fetchMembers { [weak self] members in
roomMemberProvider.fetchMembers { [weak self] members in
guard let self = self else {
return
}

1
changelog.d/3908.bugfix Normal file
View File

@@ -0,0 +1 @@
Remove duplicate sources for some strings files in Riot/target.yml.

1
changelog.d/4815.change Normal file
View File

@@ -0,0 +1 @@
Share Extension: Remove the image compression prompt when the showMediaSizeSelection setting is disabled.

1
changelog.d/4976.change Normal file
View File

@@ -0,0 +1 @@
Replaced GrowingTextView with simpler, custom implementation. Cleaned up the RoomInputToolbar header.

1
changelog.d/4981.feature Normal file
View File

@@ -0,0 +1 @@
Adds clientPermalinkBaseUrl for a custom permalink base url.

1
changelog.d/5055.bugfix Normal file
View File

@@ -0,0 +1 @@
RoomVC: Fix retain cycles that prevents `RoomViewController` to be deallocated.

1
changelog.d/5057.bugfix Normal file
View File

@@ -0,0 +1 @@
Share Extension: Fix missing avatars and don't list spaces as rooms.

1
changelog.d/5058.bugfix Normal file
View File

@@ -0,0 +1 @@
Fix retain cycles that prevents deallocation in several classes.

1
changelog.d/5063.bugfix Normal file
View File

@@ -0,0 +1 @@
Fixed retain cycles between the user suggestion coordinator and the suggestion service, and in the suggestion service currentTextTrigger subject sink.

1
changelog.d/5067.feature Normal file
View File

@@ -0,0 +1 @@
Remember keyboard layout per room and restore it when opening the room again.

1
changelog.d/5098.bugfix Normal file
View File

@@ -0,0 +1 @@
Invalid default value set for clientPermalinkBaseUrl.