diff --git a/Mixin/Resources/en.lproj/Localizable.strings b/Mixin/Resources/en.lproj/Localizable.strings index bdf3ea8ed8..f2934f4ead 100644 --- a/Mixin/Resources/en.lproj/Localizable.strings +++ b/Mixin/Resources/en.lproj/Localizable.strings @@ -785,6 +785,7 @@ "shopping" = "Shopping"; "show" = "Show"; "show_asset" = "Show asset"; +"show_in_chat" = "Show in chat"; "sign_in" = "Sign in"; "sign_with_emergency_contact" = "Sign in with emergency contact"; "sign_with_phone_number" = "Sign in with phone number"; diff --git a/Mixin/Resources/ja.lproj/Localizable.strings b/Mixin/Resources/ja.lproj/Localizable.strings index 9829744b6f..149047d05e 100644 --- a/Mixin/Resources/ja.lproj/Localizable.strings +++ b/Mixin/Resources/ja.lproj/Localizable.strings @@ -785,6 +785,7 @@ "shopping" = "買い物"; "show" = "表示"; "show_asset" = "資産を表示する"; +"show_in_chat" = "チャット内で表示"; "sign_in" = "ログイン"; "sign_with_emergency_contact" = "緊急連絡先でログイン"; "sign_with_phone_number" = "電話番号でログイン"; diff --git a/Mixin/Resources/ru.lproj/Localizable.strings b/Mixin/Resources/ru.lproj/Localizable.strings index a0ae434af4..e3160e4389 100644 --- a/Mixin/Resources/ru.lproj/Localizable.strings +++ b/Mixin/Resources/ru.lproj/Localizable.strings @@ -785,6 +785,7 @@ "shopping" = "Покупка"; "show" = "Показать"; "show_asset" = "Показать актив"; +"show_in_chat" = "Показать в чате"; "sign_in" = "Войти"; "sign_with_emergency_contact" = "Войти через контакт для экстренных случаев"; "sign_with_phone_number" = "Войти через номер телефона"; diff --git a/Mixin/Resources/zh-Hans.lproj/Localizable.strings b/Mixin/Resources/zh-Hans.lproj/Localizable.strings index 66859b5cb9..80200417fb 100644 --- a/Mixin/Resources/zh-Hans.lproj/Localizable.strings +++ b/Mixin/Resources/zh-Hans.lproj/Localizable.strings @@ -785,6 +785,7 @@ "shopping" = "购物"; "show" = "显示"; "show_asset" = "显示资产"; +"show_in_chat" = "在聊天中展示"; "sign_in" = "登录"; "sign_with_emergency_contact" = "通过紧急联系人登录"; "sign_with_phone_number" = "通过手机号登录"; diff --git a/Mixin/Resources/zh-Hant.lproj/Localizable.strings b/Mixin/Resources/zh-Hant.lproj/Localizable.strings index a990b118a5..0d916b874b 100644 --- a/Mixin/Resources/zh-Hant.lproj/Localizable.strings +++ b/Mixin/Resources/zh-Hant.lproj/Localizable.strings @@ -785,6 +785,7 @@ "shopping" = "購物"; "show" = "顯示"; "show_asset" = "顯示資產"; +"show_in_chat" = "在聊天中展示"; "sign_in" = "登入"; "sign_with_emergency_contact" = "透過緊急聯絡人登入"; "sign_with_phone_number" = "透過手機號登入"; diff --git a/Mixin/UserInterface/Controllers/Chat/ConversationViewController.swift b/Mixin/UserInterface/Controllers/Chat/ConversationViewController.swift index 24e3e08495..e4bbc6585b 100644 --- a/Mixin/UserInterface/Controllers/Chat/ConversationViewController.swift +++ b/Mixin/UserInterface/Controllers/Chat/ConversationViewController.swift @@ -1374,6 +1374,16 @@ class ConversationViewController: UIViewController { } } + func scrollToMessage(messageId: String) { + if let indexPath = dataSource.indexPath(where: { $0.messageId == messageId }) { + scheduleCellBackgroundFlash(messageId: messageId) + tableView.scrollToRow(at: indexPath, at: .middle, animated: true) + } else if MessageDAO.shared.hasMessage(id: messageId) { + messageIdToFlashAfterAnimationFinished = messageId + reloadWithMessageId(messageId, scrollUpwards: true) + } + } + } // MARK: - UIGestureRecognizerDelegate @@ -1863,7 +1873,7 @@ extension ConversationViewController: PinMessageBannerViewDelegate { let quoteMessageId = MessageDAO.shared.quoteMessageId(messageId: id) else { return } - scrollToPinnedMessage(messageId: quoteMessageId) + scrollToMessage(messageId: quoteMessageId) } } @@ -1872,8 +1882,8 @@ extension ConversationViewController: PinMessageBannerViewDelegate { extension ConversationViewController: PinMessagesPreviewViewControllerDelegate { func pinMessagesPreviewViewController(_ controller: PinMessagesPreviewViewController, needsShowMessage messageId: String) { - controller.dismissAsChild { - self.scrollToPinnedMessage(messageId: messageId) + controller.dismissAsChild(animated: true) { + self.scrollToMessage(messageId: messageId) } } @@ -2803,16 +2813,6 @@ extension ConversationViewController { } } - private func scrollToPinnedMessage(messageId: String) { - if let indexPath = dataSource.indexPath(where: { $0.messageId == messageId }) { - scheduleCellBackgroundFlash(messageId: messageId) - tableView.scrollToRow(at: indexPath, at: .middle, animated: true) - } else if MessageDAO.shared.hasMessage(id: messageId) { - messageIdToFlashAfterAnimationFinished = messageId - reloadWithMessageId(messageId, scrollUpwards: true) - } - } - private func updateMessagePinningAvailability() { let isAvailable = dataSource.category != .group || ParticipantDAO.shared.isAdmin(conversationId: conversationId, userId: myUserId) diff --git a/Mixin/UserInterface/Controllers/Chat/GalleryImageItemViewController.swift b/Mixin/UserInterface/Controllers/Chat/GalleryImageItemViewController.swift index 7a6b46d25b..3602fd418a 100644 --- a/Mixin/UserInterface/Controllers/Chat/GalleryImageItemViewController.swift +++ b/Mixin/UserInterface/Controllers/Chat/GalleryImageItemViewController.swift @@ -59,10 +59,27 @@ final class GalleryImageItemViewController: GalleryItemViewController { override var supportedActions: Action { if let item = item { - if item.url == nil { - return [.forward] + let isTranscriptPreviewPresented = UIApplication + .currentConversationViewController()? + .children + .contains(where: { $0 is TranscriptPreviewViewController }) ?? false + if isTranscriptPreviewPresented { + if item.url == nil { + return [.forward] + } else { + return [.forward, .saveToLibrary, .share] + } } else { - return [.forward, .saveToLibrary, .share] + if item.url == nil { + return [.forward, .showInChat] + } else { + let conversation = UIApplication.homeNavigationController?.viewControllers.compactMap({ $0 as? ConversationViewController }).last + if let id = conversation?.conversationId, id == item.conversationId { + return [.forward, .saveToLibrary, .share, .showInChat] + } else { + return [.forward, .saveToLibrary, .share] + } + } } } else { return [] diff --git a/Mixin/UserInterface/Controllers/Chat/GalleryItemViewController.swift b/Mixin/UserInterface/Controllers/Chat/GalleryItemViewController.swift index 4f30908c70..f035c0a260 100644 --- a/Mixin/UserInterface/Controllers/Chat/GalleryItemViewController.swift +++ b/Mixin/UserInterface/Controllers/Chat/GalleryItemViewController.swift @@ -9,6 +9,7 @@ class GalleryItemViewController: UIViewController { static let forward = Action(rawValue: 1 << 0) static let saveToLibrary = Action(rawValue: 1 << 1) static let share = Action(rawValue: 1 << 2) + static let showInChat = Action(rawValue: 1 << 3) } let operationButton = LargeModernNetworkOperationButton() diff --git a/Mixin/UserInterface/Controllers/Chat/GalleryViewController.swift b/Mixin/UserInterface/Controllers/Chat/GalleryViewController.swift index 90faf81c02..351c041583 100644 --- a/Mixin/UserInterface/Controllers/Chat/GalleryViewController.swift +++ b/Mixin/UserInterface/Controllers/Chat/GalleryViewController.swift @@ -312,6 +312,27 @@ final class GalleryViewController: UIViewController, GalleryAnimatable { } } + func showInChat() { + guard let messageId = currentItemViewController?.item?.messageId else { + return + } + guard let conversationController = UIApplication.homeNavigationController?.viewControllers.first(where: { $0 is ConversationViewController }) as? ConversationViewController else { + return + } + let sharedMedia = UIApplication.homeNavigationController?.viewControllers + .compactMap({ $0 as? ContainerViewController }) + .compactMap({ $0.viewController as? SharedMediaViewController }) + .first + if let sharedMedia { + sharedMedia.navigationController?.popViewController(animated: false) + } else if let pinMessagesPreview = conversationController.children.first(where: { $0 is PinMessagesPreviewViewController }) as? PinMessagesPreviewViewController { + pinMessagesPreview.dismissAsChild(animated: false, completion: nil) + } + dismiss(transitionViewInitialOffsetY: 0) { + conversationController.scrollToMessage(messageId: messageId) + } + } + @objc func panAction(_ recognizer: UIPanGestureRecognizer) { let translation = recognizer.translation(in: view) let progress = min(1, max(0, translation.y / (view.bounds.height / 3))) @@ -382,6 +403,11 @@ final class GalleryViewController: UIViewController, GalleryAnimatable { } })) } + if itemViewController.supportedActions.contains(.showInChat) { + alert.addAction(UIAlertAction(title: R.string.localizable.show_in_chat(), style: .default, handler: { (_) in + self.showInChat() + })) + } alert.addAction(UIAlertAction(title: R.string.localizable.cancel(), style: .cancel, handler: nil)) present(alert, animated: true, completion: nil) } diff --git a/Mixin/UserInterface/Controllers/Chat/PinMessagesPreviewViewController.swift b/Mixin/UserInterface/Controllers/Chat/PinMessagesPreviewViewController.swift index 7ff6d05a92..44b70466e3 100644 --- a/Mixin/UserInterface/Controllers/Chat/PinMessagesPreviewViewController.swift +++ b/Mixin/UserInterface/Controllers/Chat/PinMessagesPreviewViewController.swift @@ -134,7 +134,7 @@ extension PinMessagesPreviewViewController { controller.addAction(UIAlertAction(title: R.string.localizable.unpin(), style: .default) { _ in self.ignoresPinMessageChangeNotification = true SendMessageService.shared.sendPinMessages(items: self.pinnedMessageItems, conversationId: self.conversationId, action: .unpin) - self.dismissAsChild(completion: nil) + self.dismissAsChild(animated: true, completion: nil) }) present(controller, animated: true, completion: nil) } @@ -163,7 +163,7 @@ extension PinMessagesPreviewViewController { queue.async { guard PinMessageDAO.shared.hasMessage(conversationId: conversationId) else { DispatchQueue.main.async { - self.dismissAsChild(completion: nil) + self.dismissAsChild(animated: true, completion: nil) } return } diff --git a/Mixin/UserInterface/Controllers/Chat/StaticMessagesViewController.swift b/Mixin/UserInterface/Controllers/Chat/StaticMessagesViewController.swift index 07388ce3fb..9a45868265 100644 --- a/Mixin/UserInterface/Controllers/Chat/StaticMessagesViewController.swift +++ b/Mixin/UserInterface/Controllers/Chat/StaticMessagesViewController.swift @@ -71,7 +71,7 @@ class StaticMessagesViewController: UIViewController { } @IBAction func dismissAction(_ sender: Any) { - dismissAsChild(completion: nil) + dismissAsChild(animated: true, completion: nil) } func attachmentURL(withFilename filename: String) -> URL? { @@ -132,13 +132,13 @@ class StaticMessagesViewController: UIViewController { }) } - func dismissAsChild(completion: (() -> Void)?) { + func dismissAsChild(animated: Bool, completion: (() -> Void)?) { if didPlayAudioMessage { audioManager.stop() } showContentConstraint.priority = .defaultLow hideContentConstraint.priority = .defaultHigh - UIView.animate(withDuration: 0.5, delay: 0, options: .overdampedCurve) { + UIView.animate(withDuration: animated ? 0.5 : 0, delay: 0, options: .overdampedCurve) { self.view.layoutIfNeeded() self.view.backgroundColor = .black.withAlphaComponent(0) } completion: { _ in diff --git a/Mixin/UserInterface/Controllers/Chat/TranscriptPreviewViewController.swift b/Mixin/UserInterface/Controllers/Chat/TranscriptPreviewViewController.swift index 5ab2676bbd..baca947eae 100644 --- a/Mixin/UserInterface/Controllers/Chat/TranscriptPreviewViewController.swift +++ b/Mixin/UserInterface/Controllers/Chat/TranscriptPreviewViewController.swift @@ -90,7 +90,7 @@ extension TranscriptPreviewViewController { else { return } - dismissAsChild(completion: nil) + dismissAsChild(animated: true, completion: nil) } @objc private func mediaStatusDidUpdate(_ notification: Notification) { @@ -131,7 +131,7 @@ extension TranscriptPreviewViewController { guard let messageId = notification.userInfo?[ExpiredMessageDAO.messageIdKey] as? String, messageId == transcriptMessage.messageId else { return } - dismissAsChild(completion: nil) + dismissAsChild(animated: true, completion: nil) } }