mirror of
https://gitlab.opencode.de/bwi/bundesmessenger/clients/bundesmessenger-ios.git
synced 2026-05-02 06:06:57 +02:00
Merge branch 'develop' into steve/5210_bubble_polls
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user