Merge branch 'develop' into steve/5210_bubble_polls

This commit is contained in:
SBiOSoftWhare
2022-02-04 16:32:33 +01:00
87 changed files with 1698 additions and 527 deletions
@@ -128,9 +128,11 @@ class AvatarView: UIView, Themable {
previewImage: defaultAvatarImage,
mediaManager: viewData.mediaManager)
avatarImageView.contentMode = .scaleAspectFill
avatarImageView.imageView?.contentMode = .scaleAspectFill
} else {
avatarImageView.image = defaultAvatarImage
avatarImageView.contentMode = defaultAvatarImageContentMode
avatarImageView.imageView?.contentMode = defaultAvatarImageContentMode
}
}
@@ -1467,12 +1467,14 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
#pragma mark - RecentsListServiceDelegate
- (void)recentsListServiceDidChangeData:(id<RecentsListServiceProtocol>)service
totalCountsChanged:(BOOL)totalCountsChanged
{
// no-op
}
- (void)recentsListServiceDidChangeData:(id<RecentsListServiceProtocol>)service
forSection:(RecentsListServiceSection)section
totalCountsChanged:(BOOL)totalCountsChanged
{
NSInteger sectionIndex = -1;
switch (section)
@@ -1499,8 +1501,10 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou
sectionIndex = suggestedRoomsSection;
break;
}
[self.delegate dataSource:self didCellChange:@(sectionIndex)];
RecentsSectionUpdate *update = [[RecentsSectionUpdate alloc] initWithSectionIndex:sectionIndex
totalCountsChanged:totalCountsChanged];
[self.delegate dataSource:self didCellChange:update];
}
@end
@@ -0,0 +1,40 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Foundation
/// Object to represent a recents section update. Will be used as the `changes` parameter of `-[MXKDataSourceDelegate dataSource:didCellChange:]`method.
@objcMembers
class RecentsSectionUpdate: NSObject {
/// Updated section index.
let sectionIndex: Int
/// Flag indicating the total counts on the section have changed or not.
let totalCountsChanged: Bool
init(withSectionIndex sectionIndex: Int,
totalCountsChanged: Bool) {
self.sectionIndex = sectionIndex
self.totalCountsChanged = totalCountsChanged
super.init()
}
/// Flag indicating the section update info is valid. If `true`, only the related section at `sectionIndex` can be reloaded.
var isValid: Bool {
return sectionIndex >= 0
}
}
@@ -1006,12 +1006,12 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
- (void)dataSource:(MXKDataSource *)dataSource didCellChange:(id)changes
{
BOOL cellReloaded = NO;
if ([changes isKindOfClass:NSNumber.class])
if ([changes isKindOfClass:RecentsSectionUpdate.class])
{
NSInteger section = ((NSNumber *)changes).integerValue;
if (section >= 0)
RecentsSectionUpdate *update = (RecentsSectionUpdate*)changes;
if (update.isValid && !update.totalCountsChanged)
{
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:section];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:update.sectionIndex];
UITableViewCell *cell = [self.recentsTableView cellForRowAtIndexPath:indexPath];
if ([cell isKindOfClass:TableViewCellWithCollectionView.class])
{
@@ -1343,8 +1343,6 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
{
if (editedRoomId)
{
__weak typeof(self) weakSelf = self;
// Check whether the user didn't leave the room
// TODO: handle multi-account
MXRoom *room = [self.mainSession roomWithRoomId:editedRoomId];
@@ -1352,34 +1350,32 @@ NSString *const RecentsViewControllerDataReadyNotification = @"RecentsViewContro
{
[self startActivityIndicator];
MXWeakify(self);
[room setIsDirect:isDirect withUserId:nil success:^{
if (weakSelf)
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
// Leave editing mode
[self cancelEditionMode:isRefreshPending];
}
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
// Leave editing mode
[self cancelEditionMode:isRefreshPending];
} failure:^(NSError *error) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self stopActivityIndicator];
MXLogDebug(@"[RecentsViewController] Failed to update direct tag of the room (%@)", editedRoomId);
// Notify the end user
NSString *userId = self.mainSession.myUser.userId; // TODO: handle multi-account
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification
object:error
userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil];
// Leave editing mode
[self cancelEditionMode:isRefreshPending];
}
MXStrongifyAndReturnIfNil(self);
[self stopActivityIndicator];
MXLogDebug(@"[RecentsViewController] Failed to update direct tag of the room (%@)", editedRoomId);
// Notify the end user
NSString *userId = self.mainSession.myUser.userId; // TODO: handle multi-account
[[NSNotificationCenter defaultCenter] postNotificationName:kMXKErrorNotification
object:error
userInfo:userId ? @{kMXKErrorUserIdKey: userId} : nil];
// Leave editing mode
[self cancelEditionMode:isRefreshPending];
}];
}
@@ -245,7 +245,7 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
if let fetcher = favoritedRoomListDataFetcher {
updateFavoritedFetcher(fetcher, for: mode)
}
allFetchers.forEach({ notifyDataChange(on: $0) })
allFetchers.forEach({ notifyDataChange(on: $0, totalCountsChanged: true) })
}
public func updateQuery(_ query: String?) {
@@ -558,11 +558,14 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
}
}
private func notifyDataChange(on fetcher: MXRoomListDataFetcher) {
private func notifyDataChange(on fetcher: MXRoomListDataFetcher, totalCountsChanged: Bool) {
if let section = section(forFetcher: fetcher) {
multicastDelegate.invoke { $0.recentsListServiceDidChangeData?(self, forSection: section) }
multicastDelegate.invoke { $0.recentsListServiceDidChangeData?(self,
forSection: section,
totalCountsChanged: totalCountsChanged) }
}
multicastDelegate.invoke { $0.recentsListServiceDidChangeData?(self) }
multicastDelegate.invoke { $0.recentsListServiceDidChangeData?(self,
totalCountsChanged: totalCountsChanged) }
}
deinit {
@@ -575,8 +578,8 @@ public class RecentsListService: NSObject, RecentsListServiceProtocol {
extension RecentsListService: MXRoomListDataFetcherDelegate {
public func fetcherDidChangeData(_ fetcher: MXRoomListDataFetcher) {
notifyDataChange(on: fetcher)
public func fetcherDidChangeData(_ fetcher: MXRoomListDataFetcher, totalCountsChanged: Bool) {
notifyDataChange(on: fetcher, totalCountsChanged: totalCountsChanged)
}
}
@@ -214,7 +214,7 @@ public class MockRecentsListService: NSObject, RecentsListServiceProtocol {
}
private func notifyDataChange() {
multicastDelegate.invoke({ $0.recentsListServiceDidChangeData?(self) })
multicastDelegate.invoke({ $0.recentsListServiceDidChangeData?(self, totalCountsChanged: true) })
}
}
@@ -21,11 +21,15 @@ public protocol RecentsListServiceDelegate: AnyObject {
/// Delegate method to be called when service data updated
/// - Parameter service: service object
@objc optional func recentsListServiceDidChangeData(_ service: RecentsListServiceProtocol)
/// - Parameter totalCountsChanged: true if total rooms count changed
@objc optional func recentsListServiceDidChangeData(_ service: RecentsListServiceProtocol,
totalCountsChanged: Bool)
/// Delegate method to be called when a specific section data updated. Called for each updated section before `recentsListServiceDidChangeData` if implemented.
/// - Parameter service: service object
/// - Parameter section: updated section
/// - Parameter totalCountsChanged: true if total rooms count changed for the section
@objc optional func recentsListServiceDidChangeData(_ service: RecentsListServiceProtocol,
forSection section: RecentsListServiceSection)
forSection section: RecentsListServiceSection,
totalCountsChanged: Bool)
}
@@ -0,0 +1,122 @@
//
// Copyright 2021 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import UIKit
@objcMembers
class BadgedBarButtonItem: UIBarButtonItem {
var baseButton: UIButton
private var badgeLabel: UILabel
private var theme: Theme
var badgeText: String? {
didSet {
updateBadgeLabel()
}
}
var badgeBackgroundColor: UIColor {
didSet {
updateBadgeLabel()
}
}
var badgeTextColor: UIColor {
didSet {
updateBadgeLabel()
}
}
var badgeFont: UIFont {
didSet {
updateBadgeLabel()
}
}
var badgePadding: UIOffset {
didSet {
updateBadgeLabel()
}
}
private var shouldHideBadge: Bool {
guard let text = badgeText else {
return true
}
return text.isEmpty || text == "0" || text == "nil" || text == "null"
}
init(withBaseButton baseButton: UIButton, theme: Theme) {
self.baseButton = baseButton
self.theme = theme
badgeBackgroundColor = .gray
badgeTextColor = .white
badgeFont = .systemFont(ofSize: 12, weight: .semibold)
badgePadding = UIOffset(horizontal: 8, vertical: 2)
badgeLabel = UILabel(frame: .zero)
badgeLabel.textAlignment = .center
badgeLabel.clipsToBounds = true
baseButton.addSubview(badgeLabel)
super.init()
customView = baseButton
update(theme: theme)
updateBadgeLabel()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func updateBadgeLabel() {
badgeLabel.isHidden = shouldHideBadge
badgeLabel.backgroundColor = badgeBackgroundColor
badgeLabel.font = badgeFont
badgeLabel.textColor = badgeTextColor
let labelSize = calculateLabelSize()
var width = labelSize.width + badgePadding.horizontal
let height = labelSize.height + badgePadding.vertical
if width < height {
// let width at least be as height
width = height
}
baseButton.sizeToFit()
badgeLabel.frame = CGRect(x: baseButton.frame.width - baseButton.contentEdgeInsets.right - width/2,
y: baseButton.contentEdgeInsets.top - height/2,
width: width,
height: height)
badgeLabel.text = badgeText
badgeLabel.layer.cornerRadius = badgeLabel.frame.height/2
}
private func calculateLabelSize() -> CGSize {
let tmpLabel = UILabel(frame: badgeLabel.frame)
tmpLabel.font = badgeFont
tmpLabel.text = badgeText
tmpLabel.sizeToFit()
return tmpLabel.frame.size
}
}
extension BadgedBarButtonItem: Themable {
func update(theme: Theme) {
self.theme = theme
tintColor = theme.colors.accent
baseButton.tintColor = theme.colors.accent
}
}