Skip to content

Commit

Permalink
Merge pull request #1437 from nextcloud/improve-shared-item-list
Browse files Browse the repository at this point in the history
Improve shared item list
  • Loading branch information
Ivansss authored Dec 4, 2023
2 parents b8997df + c1498c5 commit d3174dd
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 52 deletions.
61 changes: 45 additions & 16 deletions NextcloudTalk/BaseChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,9 @@ import QuickLook
NCUserInterfaceController.sharedInstance().numberOfAllocatedChatViewControllers += 1
}

public convenience init?(for room: NCRoom, withMessage messages: [NCChatMessage]) {
// Not using an optional here, because it is not available from ObjC
// Pass "0" as highlightMessageId to not highlight a message
public convenience init?(for room: NCRoom, withMessage messages: [NCChatMessage], withHighlightId highlightMessageId: Int) {
self.init(for: room)

// When we pass in a fixed number of messages, we hide the inputbar by default
Expand All @@ -213,18 +215,12 @@ import QuickLook
self.tableView?.slk_scrollToBottom(animated: false)

self.appendMessages(messages: messages)
self.tableView?.reloadData()
}

// Not using an optional here, because it is not available from ObjC
public convenience init?(for room: NCRoom, withMessage messages: [NCChatMessage], withHighlightId highlightMessageId: Int) {
self.init(for: room, withMessage: messages)

DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
if let (indexPath, _) = self.indexPathAndMessage(forMessageId: highlightMessageId) {
self.highlightMessage(at: indexPath, with: .middle)
}
}
self.tableView?.performBatchUpdates({
self.tableView?.reloadData()
}, completion: { _ in
self.highlightMessageWithContentOffset(messageId: highlightMessageId)
})
}

required init?(coder decoder: NSCoder) {
Expand Down Expand Up @@ -2550,15 +2546,21 @@ import QuickLook
}

if let message = self.message(for: indexPath) {
var width = tableView.frame.width - kChatCellAvatarHeight
width -= tableView.safeAreaInsets.left + tableView.safeAreaInsets.right

return self.getCellHeight(for: message, with: width)
return self.getCellHeight(for: message)
}

return kChatMessageCellMinimumHeight
}

func getCellHeight(for message: NCChatMessage) -> CGFloat {
guard let tableView = self.tableView else { return kChatMessageCellMinimumHeight }

var width = tableView.frame.width - kChatCellAvatarHeight
width -= tableView.safeAreaInsets.left + tableView.safeAreaInsets.right

return self.getCellHeight(for: message, with: width)
}

// swiftlint:disable:next cyclomatic_complexity
func getCellHeight(for message: NCChatMessage, with originalWidth: CGFloat) -> CGFloat {
// Chat separators
Expand Down Expand Up @@ -3006,6 +3008,33 @@ import QuickLook
}
}

internal func highlightMessageWithContentOffset(messageId: Int) {
guard messageId > 0,
let tableView = self.tableView,
let (indexPath, _) = self.indexPathAndMessage(forMessageId: messageId)
else { return }

self.highlightMessage(at: indexPath, with: .none)

let rect = tableView.rectForRow(at: indexPath)

// ContentOffset when the cell is at the top of the tableView
let contentOffsetTop = rect.origin.y - tableView.safeAreaInsets.top

// ContentOffset when the cell is at the middle of the tableView
let contentOffsetMiddle = contentOffsetTop - tableView.frame.height / 2 + rect.height / 2

// Fallback to the top offset in case the top of the cell would be scrolled outside of the view
let newContentOffset = min(contentOffsetTop, contentOffsetMiddle)

tableView.contentOffset.y = newContentOffset
}

public func reloadDataAndHighlightMessage(messageId: Int) {
self.tableView?.reloadData()
self.highlightMessageWithContentOffset(messageId: messageId)
}

