mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Merge commit '5d25b32ac4f63a08b06eaa65bb4f8153f7c322fd'
# Conflicts: # Telegram/Telegram-iOS/en.lproj/Localizable.strings
This commit is contained in:
commit
9243444129
@ -8295,10 +8295,10 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Notification.ForumTopicHidden" = "Topic hidden";
|
||||
"Notification.ForumTopicUnhidden" = "Topic unhidden";
|
||||
"Notification.ForumTopicHiddenAuthor" = "%1$@ hidden topic";
|
||||
"Notification.ForumTopicUnhiddenAuthor" = "%1$@ unhidden topic";
|
||||
"Notification.OverviewTopicHidden" = "%1$@ hidden %2$@ %3$@";
|
||||
"Notification.OverviewTopicUnhidden" = "%1$@ unhidden %2$@ %3$@";
|
||||
"Notification.ForumTopicHiddenAuthor" = "%1$@ hid topic";
|
||||
"Notification.ForumTopicUnhiddenAuthor" = "%1$@ unhid topic";
|
||||
"Notification.OverviewTopicHidden" = "%1$@ hid %2$@ %3$@";
|
||||
"Notification.OverviewTopicUnhidden" = "%1$@ unhid %2$@ %3$@";
|
||||
|
||||
"CreateTopic.ShowGeneral" = "Show in Topics";
|
||||
"CreateTopic.ShowGeneralInfo" = "If the 'General' topic is hidden, group members can pull down in the topic list to view it.";
|
||||
@ -8309,3 +8309,6 @@ Sorry for the inconvenience.";
|
||||
"ChatList.DeletedThreads_any" = "Deleted %@ threads";
|
||||
|
||||
"DialogList.SearchSectionMessagesIn" = "Messages in %@";
|
||||
|
||||
"Conversation.ContextMenuReportFalsePositive" = "Report False Positive";
|
||||
"Group.AdminLog.AntiSpamFalsePositiveReportedText" = "Telegram moderators will review your report. Thank you!";
|
||||
|
@ -925,18 +925,20 @@ public struct PremiumConfiguration {
|
||||
|
||||
public struct AntiSpamBotConfiguration {
|
||||
public static var defaultValue: AntiSpamBotConfiguration {
|
||||
return AntiSpamBotConfiguration(antiSpamBotId: nil)
|
||||
return AntiSpamBotConfiguration(antiSpamBotId: nil, minimumGroupParticipants: 100)
|
||||
}
|
||||
|
||||
public let antiSpamBotId: EnginePeer.Id?
|
||||
public let minimumGroupParticipants: Int32
|
||||
|
||||
fileprivate init(antiSpamBotId: EnginePeer.Id?) {
|
||||
fileprivate init(antiSpamBotId: EnginePeer.Id?, minimumGroupParticipants: Int32) {
|
||||
self.antiSpamBotId = antiSpamBotId
|
||||
self.minimumGroupParticipants = minimumGroupParticipants
|
||||
}
|
||||
|
||||
public static func with(appConfiguration: AppConfiguration) -> AntiSpamBotConfiguration {
|
||||
if let data = appConfiguration.data, let string = data["telegram_antispam_user_id"] as? String, let value = Int64(string) {
|
||||
return AntiSpamBotConfiguration(antiSpamBotId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(value)))
|
||||
if let data = appConfiguration.data, let botIdString = data["telegram_antispam_user_id"] as? String, let botIdValue = Int64(botIdString), let groupSize = data["telegram_antispam_group_size_min"] as? Double {
|
||||
return AntiSpamBotConfiguration(antiSpamBotId: EnginePeer.Id(namespace: Namespaces.Peer.CloudUser, id: EnginePeer.Id.Id._internalFromInt64Value(botIdValue)), minimumGroupParticipants: Int32(groupSize))
|
||||
} else {
|
||||
return .defaultValue
|
||||
}
|
||||
|
@ -514,14 +514,14 @@ func chatForumTopicMenuItems(context: AccountContext, peerId: PeerId, threadId:
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
if let isClosed = isClosed, isClosed {
|
||||
if let isClosed = isClosed, isClosed && threadId != 1 {
|
||||
} else {
|
||||
if let isPinned = isPinned, channel.hasPermission(.manageTopics) {
|
||||
items.append(.action(ContextMenuActionItem(text: isPinned ? presentationData.strings.ChatList_Context_Unpin : presentationData.strings.ChatList_Context_Pin, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: isPinned ? "Chat/Context Menu/Unpin": "Chat/Context Menu/Pin"), color: theme.contextMenu.primaryColor) }, action: { _, f in
|
||||
f(.default)
|
||||
|
||||
let _ = (context.engine.peers.toggleForumChannelTopicPinned(id: peerId, threadId: threadId)
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
switch error {
|
||||
case let .limitReached(count):
|
||||
if let chatListController = chatListController {
|
||||
|
@ -338,75 +338,75 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
|
||||
case .medium, .full:
|
||||
strongSelf.statusNodeContainer.isHidden = true
|
||||
|
||||
// Queue.concurrentDefaultQueue().async {
|
||||
// if let message = strongSelf.message, !message.isCopyProtected() && !imageReference.media.flags.contains(.hasStickers) {
|
||||
// strongSelf.recognitionDisposable.set((recognizedContent(engine: strongSelf.context.engine, image: { return generate(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets()))?.generateImage() }, messageId: message.id)
|
||||
// |> deliverOnMainQueue).start(next: { [weak self] results in
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.recognizedContentNode?.removeFromSupernode()
|
||||
// if !results.isEmpty {
|
||||
// let size = strongSelf.imageNode.bounds.size
|
||||
// let recognizedContentNode = RecognizedContentContainer(size: size, recognitions: results, presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, present: { [weak self] c, a in
|
||||
// if let strongSelf = self {
|
||||
// strongSelf.galleryController()?.presentInGlobalOverlay(c, with: a)
|
||||
// }
|
||||
// }, performAction: { [weak self] string, action in
|
||||
// guard let strongSelf = self else {
|
||||
// return
|
||||
// }
|
||||
// switch action {
|
||||
// case .copy:
|
||||
// UIPasteboard.general.string = string
|
||||
// if let controller = strongSelf.baseNavigationController()?.topViewController as? ViewController {
|
||||
// let presentationData = strongSelf.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
// let tooltipController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false })
|
||||
// controller.present(tooltipController, in: .window(.root))
|
||||
// }
|
||||
// case .share:
|
||||
// if let controller = strongSelf.baseNavigationController()?.topViewController as? ViewController {
|
||||
// let shareController = ShareController(context: strongSelf.context, subject: .text(string), externalShare: true, immediateExternalShare: false, updatedPresentationData: (strongSelf.context.sharedContext.currentPresentationData.with({ $0 }), strongSelf.context.sharedContext.presentationData))
|
||||
// controller.present(shareController, in: .window(.root))
|
||||
// }
|
||||
// case .lookup:
|
||||
// let controller = UIReferenceLibraryViewController(term: string)
|
||||
// if let window = strongSelf.baseNavigationController()?.view.window {
|
||||
// controller.popoverPresentationController?.sourceView = window
|
||||
// controller.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
||||
// window.rootViewController?.present(controller, animated: true)
|
||||
// }
|
||||
// case .speak:
|
||||
// let _ = speakText(context: strongSelf.context, text: string)
|
||||
// case .translate:
|
||||
// if let parentController = strongSelf.baseNavigationController()?.topViewController as? ViewController {
|
||||
// let controller = TranslateScreen(context: strongSelf.context, text: string, fromLanguage: nil)
|
||||
// controller.pushController = { [weak parentController] c in
|
||||
// (parentController?.navigationController as? NavigationController)?._keepModalDismissProgress = true
|
||||
// parentController?.push(c)
|
||||
// }
|
||||
// controller.presentController = { [weak parentController] c in
|
||||
// parentController?.present(c, in: .window(.root))
|
||||
// }
|
||||
// parentController.present(controller, in: .window(.root))
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// recognizedContentNode.barcodeAction = { [weak self] payload, rect in
|
||||
// guard let strongSelf = self, let message = strongSelf.message else {
|
||||
// return
|
||||
// }
|
||||
// strongSelf.footerContentNode.openActionOptions?(.url(url: payload, concealed: true), message)
|
||||
// }
|
||||
// recognizedContentNode.alpha = 0.0
|
||||
// recognizedContentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
// recognizedContentNode.update(size: strongSelf.imageNode.bounds.size, transition: .immediate)
|
||||
// strongSelf.imageNode.addSubnode(recognizedContentNode)
|
||||
// strongSelf.recognizedContentNode = recognizedContentNode
|
||||
// strongSelf.recognitionOverlayContentNode.transitionIn()
|
||||
// }
|
||||
// }
|
||||
// }))
|
||||
// }
|
||||
// }
|
||||
Queue.concurrentDefaultQueue().async {
|
||||
if let message = strongSelf.message, !message.isCopyProtected() && !imageReference.media.flags.contains(.hasStickers) {
|
||||
strongSelf.recognitionDisposable.set((recognizedContent(engine: strongSelf.context.engine, image: { return generate(TransformImageArguments(corners: ImageCorners(), imageSize: displaySize, boundingSize: displaySize, intrinsicInsets: UIEdgeInsets()))?.generateImage() }, messageId: message.id)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] results in
|
||||
if let strongSelf = self {
|
||||
strongSelf.recognizedContentNode?.removeFromSupernode()
|
||||
if !results.isEmpty {
|
||||
let size = strongSelf.imageNode.bounds.size
|
||||
let recognizedContentNode = RecognizedContentContainer(size: size, recognitions: results, presentationData: strongSelf.context.sharedContext.currentPresentationData.with { $0 }, present: { [weak self] c, a in
|
||||
if let strongSelf = self {
|
||||
strongSelf.galleryController()?.presentInGlobalOverlay(c, with: a)
|
||||
}
|
||||
}, performAction: { [weak self] string, action in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
switch action {
|
||||
case .copy:
|
||||
UIPasteboard.general.string = string
|
||||
if let controller = strongSelf.baseNavigationController()?.topViewController as? ViewController {
|
||||
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with({ $0 })
|
||||
let tooltipController = UndoOverlayController(presentationData: presentationData, content: .copy(text: presentationData.strings.Conversation_TextCopied), elevatedLayout: true, animateInAsReplacement: false, action: { _ in return false })
|
||||
controller.present(tooltipController, in: .window(.root))
|
||||
}
|
||||
case .share:
|
||||
if let controller = strongSelf.baseNavigationController()?.topViewController as? ViewController {
|
||||
let shareController = ShareController(context: strongSelf.context, subject: .text(string), externalShare: true, immediateExternalShare: false, updatedPresentationData: (strongSelf.context.sharedContext.currentPresentationData.with({ $0 }), strongSelf.context.sharedContext.presentationData))
|
||||
controller.present(shareController, in: .window(.root))
|
||||
}
|
||||
case .lookup:
|
||||
let controller = UIReferenceLibraryViewController(term: string)
|
||||
if let window = strongSelf.baseNavigationController()?.view.window {
|
||||
controller.popoverPresentationController?.sourceView = window
|
||||
controller.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0))
|
||||
window.rootViewController?.present(controller, animated: true)
|
||||
}
|
||||
case .speak:
|
||||
let _ = speakText(context: strongSelf.context, text: string)
|
||||
case .translate:
|
||||
if let parentController = strongSelf.baseNavigationController()?.topViewController as? ViewController {
|
||||
let controller = TranslateScreen(context: strongSelf.context, text: string, canCopy: true, fromLanguage: nil)
|
||||
controller.pushController = { [weak parentController] c in
|
||||
(parentController?.navigationController as? NavigationController)?._keepModalDismissProgress = true
|
||||
parentController?.push(c)
|
||||
}
|
||||
controller.presentController = { [weak parentController] c in
|
||||
parentController?.present(c, in: .window(.root))
|
||||
}
|
||||
parentController.present(controller, in: .window(.root))
|
||||
}
|
||||
}
|
||||
})
|
||||
recognizedContentNode.barcodeAction = { [weak self] payload, rect in
|
||||
guard let strongSelf = self, let message = strongSelf.message else {
|
||||
return
|
||||
}
|
||||
strongSelf.footerContentNode.openActionOptions?(.url(url: payload, concealed: true), message)
|
||||
}
|
||||
recognizedContentNode.alpha = 0.0
|
||||
recognizedContentNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
recognizedContentNode.update(size: strongSelf.imageNode.bounds.size, transition: .immediate)
|
||||
strongSelf.imageNode.addSubnode(recognizedContentNode)
|
||||
strongSelf.recognizedContentNode = recognizedContentNode
|
||||
strongSelf.recognitionOverlayContentNode.transitionIn()
|
||||
}
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
case .none, .blurred:
|
||||
strongSelf.statusNodeContainer.isHidden = false
|
||||
@ -1353,6 +1353,9 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode {
|
||||
var action: ((Bool) -> Void)?
|
||||
private var appeared = false
|
||||
|
||||
private var validLayout: (CGSize, LayoutMetrics, UIEdgeInsets)?
|
||||
private var interfaceIsHidden: Bool = false
|
||||
|
||||
init(theme: PresentationTheme) {
|
||||
self.backgroundNode = ASImageNode()
|
||||
self.backgroundNode.displaysAsynchronously = false
|
||||
@ -1377,13 +1380,25 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode {
|
||||
self.selectedIconNode.isHidden = true
|
||||
|
||||
super.init()
|
||||
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.buttonNode.addSubnode(self.backgroundNode)
|
||||
self.buttonNode.addSubnode(self.selectedBackgroundNode)
|
||||
self.buttonNode.addSubnode(self.iconNode)
|
||||
self.buttonNode.addSubnode(self.selectedIconNode)
|
||||
|
||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.iconNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.iconNode.alpha = 0.4
|
||||
} else {
|
||||
strongSelf.iconNode.alpha = 1.0
|
||||
strongSelf.iconNode.layer.animateAlpha(from: 0.4, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
@ -1392,6 +1407,10 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode {
|
||||
self.selectedBackgroundNode.isHidden = !newValue
|
||||
self.selectedIconNode.isHidden = !newValue
|
||||
|
||||
if !newValue && !self.interfaceIsHidden, let (size, metrics, insets) = self.validLayout {
|
||||
self.updateLayout(size: size, metrics: metrics, insets: insets, isHidden: self.interfaceIsHidden, transition: .animated(duration: 0.3, curve: .easeInOut))
|
||||
}
|
||||
|
||||
self.action?(newValue)
|
||||
|
||||
if self.interfaceIsHidden && !newValue {
|
||||
@ -1409,8 +1428,9 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode {
|
||||
self.buttonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
||||
}
|
||||
|
||||
private var interfaceIsHidden: Bool = false
|
||||
override func updateLayout(size: CGSize, metrics: LayoutMetrics, insets: UIEdgeInsets, isHidden: Bool, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, metrics, insets)
|
||||
|
||||
self.interfaceIsHidden = isHidden
|
||||
|
||||
let buttonSize = CGSize(width: 32.0, height: 32.0)
|
||||
@ -1427,7 +1447,14 @@ private class ImageRecognitionOverlayContentNode: GalleryOverlayContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(x: size.width - insets.right - buttonSize.width - 24.0, y: insets.top - 50.0, width: buttonSize.width + 24.0, height: buttonSize.height + 24.0))
|
||||
var buttonPosition: CGPoint
|
||||
if isHidden && !self.buttonNode.isSelected {
|
||||
buttonPosition = CGPoint(x: size.width - insets.right - buttonSize.width - 59.0, y: -50.0)
|
||||
} else {
|
||||
buttonPosition = CGPoint(x: size.width - insets.right - buttonSize.width - (self.buttonNode.isSelected ? 24.0 : 59.0), y: insets.top - 50.0)
|
||||
}
|
||||
|
||||
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: buttonPosition, size: CGSize(width: buttonSize.width + 24.0, height: buttonSize.height + 24.0)))
|
||||
}
|
||||
|
||||
override func animateIn(previousContentNode: GalleryOverlayContentNode?, transition: ContainedViewLayoutTransition) {
|
||||
|
@ -338,7 +338,7 @@ private struct ChannelAdminsControllerState: Equatable {
|
||||
}
|
||||
}
|
||||
|
||||
private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: PeerId, view: PeerView, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamEnabled: Bool) -> [ChannelAdminsEntry] {
|
||||
private func channelAdminsControllerEntries(presentationData: PresentationData, accountPeerId: PeerId, view: PeerView, state: ChannelAdminsControllerState, participants: [RenderedChannelParticipant]?, antiSpamAvailable: Bool, antiSpamEnabled: Bool) -> [ChannelAdminsEntry] {
|
||||
if participants == nil || participants?.count == nil {
|
||||
return []
|
||||
}
|
||||
@ -351,7 +351,7 @@ private func channelAdminsControllerEntries(presentationData: PresentationData,
|
||||
}
|
||||
entries.append(.recentActions(presentationData.theme, presentationData.strings.Group_Info_AdminLog))
|
||||
|
||||
if isGroup && peer.hasPermission(.deleteAllMessages) {
|
||||
if isGroup && peer.hasPermission(.deleteAllMessages) && antiSpamAvailable {
|
||||
entries.append(.antiSpam(presentationData.theme, presentationData.strings.Group_Management_AntiSpam, antiSpamEnabled))
|
||||
entries.append(.antiSpamInfo(presentationData.theme, presentationData.strings.Group_Management_AntiSpamInfo))
|
||||
}
|
||||
@ -539,10 +539,10 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
||||
|
||||
let adminsPromise = Promise<[RenderedChannelParticipant]?>(nil)
|
||||
|
||||
let antiSpamBotConfiguration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
let antiSpamConfiguration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
|
||||
let resolveAntiSpamPeerDisposable = MetaDisposable()
|
||||
if let antiSpamBotId = antiSpamBotConfiguration.antiSpamBotId {
|
||||
if let antiSpamBotId = antiSpamConfiguration.antiSpamBotId {
|
||||
resolveAntiSpamPeerDisposable.set(
|
||||
(context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: antiSpamBotId))
|
||||
|> mapToSignal { peer -> Signal<Never, NoError> in
|
||||
@ -786,6 +786,11 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
||||
|> map { presentationData, state, view, admins, antiSpamEnabled -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let peerId = view.peerId
|
||||
|
||||
var antiSpamAvailable = false
|
||||
if let cachedData = view.cachedData as? CachedChannelData, let memberCount = cachedData.participantsSummary.memberCount, memberCount >= antiSpamConfiguration.minimumGroupParticipants {
|
||||
antiSpamAvailable = true
|
||||
}
|
||||
|
||||
var rightNavigationButton: ItemListNavigationButton?
|
||||
var secondaryRightNavigationButton: ItemListNavigationButton?
|
||||
if let admins = admins, admins.count > 1 {
|
||||
@ -857,7 +862,7 @@ public func channelAdminsController(context: AccountContext, updatedPresentation
|
||||
}
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(isGroup ? presentationData.strings.ChatAdmins_Title : presentationData.strings.Channel_Management_Title), leftNavigationButton: nil, rightNavigationButton: rightNavigationButton, secondaryRightNavigationButton: secondaryRightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: true)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, view: view, state: state, participants: admins, antiSpamEnabled: antiSpamEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: channelAdminsControllerEntries(presentationData: presentationData, accountPeerId: context.account.peerId, view: view, state: state, participants: admins, antiSpamAvailable: antiSpamAvailable, antiSpamEnabled: antiSpamEnabled), style: .blocks, emptyStateItem: emptyStateItem, searchItem: searchItem, animateChanges: previous != nil && admins != nil && previous!.count >= admins!.count)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
} |> afterDisposed {
|
||||
|
@ -340,13 +340,7 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t
|
||||
}
|
||||
var flags: Int32 = 0
|
||||
flags |= (1 << 2)
|
||||
|
||||
var hidden: Api.Bool? = nil
|
||||
if threadId == 1, !isClosed {
|
||||
flags |= (1 << 3)
|
||||
hidden = .boolFalse
|
||||
}
|
||||
|
||||
|
||||
return account.network.request(Api.functions.channels.editForumTopic(
|
||||
flags: flags,
|
||||
channel: inputChannel,
|
||||
@ -354,7 +348,7 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t
|
||||
title: nil,
|
||||
iconEmojiId: nil,
|
||||
closed: isClosed ? .boolTrue : .boolFalse,
|
||||
hidden: hidden
|
||||
hidden: nil
|
||||
))
|
||||
|> mapError { _ -> EditForumChannelTopicError in
|
||||
return .generic
|
||||
@ -367,7 +361,7 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t
|
||||
var data = initialData
|
||||
|
||||
data.isClosed = isClosed
|
||||
if let _ = hidden {
|
||||
if !isClosed && threadId == 1 {
|
||||
data.isHidden = false
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,7 @@ import SwiftSignalKit
|
||||
|
||||
public struct ChannelAdminEventLogEntry: Comparable {
|
||||
public let stableId: UInt32
|
||||
public let headerStableId: UInt32
|
||||
public let event: AdminLogEvent
|
||||
public let peers: [PeerId: Peer]
|
||||
|
||||
@ -85,6 +86,7 @@ public final class ChannelAdminEventLogContext {
|
||||
private var filter: ChannelAdminEventLogFilter = ChannelAdminEventLogFilter()
|
||||
|
||||
private var nextStableId: UInt32 = 1
|
||||
private var headerStableIds: [AdminLogEventId: UInt32] = [:]
|
||||
private var stableIds: [AdminLogEventId: UInt32] = [:]
|
||||
|
||||
private var entries: ([ChannelAdminEventLogEntry], ChannelAdminEventLogFilter) = ([], ChannelAdminEventLogFilter())
|
||||
@ -182,13 +184,13 @@ public final class ChannelAdminEventLogContext {
|
||||
}
|
||||
|
||||
var entries: [ChannelAdminEventLogEntry] = events.map { event in
|
||||
return ChannelAdminEventLogEntry(stableId: strongSelf.stableIdForEventId(event.id), event: event, peers: result.peers)
|
||||
return ChannelAdminEventLogEntry(stableId: strongSelf.stableIdForEventId(event.id), headerStableId: strongSelf.headerStableIdForEventId(event.id), event: event, peers: result.peers)
|
||||
}
|
||||
entries.append(contentsOf: strongSelf.entries.0)
|
||||
strongSelf.entries = (entries, strongSelf.filter)
|
||||
} else {
|
||||
let entries: [ChannelAdminEventLogEntry] = events.map { event in
|
||||
return ChannelAdminEventLogEntry(stableId: strongSelf.stableIdForEventId(event.id), event: event, peers: result.peers)
|
||||
return ChannelAdminEventLogEntry(stableId: strongSelf.stableIdForEventId(event.id), headerStableId: strongSelf.headerStableIdForEventId(event.id), event: event, peers: result.peers)
|
||||
}
|
||||
strongSelf.entries = (entries, strongSelf.filter)
|
||||
}
|
||||
@ -214,4 +216,15 @@ public final class ChannelAdminEventLogContext {
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
private func headerStableIdForEventId(_ id: AdminLogEventId) -> UInt32 {
|
||||
if let value = self.headerStableIds[id] {
|
||||
return value
|
||||
} else {
|
||||
let value = self.nextStableId
|
||||
self.nextStableId += 1
|
||||
self.headerStableIds[id] = value
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -292,6 +292,10 @@ public extension TelegramEngine {
|
||||
return _internal_toggleAntiSpamProtection(account: self.account, peerId: peerId, enabled: enabled)
|
||||
}
|
||||
|
||||
public func reportAntiSpamFalsePositive(peerId: PeerId, messageId: MessageId) -> Signal<Bool, NoError> {
|
||||
return _internal_reportAntiSpamFalsePositive(account: self.account, peerId: peerId, messageId: messageId)
|
||||
}
|
||||
|
||||
public func requestPeerPhotos(peerId: PeerId) -> Signal<[TelegramPeerPhoto], NoError> {
|
||||
return _internal_requestPeerPhotos(postbox: self.account.postbox, network: self.account.network, peerId: peerId)
|
||||
}
|
||||
|
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AntiSpam.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AntiSpam.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "reportfalse_24.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
135
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AntiSpam.imageset/reportfalse_24.pdf
vendored
Normal file
135
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/AntiSpam.imageset/reportfalse_24.pdf
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj
|
||||
<< >>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<< /Length 3 0 R >>
|
||||
stream
|
||||
/DeviceRGB CS
|
||||
/DeviceRGB cs
|
||||
q
|
||||
1.000000 0.000000 -0.000000 1.000000 4.834961 3.335003 cm
|
||||
0.000000 0.000000 0.000000 scn
|
||||
5.861066 15.587957 m
|
||||
6.196899 15.711591 6.498980 15.822798 6.742460 15.912901 c
|
||||
6.938185 15.972766 7.061687 15.999998 7.169050 15.999998 c
|
||||
7.276096 15.999998 7.389237 15.973757 7.604651 15.910002 c
|
||||
8.743544 15.512181 11.235205 14.579237 12.364463 14.129102 c
|
||||
12.722498 13.984460 12.842218 13.892902 12.896966 13.823884 c
|
||||
12.934976 13.775966 13.000000 13.665576 13.000000 13.307275 c
|
||||
13.000000 7.185324 l
|
||||
13.000000 6.553703 12.915850 6.062992 12.747086 5.640941 c
|
||||
12.579326 5.221397 12.313388 4.833261 11.900326 4.426327 c
|
||||
11.044427 3.583125 9.640606 2.731597 7.372818 1.412622 c
|
||||
7.318006 1.381764 7.263755 1.358576 7.218547 1.344114 c
|
||||
7.193927 1.336239 7.177481 1.332685 7.169050 1.331110 c
|
||||
7.160619 1.332685 7.144172 1.336239 7.119552 1.344114 c
|
||||
7.074736 1.358451 7.021033 1.381364 6.966707 1.411822 c
|
||||
4.705894 2.743835 3.299533 3.594983 2.437976 4.437263 c
|
||||
2.022268 4.843668 1.754397 5.229964 1.585310 5.647222 c
|
||||
1.415345 6.066645 1.330000 6.554857 1.330000 7.185324 c
|
||||
1.330000 13.307275 l
|
||||
1.330000 13.662965 1.395993 13.779277 1.436774 13.830870 c
|
||||
1.491529 13.900139 1.610740 13.992070 1.957460 14.125925 c
|
||||
1.957469 14.125902 l
|
||||
1.962718 14.127980 l
|
||||
2.859330 14.482901 4.598611 15.123198 5.860996 15.587932 c
|
||||
5.861066 15.587957 l
|
||||
h
|
||||
7.169050 17.329998 m
|
||||
6.842114 17.329998 6.548457 17.244995 6.332937 17.178432 c
|
||||
6.321291 17.174837 6.309747 17.170919 6.298316 17.166689 c
|
||||
6.058186 17.077799 5.755209 16.966265 5.415772 16.841309 c
|
||||
5.415587 16.841240 l
|
||||
4.154459 16.376986 2.390144 15.727495 1.475762 15.365632 c
|
||||
1.086233 15.215069 0.680177 15.018452 0.393381 14.655631 c
|
||||
0.091951 14.274296 0.000000 13.813374 0.000000 13.307275 c
|
||||
0.000000 7.185324 l
|
||||
0.000000 6.433676 0.101960 5.766401 0.352673 5.147715 c
|
||||
0.604264 4.526862 0.990442 3.992426 1.508224 3.486230 c
|
||||
2.512969 2.503967 4.089810 1.563107 6.296909 0.262775 c
|
||||
6.296864 0.262699 l
|
||||
6.306323 0.257332 l
|
||||
6.547542 0.120480 6.865578 0.000000 7.169050 0.000000 c
|
||||
7.472521 0.000000 7.790557 0.120480 8.031776 0.257332 c
|
||||
8.031796 0.257298 l
|
||||
8.037958 0.260883 l
|
||||
10.255331 1.550500 11.831186 2.491209 12.833723 3.478873 c
|
||||
13.350101 3.987590 13.733150 4.524757 13.982018 5.147135 c
|
||||
14.229882 5.767005 14.330000 6.434830 14.330000 7.185324 c
|
||||
14.330000 13.307275 l
|
||||
14.330000 13.810762 14.241129 14.269478 13.938952 14.650421 c
|
||||
13.653583 15.010178 13.247056 15.207094 12.860974 15.362955 c
|
||||
12.858298 15.364022 l
|
||||
11.707717 15.822705 9.179629 16.769213 8.027948 17.170942 c
|
||||
8.018206 17.174341 8.008386 17.177513 7.998495 17.180454 c
|
||||
7.995981 17.181202 l
|
||||
7.783853 17.244291 7.495684 17.329998 7.169050 17.329998 c
|
||||
h
|
||||
7.165000 12.829998 m
|
||||
7.532269 12.829998 7.830000 12.532268 7.830000 12.164998 c
|
||||
7.830000 7.664998 l
|
||||
7.830000 7.297729 7.532269 6.999998 7.165000 6.999998 c
|
||||
6.797730 6.999998 6.500000 7.297729 6.500000 7.664998 c
|
||||
6.500000 12.164998 l
|
||||
6.500000 12.532268 6.797730 12.829998 7.165000 12.829998 c
|
||||
h
|
||||
8.165000 5.164998 m
|
||||
8.165000 4.612713 7.717285 4.164998 7.165000 4.164998 c
|
||||
6.612715 4.164998 6.165000 4.612713 6.165000 5.164998 c
|
||||
6.165000 5.717283 6.612715 6.164998 7.165000 6.164998 c
|
||||
7.717285 6.164998 8.165000 5.717283 8.165000 5.164998 c
|
||||
h
|
||||
f*
|
||||
n
|
||||
Q
|
||||
|
||||
endstream
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
3222
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<< /Annots []
|
||||
/Type /Page
|
||||
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
|
||||
/Resources 1 0 R
|
||||
/Contents 2 0 R
|
||||
/Parent 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj
|
||||
<< /Kids [ 4 0 R ]
|
||||
/Count 1
|
||||
/Type /Pages
|
||||
>>
|
||||
endobj
|
||||
|
||||
6 0 obj
|
||||
<< /Pages 5 0 R
|
||||
/Type /Catalog
|
||||
>>
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 7
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000034 00000 n
|
||||
0000003312 00000 n
|
||||
0000003335 00000 n
|
||||
0000003508 00000 n
|
||||
0000003582 00000 n
|
||||
trailer
|
||||
<< /ID [ (some) (id) ]
|
||||
/Root 6 0 R
|
||||
/Size 7
|
||||
>>
|
||||
startxref
|
||||
3641
|
||||
%%EOF
|
@ -457,7 +457,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
|
||||
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
if let peer = peer {
|
||||
self?.item?.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
self?.item?.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -409,7 +409,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
peer = botPeer
|
||||
}
|
||||
if let peer = peer, let botPeer = botPeer, let addressName = botPeer.addressName {
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil, false)
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil, .default)
|
||||
}
|
||||
}
|
||||
case .payment:
|
||||
@ -426,7 +426,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
self.controllerInteraction.openPeer(peer, .info, nil, false)
|
||||
self.controllerInteraction.openPeer(peer, .info, nil, .default)
|
||||
})
|
||||
case let .openWebView(url, simple):
|
||||
self.controllerInteraction.openWebView(markupButton.title, url, simple, false)
|
||||
|
@ -926,7 +926,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}, openPeer: { [weak self] peer in
|
||||
if let strongSelf = self {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
}
|
||||
}, openHashtag: { [weak self] peerName, hashtag in
|
||||
if let strongSelf = self {
|
||||
@ -995,8 +995,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
})))
|
||||
}, openPeer: { [weak self] peer, navigation, fromMessage, isReaction in
|
||||
self?.openPeer(peer: peer, navigation: navigation, fromMessage: fromMessage, fromReactionMessageId: isReaction ? fromMessage?.id : nil)
|
||||
}, openPeer: { [weak self] peer, navigation, fromMessage, source in
|
||||
self?.openPeer(peer: peer, navigation: navigation, fromMessage: fromMessage, fromReactionMessageId: source == .reaction ? fromMessage?.id : nil, expandAvatar: source == .groupParticipant)
|
||||
}, openPeerMention: { [weak self] name in
|
||||
self?.openPeerMention(name)
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer, location in
|
||||
@ -13224,7 +13224,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
}
|
||||
})
|
||||
case .longTap:
|
||||
@ -13317,7 +13317,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
}
|
||||
})
|
||||
case .longTap:
|
||||
@ -13431,7 +13431,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let strongSelf = self, let peer = peer {
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, false)
|
||||
strongSelf.controllerInteraction?.openPeer(peer, .default, nil, .default)
|
||||
}
|
||||
})
|
||||
case .longTap:
|
||||
|
@ -60,8 +60,14 @@ struct UnreadMessageRangeKey: Hashable {
|
||||
}
|
||||
|
||||
public final class ChatControllerInteraction {
|
||||
enum OpenPeerSource {
|
||||
case `default`
|
||||
case reaction
|
||||
case groupParticipant
|
||||
}
|
||||
|
||||
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
|
||||
let openPeer: (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool) -> Void
|
||||
let openPeer: (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void
|
||||
let openPeerMention: (String) -> Void
|
||||
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void
|
||||
let updateMessageReaction: (Message, ChatControllerInteractionReaction) -> Void
|
||||
@ -170,7 +176,7 @@ public final class ChatControllerInteraction {
|
||||
|
||||
init(
|
||||
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,
|
||||
openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool) -> Void,
|
||||
openPeer: @escaping (EnginePeer, ChatControllerInteractionNavigateToPeer, MessageReference?, OpenPeerSource) -> Void,
|
||||
openPeerMention: @escaping (String) -> Void,
|
||||
openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void,
|
||||
openMessageReactionContextMenu: @escaping (Message, ContextExtractedContentContainingView, ContextGesture?, MessageReaction.Reaction) -> Void,
|
||||
|
@ -1689,7 +1689,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
actions.insert(.custom(ChatReadReportContextItem(context: context, message: message, hasReadReports: hasReadReports, stats: readStats, action: { c, f, stats, customReactionEmojiPacks, firstCustomEmojiReaction in
|
||||
if reactionCount == 0, let stats = stats, stats.peers.count == 1 {
|
||||
c.dismiss(completion: {
|
||||
controllerInteraction.openPeer(stats.peers[0], .default, nil, false)
|
||||
controllerInteraction.openPeer(stats.peers[0], .default, nil, .default)
|
||||
})
|
||||
} else if (stats != nil && !stats!.peers.isEmpty) || reactionCount != 0 {
|
||||
var tip: ContextController.Tip?
|
||||
@ -1733,7 +1733,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
||||
},
|
||||
openPeer: { [weak c] peer in
|
||||
c?.dismiss(completion: {
|
||||
controllerInteraction.openPeer(peer, .default, MessageReference(message), true)
|
||||
controllerInteraction.openPeer(peer, .default, MessageReference(message), .reaction)
|
||||
})
|
||||
}
|
||||
)), tip: tip)))
|
||||
|
@ -2112,7 +2112,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||
}
|
||||
|
@ -3567,7 +3567,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
} else {
|
||||
return .optionalAction({
|
||||
if let peer = item.message.peers[peerId] {
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -3610,7 +3610,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||
}
|
||||
@ -3653,7 +3653,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let self = self, let item = self.item, let peer = peer {
|
||||
item.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
item.controllerInteraction.openPeer(peer, .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -648,9 +648,9 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
|
||||
self.controllerInteraction.activateAdAction(adMessageId)
|
||||
} else {
|
||||
if let channel = peer as? TelegramChannel, case .broadcast = channel.info {
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, false)
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, .default)
|
||||
} else {
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .info, self.messageReference, false)
|
||||
self.controllerInteraction.openPeer(EnginePeer(peer), .info, self.messageReference, .groupParticipant)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -927,7 +927,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
}
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||
}
|
||||
|
@ -1247,7 +1247,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
|
||||
return
|
||||
} else if let peer = forwardInfo.source ?? forwardInfo.author {
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false)
|
||||
item.controllerInteraction.openPeer(EnginePeer(peer), peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, .default)
|
||||
return
|
||||
} else if let _ = forwardInfo.authorSignature {
|
||||
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
|
||||
|
@ -855,7 +855,7 @@ public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol
|
||||
let _ = (item.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
item.controllerInteraction.openPeer(peer, .info, nil, false)
|
||||
item.controllerInteraction.openPeer(peer, .info, nil, .default)
|
||||
}
|
||||
})
|
||||
case let .openWebView(url, simple):
|
||||
|
@ -854,7 +854,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
let _ = (self.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
if let peer = peer {
|
||||
controllerInteraction.openPeer(peer, .info, nil, false)
|
||||
controllerInteraction.openPeer(peer, .info, nil, .default)
|
||||
}
|
||||
})
|
||||
case let .openWebView(url, simple):
|
||||
|
@ -24,6 +24,8 @@ import UndoUI
|
||||
import TelegramCallsUI
|
||||
import WallpaperBackgroundNode
|
||||
import BotPaymentsUI
|
||||
import ContextUI
|
||||
import Pasteboard
|
||||
|
||||
private final class ChatRecentActionsListOpaqueState {
|
||||
let entries: [ChatRecentActionsEntry]
|
||||
@ -56,13 +58,14 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
private var automaticMediaDownloadSettings: MediaAutoDownloadSettings
|
||||
|
||||
private var containerLayout: (ContainerViewLayout, CGFloat)?
|
||||
private var visibleAreaInset = UIEdgeInsets()
|
||||
|
||||
private let backgroundNode: WallpaperBackgroundNode
|
||||
private let panelBackgroundNode: NavigationBackgroundNode
|
||||
private let panelSeparatorNode: ASDisplayNode
|
||||
private let panelButtonNode: HighlightableButtonNode
|
||||
|
||||
private let listNode: ListView
|
||||
fileprivate let listNode: ListView
|
||||
private let loadingNode: ChatLoadingNode
|
||||
private let emptyNode: ChatRecentActionsEmptyNode
|
||||
|
||||
@ -86,6 +89,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
private var adminsDisposable: Disposable?
|
||||
private var adminsState: ChannelMemberListState?
|
||||
private let banDisposables = DisposableDict<PeerId>()
|
||||
private let reportFalsePositiveDisposables = DisposableDict<MessageId>()
|
||||
|
||||
private weak var antiSpamTooltipController: UndoOverlayController?
|
||||
|
||||
@ -258,8 +262,10 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
}, openPeerMention: { [weak self] name in
|
||||
self?.openPeerMention(name)
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, _, location in
|
||||
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame, location: location)
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer, location in
|
||||
let recognizer: TapLongTapOrDoubleTapGestureRecognizer? = anyRecognizer as? TapLongTapOrDoubleTapGestureRecognizer
|
||||
let gesture: ContextGesture? = anyRecognizer as? ContextGesture
|
||||
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame, recognizer: recognizer, gesture: gesture, location: location)
|
||||
}, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _ in
|
||||
}, activateMessagePinch: { _ in
|
||||
@ -628,6 +634,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
self.resolvePeerByNameDisposable.dispose()
|
||||
self.adminsDisposable?.dispose()
|
||||
self.banDisposables.dispose()
|
||||
self.reportFalsePositiveDisposables.dispose()
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
@ -664,6 +671,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
transition.updateFrame(node: self.panelSeparatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - panelHeight), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
|
||||
transition.updateFrame(node: self.panelButtonNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - panelHeight), size: CGSize(width: layout.size.width, height: intrinsicPanelHeight)))
|
||||
|
||||
self.visibleAreaInset = UIEdgeInsets(top: 0.0, left: 0.0, bottom: panelHeight, right: 0.0)
|
||||
|
||||
transition.updateBounds(node: self.listNode, bounds: CGRect(origin: CGPoint(), size: layout.size))
|
||||
transition.updatePosition(node: self.listNode, position: CGRect(origin: CGPoint(), size: layout.size).center)
|
||||
|
||||
@ -819,15 +828,51 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}))
|
||||
}
|
||||
|
||||
private func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect, location: CGPoint?) {
|
||||
var actions: [ContextMenuAction] = []
|
||||
private func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect, recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, location: CGPoint? = nil) {
|
||||
guard let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
self.dismissAllTooltips()
|
||||
|
||||
let context = self.context
|
||||
let source: ContextContentSource
|
||||
if let location = location {
|
||||
source = .location(ChatMessageContextLocationContentSource(controller: controller, location: node.view.convert(node.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y)))
|
||||
} else {
|
||||
source = .extracted(ChatRecentActionsMessageContextExtractedContentSource(controllerNode: self, message: message, selectAll: selectAll))
|
||||
}
|
||||
|
||||
var actions: [ContextMenuItem] = []
|
||||
if !message.text.isEmpty {
|
||||
actions.append(ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: {
|
||||
UIPasteboard.general.string = message.text
|
||||
|
||||
let content: UndoOverlayContent = .copy(text: self.presentationData.strings.Conversation_TextCopied)
|
||||
self.presentController(UndoOverlayController(presentationData: self.presentationData, content: content, elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), .current, nil)
|
||||
}))
|
||||
actions.append(
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuCopy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
if let strongSelf = self {
|
||||
var messageEntities: [MessageTextEntity]?
|
||||
var restrictedText: String?
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? TextEntitiesMessageAttribute {
|
||||
messageEntities = attribute.entities
|
||||
}
|
||||
if let attribute = attribute as? RestrictedContentMessageAttribute {
|
||||
restrictedText = attribute.platformText(platform: "ios", contentSettings: context.currentContentSettings.with { $0 }) ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
if let restrictedText = restrictedText {
|
||||
storeMessageTextInPasteboard(restrictedText, entities: nil)
|
||||
} else {
|
||||
storeMessageTextInPasteboard(message.text, entities: messageEntities)
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(0.2, {
|
||||
let content: UndoOverlayContent = .copy(text: strongSelf.presentationData.strings.Conversation_TextCopied)
|
||||
strongSelf.presentController(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), .current, nil)
|
||||
})
|
||||
}
|
||||
}))
|
||||
)
|
||||
}
|
||||
|
||||
if let author = message.author, let adminsState = self.adminsState {
|
||||
@ -847,42 +892,54 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
|
||||
if canBan {
|
||||
actions.append(ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuBan, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuBan), action: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.banDisposables.set((strongSelf.context.engine.peers.fetchChannelParticipant(peerId: strongSelf.peer.id, participantId: author.id)
|
||||
|> deliverOnMainQueue).start(next: { participant in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentController(channelBannedMemberController(context: strongSelf.context, peerId: strongSelf.peer.id, memberId: author.id, initialParticipant: participant, updated: { _ in }, upgradedToSupergroup: { _, f in f() }), .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}), forKey: author.id)
|
||||
}
|
||||
}))
|
||||
actions.append(
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuBan, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Restrict"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
if let strongSelf = self {
|
||||
f(.default)
|
||||
strongSelf.banDisposables.set((strongSelf.context.engine.peers.fetchChannelParticipant(peerId: strongSelf.peer.id, participantId: author.id)
|
||||
|> deliverOnMainQueue).start(next: { participant in
|
||||
if let strongSelf = self {
|
||||
strongSelf.presentController(channelBannedMemberController(context: strongSelf.context, peerId: strongSelf.peer.id, memberId: author.id, initialParticipant: participant, updated: { _ in }, upgradedToSupergroup: { _, f in f() }), .window(.root), ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}
|
||||
}), forKey: author.id)
|
||||
}
|
||||
}))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if !actions.isEmpty {
|
||||
let contextMenuController = ContextMenuController(actions: actions)
|
||||
|
||||
self.controllerInteraction.highlightedState = ChatInterfaceHighlightedState(messageStableId: message.stableId)
|
||||
self.updateItemNodesHighlightedStates(animated: true)
|
||||
|
||||
contextMenuController.dismissed = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if strongSelf.controllerInteraction.highlightedState?.messageStableId == message.stableId {
|
||||
strongSelf.controllerInteraction.highlightedState = nil
|
||||
strongSelf.updateItemNodesHighlightedStates(animated: true)
|
||||
}
|
||||
let configuration = AntiSpamBotConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 })
|
||||
for peer in message.peers {
|
||||
if peer.0 == configuration.antiSpamBotId {
|
||||
if !actions.isEmpty {
|
||||
actions.insert(.separator, at: 0)
|
||||
}
|
||||
actions.insert(
|
||||
.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuReportFalsePositive, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/AntiSpam"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in
|
||||
f(.default)
|
||||
|
||||
if let strongSelf = self {
|
||||
strongSelf.reportFalsePositiveDisposables.set((strongSelf.context.engine.peers.reportAntiSpamFalsePositive(peerId: message.id.peerId, messageId: message.id)
|
||||
|> deliverOnMainQueue).start(), forKey: message.id)
|
||||
|
||||
Queue.mainQueue().after(0.2, {
|
||||
let content: UndoOverlayContent = .image(image: UIImage(bundleImageName: "Chat/AntiSpamTooltipIcon")!, title: nil, text: strongSelf.presentationData.strings.Group_AdminLog_AntiSpamFalsePositiveReportedText, undo: false)
|
||||
strongSelf.presentController(UndoOverlayController(presentationData: strongSelf.presentationData, content: content, elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), .current, nil)
|
||||
})
|
||||
}
|
||||
})), at: 0
|
||||
)
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
self.presentController(contextMenuController, .window(.root), ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self, weak node] in
|
||||
if let strongSelf = self, let node = node {
|
||||
return (node, frame, strongSelf, strongSelf.bounds)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
guard !actions.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
let contextController = ContextController(account: self.context.account, presentationData: self.presentationData, source: source, items: .single(ContextController.Items(content: .list(actions))), recognizer: recognizer, gesture: gesture)
|
||||
controller.window?.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
private func updateItemNodesHighlightedStates(animated: Bool) {
|
||||
@ -1043,4 +1100,70 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
func frameForVisibleArea() -> CGRect {
|
||||
let rect = CGRect(origin: CGPoint(x: self.visibleAreaInset.left, y: self.visibleAreaInset.top), size: CGSize(width: self.bounds.size.width - self.visibleAreaInset.left - self.visibleAreaInset.right, height: self.bounds.size.height - self.visibleAreaInset.top - self.visibleAreaInset.bottom))
|
||||
|
||||
return rect
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatRecentActionsMessageContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = false
|
||||
let ignoreContentTouches: Bool = false
|
||||
let blurBackground: Bool = true
|
||||
|
||||
private weak var controllerNode: ChatRecentActionsControllerNode?
|
||||
private let message: Message
|
||||
private let selectAll: Bool
|
||||
|
||||
var shouldBeDismissed: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
|
||||
init(controllerNode: ChatRecentActionsControllerNode, message: Message, selectAll: Bool) {
|
||||
self.controllerNode = controllerNode
|
||||
self.message = message
|
||||
self.selectAll = selectAll
|
||||
}
|
||||
|
||||
func takeView() -> ContextControllerTakeViewInfo? {
|
||||
guard let controllerNode = self.controllerNode else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result: ContextControllerTakeViewInfo?
|
||||
controllerNode.listNode.forEachItemNode { itemNode in
|
||||
guard let itemNode = itemNode as? ChatMessageItemView else {
|
||||
return
|
||||
}
|
||||
guard let item = itemNode.item else {
|
||||
return
|
||||
}
|
||||
if item.content.contains(where: { $0.0.stableId == self.message.stableId }), let contentNode = itemNode.getMessageContextSourceNode(stableId: self.selectAll ? nil : self.message.stableId) {
|
||||
result = ContextControllerTakeViewInfo(containingItem: .node(contentNode), contentAreaInScreenSpace: controllerNode.convert(controllerNode.frameForVisibleArea(), to: nil))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func putBack() -> ContextControllerPutBackViewInfo? {
|
||||
guard let controllerNode = self.controllerNode else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result: ContextControllerPutBackViewInfo?
|
||||
controllerNode.listNode.forEachItemNode { itemNode in
|
||||
guard let itemNode = itemNode as? ChatMessageItemView else {
|
||||
return
|
||||
}
|
||||
guard let item = itemNode.item else {
|
||||
return
|
||||
}
|
||||
if item.content.contains(where: { $0.0.stableId == self.message.stableId }) {
|
||||
result = ContextControllerPutBackViewInfo(contentAreaInScreenSpace: controllerNode.convert(controllerNode.frameForVisibleArea(), to: nil))
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
}, to: &text, entities: &entities)
|
||||
}
|
||||
let action = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
let peers = SimpleDictionary<PeerId, Peer>()
|
||||
@ -182,7 +182,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
}, to: &text, entities: &entities)
|
||||
}
|
||||
let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
var previousAttributes: [MessageAttribute] = []
|
||||
@ -232,7 +232,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
}, to: &text, entities: &entities)
|
||||
}
|
||||
let action: TelegramMediaActionType = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
var previousAttributes: [MessageAttribute] = []
|
||||
@ -374,7 +374,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
}, to: &text, entities: &entities)
|
||||
|
||||
let action = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
if let message = message {
|
||||
@ -459,7 +459,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
|
||||
let action = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
@ -502,7 +502,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
|
||||
let action = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
@ -526,6 +526,9 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
}
|
||||
if let peer = self.entry.peers[self.entry.event.peerId] {
|
||||
peers[peer.id] = peer
|
||||
}
|
||||
let message = Message(stableId: self.entry.stableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: Int32(bitPattern: self.entry.stableId)), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: message.effectiveAuthor, text: message.text, attributes: attributes, media: message.media, peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
}
|
||||
@ -1072,7 +1075,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
|
||||
let action = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
@ -1634,7 +1637,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
|
||||
}, to: &text, entities: &entities)
|
||||
|
||||
let action = TelegramMediaActionType.customText(text: text, entities: entities)
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
let message = Message(stableId: self.entry.headerStableId, stableVersion: 0, id: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: 1), globallyUniqueId: self.entry.event.id, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: self.entry.event.date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: author, text: "", attributes: [], media: [TelegramMediaAction(action: action)], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil)
|
||||
return ChatMessageItem(presentationData: self.presentationData, context: context, chatLocation: .peer(id: peer.id), associatedData: ChatMessageItemAssociatedData(automaticDownloadPeerType: .channel, automaticDownloadNetworkType: .cellular, isRecentActions: true, availableReactions: nil, defaultReaction: nil, isPremium: false, accountPeer: nil), controllerInteraction: controllerInteraction, content: .message(message: message, read: true, selection: .none, attributes: ChatMessageEntryAttributes(), location: nil))
|
||||
case .content:
|
||||
var peers = SimpleDictionary<PeerId, Peer>()
|
||||
|
@ -186,7 +186,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
}
|
||||
}
|
||||
let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, openPeer: { [weak self] peer in
|
||||
self?.chatControllerInteraction.openPeer(EnginePeer(peer), .default, nil, false)
|
||||
self?.chatControllerInteraction.openPeer(EnginePeer(peer), .default, nil, .default)
|
||||
}, openPeerContextAction: { [weak self] peer, node, gesture in
|
||||
self?.openPeerContextAction(peer, node, gesture)
|
||||
})
|
||||
|
@ -2666,7 +2666,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
let items: [ContextMenuItem] = [
|
||||
.action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in
|
||||
f(.dismissWithoutContent)
|
||||
self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, false)
|
||||
self?.chatInterfaceInteraction.openPeer(EnginePeer(peer), .default, nil, .default)
|
||||
}))
|
||||
]
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture)
|
||||
|
Loading…
x
Reference in New Issue
Block a user