From 9fe004a7ecad9a87e78c9cded03422382779f94b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 25 Feb 2026 02:08:33 +0400 Subject: [PATCH] Various improvements --- .../Sources/NotificationService.swift | 4 +++ .../Telegram-iOS/en.lproj/Localizable.strings | 1 + .../LegacyComponents/TGVideoEditAdjustments.h | 1 + .../Sources/TGVideoEditAdjustments.m | 5 ++++ .../Sources/LegacyAttachmentMenu.swift | 14 +++++++++- .../Sources/LegacyMediaPickers.swift | 16 ++++++++--- .../State/AccountStateManagementUtils.swift | 2 ++ .../Sources/AuthConfirmationScreen.swift | 24 ++++++++++++++-- .../TelegramUI/Sources/AppDelegate.swift | 18 ++++++++---- .../Sources/Chat/ChatControllerEditGif.swift | 12 +++++++- .../TelegramUI/Sources/ChatController.swift | 28 ++++++++++++++++--- .../TelegramUI/Sources/OpenResolvedUrl.swift | 23 +++++++++++---- 12 files changed, 123 insertions(+), 25 deletions(-) diff --git a/Telegram/NotificationService/Sources/NotificationService.swift b/Telegram/NotificationService/Sources/NotificationService.swift index b154af734f..e5be042e7f 100644 --- a/Telegram/NotificationService/Sources/NotificationService.swift +++ b/Telegram/NotificationService/Sources/NotificationService.swift @@ -1223,6 +1223,10 @@ private final class NotificationServiceHandler { content.userInfo["peerId"] = "\(peerId.toInt64())" content.userInfo["accountId"] = "\(recordId.int64)" + if let dataUrl = payloadJson["data_url"] as? String { + content.userInfo["data_url"] = dataUrl + } + if let silentString = payloadJson["silent"] as? String { if let silentValue = Int(silentString), silentValue != 0 { content.silent = true diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 8f9b5cb365..f246c25197 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -15752,6 +15752,7 @@ Error: %8$@"; "AuthConfirmation.LoginFail.Title" = "Login Failed"; "AuthConfirmation.LoginFail.Text" = "Please try logging in to [%@]() again."; +"AuthConfirmation.LoginFail.TextUnknown" = "Please try logging in again."; "Notification.GroupCreatorChangePending" = "%2$@ will become the new main admin in 7 days if %1$@ does not return."; "Notification.GroupCreatorChangeApplied" = "%1$@ has transferred ownership of the group to %2$@."; diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h index c5925658a9..8c219596f8 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoEditAdjustments.h @@ -41,6 +41,7 @@ typedef enum - (bool)trimApplied; - (bool)isCropAndRotationEqualWith:(id)adjustments; +- (bool)isDefaultValuesForGif; - (NSDictionary *)dictionary; diff --git a/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m b/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m index 88cfff3248..1a41c032c4 100644 --- a/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m +++ b/submodules/LegacyComponents/Sources/TGVideoEditAdjustments.m @@ -429,6 +429,11 @@ const NSTimeInterval TGVideoEditMaximumGifDuration = 30.5; return ![self cropAppliedForAvatar:forAvatar] && ![self toolsApplied] && ![self hasPainting] && !_sendAsGif && _preset == TGMediaVideoConversionPresetCompressedDefault; } +- (bool)isDefaultValuesForGif +{ + return ![self cropAppliedForAvatar:false] && ![self toolsApplied] && ![self hasPainting]; +} + - (bool)isCropEqualWith:(id)adjusments { return (_CGRectEqualToRectWithEpsilon(self.cropRect, adjusments.cropRect, [self _cropRectEpsilon])); diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift index b062edc22a..acc28dacf4 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyAttachmentMenu.swift @@ -157,7 +157,19 @@ public func legacyStoryMediaEditor(context: AccountContext, item: TGMediaEditabl }) } -public func legacyMediaEditor(context: AccountContext, peer: Peer, threadTitle: String?, media: AnyMediaReference, mode: LegacyMediaEditorMode, initialCaption: NSAttributedString, snapshots: [UIView], transitionCompletion: (() -> Void)?, getCaptionPanelView: @escaping () -> TGCaptionPanelView?, sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, Bool) -> Void, present: @escaping (ViewController, Any?) -> Void) { +public func legacyMediaEditor( + context: AccountContext, + peer: Peer, + threadTitle: String?, + media: AnyMediaReference, + mode: LegacyMediaEditorMode, + initialCaption: NSAttributedString, + snapshots: [UIView], + transitionCompletion: (() -> Void)?, + getCaptionPanelView: @escaping () -> TGCaptionPanelView?, + sendMessagesWithSignals: @escaping ([Any]?, Bool, Int32, Bool) -> Void, + present: @escaping (ViewController, Any?) -> Void +) { let _ = (fetchMediaData(context: context, postbox: context.account.postbox, userLocation: .other, mediaReference: media) |> deliverOnMainQueue).start(next: { (value, isImage) in guard case let .data(data) = value, data.complete else { diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index 5b5b7d6be8..bb8addb1fa 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -374,7 +374,7 @@ public struct LegacyAssetPickerEnqueueMessage { public var isFile: Bool } -public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: Account, signals: [Any]) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> { +public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: Account, signals: [Any], originalMediaReference: AnyMediaReference? = nil) -> Signal<[LegacyAssetPickerEnqueueMessage], Void> { return Signal { subscriber in let disposable = SSignal.combineSignals(signals).start(next: { anyValues in var messages: [LegacyAssetPickerEnqueueMessage] = [] @@ -934,8 +934,16 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A fileAttributes.append(.HasLinkedStickers) } - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], videoCover: videoCover, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: []) - + let media: Media + let mediaReference: AnyMediaReference + if let adjustments, adjustments.isDefaultValuesForGif(), let originalMediaReference { + media = originalMediaReference.media + mediaReference = originalMediaReference + } else { + media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], videoCover: videoCover, immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes, alternativeRepresentations: []) + mediaReference = .standalone(media: media) + } + if let timer = item.timer, timer > 0 && (timer <= 60 || timer == viewOnceTimeout) { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil)) } @@ -987,7 +995,7 @@ public func legacyAssetPickerEnqueueMessages(context: AccountContext, account: A ) } } else { - messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: .standalone(media: media), threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: asFile)) + messages.append(LegacyAssetPickerEnqueueMessage(message: .message(text: text.string, attributes: attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: nil, replyToMessageId: nil, replyToStoryId: nil, localGroupingKey: item.groupedId, correlationId: nil, bubbleUpEmojiOrStickersets: bubbleUpEmojiOrStickersets), uniqueId: item.uniqueId, isFile: asFile)) } } } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index cb67c334fc..bb12e5a493 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -4329,6 +4329,8 @@ func replayFinalState( if !message.flags.contains(.Incoming), message.forwardInfo == nil { if [Namespaces.Peer.CloudGroup, Namespaces.Peer.CloudChannel].contains(message.id.peerId.namespace), let peer = transaction.getPeer(message.id.peerId), peer.isCopyProtectionEnabled { + } else if message.id.peerId.namespace == Namespaces.Peer.CloudUser, let cachedUserData = transaction.getPeerCachedData(peerId: message.id.peerId) as? CachedUserData, cachedUserData.flags.contains(.copyProtectionEnabled) || cachedUserData.flags.contains(.myCopyProtectionEnabled) { + } else { inner: for media in message.media { if let file = media as? TelegramMediaFile { diff --git a/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift b/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift index 6e10b9d8f9..4efb10536e 100644 --- a/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift +++ b/submodules/TelegramUI/Components/AuthConfirmationScreen/Sources/AuthConfirmationScreen.swift @@ -64,6 +64,8 @@ private final class AuthConfirmationSheetContent: CombinedComponent { private let subject: MessageActionUrlAuthResult private let completion: (AccountContext, EnginePeer, AuthConfirmationScreen.Result) -> Void + private let disposables = DisposableSet() + var peer: EnginePeer? var forcedAccount: (AccountContext, EnginePeer)? @@ -92,9 +94,23 @@ private final class AuthConfirmationSheetContent: CombinedComponent { self.updated() }) - if case let .request(_, _, _, flags, matchCodes, _) = self.subject, let matchCodes, flags.contains(.showMatchCodesFirst) { - self.displayEmoji = true - self.matchCodes = matchCodes.shuffled() + if case let .request(_, _, _, flags, matchCodes, _) = self.subject, let matchCodes { + if flags.contains(.showMatchCodesFirst) { + self.displayEmoji = true + self.matchCodes = matchCodes.shuffled() + } else { + for code in matchCodes { + var file: TelegramMediaFile? + if let item = context.animatedEmojiStickersValue[code] { + file = item.first?.file._parse() + } else if let item = context.animatedEmojiStickersValue[code.strippedEmoji] { + file = item.first?.file._parse() + } + if let file { + self.disposables.add(freeMediaFileResourceInteractiveFetched(account: context.account, userLocation: .other, fileReference: .standalone(media: file), resource: file.resource).start()) + } + } + } } if case let .request(_, _, _, _, _, userIdHint) = self.subject, let userIdHint, userIdHint != context.account.peerId { @@ -119,6 +135,8 @@ private final class AuthConfirmationSheetContent: CombinedComponent { } deinit { + self.disposables.dispose() + if !self.inProgress { if let (context, _) = self.forcedAccount { context.account.shouldBeServiceTaskMaster.set(.single(.never)) diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index 068f80f757..6de065f2bc 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -2811,13 +2811,19 @@ private func extractAccountManagerState(records: AccountRecordsView deliverOnMainQueue).start(next: { accountId in if response.actionIdentifier == UNNotificationDefaultActionIdentifier { - if let (peerId, threadId) = peerIdFromNotification(response.notification) { - var messageId: MessageId? = nil - if response.notification.request.content.categoryIdentifier == "c" || response.notification.request.content.categoryIdentifier == "t" { - messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) + if let dataUrl = response.notification.request.content.userInfo["data_url"] as? String { + if let url = URL(string: dataUrl) { + self.openUrlWhenReady(url: url) + } + } else { + if let (peerId, threadId) = peerIdFromNotification(response.notification) { + var messageId: MessageId? = nil + if response.notification.request.content.categoryIdentifier == "c" || response.notification.request.content.categoryIdentifier == "t" { + messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) + } + let storyId = storyIdFromNotification(peerId: peerId, notification: response.notification) + self.openChatWhenReady(accountId: accountId, peerId: peerId, threadId: threadId, messageId: messageId, storyId: storyId) } - let storyId = storyIdFromNotification(peerId: peerId, notification: response.notification) - self.openChatWhenReady(accountId: accountId, peerId: peerId, threadId: threadId, messageId: messageId, storyId: storyId) } completionHandler() } else if response.actionIdentifier == "reply", let (peerId, threadId) = peerIdFromNotification(response.notification), let accountId = accountId { diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift index d179ee7688..dba3277cd6 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerEditGif.swift @@ -31,7 +31,17 @@ extension ChatControllerImpl { guard let self else { return } - self.enqueueMediaMessages(fromGallery: false, signals: signals, silentPosting: false, scheduleTime: nil, replyToSubject: nil, parameters: nil, getAnimatedTransitionSource: nil, completion: {}) + self.enqueueMediaMessages( + fromGallery: false, + signals: signals, + originalMediaReference: file.abstract, + silentPosting: false, + scheduleTime: nil, + replyToSubject: nil, + parameters: nil, + getAnimatedTransitionSource: nil, + completion: {} + ) }, present: { [weak self] c, a in self?.present(c, in: .window(.root), with: a) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 37ef51110f..1b4f6d80d1 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -8452,18 +8452,38 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - func enqueueMediaMessages(fromGallery: Bool = false, signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil, parameters: ChatSendMessageActionSheetController.SendParameters? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) { + func enqueueMediaMessages( + fromGallery: Bool = false, + signals: [Any]?, + originalMediaReference: AnyMediaReference? = nil, + silentPosting: Bool, + scheduleTime: Int32? = nil, + replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil, + parameters: ChatSendMessageActionSheetController.SendParameters? = nil, + getAnimatedTransitionSource: ((String) -> UIView?)? = nil, + completion: @escaping () -> Void = {} + ) { if let _ = self.presentationInterfaceState.sendPaidMessageStars { self.presentPaidMessageAlertIfNeeded(count: Int32(signals?.count ?? 1), forceDark: fromGallery, completion: { [weak self] postpone in self?.commitEnqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, postpone: postpone, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion) }) } else { - self.commitEnqueueMediaMessages(signals: signals, silentPosting: silentPosting, scheduleTime: scheduleTime, replyToSubject: replyToSubject, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion) + self.commitEnqueueMediaMessages(signals: signals, originalMediaReference: originalMediaReference, silentPosting: silentPosting, scheduleTime: scheduleTime, replyToSubject: replyToSubject, parameters: parameters, getAnimatedTransitionSource: getAnimatedTransitionSource, completion: completion) } } - private func commitEnqueueMediaMessages(signals: [Any]?, silentPosting: Bool, scheduleTime: Int32? = nil, postpone: Bool = false, replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil, parameters: ChatSendMessageActionSheetController.SendParameters? = nil, getAnimatedTransitionSource: ((String) -> UIView?)? = nil, completion: @escaping () -> Void = {}) { - self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: self.context, account: self.context.account, signals: signals!) + private func commitEnqueueMediaMessages( + signals: [Any]?, + originalMediaReference: AnyMediaReference? = nil, + silentPosting: Bool, + scheduleTime: Int32? = nil, + postpone: Bool = false, + replyToSubject: ChatInterfaceState.ReplyMessageSubject? = nil, + parameters: ChatSendMessageActionSheetController.SendParameters? = nil, + getAnimatedTransitionSource: ((String) -> UIView?)? = nil, + completion: @escaping () -> Void = {} + ) { + self.enqueueMediaMessageDisposable.set((legacyAssetPickerEnqueueMessages(context: self.context, account: self.context.account, signals: signals!, originalMediaReference: originalMediaReference) |> deliverOnMainQueue).startStrict(next: { [weak self] items in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index 9effcb12f9..7403a6a358 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -1844,8 +1844,10 @@ func openResolvedUrlImpl( } else { text = presentationData.strings.AuthConfirmation_LoginSuccess_Text(domain).string } - let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AuthConfirmation_LoginSuccess_Title, text: text, cancel: nil, destructive: false), action: { _ in return true }) - (navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root)) + if let topViewController = navigationController?.topViewController as? ViewController { + let controller = UndoOverlayController(presentationData: presentationData, content: .actionSucceeded(title: presentationData.strings.AuthConfirmation_LoginSuccess_Title, text: text, cancel: nil, destructive: false), elevatedLayout: topViewController is TabBarController, action: { _ in return true }) + topViewController.present(controller, in: .current) + } } } @@ -1887,8 +1889,10 @@ func openResolvedUrlImpl( dismissImpl?() if case let .request(domain, _, _, _, _, _) = result { - let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, timeout: nil, customUndoText: nil), action: { _ in return true }) - (navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root)) + if let topViewController = navigationController?.topViewController as? ViewController { + let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, timeout: nil, customUndoText: nil), elevatedLayout: topViewController is TabBarController, action: { _ in return true }) + topViewController.present(controller, in: .current) + } } HapticFeedback().error() @@ -1899,8 +1903,10 @@ func openResolvedUrlImpl( dismissImpl?() if case let .request(domain, _, _, _, _, _) = result { - let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, timeout: nil, customUndoText: nil), action: { _ in return true }) - (navigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root)) + if let topViewController = navigationController?.topViewController as? ViewController { + let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_Text(domain).string, timeout: nil, customUndoText: nil), elevatedLayout: topViewController is TabBarController, action: { _ in return true }) + topViewController.present(controller, in: .current) + } } HapticFeedback().error() @@ -1910,6 +1916,11 @@ func openResolvedUrlImpl( dismissImpl = { [weak controller] in controller?.dismissAnimated() } + } else { + if let topViewController = navigationController?.topViewController as? ViewController { + let controller = UndoOverlayController(presentationData: presentationData, content: .info(title: presentationData.strings.AuthConfirmation_LoginFail_Title, text: presentationData.strings.AuthConfirmation_LoginFail_TextUnknown, timeout: nil, customUndoText: nil), elevatedLayout: topViewController is TabBarController, action: { _ in return true }) + topViewController.present(controller, in: .current) + } } }) }