func showNewMessagesView(until message: NCChatMessage) {
self.firstUnreadMessage = message
self.unreadMessageButton.isHidden = false
Expand Down
2 changes: 2 additions & 0 deletions NextcloudTalk/NCAPIController.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ typedef void (^DeleteChatMessageCompletionBlock)(NSDictionary *messageDict, NSEr
typedef void (^ClearChatHistoryCompletionBlock)(NSDictionary *messageDict, NSError *error, NSInteger statusCode);
typedef void (^GetSharedItemsOverviewCompletionBlock)(NSDictionary *sharedItemsOverview, NSError *error, NSInteger statusCode);
typedef void (^GetSharedItemsCompletionBlock)(NSArray *sharedItems, NSInteger lastKnownMessage, NSError *error, NSInteger statusCode);
typedef void (^GetMessageContextInRoomCompletionBlock)(NSArray *messages, NSError *error, NSInteger statusCode);

typedef void (^GetTranslationsCompletionBlock)(NSArray *languages, BOOL languageDetection, NSError *error, NSInteger statusCode);
typedef void (^MessageTranslationCompletionBlock)(NSDictionary *translationDict, NSError *error, NSInteger statusCode);
Expand Down Expand Up @@ -224,6 +225,7 @@ extern NSInteger const kReceivedChatMessagesLimit;
- (NSURLSessionDataTask *)markChatAsUnreadInRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(SendChatMessagesCompletionBlock)block;
- (NSURLSessionDataTask *)getSharedItemsOverviewInRoom:(NSString *)token withLimit:(NSInteger)limit forAccount:(TalkAccount *)account withCompletionBlock:(GetSharedItemsOverviewCompletionBlock)block;
- (NSURLSessionDataTask *)getSharedItemsOfType:(NSString *)objectType fromLastMessageId:(NSInteger)messageId withLimit:(NSInteger)limit inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(GetSharedItemsCompletionBlock)block;
- (NSURLSessionDataTask *)getMessageContextInRoom:(NSString *)token forMessageId:(NSInteger)messageId withLimit:(NSInteger)limit forAccount:(TalkAccount *)account withCompletionBlock:(GetMessageContextInRoomCompletionBlock)block;

// Translations
- (NSURLSessionDataTask *)getAvailableTranslationsForAccount:(TalkAccount *)account withCompletionBlock:(GetTranslationsCompletionBlock)block;
Expand Down
34 changes: 34 additions & 0 deletions NextcloudTalk/NCAPIController.m
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,7 @@ - (NSURLSessionDataTask *)getSharedItemsOverviewInRoom:(NSString *)token withLim

return task;
}

- (NSURLSessionDataTask *)getSharedItemsOfType:(NSString *)objectType fromLastMessageId:(NSInteger)messageId withLimit:(NSInteger)limit inRoom:(NSString *)token forAccount:(TalkAccount *)account withCompletionBlock:(GetSharedItemsCompletionBlock)block
{
NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
Expand Down Expand Up @@ -1637,6 +1638,39 @@ - (NSURLSessionDataTask *)getSharedItemsOfType:(NSString *)objectType fromLastMe
return task;
}

- (NSURLSessionDataTask *)getMessageContextInRoom:(NSString *)token forMessageId:(NSInteger)messageId withLimit:(NSInteger)limit forAccount:(TalkAccount *)account withCompletionBlock:(GetMessageContextInRoomCompletionBlock)block
{
NSString *encodedToken = [token stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLHostAllowedCharacterSet]];
NSString *endpoint = [NSString stringWithFormat:@"chat/%@/%ld/context", encodedToken, (long)messageId];
NSInteger chatAPIVersion = [self chatAPIVersionForAccount:account];
NSString *URLString = [self getRequestURLForEndpoint:endpoint withAPIVersion:chatAPIVersion forAccount:account];

NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];

if (limit && limit > 0) {
// Limit is optional server-side and defaults to 50, maximum is 100
[parameters setObject:@(limit) forKey:@"limit"];
}

NCAPISessionManager *apiSessionManager = [_apiSessionManagers objectForKey:account.accountId];

NSURLSessionDataTask *task = [apiSessionManager GET:URLString parameters:parameters progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSArray *responseMessages = [[responseObject objectForKey:@"ocs"] objectForKey:@"data"];

if (block) {
block(responseMessages, nil, 0);
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSInteger statusCode = [self getResponseStatusCode:task.response];
[self checkResponseStatusCode:statusCode forAccount:account];
if (block) {
block(nil, error, statusCode);
}
}];

