mirror of https://github.com/oxen-io/session-ios
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.
244 lines
11 KiB
Swift
244 lines
11 KiB
Swift
|
|
extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuActionDelegate, ScrollToBottomButtonDelegate {
|
|
|
|
@objc func openSettings() {
|
|
let settingsVC = OWSConversationSettingsViewController()
|
|
settingsVC.configure(with: thread, uiDatabaseConnection: OWSPrimaryStorage.shared().uiDatabaseConnection)
|
|
navigationController!.pushViewController(settingsVC, animated: true, completion: nil)
|
|
}
|
|
|
|
func handleCameraButtonTapped() {
|
|
// TODO: Implement
|
|
}
|
|
|
|
func handleLibraryButtonTapped() {
|
|
// TODO: Implement
|
|
}
|
|
|
|
func handleGIFButtonTapped() {
|
|
// TODO: Implement
|
|
}
|
|
|
|
func handleDocumentButtonTapped() {
|
|
// TODO: Implement
|
|
}
|
|
|
|
func handleSendButtonTapped() {
|
|
if let thread = thread as? TSContactThread {
|
|
let publicKey = thread.contactIdentifier()
|
|
guard !OWSBlockingManager.shared().isRecipientIdBlocked(publicKey) else {
|
|
let blockedModal = BlockedModal(publicKey: publicKey)
|
|
blockedModal.modalPresentationStyle = .overFullScreen
|
|
blockedModal.modalTransitionStyle = .crossDissolve
|
|
return present(blockedModal, animated: true, completion: nil)
|
|
}
|
|
}
|
|
// TODO: Attachments
|
|
let text = snInputView.text.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let thread = self.thread
|
|
// TODO: Blocking
|
|
guard !text.isEmpty else { return }
|
|
let message = VisibleMessage()
|
|
message.sentTimestamp = NSDate.millisecondTimestamp()
|
|
message.text = text
|
|
message.quote = VisibleMessage.Quote.from(snInputView.quoteDraftInfo?.model)
|
|
let linkPreviewDraft = snInputView.linkPreviewInfo?.draft
|
|
let tsMessage = TSOutgoingMessage.from(message, associatedWith: thread)
|
|
viewModel.appendUnsavedOutgoingTextMessage(tsMessage)
|
|
Storage.write(with: { transaction in
|
|
message.linkPreview = VisibleMessage.LinkPreview.from(linkPreviewDraft, using: transaction)
|
|
}, completion: { [weak self] in
|
|
tsMessage.linkPreview = OWSLinkPreview.from(message.linkPreview)
|
|
Storage.shared.write { transaction in
|
|
tsMessage.save(with: transaction as! YapDatabaseReadWriteTransaction)
|
|
}
|
|
Storage.shared.write { transaction in
|
|
MessageSender.send(message, with: [], in: thread, using: transaction as! YapDatabaseReadWriteTransaction)
|
|
}
|
|
// TODO: Sent handling
|
|
guard let self = self else { return }
|
|
self.snInputView.text = ""
|
|
self.snInputView.quoteDraftInfo = nil
|
|
self.markAllAsRead()
|
|
// TODO: Reset mentions
|
|
})
|
|
}
|
|
|
|
func handleViewItemLongPressed(_ viewItem: ConversationViewItem) {
|
|
guard let index = viewItems.firstIndex(where: { $0 === viewItem }),
|
|
let cell = messagesTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? VisibleMessageCell,
|
|
let snapshot = cell.bubbleView.snapshotView(afterScreenUpdates: false), contextMenuWindow == nil,
|
|
!ContextMenuVC.actions(for: viewItem, delegate: self).isEmpty else { return }
|
|
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
|
|
let frame = cell.convert(cell.bubbleView.frame, to: UIApplication.shared.keyWindow!)
|
|
let window = ContextMenuWindow()
|
|
let contextMenuVC = ContextMenuVC(snapshot: snapshot, viewItem: viewItem, frame: frame, delegate: self) {
|
|
window.isHidden = true
|
|
self.contextMenuVC = nil
|
|
self.contextMenuWindow = nil
|
|
}
|
|
self.contextMenuVC = contextMenuVC
|
|
contextMenuWindow = window
|
|
window.rootViewController = contextMenuVC
|
|
window.makeKeyAndVisible()
|
|
window.backgroundColor = .clear
|
|
}
|
|
|
|
func handleViewItemTapped(_ viewItem: ConversationViewItem, gestureRecognizer: UITapGestureRecognizer) {
|
|
if let message = viewItem.interaction as? TSOutgoingMessage, message.messageState == .failed {
|
|
showFailedMessageSheet(for: message)
|
|
} else {
|
|
switch viewItem.messageCellType {
|
|
case .audio: playOrPauseAudio(for: viewItem)
|
|
case .mediaMessage:
|
|
guard let index = viewItems.firstIndex(where: { $0 === viewItem }),
|
|
let cell = messagesTableView.cellForRow(at: IndexPath(row: index, section: 0)) as? VisibleMessageCell, let albumView = cell.albumView else { return }
|
|
let locationInCell = gestureRecognizer.location(in: cell)
|
|
if let overlayView = cell.mediaTextOverlayView {
|
|
let locationInOverlayView = cell.convert(locationInCell, to: overlayView)
|
|
if let readMoreButton = overlayView.readMoreButton, readMoreButton.frame.contains(locationInOverlayView) {
|
|
return showFullText(viewItem) // FIXME: Bit of a hack to do it this way
|
|
}
|
|
}
|
|
let locationInAlbumView = cell.convert(locationInCell, to: albumView)
|
|
guard let mediaView = albumView.mediaView(forLocation: locationInAlbumView) else { return }
|
|
if albumView.isMoreItemsView(mediaView: mediaView) && viewItem.mediaAlbumHasFailedAttachment() {
|
|
// TODO: Tapped a failed incoming attachment
|
|
}
|
|
let attachment = mediaView.attachment
|
|
if let pointer = attachment as? TSAttachmentPointer {
|
|
if pointer.state == .failed {
|
|
// TODO: Tapped a failed incoming attachment
|
|
}
|
|
}
|
|
guard let stream = attachment as? TSAttachmentStream else { return }
|
|
let gallery = MediaGallery(thread: thread, options: [ .sliderEnabled, .showAllMediaButton ])
|
|
gallery.presentDetailView(fromViewController: self, mediaAttachment: stream, replacingView: mediaView)
|
|
case .genericAttachment:
|
|
guard let url = viewItem.attachmentStream?.originalMediaURL else { return }
|
|
let shareVC = UIActivityViewController(activityItems: [ url ], applicationActivities: nil)
|
|
navigationController!.present(shareVC, animated: true, completion: nil)
|
|
case .textOnlyMessage:
|
|
guard let preview = viewItem.linkPreview, let urlAsString = preview.urlString, let url = URL(string: urlAsString) else { return }
|
|
openURL(url)
|
|
default: break
|
|
}
|
|
}
|
|
}
|
|
|
|
func handleViewItemDoubleTapped(_ viewItem: ConversationViewItem) {
|
|
switch viewItem.messageCellType {
|
|
case .audio: speedUpAudio(for: viewItem)
|
|
default: break
|
|
}
|
|
}
|
|
|
|
func showFullText(_ viewItem: ConversationViewItem) {
|
|
let longMessageVC = LongTextViewController(viewItem: viewItem)
|
|
navigationController!.pushViewController(longMessageVC, animated: true)
|
|
}
|
|
|
|
func playOrPauseAudio(for viewItem: ConversationViewItem) {
|
|
guard let attachment = viewItem.attachmentStream else { return }
|
|
let fileManager = FileManager.default
|
|
guard let path = attachment.originalFilePath, fileManager.fileExists(atPath: path),
|
|
let url = attachment.originalMediaURL else { return }
|
|
if let audioPlayer = audioPlayer {
|
|
if let owner = audioPlayer.owner as? ConversationViewItem, owner === viewItem {
|
|
audioPlayer.playbackRate = 1
|
|
audioPlayer.togglePlayState()
|
|
return
|
|
} else {
|
|
audioPlayer.stop()
|
|
self.audioPlayer = nil
|
|
}
|
|
}
|
|
let audioPlayer = OWSAudioPlayer(mediaUrl: url, audioBehavior: .audioMessagePlayback, delegate: viewItem)
|
|
self.audioPlayer = audioPlayer
|
|
audioPlayer.owner = viewItem
|
|
audioPlayer.play()
|
|
audioPlayer.setCurrentTime(Double(viewItem.audioProgressSeconds))
|
|
}
|
|
|
|
func speedUpAudio(for viewItem: ConversationViewItem) {
|
|
guard let audioPlayer = audioPlayer, let owner = audioPlayer.owner as? ConversationViewItem, owner === viewItem, audioPlayer.isPlaying else { return }
|
|
audioPlayer.playbackRate = 1.5
|
|
viewItem.lastAudioMessageView?.showSpeedUpLabel()
|
|
}
|
|
|
|
func reply(_ viewItem: ConversationViewItem) {
|
|
var quoteDraftOrNil: OWSQuotedReplyModel?
|
|
Storage.read { transaction in
|
|
quoteDraftOrNil = OWSQuotedReplyModel.quotedReplyForSending(with: viewItem, threadId: viewItem.interaction.uniqueThreadId, transaction: transaction)
|
|
}
|
|
guard let quoteDraft = quoteDraftOrNil else { return }
|
|
let isOutgoing = (viewItem.interaction.interactionType() == .outgoingMessage)
|
|
snInputView.quoteDraftInfo = (model: quoteDraft, isOutgoing: isOutgoing)
|
|
snInputView.becomeFirstResponder()
|
|
}
|
|
|
|
func copy(_ viewItem: ConversationViewItem) {
|
|
if viewItem.canCopyMedia() {
|
|
viewItem.copyMediaAction()
|
|
} else {
|
|
viewItem.copyTextAction()
|
|
}
|
|
}
|
|
|
|
func copySessionID(_ viewItem: ConversationViewItem) {
|
|
guard let message = viewItem.interaction as? TSIncomingMessage else { return }
|
|
UIPasteboard.general.string = message.authorId
|
|
}
|
|
|
|
func delete(_ viewItem: ConversationViewItem) {
|
|
viewItem.deleteAction()
|
|
}
|
|
|
|
func save(_ viewItem: ConversationViewItem) {
|
|
guard viewItem.canSaveMedia() else { return }
|
|
viewItem.saveMediaAction()
|
|
}
|
|
|
|
func ban(_ viewItem: ConversationViewItem) {
|
|
guard let message = viewItem.interaction as? TSIncomingMessage, message.isOpenGroupMessage else { return }
|
|
let alert = UIAlertController(title: "Ban This User?", message: nil, preferredStyle: .alert)
|
|
let threadID = thread.uniqueId!
|
|
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
|
|
guard let openGroup = Storage.shared.getOpenGroup(for: threadID) else { return }
|
|
let publicKey = message.authorId
|
|
OpenGroupAPI.ban(publicKey, from: openGroup.server).retainUntilComplete()
|
|
}))
|
|
alert.addAction(UIAlertAction(title: "Cancel", style: .default, handler: nil))
|
|
present(alert, animated: true, completion: nil)
|
|
}
|
|
|
|
func handleScrollToBottomButtonTapped() {
|
|
scrollToBottom(isAnimated: true)
|
|
}
|
|
|
|
func handleQuoteViewCancelButtonTapped() {
|
|
snInputView.quoteDraftInfo = nil
|
|
}
|
|
|
|
func openURL(_ url: URL) {
|
|
let urlModal = URLModal(url: url)
|
|
urlModal.modalPresentationStyle = .overFullScreen
|
|
urlModal.modalTransitionStyle = .crossDissolve
|
|
present(urlModal, animated: true, completion: nil)
|
|
}
|
|
|
|
func handleReplyButtonTapped(for viewItem: ConversationViewItem) {
|
|
reply(viewItem)
|
|
}
|
|
|
|
@objc func unblock() {
|
|
guard let thread = thread as? TSContactThread else { return }
|
|
let publicKey = thread.contactIdentifier()
|
|
UIView.animate(withDuration: 0.25, animations: {
|
|
self.blockedBanner.alpha = 0
|
|
}, completion: { _ in
|
|
OWSBlockingManager.shared().removeBlockedPhoneNumber(publicKey)
|
|
})
|
|
}
|
|
}
|