Various improvements

This commit is contained in:
Ilya Laktyushin 2026-02-25 02:08:33 +04:00
parent 32660b3806
commit 9fe004a7ec
12 changed files with 123 additions and 25 deletions

View file

@ -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

View file

@ -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$@.";

View file

@ -41,6 +41,7 @@ typedef enum
- (bool)trimApplied;
- (bool)isCropAndRotationEqualWith:(id<TGMediaEditAdjustments>)adjustments;
- (bool)isDefaultValuesForGif;
- (NSDictionary *)dictionary;

View file

@ -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<TGMediaEditAdjustments>)adjusments
{
return (_CGRectEqualToRectWithEpsilon(self.cropRect, adjusments.cropRect, [self _cropRectEpsilon]));

View file

@ -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 {

View file

@ -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))
}
}
}

View file

@ -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 {

View file

@ -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))

View file

@ -2811,13 +2811,19 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
let _ = (accountIdFromNotification(response.notification, sharedContext: self.sharedContextPromise.get())
|> 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 {

View file

@ -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)

View file

@ -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

View file

@ -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)
}
}
})
}