return task;
}

#pragma mark - Translations Controller

- (NSURLSessionDataTask *)getAvailableTranslationsForAccount:(TalkAccount *)account withCompletionBlock:(GetTranslationsCompletionBlock)block
Expand Down
2 changes: 2 additions & 0 deletions NextcloudTalk/NCChatController.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "NCChatMessage.h"

typedef void (^UpdateHistoryInBackgroundCompletionBlock)(NSError *error);
typedef void (^GetMessagesContextCompletionBlock)(NSArray<NCChatMessage *> * _Nullable messages);

@class NCRoom;

Expand Down Expand Up @@ -65,5 +66,6 @@ extern NSString * const NCChatControllerDidReceiveMessagesInBackgroundNotificati
- (void)removeExpiredMessages;
- (BOOL)hasHistoryFromMessageId:(NSInteger)messageId;
- (void)storeMessages:(NSArray *)messages withRealm:(RLMRealm *)realm;
- (void)getMessageContextForMessageId:(NSInteger)messageId withLimit:(NSInteger)limit withCompletionBlock:(GetMessagesContextCompletionBlock)block;

@end
35 changes: 35 additions & 0 deletions NextcloudTalk/NCChatController.m
Original file line number Diff line number Diff line change
Expand Up @@ -814,4 +814,39 @@ - (BOOL)hasHistoryFromMessageId:(NSInteger)messageId
return YES;
}

- (void)getMessageContextForMessageId:(NSInteger)messageId withLimit:(NSInteger)limit withCompletionBlock:(GetMessagesContextCompletionBlock)block
{
[[NCAPIController sharedInstance] getMessageContextInRoom:self.room.token forMessageId:messageId withLimit:limit forAccount:self.account withCompletionBlock:^(NSArray *messages, NSError *error, NSInteger statusCode) {
if (error) {
if (block) {
block(nil);
}

return;
}

NSMutableArray *chatMessages = [[NSMutableArray alloc] initWithCapacity:messages.count];

for (NSDictionary *messageDict in messages) {
NCChatMessage *message = [NCChatMessage messageWithDictionary:messageDict andAccountId:self.account.accountId];
[chatMessages addObject:message];

if (!message.file) {
continue;
}

// Try to get the stored preview height from our database, when the message is already stored
NCChatMessage *managedMessage = [NCChatMessage objectsWhere:@"internalId = %@", message.internalId].firstObject;

if (managedMessage && managedMessage.file && managedMessage.file.previewImageHeight > 0) {
message.file.previewImageHeight = managedMessage.file.previewImageHeight;
}
}

if (block) {
block(chatMessages);
}
}];
}

@end
84 changes: 58 additions & 26 deletions NextcloudTalk/RoomSharedItemsTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,30 +73,6 @@ import QuickLook
self.getItemsOverview()
}

func presentItemTypeSelector() {
let itemTypesActionSheet = UIAlertController(title: NSLocalizedString("Shared items", comment: ""), message: nil, preferredStyle: .actionSheet)

for itemType in availableItemTypes() {
let itemTypeName = nameForItemType(itemType: itemType)
let action = UIAlertAction(title: itemTypeName, style: .default) { _ in
self.setupViewForItemType(itemType: itemType)
}

if itemType == currentItemType {
action.setValue(UIImage(named: "checkmark")?.withRenderingMode(_: .alwaysOriginal), forKey: "image")
}
itemTypesActionSheet.addAction(action)
}

itemTypesActionSheet.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: ""), style: .cancel, handler: nil))

// Presentation on iPads
itemTypesActionSheet.popoverPresentationController?.sourceView = self.navigationItem.titleView
itemTypesActionSheet.popoverPresentationController?.sourceRect = self.navigationItem.titleView?.frame ?? CGRect()

self.present(itemTypesActionSheet, animated: true, completion: nil)
}

