Skip to content

Commit

Permalink
Merge pull request #1960 from nextcloud/feat/noid/improve-context-menu
Browse files Browse the repository at this point in the history
Feat/noid/improve context menu
  • Loading branch information
Ivansss authored Jan 27, 2025
2 parents 900e691 + 202a727 commit a7c4707
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 31 deletions.
8 changes: 8 additions & 0 deletions NextcloudTalk.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@
1F5499202D35B07700E9AA9E /* ButtonContainerSwiftUI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F54991F2D35B07700E9AA9E /* ButtonContainerSwiftUI.swift */; };
1F549B662D3995C600E9AA9E /* UserSelectionSwiftUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F549B652D3995C600E9AA9E /* UserSelectionSwiftUIView.swift */; };
1F549B692D3A9AA500E9AA9E /* DebouncedOnChange in Frameworks */ = {isa = PBXBuildFile; productRef = 1F549B682D3A9AA500E9AA9E /* DebouncedOnChange */; };
1F549E2B2D45695C00E9AA9E /* MessageTextViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1F549E2A2D45695C00E9AA9E /* MessageTextViewController.xib */; };
1F549E2D2D45695D00E9AA9E /* MessageTextViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F549E2C2D45695D00E9AA9E /* MessageTextViewController.swift */; };
1F5683CF2BA7980C0023E151 /* FilePreviewImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5683CE2BA7980C0023E151 /* FilePreviewImageView.swift */; };
1F5813F828EB23EF00318FC3 /* NCSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5813F628EB23EF00318FC3 /* NCSplitViewController.swift */; };
1F5813F928EB23EF00318FC3 /* NCSplitViewPlaceholderViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1F5813F728EB23EF00318FC3 /* NCSplitViewPlaceholderViewController.swift */; };
Expand Down Expand Up @@ -743,6 +745,8 @@
1F54991D2D346F9700E9AA9E /* UserStatusAbsenceSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserStatusAbsenceSwiftUIView.swift; sourceTree = "<group>"; };
1F54991F2D35B07700E9AA9E /* ButtonContainerSwiftUI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonContainerSwiftUI.swift; sourceTree = "<group>"; };
1F549B652D3995C600E9AA9E /* UserSelectionSwiftUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSelectionSwiftUIView.swift; sourceTree = "<group>"; };
1F549E2A2D45695C00E9AA9E /* MessageTextViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MessageTextViewController.xib; sourceTree = "<group>"; };
1F549E2C2D45695D00E9AA9E /* MessageTextViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageTextViewController.swift; sourceTree = "<group>"; };
1F5683CE2BA7980C0023E151 /* FilePreviewImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilePreviewImageView.swift; sourceTree = "<group>"; };
1F5813F628EB23EF00318FC3 /* NCSplitViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCSplitViewController.swift; sourceTree = "<group>"; };
1F5813F728EB23EF00318FC3 /* NCSplitViewPlaceholderViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NCSplitViewPlaceholderViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1985,6 +1989,8 @@
C65D252C2C7581A200157A89 /* ExpandedVoiceMessageRecordingView.swift */,
1F205C562CEFA01900AAA673 /* OutOfOfficeView.swift */,
1F205C542CEFA01200AAA673 /* OutOfOfficeView.xib */,
1F549E2C2D45695D00E9AA9E /* MessageTextViewController.swift */,
1F549E2A2D45695C00E9AA9E /* MessageTextViewController.xib */,
);
name = "Chat views";
sourceTree = "<group>";
Expand Down Expand Up @@ -2566,6 +2572,7 @@
2C440D1220EA4A770005F9BB /* RoomInfoTableViewController.xib in Resources */,
2C1D13A3253760EE00EC0533 /* LaunchScreen.xib in Resources */,
1FB7B99C2BF0DF360093CE98 /* BannedActorCell.xib in Resources */,
1F549E2B2D45695C00E9AA9E /* MessageTextViewController.xib in Resources */,
2C4747E22CB58FD2002828F2 /* PollMessageView.xib in Resources */,
2CA1CCAC1F067F35002FE6A2 /* Images.xcassets in Resources */,
2CA1CCD71F1E664C002FE6A2 /* ContactsTableViewCell.xib in Resources */,
Expand Down Expand Up @@ -2921,6 +2928,7 @@
2C5BFBEF288A947900E75118 /* PollVotingView.swift in Sources */,
1FAB2EF02AD1EAA3001214EB /* RLMSupport.swift in Sources */,
1F1B50342B8E069800B0F2F4 /* BaseChatTableViewCell.swift in Sources */,
1F549E2D2D45695D00E9AA9E /* MessageTextViewController.swift in Sources */,
2C1EF36B25505DCE007C9768 /* NCNavigationController.m in Sources */,
1FA38C9029A4B3C6008871B8 /* NCNotificationAction.swift in Sources */,
2C44B4D127FF05A000AD1C86 /* ReactionsSummaryView.swift in Sources */,
Expand Down
24 changes: 19 additions & 5 deletions NextcloudTalk/BaseChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1055,6 +1055,11 @@ import SwiftUI
NotificationPresenter.shared().present(text: NSLocalizedString("Message link copied", comment: ""), dismissAfterDelay: 5.0, includedStyle: .dark)
}

