mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-04-17 15:09:31 +02:00
Merge branch 'develop' into phlpro/4722_objc_headers
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
1
Podfile
1
Podfile
@@ -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']
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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];
|
||||
|
||||
}];
|
||||
|
||||
@@ -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];
|
||||
|
||||
}];
|
||||
|
||||
@@ -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];
|
||||
|
||||
}];
|
||||
|
||||
@@ -43,6 +43,10 @@ final class GroupDetailsCoordinator: GroupDetailsCoordinatorProtocol {
|
||||
self.groupDetailsViewController = groupDetailsViewController
|
||||
}
|
||||
|
||||
deinit {
|
||||
groupDetailsViewController.destroy()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func start() {
|
||||
|
||||
@@ -122,7 +122,7 @@
|
||||
/**
|
||||
The delegate for the view controller.
|
||||
*/
|
||||
@property (nonatomic) id<ContactsTableViewControllerDelegate> contactsTableViewControllerDelegate;
|
||||
@property (nonatomic, weak) id<ContactsTableViewControllerDelegate> contactsTableViewControllerDelegate;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
}];
|
||||
|
||||
@@ -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])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
/**
|
||||
The delegate for the view controller.
|
||||
*/
|
||||
@property (nonatomic) id<JitsiViewControllerDelegate> delegate;
|
||||
@property (nonatomic, weak) id<JitsiViewControllerDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -54,6 +54,6 @@
|
||||
/**
|
||||
The delegate.
|
||||
*/
|
||||
@property (nonatomic) id<RoomMemberTitleViewDelegate> delegate;
|
||||
@property (nonatomic, weak) id<RoomMemberTitleViewDelegate> delegate;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
}()
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -25,4 +25,6 @@
|
||||
*/
|
||||
@property (nonatomic) MXKRoomDataSource *roomDataSource;
|
||||
|
||||
+ (instancetype)instantiate;
|
||||
|
||||
@end
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
}];
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -86,7 +86,7 @@
|
||||
|
||||
for (MXRoomSummary *roomSummary in roomsSummaries)
|
||||
{
|
||||
if (!roomSummary.hiddenFromUser)
|
||||
if (!roomSummary.hiddenFromUser && roomSummary.roomType == MXRoomTypeRoom)
|
||||
{
|
||||
[roomSummary setMatrixSession:session];
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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))")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
1
changelog.d/3908.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Remove duplicate sources for some strings files in Riot/target.yml.
|
||||
1
changelog.d/4815.change
Normal file
1
changelog.d/4815.change
Normal file
@@ -0,0 +1 @@
|
||||
Share Extension: Remove the image compression prompt when the showMediaSizeSelection setting is disabled.
|
||||
1
changelog.d/4976.change
Normal file
1
changelog.d/4976.change
Normal file
@@ -0,0 +1 @@
|
||||
Replaced GrowingTextView with simpler, custom implementation. Cleaned up the RoomInputToolbar header.
|
||||
1
changelog.d/4981.feature
Normal file
1
changelog.d/4981.feature
Normal file
@@ -0,0 +1 @@
|
||||
Adds clientPermalinkBaseUrl for a custom permalink base url.
|
||||
1
changelog.d/5055.bugfix
Normal file
1
changelog.d/5055.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
RoomVC: Fix retain cycles that prevents `RoomViewController` to be deallocated.
|
||||
1
changelog.d/5057.bugfix
Normal file
1
changelog.d/5057.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Share Extension: Fix missing avatars and don't list spaces as rooms.
|
||||
1
changelog.d/5058.bugfix
Normal file
1
changelog.d/5058.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Fix retain cycles that prevents deallocation in several classes.
|
||||
1
changelog.d/5063.bugfix
Normal file
1
changelog.d/5063.bugfix
Normal 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
1
changelog.d/5067.feature
Normal file
@@ -0,0 +1 @@
|
||||
Remember keyboard layout per room and restore it when opening the room again.
|
||||
1
changelog.d/5098.bugfix
Normal file
1
changelog.d/5098.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Invalid default value set for clientPermalinkBaseUrl.
|
||||
Reference in New Issue
Block a user