func availableItemTypes() -> [String] {
var availableItemTypes: [String] = []
for itemType in sharedItemsOverview.keys {
Expand Down Expand Up @@ -183,8 +159,25 @@ import QuickLook
itemTypeSelectorButton.setTitle(buttonTitle, for: .normal)
itemTypeSelectorButton.titleLabel?.font = UIFont.systemFont(ofSize: 17, weight: .medium)
itemTypeSelectorButton.setTitleColor(NCAppBranding.themeTextColor(), for: .normal)
itemTypeSelectorButton.addTarget(self, action: #selector(presentItemTypeSelector), for: .touchUpInside)
self.navigationItem.titleView = itemTypeSelectorButton

var menuActions: [UIAction] = []

for itemType in availableItemTypes() {
let itemTypeName = nameForItemType(itemType: itemType)
let action = UIAction(title: itemTypeName, image: nil) { [unowned self] _ in
self.setupViewForItemType(itemType: itemType)
}

if itemType == currentItemType {
action.state = .on
}

menuActions.append(action)
}

itemTypeSelectorButton.showsMenuAsPrimaryAction = true
itemTypeSelectorButton.menu = UIMenu(children: menuActions)
}

func showFetchingItemsPlaceholderView() {
Expand Down Expand Up @@ -383,7 +376,12 @@ import QuickLook

let message = currentItems[indexPath.row]

cell.fileNameLabel?.text = message.parsedMessage().string
if let file = message.file() {
cell.fileNameLabel?.text = file.name
} else {
cell.fileNameLabel?.text = message.parsedMessage().string
}

var infoLabelText = NCUtils.relativeTimeFromDate(date: Date(timeIntervalSince1970: Double(message.timestamp)))
if !message.actorDisplayName.isEmpty {
infoLabelText += "" + message.actorDisplayName
Expand All @@ -409,6 +407,40 @@ import QuickLook
return cell
}

weak var previewChatViewController: BaseChatViewController?

override func tableView(_ tableView: UITableView, contextMenuConfigurationForRowAt indexPath: IndexPath, point: CGPoint) -> UIContextMenuConfiguration? {
return UIContextMenuConfiguration(identifier: indexPath as NSCopying, previewProvider: {

// Init the BaseChatViewController without message to directly show a preview
if let chatViewController = BaseChatViewController(for: self.room, withMessage: [], withHighlightId: 0) {
self.previewChatViewController = chatViewController

// Fetch the context of the message and update the BaseChatViewController
let message = self.currentItems[indexPath.row]
NCChatController(for: self.room).getMessageContext(forMessageId: message.messageId, withLimit: 50) { messages in
guard let messages else { return }

chatViewController.appendMessages(messages: messages)
chatViewController.reloadDataAndHighlightMessage(messageId: message.messageId)
}

let navController = NCNavigationController(rootViewController: chatViewController)
return navController
}

return nil
}, actionProvider: nil)
}

override func tableView(_ tableView: UITableView, willPerformPreviewActionForMenuWith configuration: UIContextMenuConfiguration, animator: UIContextMenuInteractionCommitAnimating) {
animator.addAnimations {
if let previewChatViewController = self.previewChatViewController {
self.navigationController?.pushViewController(previewChatViewController, animated: false)
}
}
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let cell = tableView.cellForRow(at: indexPath) as? DirectoryTableViewCell ?? DirectoryTableViewCell()
let message = currentItems[indexPath.row]
Expand Down
13 changes: 3 additions & 10 deletions NextcloudTalk/RoomSharedItemsTableViewController.xib
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
Expand All @@ -15,21 +14,15 @@
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" bouncesZoom="NO" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="22" sectionFooterHeight="22" id="i5M-Pr-FkT">
<tableView opaque="NO" clipsSubviews="YES" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" bouncesZoom="NO" style="insetGrouped" separatorStyle="default" rowHeight="44" sectionHeaderHeight="18" sectionFooterHeight="18" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="vLr-E1-eTs"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<connections>
<outlet property="dataSource" destination="-1" id="Tng-2m-Rnh"/>
<outlet property="delegate" destination="-1" id="9aC-8N-iBw"/>
</connections>
<point key="canvasLocation" x="139" y="98"/>
</tableView>
</objects>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

0 comments on commit d3174dd

Please sign in to comment.