func didPressCopySelection(for message: NCChatMessage) {
let vc = MessageTextViewController(messageText: message.parsedMessage().string)
self.presentWithNavigation(vc, animated: true)
}

func didPressTranslate(for message: NCChatMessage) {
let translateMessageVC = MessageTranslationViewController(message: message.parsedMessage().string, availableTranslations: NCDatabaseManager.sharedInstance().availableTranslations(forAccountId: self.room.accountId))
self.presentWithNavigation(translateMessageVC, animated: true)
Expand Down Expand Up @@ -3070,10 +3075,15 @@ import SwiftUI
var actions: [UIMenuElement] = []

// Copy option
actions.append(UIAction(title: NSLocalizedString("Copy", comment: ""), image: .init(systemName: "square.on.square")) { _ in
actions.append(UIAction(title: NSLocalizedString("Copy", comment: ""), image: .init(systemName: "doc.on.doc")) { _ in
self.didPressCopy(for: message)
})

// Copy Selection
actions.append(UIAction(title: NSLocalizedString("Copy message selection", comment: ""), image: .init(systemName: "text.viewfinder")) { _ in
self.didPressCopySelection(for: message)
})

// Copy Link
actions.append(UIAction(title: NSLocalizedString("Copy message link", comment: ""), image: .init(systemName: "link")) { _ in
self.didPressCopyLink(for: message)
Expand Down Expand Up @@ -3120,7 +3130,7 @@ import SwiftUI
else { return nil }

let maxPreviewWidth = self.view.bounds.size.width - self.view.safeAreaInsets.left - self.view.safeAreaInsets.right
let maxPreviewHeight = self.view.bounds.size.height * 0.6
let maxPreviewHeight = self.view.bounds.size.height * 0.4

// TODO: Take padding into account
let maxTextWidth = maxPreviewWidth - kChatCellAvatarHeight
Expand All @@ -3135,11 +3145,15 @@ import SwiftUI
let previewTableViewCell = self.getCell(for: message)
var cellHeight = self.getCellHeight(for: message, with: maxTextWidth)

let heightDifferenceGroupedToNonGrouped = cellHeight - heightOfOriginalCell

// Cut the height if bigger than max height
if cellHeight > maxPreviewHeight {
cellHeight = maxPreviewHeight
}

let heightdifferenceOriginalToPreview = cellHeight - heightOfOriginalCell

// Use the contentView of the UITableViewCell as a preview view
let previewMessageView = previewTableViewCell.contentView
previewMessageView.frame = CGRect(x: 0, y: 0, width: maxPreviewWidth, height: cellHeight)
Expand All @@ -3148,7 +3162,7 @@ import SwiftUI
// Create a mask to not show the avatar part when showing a grouped messages while animating
// The mask will be reset in willDisplayContextMenuWithConfiguration so the avatar is visible when the context menu is shown
let maskLayer = CAShapeLayer()
let maskRect = CGRect(x: 0, y: previewMessageView.frame.size.height - heightOfOriginalCell, width: previewMessageView.frame.size.width, height: heightOfOriginalCell)
let maskRect = CGRect(x: 0, y: heightDifferenceGroupedToNonGrouped, width: previewMessageView.frame.size.width, height: cellHeight)
maskLayer.path = CGPath(rect: maskRect, transform: nil)

previewMessageView.layer.mask = maskLayer
Expand All @@ -3175,7 +3189,7 @@ import SwiftUI
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
// On large iPhones (with regular landscape size, like iPhone X) we need to take the safe area into account when calculating the center
let cellCenterX = cell.center.x + self.view.safeAreaInsets.left / 2 - self.view.safeAreaInsets.right / 2
let cellCenterY = cell.center.y + (totalAccessoryFrameHeight) / 2 - (cellHeight - heightOfOriginalCell) / 2
let cellCenterY = cell.center.y + totalAccessoryFrameHeight / 2 + heightdifferenceOriginalToPreview / 2 - heightDifferenceGroupedToNonGrouped
cellCenter = CGPoint(x: cellCenterX, y: cellCenterY)
}
} else {
Expand All @@ -3186,7 +3200,7 @@ import SwiftUI
if let cell = tableView.cellForRow(at: indexPath as IndexPath) {
// On large iPhones (with regular landscape size, like iPhone X) we need to take the safe area into account when calculating the center
let cellCenterX = cell.center.x + self.view.safeAreaInsets.left / 2 - self.view.safeAreaInsets.right / 2
let cellCenterY = cell.center.y - (cellHeight - heightOfOriginalCell) / 2
let cellCenterY = cell.center.y + heightdifferenceOriginalToPreview / 2 - heightDifferenceGroupedToNonGrouped
cellCenter = CGPoint(x: cellCenterX, y: cellCenterY)
}
}
Expand Down
75 changes: 49 additions & 26 deletions NextcloudTalk/ChatViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1779,27 +1779,31 @@ import SwiftyAttributes
})
}

