You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
session-ios/Session/Conversations/Context Menu/ContextMenuVC.swift

184 lines
5.8 KiB
Swift

// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
import SessionUIKit
import SessionMessagingKit
final class ContextMenuVC: UIViewController {
private static let actionViewHeight: CGFloat = 40
private static let menuCornerRadius: CGFloat = 8
private let snapshot: UIView
private let frame: CGRect
private let cellViewModel: MessageViewModel
private let actions: [Action]
private let dismiss: () -> Void
// MARK: - UI
private lazy var blurView: UIVisualEffectView = UIVisualEffectView(effect: nil)
private lazy var menuView: UIView = {
let result: UIView = UIView()
result.layer.shadowColor = UIColor.black.cgColor
result.layer.shadowOffset = CGSize.zero
result.layer.shadowOpacity = 0.4
result.layer.shadowRadius = 4
return result
}()
4 years ago
private lazy var timestampLabel: UILabel = {
let result: UILabel = UILabel()
4 years ago
result.font = .systemFont(ofSize: Values.verySmallFontSize)
result.textColor = (isLightMode ? .black : .white)
if let dateForUI: Date = cellViewModel.dateForUI {
result.text = dateForUI.formattedForDisplay
}
4 years ago
return result
}()
// MARK: - Initialization
init(
snapshot: UIView,
frame: CGRect,
cellViewModel: MessageViewModel,
actions: [Action],
dismiss: @escaping () -> Void
) {
self.snapshot = snapshot
self.frame = frame
self.cellViewModel = cellViewModel
self.actions = actions
self.dismiss = dismiss
super.init(nibName: nil, bundle: nil)
}
override init(nibName: String?, bundle: Bundle?) {
preconditionFailure("Use init(snapshot:) instead.")
}
required init?(coder: NSCoder) {
preconditionFailure("Use init(coder:) instead.")
}
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
// Background color
view.backgroundColor = .clear
// Blur
view.addSubview(blurView)
blurView.pin(to: view)
// Snapshot
snapshot.layer.shadowColor = UIColor.black.cgColor
snapshot.layer.shadowOffset = CGSize.zero
snapshot.layer.shadowOpacity = 0.4
snapshot.layer.shadowRadius = 4
view.addSubview(snapshot)
snapshot.pin(.left, to: .left, of: view, withInset: frame.origin.x)
snapshot.pin(.top, to: .top, of: view, withInset: frame.origin.y)
snapshot.set(.width, to: frame.width)
snapshot.set(.height, to: frame.height)
4 years ago
// Timestamp
view.addSubview(timestampLabel)
timestampLabel.center(.vertical, in: snapshot)
if cellViewModel.variant == .standardOutgoing {
4 years ago
timestampLabel.pin(.right, to: .left, of: snapshot, withInset: -Values.smallSpacing)
}
else {
4 years ago
timestampLabel.pin(.left, to: .right, of: snapshot, withInset: Values.smallSpacing)
}
// Menu
let menuBackgroundView = UIView()
menuBackgroundView.backgroundColor = Colors.receivedMessageBackground
4 years ago
menuBackgroundView.layer.cornerRadius = ContextMenuVC.menuCornerRadius
menuBackgroundView.layer.masksToBounds = true
menuView.addSubview(menuBackgroundView)
menuBackgroundView.pin(to: menuView)
let menuStackView = UIStackView(
arrangedSubviews: actions
.map { action -> ActionView in ActionView(for: action, dismiss: snDismiss) }
)
menuStackView.axis = .vertical
menuView.addSubview(menuStackView)
menuStackView.pin(to: menuView)
view.addSubview(menuView)
let menuHeight = (CGFloat(actions.count) * ContextMenuVC.actionViewHeight)
let spacing = Values.smallSpacing
let margin = max(UIApplication.shared.keyWindow!.safeAreaInsets.bottom, Values.mediumSpacing)
if frame.maxY + spacing + menuHeight > UIScreen.main.bounds.height - margin {
menuView.pin(.bottom, to: .top, of: snapshot, withInset: -spacing)
}
else {
menuView.pin(.top, to: .bottom, of: snapshot, withInset: spacing)
}
switch cellViewModel.variant {
case .standardOutgoing: menuView.pin(.right, to: .right, of: snapshot)
case .standardIncoming: menuView.pin(.left, to: .left, of: snapshot)
default: break // Should never occur
}
// Tap gesture
let mainTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap))
view.addGestureRecognizer(mainTapGestureRecognizer)
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 0.25) {
self.blurView.effect = UIBlurEffect(style: .regular)
self.menuView.alpha = 1
}
}
// MARK: - Layout
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
menuView.layer.shadowPath = UIBezierPath(
roundedRect: menuView.bounds,
cornerRadius: ContextMenuVC.menuCornerRadius
).cgPath
}
// MARK: - Interaction
@objc private func handleTap() {
snDismiss()
}
func snDismiss() {
UIView.animate(
withDuration: 0.25,
animations: { [weak self] in
self?.blurView.effect = nil
self?.menuView.alpha = 0
self?.timestampLabel.alpha = 0
},
completion: { [weak self] _ in
self?.dismiss()
Merge branch 'feature/session-id-blinding-part-2' into feature/database-refactor # Conflicts: # Podfile # Podfile.lock # Session.xcodeproj/project.pbxproj # Session/Closed Groups/EditClosedGroupVC.swift # Session/Closed Groups/NewClosedGroupVC.swift # Session/Conversations/Context Menu/ContextMenuVC+Action.swift # Session/Conversations/Context Menu/ContextMenuVC.swift # Session/Conversations/ConversationMessageMapping.swift # Session/Conversations/ConversationSearch.swift # Session/Conversations/ConversationVC+Interaction.swift # Session/Conversations/ConversationVC.swift # Session/Conversations/ConversationViewItem.h # Session/Conversations/ConversationViewItem.m # Session/Conversations/ConversationViewModel.m # Session/Conversations/Input View/InputView.swift # Session/Conversations/Input View/MentionSelectionView.swift # Session/Conversations/LongTextViewController.swift # Session/Conversations/Message Cells/Content Views/LinkPreviewView.swift # Session/Conversations/Message Cells/MessageCell.swift # Session/Conversations/Message Cells/VisibleMessageCell.swift # Session/Conversations/Settings/OWSConversationSettingsViewController.m # Session/Conversations/Views & Modals/ConversationTitleView.swift # Session/Conversations/Views & Modals/DownloadAttachmentModal.swift # Session/Conversations/Views & Modals/JoinOpenGroupModal.swift # Session/Conversations/Views & Modals/LinkPreviewModal.swift # Session/Conversations/Views & Modals/MessagesTableView.swift # Session/Conversations/Views & Modals/URLModal.swift # Session/Home/GlobalSearch/GlobalSearchViewController.swift # Session/Home/HomeVC.swift # Session/Home/Message Requests/MessageRequestsViewController.swift # Session/Media Viewing & Editing/MediaDetailViewController.m # Session/Media Viewing & Editing/MediaPageViewController.swift # Session/Meta/AppDelegate.m # Session/Meta/AppDelegate.swift # Session/Meta/AppEnvironment.swift # Session/Meta/Signal-Bridging-Header.h # Session/Meta/Translations/en.lproj/Localizable.strings # Session/Meta/Translations/hi.lproj/Localizable.strings # Session/Meta/Translations/si.lproj/Localizable.strings # Session/Meta/Translations/zh-Hant.lproj/Localizable.strings # Session/Notifications/AppNotifications.swift # Session/Open Groups/JoinOpenGroupVC.swift # Session/Settings/NukeDataModal.swift # Session/Settings/SeedModal.swift # Session/Settings/SettingsVC.swift # Session/Settings/ShareLogsModal.swift # Session/Shared/ConversationCell.swift # Session/Shared/UserSelectionVC.swift # Session/Utilities/BackgroundPoller.swift # Session/Utilities/MentionUtilities.swift # Session/Utilities/MockDataGenerator.swift # SessionMessagingKit/Database/OWSPrimaryStorage.m # SessionMessagingKit/Database/SSKPreferences.swift # SessionMessagingKit/Database/Storage+Contacts.swift # SessionMessagingKit/Database/Storage+Jobs.swift # SessionMessagingKit/Database/Storage+Messaging.swift # SessionMessagingKit/Database/Storage+OpenGroups.swift # SessionMessagingKit/Database/TSDatabaseView.m # SessionMessagingKit/File Server/FileServerAPIV2.swift # SessionMessagingKit/Jobs/AttachmentDownloadJob.swift # SessionMessagingKit/Jobs/AttachmentUploadJob.swift # SessionMessagingKit/Jobs/JobQueue.swift # SessionMessagingKit/Jobs/MessageReceiveJob.swift # SessionMessagingKit/Jobs/MessageSendJob.swift # SessionMessagingKit/Jobs/NotifyPNServerJob.swift # SessionMessagingKit/Messages/Control Messages/ClosedGroupControlMessage.swift # SessionMessagingKit/Messages/Control Messages/ConfigurationMessage+Convenience.swift # SessionMessagingKit/Messages/Message+Destination.swift # SessionMessagingKit/Messages/Signal/TSIncomingMessage.h # SessionMessagingKit/Messages/Signal/TSIncomingMessage.m # SessionMessagingKit/Messages/Signal/TSInfoMessage.h # SessionMessagingKit/Messages/Signal/TSInfoMessage.m # SessionMessagingKit/Messages/Signal/TSInteraction.h # SessionMessagingKit/Messages/Signal/TSInteraction.m # SessionMessagingKit/Messages/Signal/TSMessage.h # SessionMessagingKit/Messages/Signal/TSMessage.m # SessionMessagingKit/Open Groups/OpenGroupAPIV2+ObjC.swift # SessionMessagingKit/Open Groups/OpenGroupAPIV2.swift # SessionMessagingKit/Open Groups/OpenGroupManagerV2.swift # SessionMessagingKit/Open Groups/OpenGroupMessageV2.swift # SessionMessagingKit/Sending & Receiving/Mentions/MentionsManager.swift # SessionMessagingKit/Sending & Receiving/MessageReceiver+Decryption.swift # SessionMessagingKit/Sending & Receiving/MessageReceiver+Handling.swift # SessionMessagingKit/Sending & Receiving/MessageReceiver.swift # SessionMessagingKit/Sending & Receiving/MessageSender+ClosedGroups.swift # SessionMessagingKit/Sending & Receiving/MessageSender+Encryption.swift # SessionMessagingKit/Sending & Receiving/MessageSender.swift # SessionMessagingKit/Sending & Receiving/Notifications/NotificationsProtocol.h # SessionMessagingKit/Sending & Receiving/Pollers/ClosedGroupPoller.swift # SessionMessagingKit/Sending & Receiving/Pollers/OpenGroupPollerV2.swift # SessionMessagingKit/Sending & Receiving/Pollers/Poller.swift # SessionMessagingKit/Storage.swift # SessionMessagingKit/Threads/Notification+Thread.swift # SessionMessagingKit/Threads/TSContactThread.h # SessionMessagingKit/Threads/TSContactThread.m # SessionMessagingKit/Threads/TSGroupModel.h # SessionMessagingKit/Threads/TSGroupModel.m # SessionMessagingKit/Threads/TSGroupThread.m # SessionMessagingKit/Utilities/General.swift # SessionNotificationServiceExtension/NSENotificationPresenter.swift # SessionNotificationServiceExtension/NotificationServiceExtension.swift # SessionSnodeKit/OnionRequestAPI+Encryption.swift # SessionSnodeKit/OnionRequestAPI.swift # SessionSnodeKit/SnodeAPI.swift # SessionSnodeKit/SnodeMessage.swift # SessionSnodeKit/Storage+SnodeAPI.swift # SessionSnodeKit/Storage.swift # SessionUtilitiesKit/General/Array+Utilities.swift # SessionUtilitiesKit/General/Dictionary+Utilities.swift # SessionUtilitiesKit/General/SNUserDefaults.swift # SessionUtilitiesKit/General/Set+Utilities.swift # SessionUtilitiesKit/Meta/SessionUtilitiesKit.h # SessionUtilitiesKit/Utilities/Optional+Utilities.swift # SessionUtilitiesKit/Utilities/Sodium+Conversion.swift # SignalUtilitiesKit/Configuration.swift # SignalUtilitiesKit/Database/Migrations/OpenGroupServerIdLookupMigration.swift # SignalUtilitiesKit/Messaging/FullTextSearcher.swift # SignalUtilitiesKit/Messaging/Sending & Receiving/MessageSender+Convenience.swift # SignalUtilitiesKit/Profile Pictures/Identicon+ObjC.swift # SignalUtilitiesKit/To Do/OWSProfileManager.m # SignalUtilitiesKit/Utilities/NoopNotificationsManager.swift # SignalUtilitiesKit/Utilities/UIView+OWS.swift
3 years ago
self.delegate?.contextMenuDismissed()
}
)
}
}