// Reply-privately option (only to other users and not in one-to-one)
if self.isMessageReplyable(message: message), self.room.type != .oneToOne, message.actorType == "users", message.actorId != self.account.userId {
actions.append(UIAction(title: NSLocalizedString("Reply privately", comment: ""), image: .init(systemName: "person")) { _ in
self.didPressReplyPrivately(for: message)
})
}

// Forward option (only normal messages for now)
if message.file() == nil, message.poll == nil, !message.isDeletedMessage {
actions.append(UIAction(title: NSLocalizedString("Forward", comment: ""), image: .init(systemName: "arrowshape.turn.up.right")) { _ in
self.didPressForward(for: message)
})
}

// Note to self
if message.file() == nil, message.poll == nil, !message.isDeletedMessage, room.type != .noteToSelf,
NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityNoteToSelf, for: room) {
actions.append(UIAction(title: NSLocalizedString("Note to self", comment: ""), image: .init(systemName: "square.and.pencil")) { _ in
self.didPressNoteToSelf(for: message)
})
}
var copyMenuActions: [UIMenuElement] = []

// Copy option
copyMenuActions.append(UIAction(title: NSLocalizedString("Message", comment: "Copy 'message'"), image: .init(systemName: "doc.text")) { _ in
self.didPressCopy(for: message)
})

// Copy part option
copyMenuActions.append(UIAction(title: NSLocalizedString("Selection", comment: "Copy a 'selection' of a message"), image: .init(systemName: "text.viewfinder")) { _ in
self.didPressCopySelection(for: message)
})

// Copy link option
copyMenuActions.append(UIAction(title: NSLocalizedString("Message link", comment: "Copy 'link' to a message"), image: .init(systemName: "link")) { _ in
self.didPressCopyLink(for: message)
})

actions.append(UIMenu(title: NSLocalizedString("Copy", comment: ""), image: .init(systemName: "doc.on.doc"), children: copyMenuActions))

// Remind me later
if !message.sendingFailed, !message.isOfflineMessage, NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityRemindMeLater, for: room) {
Expand Down Expand Up @@ -1849,18 +1853,6 @@ import SwiftyAttributes
})
}

// Copy option
actions.append(UIAction(title: NSLocalizedString("Copy", comment: ""), image: .init(systemName: "square.on.square")) { _ in
self.didPressCopy(for: message)
})

// Translate
if !self.offlineMode, NCDatabaseManager.sharedInstance().hasAvailableTranslations(forAccountId: self.account.accountId) {
actions.append(UIAction(title: NSLocalizedString("Translate", comment: ""), image: .init(systemName: "character.book.closed")) { _ in
self.didPressTranslate(for: message)
})
}

// Open in nextcloud option
if !self.offlineMode, message.file() != nil {
let openInNextcloudTitle = String(format: NSLocalizedString("Open in %@", comment: ""), filesAppName)
Expand All @@ -1877,6 +1869,37 @@ import SwiftyAttributes
})
}

var moreMenuActions: [UIMenuElement] = []

// Reply-privately option (only to other users and not in one-to-one)
if self.isMessageReplyable(message: message), self.room.type != .oneToOne, message.actorType == "users", message.actorId != self.account.userId {
moreMenuActions.append(UIAction(title: NSLocalizedString("Reply privately", comment: ""), image: .init(systemName: "person")) { _ in
self.didPressReplyPrivately(for: message)
})
}

// Translate
if !self.offlineMode, NCDatabaseManager.sharedInstance().hasAvailableTranslations(forAccountId: self.account.accountId) {
moreMenuActions.append(UIAction(title: NSLocalizedString("Translate", comment: ""), image: .init(systemName: "character.book.closed")) { _ in
self.didPressTranslate(for: message)
})
}

// Note to self
if message.file() == nil, message.poll == nil, !message.isDeletedMessage, room.type != .noteToSelf,
NCDatabaseManager.sharedInstance().roomHasTalkCapability(kCapabilityNoteToSelf, for: room) {
moreMenuActions.append(UIAction(title: NSLocalizedString("Note to self", comment: ""), image: .init(systemName: "square.and.pencil")) { _ in
self.didPressNoteToSelf(for: message)
})
}

if moreMenuActions.count == 1, let firstElement = moreMenuActions.first {
// When there's only one element, no need to create a "More" menu
actions.append(firstElement)
} else if !moreMenuActions.isEmpty {
actions.append(UIMenu(title: NSLocalizedString("More", comment: "More menu elements"), children: moreMenuActions))
}

var destructiveMenuActions: [UIMenuElement] = []

// Edit option
Expand Down
41 changes: 41 additions & 0 deletions NextcloudTalk/MessageTextViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
// SPDX-License-Identifier: GPL-3.0-or-later
//

import UIKit
import Foundation
import SwiftyAttributes

@objcMembers class MessageTextViewController: UIViewController {

@IBOutlet public weak var messageTextView: UITextView!

private var messageText = ""

init(messageText: String) {
super.init(nibName: "MessageTextViewController", bundle: nil)

self.messageText = messageText
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}

override func viewDidLoad() {
super.viewDidLoad()

NCAppBranding.styleViewController(self)

self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: NSLocalizedString("Close", comment: ""), primaryAction: UIAction { [unowned self] _ in
self.dismiss(animated: true)
})

self.messageTextView.layer.cornerRadius = 8
self.messageTextView.layer.masksToBounds = true
self.messageTextView.textContainerInset = UIEdgeInsets(top: 10, left: 10, bottom: 10, right: 10)
self.messageTextView.text = messageText
}

}
Loading

0 comments on commit a7c4707

Please sign in to comment.