diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index b3f8fa2354..c10ee63d47 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -114,7 +114,7 @@ class DefaultIntentHandler: INExtension, INSendMessageIntentHandling, INSearchFo self.rootPath = rootPath - TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: arc4random64()) + TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) let logsPath = rootPath + "/siri-logs" let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) @@ -884,7 +884,7 @@ private final class WidgetIntentHandler { self.rootPath = rootPath - TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: arc4random64()) + TempBox.initializeShared(basePath: rootPath, processType: "siri", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) let logsPath = rootPath + "/siri-logs" let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index 2cf75867e3..943953cce4 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -92,7 +92,7 @@ private func getCommonTimeline(friends: [Friend]?, in context: TimelineProviderC let rootPath = rootPathForBasePath(appGroupUrl.path) - TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: arc4random64()) + TempBox.initializeShared(basePath: rootPath, processType: "widget", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) let logsPath = rootPath + "/widget-logs" let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) diff --git a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift index 889af3cae8..ed5d1758fb 100644 --- a/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift +++ b/submodules/AnimatedStickerNode/Sources/AnimatedStickerNode.swift @@ -56,6 +56,7 @@ public enum AnimatedStickerPlaybackPosition { case start case end case timestamp(Double) + case frameIndex(Int) } public enum AnimatedStickerPlaybackMode { @@ -94,6 +95,7 @@ public protocol AnimatedStickerFrameSource: class { func takeFrame(draw: Bool) -> AnimatedStickerFrame? func skipToEnd() + func skipToFrameIndex(_ index: Int) } private final class AnimatedStickerFrameSourceWrapper { @@ -271,6 +273,9 @@ public final class AnimatedStickerCachedFrameSource: AnimatedStickerFrameSource public func skipToEnd() { } + + public func skipToFrameIndex(_ index: Int) { + } } private func wrappedWrite(_ fd: Int32, _ data: UnsafeRawPointer, _ count: Int) -> Int { @@ -656,6 +661,10 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource func skipToEnd() { self.currentFrame = self.frameCount - 1 } + + func skipToFrameIndex(_ index: Int) { + self.currentFrame = index + } } public final class AnimatedStickerFrameQueue { @@ -748,6 +757,8 @@ public final class AnimatedStickerNode: ASDisplayNode { public var completed: (Bool) -> Void = { _ in } public var frameUpdated: (Int, Int) -> Void = { _, _ in } + public private(set) var currentFrameIndex: Int = 0 + private var playFromIndex: Int? private let timer = Atomic(value: nil) private let frameSource = Atomic?>(value: nil) @@ -842,7 +853,9 @@ public final class AnimatedStickerNode: ASDisplayNode { strongSelf.isSetUpForPlayback = false strongSelf.isPlaying = true } - strongSelf.play() + var fromIndex = strongSelf.playFromIndex + strongSelf.playFromIndex = nil + strongSelf.play(fromIndex: fromIndex) } else if strongSelf.canDisplayFirstFrame { strongSelf.play(firstFrame: true) } @@ -911,7 +924,7 @@ public final class AnimatedStickerNode: ASDisplayNode { private var isSetUpForPlayback = false - public func play(firstFrame: Bool = false) { + public func play(firstFrame: Bool = false, fromIndex: Int? = nil) { switch self.playbackMode { case .once: self.isPlaying = true @@ -949,6 +962,9 @@ public final class AnimatedStickerNode: ASDisplayNode { guard let frameSource = maybeFrameSource else { return } + if let fromIndex = fromIndex { + frameSource.skipToFrameIndex(fromIndex) + } let frameQueue = QueueLocalObject(queue: queue, generate: { return AnimatedStickerFrameQueue(queue: queue, length: 1, source: frameSource) }) @@ -978,6 +994,7 @@ public final class AnimatedStickerNode: ASDisplayNode { }) strongSelf.frameUpdated(frame.index, frame.totalFrames) + strongSelf.currentFrameIndex = frame.index if frame.isLastFrame { var stopped = false @@ -1016,6 +1033,9 @@ public final class AnimatedStickerNode: ASDisplayNode { self.isSetUpForPlayback = true let directData = self.directData let cachedData = self.cachedData + if directData == nil && cachedData == nil { + self.playFromIndex = fromIndex + } let queue = self.queue let timerHolder = self.timer let frameSourceHolder = self.frameSource @@ -1039,6 +1059,9 @@ public final class AnimatedStickerNode: ASDisplayNode { guard let frameSource = maybeFrameSource else { return } + if let fromIndex = fromIndex { + frameSource.skipToFrameIndex(fromIndex) + } let frameQueue = QueueLocalObject(queue: queue, generate: { return AnimatedStickerFrameQueue(queue: queue, length: 1, source: frameSource) }) @@ -1068,6 +1091,7 @@ public final class AnimatedStickerNode: ASDisplayNode { }) strongSelf.frameUpdated(frame.index, frame.totalFrames) + strongSelf.currentFrameIndex = frame.index if frame.isLastFrame { var stopped = false @@ -1163,6 +1187,21 @@ public final class AnimatedStickerNode: ASDisplayNode { return } + var delta = targetFrame - frameSource.frameIndex + if delta < 0 { + delta = frameSource.frameCount + delta + } + for i in 0 ..< delta { + maybeFrame = frameQueue.syncWith { frameQueue in + return frameQueue.take(draw: i == delta - 1) + } + } + } else if case let .frameIndex(frameIndex) = position { + let targetFrame = frameIndex + if targetFrame == frameSource.frameIndex { + return + } + var delta = targetFrame - frameSource.frameIndex if delta < 0 { delta = frameSource.frameCount + delta diff --git a/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm b/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm index 87baeb09ac..cf7fd8bd2d 100644 --- a/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm +++ b/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm @@ -1045,7 +1045,7 @@ CGFloat baselineNudge = (lineHeight - fontLineHeight) * 0.6f; CGRect rect = *lineFragmentRect; - rect.size.height = lineHeight; + rect.size.height = lineHeight + 2.0f; CGRect usedRect = *lineFragmentUsedRect; usedRect.size.height = MAX(lineHeight, usedRect.size.height); diff --git a/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift b/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift index 847acd5a14..5c8a7c620a 100644 --- a/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift +++ b/submodules/BotPaymentsUI/Sources/BotCheckoutTipItem.swift @@ -155,6 +155,316 @@ private final class TipValueNode: ASDisplayNode { } } +private final class FormatterImpl: NSObject, UITextFieldDelegate { + private struct Representation { + private let format: CurrencyFormat + private var caretIndex: Int = 0 + private var wholePart: [Int] = [] + private var decimalPart: [Int] = [] + + init(string: String, format: CurrencyFormat) { + self.format = format + + var isDecimalPart = false + for c in string { + if c.isNumber { + if let value = Int(String(c)) { + if isDecimalPart { + self.decimalPart.append(value) + } else { + self.wholePart.append(value) + } + } + } else if String(c) == format.decimalSeparator { + isDecimalPart = true + } + } + + while self.wholePart.count > 1 { + if self.wholePart[0] != 0 { + break + } else { + self.wholePart.removeFirst() + } + } + if self.wholePart.isEmpty { + self.wholePart = [0] + } + + while self.decimalPart.count > 1 { + if self.decimalPart[self.decimalPart.count - 1] != 0 { + break + } else { + self.decimalPart.removeLast() + } + } + while self.decimalPart.count < format.decimalDigits { + self.decimalPart.append(0) + } + + self.caretIndex = self.wholePart.count + } + + var minCaretIndex: Int { + for i in 0 ..< self.wholePart.count { + if self.wholePart[i] != 0 { + return i + } + } + return self.wholePart.count + } + + mutating func moveCaret(offset: Int) { + self.caretIndex = max(self.minCaretIndex, min(self.caretIndex + offset, self.wholePart.count + self.decimalPart.count)) + } + + mutating func normalize() { + while self.wholePart.count > 1 { + if self.wholePart[0] != 0 { + break + } else { + self.wholePart.removeFirst() + self.moveCaret(offset: -1) + } + } + if self.wholePart.isEmpty { + self.wholePart = [0] + } + + while self.decimalPart.count < format.decimalDigits { + self.decimalPart.append(0) + } + while self.decimalPart.count > format.decimalDigits { + self.decimalPart.removeLast() + } + + self.caretIndex = max(self.minCaretIndex, min(self.caretIndex, self.wholePart.count + self.decimalPart.count)) + } + + mutating func backspace() { + if self.caretIndex > self.wholePart.count { + let decimalIndex = self.caretIndex - self.wholePart.count + if decimalIndex > 0 { + self.decimalPart.remove(at: decimalIndex - 1) + + self.moveCaret(offset: -1) + self.normalize() + } + } else { + if self.caretIndex > 0 { + self.wholePart.remove(at: self.caretIndex - 1) + + self.moveCaret(offset: -1) + self.normalize() + } + } + } + + mutating func insert(letter: String) { + if letter == "." || letter == "," { + if self.caretIndex == self.wholePart.count { + return + } else if self.caretIndex < self.wholePart.count { + for i in (self.caretIndex ..< self.wholePart.count).reversed() { + self.decimalPart.insert(self.wholePart[i], at: 0) + self.wholePart.remove(at: i) + } + } + + self.normalize() + } else if letter.count == 1 && letter[letter.startIndex].isNumber { + if let value = Int(letter) { + if self.caretIndex <= self.wholePart.count { + self.wholePart.insert(value, at: self.caretIndex) + } else { + let decimalIndex = self.caretIndex - self.wholePart.count + self.decimalPart.insert(value, at: decimalIndex) + } + self.moveCaret(offset: 1) + self.normalize() + } + } + } + + var string: String { + var result = "" + + for digit in self.wholePart { + result.append("\(digit)") + } + result.append(self.format.decimalSeparator) + for digit in self.decimalPart { + result.append("\(digit)") + } + + return result + } + + var stringCaretIndex: Int { + var logicalIndex = 0 + var resolvedIndex = 0 + + if logicalIndex == self.caretIndex { + return resolvedIndex + } + + for _ in self.wholePart { + logicalIndex += 1 + resolvedIndex += 1 + + if logicalIndex == self.caretIndex { + return resolvedIndex + } + } + + resolvedIndex += 1 + + for _ in self.decimalPart { + logicalIndex += 1 + resolvedIndex += 1 + + if logicalIndex == self.caretIndex { + return resolvedIndex + } + } + + return resolvedIndex + } + + var numericalValue: Int64 { + var result: Int64 = 0 + + for digit in self.wholePart { + result *= 10 + result += Int64(digit) + } + for digit in self.decimalPart { + result *= 10 + result += Int64(digit) + } + + return result + } + } + + private let format: CurrencyFormat + private let currency: String + private let maxNumericalValue: Int64 + private let updated: (Int64) -> Void + private let focusUpdated: (Bool) -> Void + + private var representation: Representation + + private var previousResolvedCaretIndex: Int = 0 + private var ignoreTextSelection: Bool = false + private var enableTextSelectionProcessing: Bool = false + + init?(textField: UITextField, currency: String, maxNumericalValue: Int64, initialValue: String, updated: @escaping (Int64) -> Void, focusUpdated: @escaping (Bool) -> Void) { + guard let format = CurrencyFormat(currency: currency) else { + return nil + } + self.format = format + self.currency = currency + self.maxNumericalValue = maxNumericalValue + self.updated = updated + self.focusUpdated = focusUpdated + + self.representation = Representation(string: initialValue, format: format) + + super.init() + + textField.text = self.representation.string + self.previousResolvedCaretIndex = self.representation.stringCaretIndex + } + + func reset(textField: UITextField, initialValue: String) { + self.representation = Representation(string: initialValue, format: self.format) + self.resetFromRepresentation(textField: textField, notifyUpdated: false) + } + + private func resetFromRepresentation(textField: UITextField, notifyUpdated: Bool) { + self.ignoreTextSelection = true + + if self.representation.numericalValue > self.maxNumericalValue { + self.representation = Representation(string: formatCurrencyAmountCustom(self.maxNumericalValue, currency: self.currency).0, format: self.format) + } + + textField.text = self.representation.string + self.previousResolvedCaretIndex = self.representation.stringCaretIndex + + if self.enableTextSelectionProcessing { + let stringCaretIndex = self.representation.stringCaretIndex + if let caretPosition = textField.position(from: textField.beginningOfDocument, offset: stringCaretIndex) { + textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition) + } + } + self.ignoreTextSelection = false + + if notifyUpdated { + self.updated(self.representation.numericalValue) + } + } + + @objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { + if string.count == 1 { + self.representation.insert(letter: string) + self.resetFromRepresentation(textField: textField, notifyUpdated: true) + } else if string.count == 0 { + self.representation.backspace() + self.resetFromRepresentation(textField: textField, notifyUpdated: true) + } + + return false + } + + @objc public func textFieldShouldReturn(_ textField: UITextField) -> Bool { + return false + } + + @objc public func textFieldDidBeginEditing(_ textField: UITextField) { + self.enableTextSelectionProcessing = true + self.focusUpdated(true) + + let stringCaretIndex = self.representation.stringCaretIndex + self.previousResolvedCaretIndex = stringCaretIndex + if let caretPosition = textField.position(from: textField.beginningOfDocument, offset: stringCaretIndex) { + self.ignoreTextSelection = true + textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition) + DispatchQueue.main.async { + textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition) + self.ignoreTextSelection = false + } + } + } + + @objc public func textFieldDidChangeSelection(_ textField: UITextField) { + if self.ignoreTextSelection { + return + } + if !self.enableTextSelectionProcessing { + return + } + + if let selectedTextRange = textField.selectedTextRange { + let index = textField.offset(from: textField.beginningOfDocument, to: selectedTextRange.end) + if self.previousResolvedCaretIndex != index { + self.representation.moveCaret(offset: self.previousResolvedCaretIndex < index ? 1 : -1) + + let stringCaretIndex = self.representation.stringCaretIndex + self.previousResolvedCaretIndex = stringCaretIndex + if let caretPosition = textField.position(from: textField.beginningOfDocument, offset: stringCaretIndex) { + textField.selectedTextRange = textField.textRange(from: caretPosition, to: caretPosition) + } + } + } + } + + @objc public func textFieldDidEndEditing(_ textField: UITextField) { + self.enableTextSelectionProcessing = false + self.focusUpdated(false) + } +} + class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { private let backgroundNode: ASDisplayNode let titleNode: TextNode @@ -167,8 +477,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { private var valueNodes: [TipValueNode] = [] private var item: BotCheckoutTipItem? - - private var formatterDelegate: CurrencyUITextFieldDelegate? + private var formatter: FormatterImpl? init() { self.backgroundNode = ASDisplayNode() @@ -178,6 +487,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { self.labelNode = TextNode() self.labelNode.isUserInteractionEnabled = false + self.labelNode.isHidden = true self.tipMeasurementNode = ImmediateTextNode() self.tipCurrencyNode = ImmediateTextNode() @@ -248,8 +558,25 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { strongSelf.titleNode.frame = CGRect(origin: CGPoint(x: leftInset, y: floor((labelsContentHeight - titleLayout.size.height) / 2.0)), size: titleLayout.size) strongSelf.labelNode.frame = CGRect(origin: CGPoint(x: params.width - leftInset - labelLayout.size.width, y: floor((labelsContentHeight - labelLayout.size.height) / 2.0)), size: labelLayout.size) - if strongSelf.formatterDelegate == nil { - strongSelf.formatterDelegate = CurrencyUITextFieldDelegate(formatter: CurrencyFormatter(currency: item.currency, { formatter in + if strongSelf.formatter == nil { + strongSelf.formatter = FormatterImpl(textField: strongSelf.textNode.textField, currency: item.currency, maxNumericalValue: item.maxValue, initialValue: item.value, updated: { value in + guard let strongSelf = self, let item = strongSelf.item else { + return + } + if item.numericValue != value { + item.updateValue(value) + } + }, focusUpdated: { value in + guard let strongSelf = self else { + return + } + if value { + strongSelf.item?.updatedFocus(true) + } + }) + strongSelf.textNode.textField.delegate = strongSelf.formatter + + /*strongSelf.formatterDelegate = CurrencyUITextFieldDelegate(formatter: CurrencyFormatter(currency: item.currency, { formatter in formatter.maxValue = currencyToFractionalAmount(value: item.maxValue, currency: item.currency) ?? 10000.0 formatter.minValue = 0.0 formatter.hasDecimals = true @@ -263,20 +590,10 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { strongSelf.textFieldTextChanged(strongSelf.textNode.textField) } + strongSelf.textNode.textField.delegate = strongSelf.formatterDelegate*/ + strongSelf.textNode.clipsToBounds = true - strongSelf.textNode.textField.delegate = strongSelf.formatterDelegate - - /*let toolbar: UIToolbar = UIToolbar() - toolbar.tintColor = item.theme.rootController.navigationBar.accentTextColor - toolbar.barTintColor = item.theme.rootController.navigationBar.backgroundColor - toolbar.barStyle = .default - toolbar.items = [ - UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil), - UIBarButtonItem(title: item.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.dismissKeyboard)) - ] - toolbar.sizeToFit() - - strongSelf.textNode.textField.inputAccessoryView = toolbar*/ + //strongSelf.textNode.textField.delegate = strongSelf } strongSelf.textNode.textField.typingAttributes = [NSAttributedString.Key.font: titleFont] @@ -297,8 +614,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { //let currencySymbolOnTheLeft = true if strongSelf.textNode.textField.text ?? "" != currencyText.0 { - strongSelf.textNode.textField.text = currencyText.0 - strongSelf.labelNode.isHidden = !currencyText.0.isEmpty + strongSelf.formatter?.reset(textField: strongSelf.textNode.textField, initialValue: currencyText.0) } strongSelf.tipMeasurementNode.attributedText = NSAttributedString(string: currencyText.0, font: titleFont, textColor: textColor) @@ -334,10 +650,14 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { } let (nodeMinWidth, nodeApply) = valueNode.update(theme: item.theme, text: variantText, isHighlighted: item.value == variantText, height: valueHeight) valueNode.action = { - guard let strongSelf = self else { + guard let strongSelf = self, let item = strongSelf.item else { return } - strongSelf.item?.updateValue(variantValue) + if item.numericValue == variantValue { + item.updateValue(0) + } else { + item.updateValue(variantValue) + } } totalMinWidth += nodeMinWidth variantLayouts.append((nodeMinWidth, nodeApply)) @@ -390,7 +710,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { @objc private func textFieldTextChanged(_ textField: UITextField) { let text = textField.text ?? "" - self.labelNode.isHidden = !text.isEmpty + //self.labelNode.isHidden = !text.isEmpty guard let item = self.item else { return @@ -401,7 +721,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { return } - var cleanText = "" + /*var cleanText = "" for c in text { if c.isNumber { cleanText.append(c) @@ -424,7 +744,7 @@ class BotCheckoutTipItemNode: ListViewItemNode, UITextFieldDelegate { } } item.updateValue(value) - } + }*/ } @objc public func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index d25743fd3a..71a48ce17a 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -2766,7 +2766,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController private func prepareRandomGreetingSticker() { let context = self.context self.preloadedSticker.set(.single(nil) - |> then(randomGreetingSticker(account: context.account) + |> then(context.engine.stickers.randomGreetingSticker() |> map { item in return item?.file })) diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift index 8b14972bfb..a2df45c8cd 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift @@ -911,7 +911,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo (strongSelf.navigationController?.topViewController as? ViewController)?.present(UndoOverlayController(presentationData: presentationData, content: .forward(savedMessages: true, text: messages.count == 1 ? presentationData.strings.Conversation_ForwardTooltip_SavedMessages_One : presentationData.strings.Conversation_ForwardTooltip_SavedMessages_Many), elevatedLayout: false, animateInAsReplacement: true, action: { _ in return false }), in: .window(.root)) let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in - return .forward(source: id, grouping: .auto, attributes: []) + return .forward(source: id, grouping: .auto, attributes: [], correlationId: nil) }) |> deliverOnMainQueue).start(next: { [weak self] messageIds in if let strongSelf = self { diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index 93af51f808..1791e51326 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -881,7 +881,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode { foundRemotePeers = ( .single((currentRemotePeersValue.0, currentRemotePeersValue.1, true)) |> then( - searchPeers(account: context.account, query: query) + context.engine.peers.searchPeers(query: query) |> map { ($0.0, $0.1, false) } |> delay(0.2, queue: Queue.concurrentDefaultQueue()) ) diff --git a/submodules/ComposePollUI/Sources/CreatePollController.swift b/submodules/ComposePollUI/Sources/CreatePollController.swift index 1ea4d85aa7..4c93ee41c6 100644 --- a/submodules/ComposePollUI/Sources/CreatePollController.swift +++ b/submodules/ComposePollUI/Sources/CreatePollController.swift @@ -825,7 +825,7 @@ public func createPollController(context: AccountContext, peer: Peer, isQuiz: Bo dismissImpl?() - completion(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.LocalPoll, id: arc4random64()), publicity: publicity, kind: kind, text: processPollText(state.text), options: options, correctAnswers: correctAnswers, results: TelegramMediaPollResults(voters: nil, totalVoters: nil, recentVoters: [], solution: resolvedSolution), isClosed: false, deadlineTimeout: deadlineTimeout)), replyToMessageId: nil, localGroupingKey: nil)) + completion(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaPoll(pollId: MediaId(namespace: Namespaces.Media.LocalPoll, id: Int64.random(in: Int64.min ... Int64.max)), publicity: publicity, kind: kind, text: processPollText(state.text), options: options, correctAnswers: correctAnswers, results: TelegramMediaPollResults(voters: nil, totalVoters: nil, recentVoters: [], solution: resolvedSolution), isClosed: false, deadlineTimeout: deadlineTimeout)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) }) let leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift index 122dd2a09f..a24a0fa1b4 100644 --- a/submodules/ContactListUI/Sources/ContactListNode.swift +++ b/submodules/ContactListUI/Sources/ContactListNode.swift @@ -1095,7 +1095,7 @@ public final class ContactListNode: ASDisplayNode { if globalSearch { foundRemoteContacts = foundRemoteContacts |> then( - searchPeers(account: context.account, query: query) + context.engine.peers.searchPeers(query: query) |> map { ($0.0, $0.1) } |> delay(0.2, queue: Queue.concurrentDefaultQueue()) ) diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index 1ec9005311..ddd00d0c2a 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -526,11 +526,10 @@ public class ContactsController: ViewController { }) } - private func prepareRandomGreetingSticker() { let context = self.context self.preloadedSticker.set(.single(nil) - |> then(randomGreetingSticker(account: context.account) + |> then(context.engine.stickers.randomGreetingSticker() |> map { item in return item?.file })) diff --git a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift index 0c727b739b..a97308a242 100644 --- a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift @@ -269,7 +269,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo if categories.contains(.global) { foundRemoteContacts = .single(previousFoundRemoteContacts.with({ $0 })) |> then( - searchPeers(account: context.account, query: query) + context.engine.peers.searchPeers(query: query) |> map { ($0.0, $0.1) } |> delay(0.2, queue: Queue.concurrentDefaultQueue()) ) diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index a31f2f4d01..b31b9d97d7 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -918,7 +918,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi intermediateCompletion() }) contentParentNode.updateAbsoluteRect?(self.contentContainerNode.frame.offsetBy(dx: 0.0, dy: -self.scrollNode.view.contentOffset.y + contentContainerOffset.y), self.bounds.size) - contentParentNode.applyAbsoluteOffset?(-contentContainerOffset.y, transitionCurve, transitionDuration) + contentParentNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: -contentContainerOffset.y), transitionCurve, transitionDuration) if let reactionContextNode = self.reactionContextNode { reactionContextNode.animateOut(to: CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: contentParentNode.contentRect.size), animatingOutToReaction: self.reactionContextNodeIsAnimatingOut) diff --git a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift index 653cf32759..da30560db6 100644 --- a/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift +++ b/submodules/CountrySelectionUI/Sources/AuthorizationSequenceCountrySelectionController.swift @@ -69,8 +69,8 @@ private func loadCountryCodes() -> [Country] { private var countryCodes: [Country] = loadCountryCodes() private var countryCodesByPrefix: [String: (Country, Country.CountryCode)] = [:] -public func loadServerCountryCodes(accountManager: AccountManager, network: Network, completion: @escaping () -> Void) { - let _ = (getCountriesList(accountManager: accountManager, network: network, langCode: nil) +public func loadServerCountryCodes(accountManager: AccountManager, engine: TelegramEngineUnauthorized, completion: @escaping () -> Void) { + let _ = (engine.localization.getCountriesList(accountManager: accountManager, langCode: nil) |> deliverOnMainQueue).start(next: { countries in countryCodes = countries @@ -93,6 +93,30 @@ public func loadServerCountryCodes(accountManager: AccountManager, network: Netw }) } +public func loadServerCountryCodes(accountManager: AccountManager, engine: TelegramEngine, completion: @escaping () -> Void) { + let _ = (engine.localization.getCountriesList(accountManager: accountManager, langCode: nil) + |> deliverOnMainQueue).start(next: { countries in + countryCodes = countries + + var countriesByPrefix: [String: (Country, Country.CountryCode)] = [:] + for country in countries { + for code in country.countryCodes { + if !code.prefixes.isEmpty { + for prefix in code.prefixes { + countriesByPrefix["\(code.code)\(prefix)"] = (country, code) + } + } else { + countriesByPrefix[code.code] = (country, code) + } + } + } + countryCodesByPrefix = countriesByPrefix + Queue.mainQueue().async { + completion() + } + }) +} + private final class AuthorizationSequenceCountrySelectionNavigationContentNode: NavigationBarContentNode { private let theme: PresentationTheme private let strings: PresentationStrings diff --git a/submodules/DebugSettingsUI/Sources/DebugController.swift b/submodules/DebugSettingsUI/Sources/DebugController.swift index bc10d7a500..fcbef6580e 100644 --- a/submodules/DebugSettingsUI/Sources/DebugController.swift +++ b/submodules/DebugSettingsUI/Sources/DebugController.swift @@ -222,12 +222,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { } } - let id = arc4random64() + let id = Int64.random(in: Int64.min ... Int64.max) let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false) context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData) let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")]) - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() } @@ -302,12 +302,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { } } - let id = arc4random64() + let id = Int64.random(in: Int64.min ... Int64.max) let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false) context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData) let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: logData.count, attributes: [.FileName(fileName: "Log-iOS-Short.txt")]) - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() } @@ -374,12 +374,12 @@ private enum DebugControllerEntry: ItemListNodeEntry { } } - let id = arc4random64() + let id = Int64.random(in: Int64.min ... Int64.max) let fileResource = LocalFileMediaResource(fileId: id, size: logData.count, isSecretRelated: false) context.account.postbox.mediaBox.storeResourceData(fileResource.id, data: logData) let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: fileResource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: logData.count, attributes: [.FileName(fileName: "Log-iOS-Full.txt")]) - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() } @@ -424,9 +424,9 @@ private enum DebugControllerEntry: ItemListNodeEntry { strongController.dismiss() let messages = logs.map { (name, path) -> EnqueueMessage in - let id = arc4random64() + let id = Int64.random(in: Int64.min ... Int64.max) let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) - return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) } let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start() } @@ -455,9 +455,9 @@ private enum DebugControllerEntry: ItemListNodeEntry { strongController.dismiss() let messages = logs.map { (name, path) -> EnqueueMessage in - let id = arc4random64() + let id = Int64.random(in: Int64.min ... Int64.max) let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) - return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) } let _ = enqueueMessages(account: context.account, peerId: peerId, messages: messages).start() } diff --git a/submodules/Display/Source/ContextContentSourceNode.swift b/submodules/Display/Source/ContextContentSourceNode.swift index 145e0d1115..ffd7ab165e 100644 --- a/submodules/Display/Source/ContextContentSourceNode.swift +++ b/submodules/Display/Source/ContextContentSourceNode.swift @@ -11,7 +11,7 @@ public final class ContextExtractedContentContainingNode: ASDisplayNode { public var willUpdateIsExtractedToContextPreview: ((Bool, ContainedViewLayoutTransition) -> Void)? public var isExtractedToContextPreviewUpdated: ((Bool) -> Void)? public var updateAbsoluteRect: ((CGRect, CGSize) -> Void)? - public var applyAbsoluteOffset: ((CGFloat, ContainedViewLayoutTransitionCurve, Double) -> Void)? + public var applyAbsoluteOffset: ((CGPoint, ContainedViewLayoutTransitionCurve, Double) -> Void)? public var applyAbsoluteOffsetSpring: ((CGFloat, Double, CGFloat) -> Void)? public var layoutUpdated: ((CGSize) -> Void)? public var updateDistractionFreeMode: ((Bool) -> Void)? diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift index ffbae84226..d188c563c7 100644 --- a/submodules/Display/Source/ListView.swift +++ b/submodules/Display/Source/ListView.swift @@ -1027,6 +1027,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture transition = .animated(duration: duration, curve: .spring) case let .Default(duration): transition = .animated(duration: max(updateSizeAndInsets.duration, duration ?? 0.3), curve: .easeInOut) + case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): + transition = .animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)) } } } else if let scrollToItem = scrollToItem { @@ -1040,6 +1042,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } else { transition = .animated(duration: duration ?? 0.3, curve: .easeInOut) } + case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): + transition = .animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)) } } } @@ -2665,6 +2669,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture scrollToItemTransition = .animated(duration: duration, curve: .spring) case let .Default(duration): scrollToItemTransition = .animated(duration: duration ?? 0.3, curve: .easeInOut) + case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): + scrollToItemTransition = .animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)) } } @@ -2751,6 +2757,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture updateSizeAndInsetsTransition = .animated(duration: duration, curve: .spring) case let .Default(duration): updateSizeAndInsetsTransition = .animated(duration: duration ?? 0.3, curve: .easeInOut) + case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): + updateSizeAndInsetsTransition = .animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)) } } @@ -2804,6 +2812,20 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } animationDuration = duration + springAnimation.isAdditive = true + animation = springAnimation + case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): + headerNodesTransition = (.animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)), false, -completeOffset) + animationCurve = .custom(cp1x, cp1y, cp2x, cp2y) + let springAnimation = CABasicAnimation(keyPath: "sublayerTransform") + springAnimation.timingFunction = CAMediaTimingFunction(controlPoints: cp1x, cp1y, cp2x, cp2y) + springAnimation.duration = duration * UIView.animationDurationFactor() + springAnimation.fromValue = NSValue(caTransform3D: CATransform3DMakeTranslation(0.0, -completeOffset, 0.0)) + springAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity) + springAnimation.isRemovedOnCompletion = true + + animationDuration = duration + springAnimation.isAdditive = true animation = springAnimation case let .Default(duration): @@ -2826,8 +2848,9 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } self.layer.add(animation, forKey: nil) for itemNode in self.itemNodes { - itemNode.applyAbsoluteOffset(value: -completeOffset, animationCurve: animationCurve, duration: animationDuration) + itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: animationCurve, duration: animationDuration) } + //self.didScrollWithOffset?(-completeOffset, ContainedViewLayoutTransition.animated(duration: animationDuration, curve: animationCurve), nil) } } else { self.visibleSize = updateSizeAndInsets.size @@ -2866,7 +2889,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture springAnimation.isAdditive = true self.layer.add(springAnimation, forKey: nil) for itemNode in self.itemNodes { - itemNode.applyAbsoluteOffset(value: -completeOffset, animationCurve: .spring, duration: duration) + itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -completeOffset), animationCurve: .spring, duration: duration) } } else { if let snapshotView = snapshotView { @@ -2993,6 +3016,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture headerNodesTransition = (.animated(duration: duration, curve: .spring), headerNodesTransition.1, headerNodesTransition.2 - offsetOrZero) case let .Default(duration): headerNodesTransition = (.animated(duration: duration ?? 0.3, curve: .easeInOut), true, headerNodesTransition.2 - offsetOrZero) + case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): + headerNodesTransition = (.animated(duration: duration, curve: .custom(cp1x, cp1y, cp2x, cp2y)), headerNodesTransition.1, headerNodesTransition.2 - offsetOrZero) } for (_, headerNode) in self.itemHeaderNodes { previousItemHeaderNodes.append(headerNode) @@ -3065,6 +3090,27 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture animation = springAnimation reverseAnimation = reverseSpringAnimation + case let .Custom(duration, cp1x, cp1y, cp2x, cp2y): + animationCurve = .custom(cp1x, cp1y, cp2x, cp2y) + animationDuration = duration + let basicAnimation = CABasicAnimation(keyPath: "sublayerTransform") + basicAnimation.timingFunction = CAMediaTimingFunction(controlPoints: cp1x, cp1y, cp2x, cp2y) + basicAnimation.duration = duration * UIView.animationDurationFactor() + basicAnimation.fromValue = NSValue(caTransform3D: CATransform3DMakeTranslation(0.0, -offset, 0.0)) + basicAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity) + basicAnimation.isRemovedOnCompletion = true + basicAnimation.isAdditive = true + + let reverseBasicAnimation = CABasicAnimation(keyPath: "sublayerTransform") + reverseBasicAnimation.timingFunction = CAMediaTimingFunction(controlPoints: cp1x, cp1y, cp2x, cp2y) + reverseBasicAnimation.duration = duration * UIView.animationDurationFactor() + reverseBasicAnimation.fromValue = NSValue(caTransform3D: CATransform3DMakeTranslation(0.0, offset, 0.0)) + reverseBasicAnimation.toValue = NSValue(caTransform3D: CATransform3DIdentity) + reverseBasicAnimation.isRemovedOnCompletion = true + reverseBasicAnimation.isAdditive = true + + animation = basicAnimation + reverseAnimation = reverseBasicAnimation case let .Default(duration): if let duration = duration { animationCurve = .easeInOut @@ -3122,11 +3168,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture } self.layer.add(animation, forKey: nil) for itemNode in self.itemNodes { - itemNode.applyAbsoluteOffset(value: -offset, animationCurve: animationCurve, duration: animationDuration) + itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -offset), animationCurve: animationCurve, duration: animationDuration) } for itemNode in temporaryPreviousNodes { - itemNode.applyAbsoluteOffset(value: -offset, animationCurve: animationCurve, duration: animationDuration) + itemNode.applyAbsoluteOffset(value: CGPoint(x: 0.0, y: -offset), animationCurve: animationCurve, duration: animationDuration) } + self.didScrollWithOffset?(offset, .animated(duration: animationDuration, curve: animationCurve), nil) if let verticalScrollIndicator = self.verticalScrollIndicator { verticalScrollIndicator.layer.add(reverseAnimation, forKey: nil) } diff --git a/submodules/Display/Source/ListViewAnimation.swift b/submodules/Display/Source/ListViewAnimation.swift index aed2d242c3..eb936f51a4 100644 --- a/submodules/Display/Source/ListViewAnimation.swift +++ b/submodules/Display/Source/ListViewAnimation.swift @@ -183,27 +183,19 @@ public final class ListViewAnimation { } public func listViewAnimationDurationAndCurve(transition: ContainedViewLayoutTransition) -> (Double, ListViewAnimationCurve) { - var duration: Double = 0.0 - var curve: UInt = 0 switch transition { case .immediate: - break + return (0.0, .Default(duration: 0.0)) case let .animated(animationDuration, animationCurve): - duration = animationDuration switch animationCurve { - case .linear, .easeInOut, .custom: - break - case .spring: - curve = 7 + case .linear: + return (animationDuration, .Default(duration: animationDuration)) + case .easeInOut: + return (animationDuration, .Default(duration: animationDuration)) + case .spring: + return (animationDuration, .Spring(duration: animationDuration)) + case let .custom(c1x, c1y, c2x, c2y): + return (animationDuration, .Custom(duration: animationDuration, c1x, c1y, c2x, c2y)) } } - - let listViewCurve: ListViewAnimationCurve - if curve == 7 { - listViewCurve = .Spring(duration: duration) - } else { - listViewCurve = .Default(duration: duration) - } - - return (duration, listViewCurve) } diff --git a/submodules/Display/Source/ListViewIntermediateState.swift b/submodules/Display/Source/ListViewIntermediateState.swift index ccc1344711..56d0e73bec 100644 --- a/submodules/Display/Source/ListViewIntermediateState.swift +++ b/submodules/Display/Source/ListViewIntermediateState.swift @@ -22,6 +22,7 @@ public enum ListViewScrollToItemDirectionHint { public enum ListViewAnimationCurve { case Spring(duration: Double) case Default(duration: Double?) + case Custom(duration: Double, Float, Float, Float, Float) } public struct ListViewScrollToItem { diff --git a/submodules/Display/Source/ListViewItemNode.swift b/submodules/Display/Source/ListViewItemNode.swift index d57694a912..0f1e3f992f 100644 --- a/submodules/Display/Source/ListViewItemNode.swift +++ b/submodules/Display/Source/ListViewItemNode.swift @@ -556,10 +556,10 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode { open func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) { } - open func applyAbsoluteOffset(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + open func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { if let extractedBackgroundNode = self.extractedBackgroundNode { let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve) - transition.animatePositionAdditive(node: extractedBackgroundNode, offset: CGPoint(x: 0.0, y: -value)) + transition.animatePositionAdditive(node: extractedBackgroundNode, offset: CGPoint(x: -value.x, y: -value.y)) } } diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 8e3bab9eb4..61111be0e2 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -353,7 +353,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { |> delay(0.15, queue: Queue.mainQueue()) let progressDisposable = progressSignal.start() - let signal = stickerPacksAttachedToMedia(account: context.account, media: media) + let signal = context.engine.stickers.stickerPacksAttachedToMedia(media: media) |> afterDisposed { Queue.mainQueue().async { progressDisposable.dispose() diff --git a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift index 6d35337792..9423124dc1 100644 --- a/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/UniversalVideoGalleryItem.swift @@ -1637,7 +1637,7 @@ final class UniversalVideoGalleryItemNode: ZoomableContentGalleryItemNode { self.isInteractingPromise.set(true) - let signal = stickerPacksAttachedToMedia(account: self.context.account, media: media) + let signal = self.context.engine.stickers.stickerPacksAttachedToMedia(media: media) |> afterDisposed { Queue.mainQueue().async { progressDisposable.dispose() diff --git a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift index 8d23425be3..931d409726 100644 --- a/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift +++ b/submodules/GalleryUI/Sources/SecretMediaPreviewController.swift @@ -349,7 +349,7 @@ public final class SecretMediaPreviewController: ViewController { |> deliverOnMainQueue).start(next: { [weak self] _ in if let strongSelf = self, strongSelf.traceVisibility() { if strongSelf.messageId.peerId.namespace == Namespaces.Peer.CloudUser { - let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.messageId.peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil)]).start() + let _ = enqueueMessages(account: strongSelf.context.account, peerId: strongSelf.messageId.peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start() } else if strongSelf.messageId.peerId.namespace == Namespaces.Peer.SecretChat { let _ = addSecretChatMessageScreenshot(account: strongSelf.context.account, peerId: strongSelf.messageId.peerId).start() } diff --git a/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift b/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift index da2ce07137..02f155b17c 100644 --- a/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPageFeedbackNode.swift @@ -73,7 +73,7 @@ final class InstantPageFeedbackNode: ASDisplayNode, InstantPageNode { } @objc func buttonPressed() { - self.resolveDisposable.set((resolvePeerByName(account: self.context.account, name: "previews") |> deliverOnMainQueue).start(next: { [weak self] peerId in + self.resolveDisposable.set((self.context.engine.peers.resolvePeerByName(name: "previews") |> deliverOnMainQueue).start(next: { [weak self] peerId in if let strongSelf = self, let _ = peerId, let webPageId = strongSelf.webPage.id?.id { strongSelf.openUrl(InstantPageUrlItem(url: "https://t.me/previews?start=webpage\(webPageId)", webpageId: nil)) } diff --git a/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift b/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift index 264b1c1117..7ee8b7a98e 100644 --- a/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift +++ b/submodules/InstantPageUI/Sources/InstantPagePeerReferenceNode.swift @@ -147,10 +147,11 @@ final class InstantPagePeerReferenceNode: ASDisplayNode, InstantPageNode { self.joinNode.addTarget(self, action: #selector(self.joinPressed), forControlEvents: .touchUpInside) let account = self.context.account + let context = self.context let signal = actualizedPeer(postbox: account.postbox, network: account.network, peer: initialPeer) |> mapToSignal({ peer -> Signal in if let peer = peer as? TelegramChannel, let username = peer.username, peer.accessHash == nil { - return .single(peer) |> then(resolvePeerByName(account: account, name: username) + return .single(peer) |> then(context.engine.peers.resolvePeerByName(name: username) |> mapToSignal({ peerId -> Signal in if let peerId = peerId { return account.postbox.transaction({ transaction -> Peer in diff --git a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageCaptureController.h b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageCaptureController.h index 73b0488a07..1a522e7eb0 100644 --- a/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageCaptureController.h +++ b/submodules/LegacyComponents/PublicHeaders/LegacyComponents/TGVideoMessageCaptureController.h @@ -36,7 +36,7 @@ - (CGRect)frameForSendButton; - (void)complete; -- (void)dismiss; +- (void)dismiss:(bool)cancelled; - (bool)stop; + (void)clearStartImage; @@ -44,4 +44,7 @@ + (void)requestCameraAccess:(void (^)(bool granted, bool wasNotDetermined))resultBlock; + (void)requestMicrophoneAccess:(void (^)(bool granted, bool wasNotDetermined))resultBlock; +- (UIView *)extractVideoContent; +- (void)hideVideoContent; + @end diff --git a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m index 34099398e4..0b7799d810 100644 --- a/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m +++ b/submodules/LegacyComponents/Sources/TGModernConversationInputMicButton.m @@ -249,7 +249,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius _innerCircleView.center = centerPoint; _outerCircleView.center = centerPoint; _decoration.center = centerPoint; - _innerIconWrapperView.center = centerPoint; + _innerIconWrapperView.center = CGPointMake(_decoration.frame.size.width / 2.0f, _decoration.frame.size.height / 2.0f); _lockPanelWrapperView.frame = CGRectMake(floor(centerPoint.x - _lockPanelWrapperView.frame.size.width / 2.0f), floor(centerPoint.y - 122.0f - _lockPanelWrapperView.frame.size.height / 2.0f), _lockPanelWrapperView.frame.size.width, _lockPanelWrapperView.frame.size.height); @@ -420,8 +420,8 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius _innerIconWrapperView.alpha = 0.0f; _innerIconWrapperView.userInteractionEnabled = false; [_innerIconWrapperView addSubview:_innerIconView]; - - [[_presentation view] addSubview:_innerIconWrapperView]; + + [_decoration addSubview:_innerIconWrapperView]; if (_lock == nil) { _stopButton = [[TGModernButton alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 40.0f, 40.0f)]; @@ -448,7 +448,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius block(); dispatch_async(dispatch_get_main_queue(), block); - _innerIconWrapperView.transform = CGAffineTransformIdentity; + //_innerIconWrapperView.transform = CGAffineTransformIdentity; _innerCircleView.transform = CGAffineTransformMakeScale(0.2f, 0.2f); _outerCircleView.transform = CGAffineTransformMakeScale(0.2f, 0.2f); _decoration.transform = CGAffineTransformMakeScale(0.2f, 0.2f); @@ -515,11 +515,11 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius _outerCircleView.transform = CGAffineTransformMakeScale(0.2f, 0.2f); if (toSmallSize) { _decoration.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(0.33f, 0.33f), CGAffineTransformMakeTranslation(0, 2 - TGScreenPixel)); - _innerIconWrapperView.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(0.492f, 0.492f), CGAffineTransformMakeTranslation(-TGScreenPixel, 1)); + //_innerIconWrapperView.transform = CGAffineTransformConcat(CGAffineTransformMakeScale(0.492f, 0.492f), CGAffineTransformMakeTranslation(-TGScreenPixel, 1)); } else { _decoration.transform = CGAffineTransformMakeScale(0.2f, 0.2f); _decoration.alpha = 0.0; - _innerIconWrapperView.transform = CGAffineTransformMakeScale(0.2f, 0.2f); + //_innerIconWrapperView.transform = CGAffineTransformMakeScale(0.2f, 0.2f); _innerIconWrapperView.alpha = 0.0f; } _innerCircleView.alpha = 0.0f; @@ -864,7 +864,7 @@ static const CGFloat outerCircleMinScale = innerCircleRadius / outerCircleRadius transform = CGAffineTransformTranslate(transform, _cancelTranslation, 0); _innerCircleView.transform = transform; - _innerIconWrapperView.transform = transform; + //_innerIconWrapperView.transform = transform; _decoration.transform = transform; } } diff --git a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h index 6d79b12348..0cb6464f84 100644 --- a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h +++ b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.h @@ -21,7 +21,7 @@ - (void)stopRunning; - (void)startRecording:(NSURL *)url preset:(TGMediaVideoConversionPreset)preset liveUpload:(bool)liveUpload; -- (void)stopRecording:(void (^)())completed; +- (void)stopRecording:(void (^)(bool))completed; - (CGAffineTransform)transformForOrientation:(AVCaptureVideoOrientation)orientation; diff --git a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m index 304099a130..f249087a91 100644 --- a/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m +++ b/submodules/LegacyComponents/Sources/TGVideoCameraPipeline.m @@ -135,7 +135,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; { _running = false; - [self stopRecording:^{}]; + [self stopRecording:^(__unused bool success) {}]; [_captureSession stopRunning]; [self captureSessionDidStopRunning]; @@ -300,7 +300,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; - (void)captureSessionDidStopRunning { - [self stopRecording:^{}]; + [self stopRecording:^(__unused bool success) {}]; [self destroyVideoPipeline]; } @@ -701,7 +701,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; [recorder prepareToRecord]; } -- (void)stopRecording:(void (^)())completed +- (void)stopRecording:(void (^)(bool))completed { [[TGVideoCameraPipeline cameraQueue] dispatch:^ { @@ -709,7 +709,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; { if (_recordingStatus != TGVideoCameraRecordingStatusRecording) { if (completed) { - completed(); + completed(false); } return; } @@ -721,7 +721,7 @@ const NSInteger TGVideoCameraRetainedBufferCount = 16; [_recorder finishRecording:^{ __unused __auto_type description = [self description]; if (completed) { - completed(); + completed(true); } }]; }]; diff --git a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m index cf05bf2cba..9285d031bc 100644 --- a/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m +++ b/submodules/LegacyComponents/Sources/TGVideoMessageCaptureController.m @@ -658,9 +658,10 @@ typedef enum return; [_activityDisposable dispose]; - [self stopRecording:^{ + [self stopRecording:^() { TGDispatchOnMainThread(^{ - [self dismiss:false]; + //[self dismiss:false]; + [self description]; }); }]; } @@ -955,7 +956,20 @@ typedef enum - (void)stopRecording:(void (^)())completed { - [_capturePipeline stopRecording:completed]; + __weak TGVideoMessageCaptureController *weakSelf = self; + [_capturePipeline stopRecording:^(bool success) { + TGDispatchOnMainThread(^{ + __strong TGVideoMessageCaptureController *strongSelf = weakSelf; + if (strongSelf == nil) { + return; + } + if (!success) { + if (!strongSelf->_dismissed && strongSelf.finishedWithVideo != nil) { + strongSelf.finishedWithVideo(nil, nil, 0, 0.0, CGSizeZero, nil, nil, false, 0); + } + } + }); + }]; [_buttonHandler ignoreEventsFor:1.0f andDisable:true]; [_capturePipeline stopRunning]; } @@ -1015,10 +1029,14 @@ typedef enum } } - if (!_dismissed && self.finishedWithVideo != nil) + if (!_dismissed) { self.finishedWithVideo(url, image, fileSize, duration, dimensions, liveUploadData, adjustments, isSilent, scheduleTimestamp); - else + } else { [[NSFileManager defaultManager] removeItemAtURL:url error:NULL]; + if (self.finishedWithVideo != nil) { + self.finishedWithVideo(nil, nil, 0, 0.0, CGSizeZero, nil, nil, false, 0); + } + } } - (UIImageOrientation)orientationForThumbnailWithTransform:(CGAffineTransform)transform mirrored:(bool)mirrored @@ -1501,6 +1519,16 @@ static UIImage *startImage = nil; return CGSizeMake(240.0f, 240.0f); } +- (UIView *)extractVideoContent { + UIView *result = [_circleView snapshotViewAfterScreenUpdates:false]; + result.frame = [_circleView convertRect:_circleView.bounds toView:nil]; + return result; +} + +- (void)hideVideoContent { + _circleWrapperView.alpha = 0.02f; +} + @end diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyLiveUploadInterface.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyLiveUploadInterface.swift index f07d6629c0..59af73ea1a 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyLiveUploadInterface.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyLiveUploadInterface.swift @@ -54,7 +54,7 @@ public final class LegacyLiveUploadInterface: VideoConversionWatcher, TGLiveUplo public init(account: Account) { self.account = account - self.id = arc4random64() + self.id = Int64.random(in: Int64.min ... Int64.max) var updateImpl: ((String, Int) -> Void)? super.init(update: { path, size in diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift index bc1fc47954..56dec373e7 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyMediaPickers.swift @@ -254,7 +254,7 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String? } } -public func legacyEnqueueGifMessage(account: Account, data: Data) -> Signal { +public func legacyEnqueueGifMessage(account: Account, data: Data, correlationId: Int64? = nil) -> Signal { return Signal { subscriber in if let previewImage = UIImage(data: data) { let dimensions = previewImage.size @@ -263,7 +263,7 @@ public func legacyEnqueueGifMessage(account: Account, data: Data) -> Signal Signal Signal Signal runOn(Queue.concurrentDefaultQueue()) } -public func legacyEnqueueVideoMessage(account: Account, data: Data) -> Signal { +public func legacyEnqueueVideoMessage(account: Account, data: Data, correlationId: Int64? = nil) -> Signal { return Signal { subscriber in if let previewImage = UIImage(data: data) { let dimensions = previewImage.size @@ -305,7 +305,7 @@ public func legacyEnqueueVideoMessage(account: Account, data: Data) -> Signal Signal Signal 0 && timer <= 60 { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil)) } - messages.append(.message(text: caption ?? "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId)) + messages.append(.message(text: caption ?? "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil)) case .tempFile: break } @@ -418,13 +418,13 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) - arc4random_buf(&randomId, 8) let resource = LocalFileReferenceMediaResource(localFilePath: path, randomId: randomId) let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)]) - messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId)) + messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil)) case let .asset(asset): var randomId: Int64 = 0 arc4random_buf(&randomId, 8) - let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64()) + let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: Int64.random(in: Int64.min ... Int64.max)) let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: nil, attributes: [.FileName(fileName: name)]) - messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId)) + messages.append(.message(text: caption ?? "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil)) default: break } @@ -462,7 +462,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) - var previewRepresentations: [TelegramMediaImageRepresentation] = [] if let thumbnail = thumbnail { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 320.0, height: 320.0)) let thumbnailImage = TGScaleImageToPixelSize(thumbnail, thumbnailSize)! if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) { @@ -505,13 +505,13 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) - case let .tempFile(path, _, _): if asFile || (asAnimation && !path.contains(".jpg")) { if let size = fileSize(path) { - resource = LocalFileMediaResource(fileId: arc4random64(), size: size) + resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: size) account.postbox.mediaBox.moveResourceData(resource.id, fromTempPath: path) } else { continue outer } } else { - resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: path, adjustments: resourceAdjustments) + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: path, adjustments: resourceAdjustments) } } @@ -547,12 +547,12 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) - fileAttributes.append(.HasLinkedStickers) } - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: fileAttributes) if let timer = item.timer, timer > 0 && timer <= 60 { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil)) } - messages.append(.message(text: caption ?? "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId)) + messages.append(.message(text: caption ?? "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: item.groupedId, correlationId: nil)) } } } diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift b/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift index 2af3181ce3..22386c4bd0 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacySuggestionContext.swift @@ -74,7 +74,7 @@ public func legacySuggestionContext(context: AccountContext, peerId: PeerId, cha return SSignal.complete() } return SSignal { subscriber in - let disposable = (searchEmojiKeywords(postbox: context.account.postbox, inputLanguageCode: inputLanguageCode, query: query, completeMatch: query.count < 3) + let disposable = (context.engine.stickers.searchEmojiKeywords(inputLanguageCode: inputLanguageCode, query: query, completeMatch: query.count < 3) |> map { keywords -> [TGAlphacodeEntry] in var result: [TGAlphacodeEntry] = [] for keyword in keywords { diff --git a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift index c882fb209d..a16dfdf565 100644 --- a/submodules/LocationUI/Sources/LocationPickerControllerNode.swift +++ b/submodules/LocationUI/Sources/LocationPickerControllerNode.swift @@ -403,7 +403,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM throttledUserLocation(userLocation) |> mapToSignal { location -> Signal<[TelegramMediaMap]?, NoError> in if let location = location, location.horizontalAccuracy > 0 { - return combineLatest(nearbyVenues(account: context.account, latitude: location.coordinate.latitude, longitude: location.coordinate.longitude), personalVenues) + return combineLatest(nearbyVenues(context: context, latitude: location.coordinate.latitude, longitude: location.coordinate.longitude), personalVenues) |> map { nearbyVenues, personalVenues -> [TelegramMediaMap]? in var resultVenues: [TelegramMediaMap] = [] if let personalVenues = personalVenues { @@ -431,7 +431,7 @@ final class LocationPickerControllerNode: ViewControllerTracingNode, CLLocationM if let coordinate = coordinate { return (.single(nil) |> then( - nearbyVenues(account: context.account, latitude: coordinate.latitude, longitude: coordinate.longitude) + nearbyVenues(context: context, latitude: coordinate.latitude, longitude: coordinate.longitude) |> map { venues -> ([TelegramMediaMap], CLLocation)? in return (venues, CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)) } diff --git a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift index cadd155df2..28a38b4999 100644 --- a/submodules/LocationUI/Sources/LocationSearchContainerNode.swift +++ b/submodules/LocationUI/Sources/LocationSearchContainerNode.swift @@ -165,7 +165,7 @@ final class LocationSearchContainerNode: ASDisplayNode { } |> mapToSignal { query -> Signal<([LocationSearchEntry], String)?, NoError> in if let query = query, !query.isEmpty { - let foundVenues = nearbyVenues(account: context.account, latitude: coordinate.latitude, longitude: coordinate.longitude, query: query) + let foundVenues = nearbyVenues(context: context, latitude: coordinate.latitude, longitude: coordinate.longitude, query: query) |> afterCompleted { isSearching.set(false) } diff --git a/submodules/LocationUI/Sources/LocationUtils.swift b/submodules/LocationUI/Sources/LocationUtils.swift index 7d658b057c..31d343c199 100644 --- a/submodules/LocationUI/Sources/LocationUtils.swift +++ b/submodules/LocationUI/Sources/LocationUtils.swift @@ -5,6 +5,7 @@ import TelegramCore import TelegramPresentationData import TelegramStringFormatting import MapKit +import AccountContext extension TelegramMediaMap { convenience init(coordinate: CLLocationCoordinate2D, liveBroadcastingTimeout: Int32? = nil, proximityNotificationRadius: Int32? = nil) { @@ -32,17 +33,17 @@ public func ==(lhs: CLLocationCoordinate2D, rhs: CLLocationCoordinate2D) -> Bool return lhs.latitude == rhs.latitude && lhs.longitude == rhs.longitude } -public func nearbyVenues(account: Account, latitude: Double, longitude: Double, query: String? = nil) -> Signal<[TelegramMediaMap], NoError> { - return account.postbox.transaction { transaction -> SearchBotsConfiguration in +public func nearbyVenues(context: AccountContext, latitude: Double, longitude: Double, query: String? = nil) -> Signal<[TelegramMediaMap], NoError> { + return context.account.postbox.transaction { transaction -> SearchBotsConfiguration in return currentSearchBotsConfiguration(transaction: transaction) } |> mapToSignal { searchBotsConfiguration in - return resolvePeerByName(account: account, name: searchBotsConfiguration.venueBotUsername ?? "foursquare") + return context.engine.peers.resolvePeerByName(name: searchBotsConfiguration.venueBotUsername ?? "foursquare") |> take(1) |> mapToSignal { peerId -> Signal in guard let peerId = peerId else { return .single(nil) } - return requestChatContextResults(account: account, botId: peerId, peerId: account.peerId, query: query ?? "", location: .single((latitude, longitude)), offset: "") + return requestChatContextResults(account: context.account, botId: peerId, peerId: context.account.peerId, query: query ?? "", location: .single((latitude, longitude)), offset: "") |> map { results -> ChatContextResultCollection? in return results?.results } diff --git a/submodules/MtProtoKit/Sources/MTProto.m b/submodules/MtProtoKit/Sources/MTProto.m index c0c1d8177b..4011446fca 100644 --- a/submodules/MtProtoKit/Sources/MTProto.m +++ b/submodules/MtProtoKit/Sources/MTProto.m @@ -2138,20 +2138,6 @@ static NSString *dumpHexString(NSData *data, int maxLength) { NSData *decryptedData = MTAesDecrypt(dataToDecrypt, encryptionKey.key, encryptionKey.iv); - int32_t messageDataLength = 0; - [decryptedData getBytes:&messageDataLength range:NSMakeRange(28, 4)]; - - int32_t paddingLength = ((int32_t)decryptedData.length) - messageDataLength; - if (paddingLength < 12 || paddingLength > 1024) { - __unused NSData *result = MTSha256(decryptedData); - return nil; - } - - if (messageDataLength < 0 || messageDataLength > (int32_t)decryptedData.length) { - __unused NSData *result = MTSha256(decryptedData); - return nil; - } - int xValue = 8; NSMutableData *msgKeyLargeData = [[NSMutableData alloc] init]; [msgKeyLargeData appendBytes:effectiveAuthKey.authKey.bytes + 88 + xValue length:32]; @@ -2160,8 +2146,21 @@ static NSString *dumpHexString(NSData *data, int maxLength) { NSData *msgKeyLarge = MTSha256(msgKeyLargeData); NSData *messageKey = [msgKeyLarge subdataWithRange:NSMakeRange(8, 16)]; - if (![messageKey isEqualToData:embeddedMessageKey]) + if (![messageKey isEqualToData:embeddedMessageKey]) { return nil; + } + + int32_t messageDataLength = 0; + [decryptedData getBytes:&messageDataLength range:NSMakeRange(28, 4)]; + + int32_t paddingLength = ((int32_t)decryptedData.length) - messageDataLength; + if (paddingLength < 12 || paddingLength > 1024) { + return nil; + } + + if (messageDataLength < 0 || messageDataLength > (int32_t)decryptedData.length) { + return nil; + } return decryptedData; } diff --git a/submodules/PassportUI/Sources/LegacySecureIdAttachmentMenu.swift b/submodules/PassportUI/Sources/LegacySecureIdAttachmentMenu.swift index eff3936045..6da5632fcf 100644 --- a/submodules/PassportUI/Sources/LegacySecureIdAttachmentMenu.swift +++ b/submodules/PassportUI/Sources/LegacySecureIdAttachmentMenu.swift @@ -158,7 +158,7 @@ private func processedLegacySecureIdAttachmentItems(postbox: Postbox, signal: SS guard let image = image else { return [] } - let randomId = arc4random64() + let randomId = Int64.random(in: Int64.min ... Int64.max) let tempFilePath = NSTemporaryDirectory() + "\(randomId).jpeg" let scaledSize = image.size.aspectFitted(CGSize(width: 2048.0, height: 2048.0)) if let scaledImage = TGScaleImageToPixelSize(image, scaledSize), let scaledImageData = compressImageToJPEG(scaledImage, quality: 0.84) { diff --git a/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift b/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift index e0f44699b3..5ec8b56f37 100644 --- a/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift +++ b/submodules/PassportUI/Sources/SecureIdDocumentFormControllerNode.swift @@ -2601,7 +2601,7 @@ final class SecureIdDocumentFormControllerNode: FormControllerNode Vi let completedImpl: (UIImage) -> Void = { image in if let data = image.jpegData(compressionQuality: 0.6) { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil) updateState { diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 9e43448063..0b7c4e97c3 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -682,7 +682,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon foundRemotePeers = .single(([], [])) } else { foundContacts = context.account.postbox.searchContacts(query: query.lowercased()) - foundRemotePeers = .single(([], [])) |> then(searchPeers(account: context.account, query: query) + foundRemotePeers = .single(([], [])) |> then(context.engine.peers.searchPeers(query: query) |> delay(0.2, queue: Queue.concurrentDefaultQueue())) } case .searchMembers, .searchBanned, .searchKicked, .searchAdmins: @@ -996,7 +996,7 @@ public final class ChannelMembersSearchContainerNode: SearchDisplayControllerCon } if mode == .banAndPromoteActions || mode == .inviteActions { - foundRemotePeers = .single(([], [])) |> then(searchPeers(account: context.account, query: query) + foundRemotePeers = .single(([], [])) |> then(context.engine.peers.searchPeers(query: query) |> delay(0.2, queue: Queue.concurrentDefaultQueue())) } else { foundRemotePeers = .single(([], [])) diff --git a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift index 42ecaef5e9..b416888015 100644 --- a/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelPermissionsController.swift @@ -762,7 +762,7 @@ public func channelPermissionsController(context: AccountContext, peerId origina } pushControllerImpl?(controller) }, openChannelExample: { - resolveDisposable.set((resolvePeerByName(account: context.account, name: "durov") |> deliverOnMainQueue).start(next: { peerId in + resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "durov") |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId { navigateToChatControllerImpl?(peerId) } diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index 36216f995c..bb0fa9d466 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -818,9 +818,9 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, } let peersDisablingAddressNameAssignment = Promise<[Peer]?>() - peersDisablingAddressNameAssignment.set(.single(nil) |> then(context.engine.peerNames.channelAddressNameAssignmentAvailability(peerId: peerId.namespace == Namespaces.Peer.CloudChannel ? peerId : nil) |> mapToSignal { result -> Signal<[Peer]?, NoError> in + peersDisablingAddressNameAssignment.set(.single(nil) |> then(context.engine.peers.channelAddressNameAssignmentAvailability(peerId: peerId.namespace == Namespaces.Peer.CloudChannel ? peerId : nil) |> mapToSignal { result -> Signal<[Peer]?, NoError> in if case .addressNameLimitReached = result { - return context.engine.peerNames.adminedPublicChannels(scope: .all) + return context.engine.peers.adminedPublicChannels(scope: .all) |> map(Optional.init) } else { return .single([]) @@ -871,7 +871,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, return state.withUpdatedEditingPublicLinkText(text) } - checkAddressNameDisposable.set((context.engine.peerNames.validateAddressNameInteractive(domain: .peer(peerId), name: text) + checkAddressNameDisposable.set((context.engine.peers.validateAddressNameInteractive(domain: .peer(peerId), name: text) |> deliverOnMainQueue).start(next: { result in updateState { state in return state.withUpdatedAddressNameValidationStatus(result) @@ -893,7 +893,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, return state.withUpdatedRevokingPeerId(peerId) } - revokeAddressNameDisposable.set((context.engine.peerNames.updateAddressName(domain: .peer(peerId), name: nil) |> deliverOnMainQueue).start(error: { _ in + revokeAddressNameDisposable.set((context.engine.peers.updateAddressName(domain: .peer(peerId), name: nil) |> deliverOnMainQueue).start(error: { _ in updateState { state in return state.withUpdatedRevokingPeerId(nil) } @@ -1101,7 +1101,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, } _ = ApplicationSpecificNotice.markAsSeenSetPublicChannelLink(accountManager: context.sharedContext.accountManager).start() - updateAddressNameDisposable.set((context.engine.peerNames.updateAddressName(domain: .peer(peerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) |> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic)) + updateAddressNameDisposable.set((context.engine.peers.updateAddressName(domain: .peer(peerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) |> timeout(10, queue: Queue.mainQueue(), alternate: .fail(.generic)) |> deliverOnMainQueue).start(error: { _ in updateState { state in return state.withUpdatedUpdatingAddressName(false) @@ -1173,7 +1173,7 @@ public func channelVisibilityController(context: AccountContext, peerId: PeerId, let signal = convertGroupToSupergroup(account: context.account, peerId: peerId) |> mapToSignal { upgradedPeerId -> Signal in - return context.engine.peerNames.updateAddressName(domain: .peer(upgradedPeerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) + return context.engine.peers.updateAddressName(domain: .peer(upgradedPeerId), name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) |> `catch` { _ -> Signal in return .complete() } diff --git a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift index 64d3203a2b..2656226d71 100644 --- a/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift +++ b/submodules/PeerInfoUI/Sources/DeviceContactInfoController.swift @@ -396,7 +396,7 @@ private enum DeviceContactInfoEntry: ItemListNodeEntry { func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { let arguments = arguments as! DeviceContactInfoControllerArguments switch self { - case let .info(_, _, strings, dateTimeFormat, peer, state, jobSummary, isPlain): + case let .info(_, _, _, dateTimeFormat, peer, state, jobSummary, _): return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .contact, peer: peer, presence: nil, label: jobSummary, cachedData: nil, state: state, sectionId: self.section, style: arguments.isPlain ? .plain : .blocks(withTopInset: false, withExtendedBottomInset: true), editingNameUpdated: { editingName in arguments.updateEditingName(editingName) }, avatarTapped: { @@ -647,8 +647,13 @@ private func deviceContactInfoEntries(account: Account, presentationData: Presen let jobSummary = jobComponents.joined(separator: " — ") let isOrganization = personName.0.isEmpty && personName.1.isEmpty && !contactData.organization.isEmpty + + var firstName: String = isOrganization ? contactData.organization : personName.0 + if firstName.isEmpty { + firstName = presentationData.strings.Message_Contact + } - entries.append(.info(entries.count, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer ?? TelegramUser(id: PeerId(namespace: .max, id: PeerId.Id._internalFromInt32Value(0)), accessHash: nil, firstName: isOrganization ? contactData.organization : personName.0, lastName: isOrganization ? nil : personName.1, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []), state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), job: isOrganization ? nil : jobSummary, isPlain: !isShare)) + entries.append(.info(entries.count, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: peer ?? TelegramUser(id: PeerId(namespace: .max, id: PeerId.Id._internalFromInt32Value(0)), accessHash: nil, firstName: firstName, lastName: isOrganization ? nil : personName.1, username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []), state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), job: isOrganization ? nil : jobSummary, isPlain: !isShare)) if !selecting { if let _ = peer { diff --git a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift index c79387453e..9ec05ffc23 100644 --- a/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift +++ b/submodules/PeerInfoUI/Sources/GroupStickerPackSetupController.swift @@ -323,7 +323,7 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee let initialData = Promise() if let currentPackInfo = currentPackInfo { - initialData.set(cachedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .id(id: currentPackInfo.id.id, accessHash: currentPackInfo.accessHash), forceRemote: false) + initialData.set(context.engine.stickers.cachedStickerPack(reference: .id(id: currentPackInfo.id.id, accessHash: currentPackInfo.accessHash), forceRemote: false) |> map { result -> InitialStickerPackData? in switch result { case .none: @@ -363,7 +363,7 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee } } return .single((searchText, .searching)) - |> then((loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .name(searchText.lowercased()), forceActualized: false) |> delay(0.3, queue: Queue.concurrentDefaultQueue())) + |> then((context.engine.stickers.loadedStickerPack(reference: .name(searchText.lowercased()), forceActualized: false) |> delay(0.3, queue: Queue.concurrentDefaultQueue())) |> mapToSignal { value -> Signal<(String, GroupStickerPackSearchState), NoError> in switch value { case .fetching: @@ -402,7 +402,7 @@ public func groupStickerPackSetupController(context: AccountContext, peerId: Pee }, updateSearchText: { text in searchText.set(text) }, openStickersBot: { - resolveDisposable.set((resolvePeerByName(account: context.account, name: "stickers") |> deliverOnMainQueue).start(next: { peerId in + resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers") |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId { dismissImpl?() navigateToChatControllerImpl?(peerId) diff --git a/submodules/PeerInfoUI/Sources/OldChannelsController.swift b/submodules/PeerInfoUI/Sources/OldChannelsController.swift index 8b1248bb1a..19ef901616 100644 --- a/submodules/PeerInfoUI/Sources/OldChannelsController.swift +++ b/submodules/PeerInfoUI/Sources/OldChannelsController.swift @@ -384,7 +384,7 @@ public func oldChannelsController(context: AccountContext, intent: OldChannelsCo let peersSignal: Signal<[InactiveChannel]?, NoError> = .single(nil) |> then( - inactiveChannelList(network: context.account.network) + context.engine.peers.inactiveChannelList() |> map { peers -> [InactiveChannel]? in return peers.sorted(by: { lhs, rhs in return lhs.lastActivityDate < rhs.lastActivityDate diff --git a/submodules/PeerInfoUI/Sources/UserInfoController.swift b/submodules/PeerInfoUI/Sources/UserInfoController.swift index 1c8c012ebc..07faaa9665 100644 --- a/submodules/PeerInfoUI/Sources/UserInfoController.swift +++ b/submodules/PeerInfoUI/Sources/UserInfoController.swift @@ -29,744 +29,6 @@ import LocalizedPeerData import PhoneNumberFormat import TelegramIntents -private final class UserInfoControllerArguments { - let context: AccountContext - let avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext - let updateEditingName: (ItemListAvatarAndNameInfoItemName) -> Void - let tapAvatarAction: () -> Void - let openChat: () -> Void - let addContact: () -> Void - let shareContact: () -> Void - let shareMyContact: () -> Void - let startSecretChat: () -> Void - let changeNotificationMuteSettings: () -> Void - let openSharedMedia: () -> Void - let openGroupsInCommon: () -> Void - let updatePeerBlocked: (Bool) -> Void - let deleteContact: () -> Void - let displayUsernameContextMenu: (String) -> Void - let displayCopyContextMenu: (UserInfoEntryTag, String) -> Void - let call: () -> Void - let openCallMenu: (String) -> Void - let requestPhoneNumber: () -> Void - let aboutLinkAction: (TextLinkItemActionType, TextLinkItem) -> Void - let displayAboutContextMenu: (String) -> Void - let openEncryptionKey: (SecretChatKeyFingerprint) -> Void - let addBotToGroup: () -> Void - let shareBot: () -> Void - let botSettings: () -> Void - let botHelp: () -> Void - let botPrivacy: () -> Void - let report: () -> Void - - init(context: AccountContext, avatarAndNameInfoContext: ItemListAvatarAndNameInfoItemContext, updateEditingName: @escaping (ItemListAvatarAndNameInfoItemName) -> Void, tapAvatarAction: @escaping () -> Void, openChat: @escaping () -> Void, addContact: @escaping () -> Void, shareContact: @escaping () -> Void, shareMyContact: @escaping () -> Void, startSecretChat: @escaping () -> Void, changeNotificationMuteSettings: @escaping () -> Void, openSharedMedia: @escaping () -> Void, openGroupsInCommon: @escaping () -> Void, updatePeerBlocked: @escaping (Bool) -> Void, deleteContact: @escaping () -> Void, displayUsernameContextMenu: @escaping (String) -> Void, displayCopyContextMenu: @escaping (UserInfoEntryTag, String) -> Void, call: @escaping () -> Void, openCallMenu: @escaping (String) -> Void, requestPhoneNumber: @escaping () -> Void, aboutLinkAction: @escaping (TextLinkItemActionType, TextLinkItem) -> Void, displayAboutContextMenu: @escaping (String) -> Void, openEncryptionKey: @escaping (SecretChatKeyFingerprint) -> Void, addBotToGroup: @escaping () -> Void, shareBot: @escaping () -> Void, botSettings: @escaping () -> Void, botHelp: @escaping () -> Void, botPrivacy: @escaping () -> Void, report: @escaping () -> Void) { - self.context = context - self.avatarAndNameInfoContext = avatarAndNameInfoContext - self.updateEditingName = updateEditingName - self.tapAvatarAction = tapAvatarAction - self.openChat = openChat - self.addContact = addContact - - self.shareContact = shareContact - self.shareMyContact = shareMyContact - self.startSecretChat = startSecretChat - self.changeNotificationMuteSettings = changeNotificationMuteSettings - self.openSharedMedia = openSharedMedia - self.openGroupsInCommon = openGroupsInCommon - self.updatePeerBlocked = updatePeerBlocked - self.deleteContact = deleteContact - self.displayUsernameContextMenu = displayUsernameContextMenu - self.displayCopyContextMenu = displayCopyContextMenu - self.call = call - self.openCallMenu = openCallMenu - self.requestPhoneNumber = requestPhoneNumber - self.aboutLinkAction = aboutLinkAction - self.displayAboutContextMenu = displayAboutContextMenu - self.openEncryptionKey = openEncryptionKey - self.addBotToGroup = addBotToGroup - self.shareBot = shareBot - self.botSettings = botSettings - self.botHelp = botHelp - self.botPrivacy = botPrivacy - self.report = report - } -} - -private enum UserInfoSection: ItemListSectionId { - case info - case actions - case sharedMediaAndNotifications - case bot - case block -} - -private enum UserInfoEntryTag { - case about - case phoneNumber - case username -} - -private func areMessagesEqual(_ lhsMessage: Message, _ rhsMessage: Message) -> Bool { - if lhsMessage.stableVersion != rhsMessage.stableVersion { - return false - } - if lhsMessage.id != rhsMessage.id || lhsMessage.flags != rhsMessage.flags { - return false - } - return true -} - -private enum UserInfoEntry: ItemListNodeEntry { - case info(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, peer: Peer?, presence: PeerPresence?, cachedData: CachedPeerData?, state: ItemListAvatarAndNameInfoItemState, displayCall: Bool) - case calls(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, messages: [Message]) - case about(PresentationTheme, Peer, String, String) - case phoneNumber(PresentationTheme, Int, String, String, Bool) - case requestPhoneNumber(PresentationTheme, String, String) - case userName(PresentationTheme, String, String) - case sendMessage(PresentationTheme, String) - case addContact(PresentationTheme, String) - case shareContact(PresentationTheme, String) - case shareMyContact(PresentationTheme, String) - case startSecretChat(PresentationTheme, String) - case sharedMedia(PresentationTheme, String) - case notifications(PresentationTheme, String, String) - case groupsInCommon(PresentationTheme, String, String) - case secretEncryptionKey(PresentationTheme, String, SecretChatKeyFingerprint) - case botAddToGroup(PresentationTheme, String) - case botShare(PresentationTheme, String) - case botSettings(PresentationTheme, String) - case botHelp(PresentationTheme, String) - case botPrivacy(PresentationTheme, String) - case botReport(PresentationTheme, String) - case block(PresentationTheme, String, DestructiveUserInfoAction) - - var section: ItemListSectionId { - switch self { - case .info, .calls, .about, .phoneNumber, .requestPhoneNumber, .userName: - return UserInfoSection.info.rawValue - case .sendMessage, .addContact, .shareContact, .shareMyContact, .startSecretChat, .botAddToGroup, .botShare: - return UserInfoSection.actions.rawValue - case .botSettings, .botHelp, .botPrivacy: - return UserInfoSection.bot.rawValue - case .sharedMedia, .notifications, .groupsInCommon, .secretEncryptionKey: - return UserInfoSection.sharedMediaAndNotifications.rawValue - case .botReport, .block: - return UserInfoSection.block.rawValue - } - } - - var stableId: Int { - return self.sortIndex - } - - static func ==(lhs: UserInfoEntry, rhs: UserInfoEntry) -> Bool { - switch lhs { - case let .info(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsPeer, lhsPresence, lhsCachedData, lhsState, lhsDisplayCall): - switch rhs { - case let .info(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsPeer, rhsPresence, rhsCachedData, rhsState, rhsDisplayCall): - if lhsTheme !== rhsTheme { - return false - } - if lhsStrings !== rhsStrings { - return false - } - if lhsDateTimeFormat != rhsDateTimeFormat { - return false - } - if let lhsPeer = lhsPeer, let rhsPeer = rhsPeer { - if !lhsPeer.isEqual(rhsPeer) { - return false - } - } else if (lhsPeer != nil) != (rhsPeer != nil) { - return false - } - if let lhsPresence = lhsPresence, let rhsPresence = rhsPresence { - if !lhsPresence.isEqual(to: rhsPresence) { - return false - } - } else if (lhsPresence != nil) != (rhsPresence != nil) { - return false - } - if let lhsCachedData = lhsCachedData, let rhsCachedData = rhsCachedData { - if !lhsCachedData.isEqual(to: rhsCachedData) { - return false - } - } else if (lhsCachedData != nil) != (rhsCachedData != nil) { - return false - } - if lhsState != rhsState { - return false - } - if lhsDisplayCall != rhsDisplayCall { - return false - } - return true - default: - return false - } - case let .calls(lhsTheme, lhsStrings, lhsDateTimeFormat, lhsMessages): - if case let .calls(rhsTheme, rhsStrings, rhsDateTimeFormat, rhsMessages) = rhs, lhsTheme === rhsTheme, lhsStrings === rhsStrings, lhsDateTimeFormat == rhsDateTimeFormat { - if lhsMessages.count != rhsMessages.count { - return false - } - for i in 0 ..< lhsMessages.count { - if !areMessagesEqual(lhsMessages[i], rhsMessages[i]) { - return false - } - } - return true - } else { - return false - } - case let .about(lhsTheme, lhsPeer, lhsText, lhsValue): - if case let .about(rhsTheme, rhsPeer, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsPeer.isEqual(rhsPeer), lhsText == rhsText, lhsValue == rhsValue { - return true - } else { - return false - } - case let .phoneNumber(lhsTheme, lhsIndex, lhsLabel, lhsValue, lhsMain): - if case let .phoneNumber(rhsTheme, rhsIndex, rhsLabel, rhsValue, rhsMain) = rhs, lhsTheme === rhsTheme, lhsIndex == rhsIndex, lhsLabel == rhsLabel, lhsValue == rhsValue, lhsMain == rhsMain { - return true - } else { - return false - } - case let .requestPhoneNumber(lhsTheme, lhsLabel, lhsValue): - if case let .requestPhoneNumber(rhsTheme, rhsLabel, rhsValue) = rhs, lhsTheme === rhsTheme, lhsLabel == rhsLabel, lhsValue == rhsValue { - return true - } else { - return false - } - case let .userName(lhsTheme, lhsText, lhsValue): - if case let .userName(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { - return true - } else { - return false - } - case let .sendMessage(lhsTheme, lhsText): - if case let .sendMessage(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .addContact(lhsTheme, lhsText): - if case let .addContact(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .shareContact(lhsTheme, lhsText): - if case let .shareContact(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .shareMyContact(lhsTheme, lhsText): - if case let .shareMyContact(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .startSecretChat(lhsTheme, lhsText): - if case let .startSecretChat(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .sharedMedia(lhsTheme, lhsText): - if case let .sharedMedia(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .notifications(lhsTheme, lhsText, lhsValue): - if case let .notifications(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { - return true - } else { - return false - } - case let .groupsInCommon(lhsTheme, lhsText, lhsValue): - if case let .groupsInCommon(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { - return true - } else { - return false - } - case let .secretEncryptionKey(lhsTheme, lhsText, lhsValue): - if case let .secretEncryptionKey(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue { - return true - } else { - return false - } - case let .botAddToGroup(lhsTheme, lhsText): - if case let .botAddToGroup(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .botShare(lhsTheme, lhsText): - if case let .botShare(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .botSettings(lhsTheme, lhsText): - if case let .botSettings(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .botHelp(lhsTheme, lhsText): - if case let .botHelp(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .botPrivacy(lhsTheme, lhsText): - if case let .botPrivacy(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .botReport(lhsTheme, lhsText): - if case let .botReport(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText { - return true - } else { - return false - } - case let .block(lhsTheme, lhsText, lhsAction): - if case let .block(rhsTheme, rhsText, rhsAction) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsAction == rhsAction { - return true - } else { - return false - } - } - } - - private var sortIndex: Int { - switch self { - case .info: - return 0 - case .calls: - return 1 - case let .phoneNumber(_, index, _, _, _): - return 2 + index - case .requestPhoneNumber: - return 998 - case .about: - return 999 - case .userName: - return 1000 - case .sendMessage: - return 1001 - case .addContact: - return 1002 - case .shareContact: - return 1003 - case .shareMyContact: - return 1004 - case .startSecretChat: - return 1005 - case .botAddToGroup: - return 1006 - case .botShare: - return 1007 - case .botSettings: - return 1008 - case .botHelp: - return 1009 - case .botPrivacy: - return 1010 - case .sharedMedia: - return 1011 - case .notifications: - return 1012 - case .secretEncryptionKey: - return 1014 - case .groupsInCommon: - return 1015 - case .botReport: - return 1016 - case .block: - return 1017 - } - } - - static func <(lhs: UserInfoEntry, rhs: UserInfoEntry) -> Bool { - return lhs.sortIndex < rhs.sortIndex - } - - func item(presentationData: ItemListPresentationData, arguments: Any) -> ListViewItem { - let arguments = arguments as! UserInfoControllerArguments - switch self { - case let .info(theme, strings, dateTimeFormat, peer, presence, cachedData, state, displayCall): - return ItemListAvatarAndNameInfoItem(accountContext: arguments.context, presentationData: presentationData, dateTimeFormat: dateTimeFormat, mode: .generic, peer: peer, presence: presence, cachedData: cachedData, state: state, sectionId: self.section, style: .plain, editingNameUpdated: { editingName in - arguments.updateEditingName(editingName) - }, avatarTapped: { - arguments.tapAvatarAction() - }, context: arguments.avatarAndNameInfoContext, call: displayCall ? { - arguments.call() - } : nil) - case let .calls(theme, strings, dateTimeFormat, messages): - return ItemListCallListItem(presentationData: presentationData, dateTimeFormat: dateTimeFormat, messages: messages, sectionId: self.section, style: .plain) - case let .about(theme, peer, text, value): - var enabledEntityTypes: EnabledEntityTypes = [] - if let peer = peer as? TelegramUser, let _ = peer.botInfo { - enabledEntityTypes = [.allUrl, .mention, .hashtag] - } - return ItemListTextWithLabelItem(presentationData: presentationData, label: text, text: foldMultipleLineBreaks(value), enabledEntityTypes: enabledEntityTypes, multiline: true, sectionId: self.section, action: nil, longTapAction: { - arguments.displayAboutContextMenu(value) - }, linkItemAction: { action, itemLink in - arguments.aboutLinkAction(action, itemLink) - }, tag: UserInfoEntryTag.about) - case let .phoneNumber(theme, _, label, value, isMain): - return ItemListTextWithLabelItem(presentationData: presentationData, label: label, text: value, textColor: isMain ? .highlighted : .accent, enabledEntityTypes: [], multiline: false, sectionId: self.section, action: { - arguments.openCallMenu(value) - }, longTapAction: { - arguments.displayCopyContextMenu(.phoneNumber, value) - }, tag: UserInfoEntryTag.phoneNumber) - case let .requestPhoneNumber(theme, label, value): - return ItemListTextWithLabelItem(presentationData: presentationData, label: label, text: value, textColor: .accent, enabledEntityTypes: [], multiline: false, sectionId: self.section, action: { - arguments.requestPhoneNumber() - }) - case let .userName(theme, text, value): - return ItemListTextWithLabelItem(presentationData: presentationData, label: text, text: "@\(value)", textColor: .accent, enabledEntityTypes: [], multiline: false, sectionId: self.section, action: { - arguments.displayUsernameContextMenu("@\(value)") - }, longTapAction: { - arguments.displayCopyContextMenu(.username, "@\(value)") - }, tag: UserInfoEntryTag.username) - case let .sendMessage(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.openChat() - }) - case let .addContact(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.addContact() - }) - case let .shareContact(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.shareContact() - }) - case let .shareMyContact(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.shareMyContact() - }) - case let .startSecretChat(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.startSecretChat() - }) - case let .sharedMedia(theme, text): - return ItemListDisclosureItem(presentationData: presentationData, title: text, label: "", sectionId: self.section, style: .plain, action: { - arguments.openSharedMedia() - }) - case let .notifications(theme, text, value): - return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .plain, action: { - arguments.changeNotificationMuteSettings() - }) - case let .groupsInCommon(theme, text, value): - return ItemListDisclosureItem(presentationData: presentationData, title: text, label: value, sectionId: self.section, style: .plain, action: { - arguments.openGroupsInCommon() - }) - case let .secretEncryptionKey(theme, text, fingerprint): - return ItemListSecretChatKeyItem(presentationData: presentationData, title: text, fingerprint: fingerprint, sectionId: self.section, style: .plain, action: { - arguments.openEncryptionKey(fingerprint) - }) - case let .botAddToGroup(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.addBotToGroup() - }) - case let .botShare(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.shareBot() - }) - case let .botSettings(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.botSettings() - }) - case let .botHelp(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.botHelp() - }) - case let .botPrivacy(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.botPrivacy() - }) - case let .botReport(theme, text): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .plain, action: { - arguments.report() - }) - case let .block(theme, text, action): - return ItemListActionItem(presentationData: presentationData, title: text, kind: .destructive, alignment: .natural, sectionId: self.section, style: .plain, action: { - switch action { - case .block: - arguments.updatePeerBlocked(true) - case .unblock: - arguments.updatePeerBlocked(false) - case .removeContact: - arguments.deleteContact() - } - }) - } - } -} - -private enum DestructiveUserInfoAction { - case block - case removeContact - case unblock -} - -private struct UserInfoEditingState: Equatable { - let editingName: ItemListAvatarAndNameInfoItemName? - - static func ==(lhs: UserInfoEditingState, rhs: UserInfoEditingState) -> Bool { - if lhs.editingName != rhs.editingName { - return false - } - return true - } -} - -private struct UserInfoState: Equatable { - let savingData: Bool - let editingState: UserInfoEditingState? - - init() { - self.savingData = false - self.editingState = nil - } - - init(savingData: Bool, editingState: UserInfoEditingState?) { - self.savingData = savingData - self.editingState = editingState - } - - static func ==(lhs: UserInfoState, rhs: UserInfoState) -> Bool { - if lhs.savingData != rhs.savingData { - return false - } - if lhs.editingState != rhs.editingState { - return false - } - return true - } - - func withUpdatedSavingData(_ savingData: Bool) -> UserInfoState { - return UserInfoState(savingData: savingData, editingState: self.editingState) - } - - func withUpdatedEditingState(_ editingState: UserInfoEditingState?) -> UserInfoState { - return UserInfoState(savingData: self.savingData, editingState: editingState) - } -} - -private func stringForBlockAction(strings: PresentationStrings, action: DestructiveUserInfoAction, peer: Peer) -> String { - switch action { - case .block: - if let user = peer as? TelegramUser, user.botInfo != nil { - return strings.Bot_Stop - } else { - return strings.Conversation_BlockUser - } - case .unblock: - if let user = peer as? TelegramUser, user.botInfo != nil { - return strings.Bot_Unblock - } else { - return strings.Conversation_UnblockUser - } - case .removeContact: - return strings.UserInfo_DeleteContact - } -} - -private func userInfoEntries(account: Account, presentationData: PresentationData, view: PeerView, cachedPeerData: CachedPeerData?, deviceContacts: [(DeviceContactStableId, DeviceContactBasicData)], mode: PeerInfoControllerMode, state: UserInfoState, peerChatState: PostboxCoding?, globalNotificationSettings: GlobalNotificationSettings) -> [UserInfoEntry] { - var entries: [UserInfoEntry] = [] - - guard let peer = view.peers[view.peerId], let user = peerViewMainPeer(view) as? TelegramUser else { - return [] - } - - var editingName: ItemListAvatarAndNameInfoItemName? - - var isEditing = false - if let editingState = state.editingState { - isEditing = true - - if view.peerIsContact { - editingName = editingState.editingName - } - } - - var callsAvailable = true - if let cachedUserData = cachedPeerData as? CachedUserData { - callsAvailable = cachedUserData.voiceCallsAvailable - } - - entries.append(UserInfoEntry.info(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer: user, presence: view.peerPresences[user.id], cachedData: cachedPeerData, state: ItemListAvatarAndNameInfoItemState(editingName: editingName, updatingName: nil), displayCall: user.botInfo == nil && callsAvailable)) - - if case let .calls(messages) = mode, !isEditing { - entries.append(UserInfoEntry.calls(presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, messages: messages)) - } - - if let phoneNumber = user.phone, !phoneNumber.isEmpty { - let formattedNumber = formatPhoneNumber(phoneNumber) - let normalizedNumber = DeviceContactNormalizedPhoneNumber(rawValue: formattedNumber) - - var index = 0 - var found = false - - var existingNumbers = Set() - var phoneNumbers: [(String, DeviceContactNormalizedPhoneNumber, Bool)] = [] - - for (_, contact) in deviceContacts { - inner: for number in contact.phoneNumbers { - var isMain = false - let normalizedContactNumber = DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(number.value)) - if !existingNumbers.contains(normalizedContactNumber) { - existingNumbers.insert(normalizedContactNumber) - } else { - continue inner - } - if normalizedContactNumber == normalizedNumber { - found = true - isMain = true - } - - phoneNumbers.append((number.label, normalizedContactNumber, isMain)) - } - } - if !found { - entries.append(UserInfoEntry.phoneNumber(presentationData.theme, index, presentationData.strings.ContactInfo_PhoneLabelMobile, formattedNumber, false)) - index += 1 - } else { - for (label, number, isMain) in phoneNumbers { - entries.append(UserInfoEntry.phoneNumber(presentationData.theme, index, localizedPhoneNumberLabel(label: label, strings: presentationData.strings), number.rawValue, isMain && phoneNumbers.count != 1)) - index += 1 - } - } - } else { - //entries.append(UserInfoEntry.requestPhoneNumber(presentationData.theme, "phone", "Request Number")) - } - - let aboutTitle: String - if let _ = user.botInfo { - aboutTitle = presentationData.strings.Profile_BotInfo - } else { - aboutTitle = presentationData.strings.Profile_About - } - if user.isFake { - let aboutValue: String - if let _ = user.botInfo { - aboutValue = presentationData.strings.UserInfo_FakeBotWarning - } else { - aboutValue = presentationData.strings.UserInfo_FakeUserWarning - } - entries.append(UserInfoEntry.about(presentationData.theme, peer, aboutTitle, aboutValue)) - } else if user.isScam { - let aboutValue: String - if let _ = user.botInfo { - aboutValue = presentationData.strings.UserInfo_ScamBotWarning - } else { - aboutValue = presentationData.strings.UserInfo_ScamUserWarning - } - entries.append(UserInfoEntry.about(presentationData.theme, peer, aboutTitle, aboutValue)) - } else if let cachedUserData = cachedPeerData as? CachedUserData, let about = cachedUserData.about, !about.isEmpty { - entries.append(UserInfoEntry.about(presentationData.theme, peer, aboutTitle, about)) - } - - if !isEditing { - if let username = user.username, !username.isEmpty { - entries.append(UserInfoEntry.userName(presentationData.theme, presentationData.strings.Profile_Username, username)) - } - - if !(peer is TelegramSecretChat) { - entries.append(UserInfoEntry.sendMessage(presentationData.theme, presentationData.strings.UserInfo_SendMessage)) - } - - if user.botInfo == nil { - if view.peerIsContact { - if let phone = user.phone, !phone.isEmpty { - entries.append(UserInfoEntry.shareContact(presentationData.theme, presentationData.strings.UserInfo_ShareContact)) - } - } else { - entries.append(UserInfoEntry.addContact(presentationData.theme, presentationData.strings.Conversation_AddToContacts)) - } - } - - if let cachedUserData = cachedPeerData as? CachedUserData, let peerStatusSettings = cachedUserData.peerStatusSettings, peerStatusSettings.contains(.canShareContact) { - entries.append(UserInfoEntry.shareMyContact(presentationData.theme, presentationData.strings.UserInfo_ShareMyContactInfo)) - } - - if let peer = peer as? TelegramUser, peer.botInfo == nil { - entries.append(UserInfoEntry.startSecretChat(presentationData.theme, presentationData.strings.UserInfo_StartSecretChat)) - } - - if let peer = peer as? TelegramUser, let botInfo = peer.botInfo { - if botInfo.flags.contains(.worksWithGroups) { - entries.append(UserInfoEntry.botAddToGroup(presentationData.theme, presentationData.strings.UserInfo_InviteBotToGroup)) - } - entries.append(UserInfoEntry.botShare(presentationData.theme, presentationData.strings.UserInfo_ShareBot)) - - if let cachedUserData = cachedPeerData as? CachedUserData, let botInfo = cachedUserData.botInfo { - for command in botInfo.commands { - if command.text == "settings" { - entries.append(UserInfoEntry.botSettings(presentationData.theme, presentationData.strings.UserInfo_BotSettings)) - } else if command.text == "help" { - entries.append(UserInfoEntry.botHelp(presentationData.theme, presentationData.strings.UserInfo_BotHelp)) - } else if command.text == "privacy" { - entries.append(UserInfoEntry.botPrivacy(presentationData.theme, presentationData.strings.UserInfo_BotPrivacy)) - } - } - } - } - - entries.append(UserInfoEntry.sharedMedia(presentationData.theme, presentationData.strings.GroupInfo_SharedMedia)) - } - let notificationsLabel: String - let notificationSettings = view.notificationSettings as? TelegramPeerNotificationSettings ?? TelegramPeerNotificationSettings.defaultSettings - if case let .muted(until) = notificationSettings.muteState, until >= Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970) { - if until < Int32.max - 1 { - notificationsLabel = stringForRemainingMuteInterval(strings: presentationData.strings, muteInterval: until) - } else { - notificationsLabel = presentationData.strings.UserInfo_NotificationsDisabled - } - } else if case .default = notificationSettings.messageSound { - notificationsLabel = presentationData.strings.UserInfo_NotificationsEnabled - } else { - notificationsLabel = localizedPeerNotificationSoundString(strings: presentationData.strings, sound: notificationSettings.messageSound, default: globalNotificationSettings.effective.channels.sound) - } - entries.append(UserInfoEntry.notifications(presentationData.theme, presentationData.strings.GroupInfo_Notifications, notificationsLabel)) - - if isEditing { - if view.peerIsContact { - entries.append(UserInfoEntry.block(presentationData.theme, stringForBlockAction(strings: presentationData.strings, action: .removeContact, peer: user), .removeContact)) - } - } else { - if peer is TelegramSecretChat, let peerChatState = peerChatState as? SecretChatKeyState, let keyFingerprint = peerChatState.keyFingerprint { - entries.append(UserInfoEntry.secretEncryptionKey(presentationData.theme, presentationData.strings.Profile_EncryptionKey, keyFingerprint)) - } - - if let groupsInCommon = (cachedPeerData as? CachedUserData)?.commonGroupCount, groupsInCommon != 0 { - entries.append(UserInfoEntry.groupsInCommon(presentationData.theme, presentationData.strings.UserInfo_GroupsInCommon, presentationStringsFormattedNumber(groupsInCommon, presentationData.dateTimeFormat.groupingSeparator))) - } - - if let peer = peer as? TelegramUser, let _ = peer.botInfo { - entries.append(UserInfoEntry.botReport(presentationData.theme, presentationData.strings.ReportPeer_Report)) - } - - if let cachedData = cachedPeerData as? CachedUserData { - if cachedData.isBlocked { - entries.append(UserInfoEntry.block(presentationData.theme, stringForBlockAction(strings: presentationData.strings, action: .unblock, peer: user), .unblock)) - } else { - if let peer = peer as? TelegramUser, peer.flags.contains(.isSupport) { - } else { - entries.append(UserInfoEntry.block(presentationData.theme, stringForBlockAction(strings: presentationData.strings, action: .block, peer: user), .block)) - } - } - } - } - - return entries -} - private func getUserPeer(postbox: Postbox, peerId: PeerId) -> Signal<(Peer?, CachedPeerData?), NoError> { return postbox.transaction { transaction -> (Peer?, CachedPeerData?) in guard let peer = transaction.getPeer(peerId) else { @@ -805,803 +67,3 @@ public func openAddPersonContactImpl(context: AccountContext, peerId: PeerId, pu }), completed: nil, cancelled: nil)) }) } - -public func userInfoController(context: AccountContext, peerId: PeerId, mode: PeerInfoControllerMode = .generic) -> ViewController { - let statePromise = ValuePromise(UserInfoState(), ignoreRepeated: true) - let stateValue = Atomic(value: UserInfoState()) - let updateState: ((UserInfoState) -> UserInfoState) -> Void = { f in - statePromise.set(stateValue.modify { f($0) }) - } - - var pushControllerImpl: ((ViewController) -> Void)? - var presentControllerImpl: ((ViewController, Any?) -> Void)? - var openChatImpl: (() -> Void)? - var shareContactImpl: (() -> Void)? - var shareMyContactImpl: (() -> Void)? - var startSecretChatImpl: (() -> Void)? - var botAddToGroupImpl: (() -> Void)? - var shareBotImpl: (() -> Void)? - var dismissInputImpl: (() -> Void)? - var dismissImpl: (() -> Void)? - - let actionsDisposable = DisposableSet() - - let updatePeerNameDisposable = MetaDisposable() - actionsDisposable.add(updatePeerNameDisposable) - - let updatePeerBlockedDisposable = MetaDisposable() - actionsDisposable.add(updatePeerBlockedDisposable) - - let changeMuteSettingsDisposable = MetaDisposable() - actionsDisposable.add(changeMuteSettingsDisposable) - - let hiddenAvatarRepresentationDisposable = MetaDisposable() - actionsDisposable.add(hiddenAvatarRepresentationDisposable) - - let createSecretChatDisposable = MetaDisposable() - actionsDisposable.add(createSecretChatDisposable) - - let navigateDisposable = MetaDisposable() - actionsDisposable.add(navigateDisposable) - - var avatarGalleryTransitionArguments: ((AvatarGalleryEntry) -> GalleryTransitionArguments?)? - let avatarAndNameInfoContext = ItemListAvatarAndNameInfoItemContext() - var updateHiddenAvatarImpl: (() -> Void)? - - var aboutLinkActionImpl: ((TextLinkItemActionType, TextLinkItem) -> Void)? - var displayAboutContextMenuImpl: ((String) -> Void)? - var displayCopyContextMenuImpl: ((UserInfoEntryTag, String) -> Void)? - var popToRootImpl: (() -> Void)? - - let cachedAvatarEntries = Atomic?>(value: nil) - - let peerView = Promise<(PeerView, CachedPeerData?)>() - peerView.set(context.account.viewTracker.peerView(peerId, updateData: true) |> mapToSignal({ view -> Signal<(PeerView, CachedPeerData?), NoError> in - if peerId.namespace == Namespaces.Peer.SecretChat { - if let peer = peerViewMainPeer(view) { - return context.account.viewTracker.peerView(peer.id) |> map({ secretChatView -> (PeerView, CachedPeerData?) in - return (view, secretChatView.cachedData) - }) - } - } - return .single((view, view.cachedData)) - })) - - let requestCallImpl: (Bool) -> Void = { isVideo in - let _ = (peerView.get() - |> take(1) - |> deliverOnMainQueue).start(next: { view in - guard let peer = peerViewMainPeer(view.0) else { - return - } - - if let cachedUserData = view.1 as? CachedUserData, cachedUserData.callsPrivate { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - presentControllerImpl?(textAlertController(context: context, title: presentationData.strings.Call_ConnectionErrorTitle, text: presentationData.strings.Call_PrivacyErrorMessage(peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), nil) - return - } - - context.requestCall(peerId: peer.id, isVideo: isVideo, completion: {}) - }) - } - - let arguments = UserInfoControllerArguments(context: context, avatarAndNameInfoContext: avatarAndNameInfoContext, updateEditingName: { editingName in - updateState { state in - if let _ = state.editingState { - return state.withUpdatedEditingState(UserInfoEditingState(editingName: editingName)) - } else { - return state - } - } - }, tapAvatarAction: { - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) |> deliverOnMainQueue).start(next: { peer, _ in - guard let peer = peer else { - return - } - - if peer.profileImageRepresentations.isEmpty { - return - } - - let galleryController = AvatarGalleryController(context: context, peer: peer, remoteEntries: cachedAvatarEntries.with { $0 }, replaceRootController: { controller, ready in - }) - hiddenAvatarRepresentationDisposable.set((galleryController.hiddenMedia |> deliverOnMainQueue).start(next: { entry in - avatarAndNameInfoContext.hiddenAvatarRepresentation = entry?.representations.first?.representation - updateHiddenAvatarImpl?() - })) - presentControllerImpl?(galleryController, AvatarGalleryControllerPresentationArguments(transitionArguments: { entry in - return avatarGalleryTransitionArguments?(entry) - })) - }) - }, openChat: { - openChatImpl?() - }, addContact: { - openAddPersonContactImpl(context: context, peerId: peerId, pushController: { c in - pushControllerImpl?(c) - }, present: { c, a in - presentControllerImpl?(c, a) - }) - }, shareContact: { - shareContactImpl?() - }, shareMyContact: { - shareMyContactImpl?() - }, startSecretChat: { - startSecretChatImpl?() - }, changeNotificationMuteSettings: { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let _ = (context.account.postbox.transaction { transaction -> (TelegramPeerNotificationSettings, GlobalNotificationSettings) in - let peerSettings: TelegramPeerNotificationSettings = (transaction.getPeerNotificationSettings(peerId) as? TelegramPeerNotificationSettings) ?? TelegramPeerNotificationSettings.defaultSettings - let globalSettings: GlobalNotificationSettings = (transaction.getPreferencesEntry(key: PreferencesKeys.globalNotifications) as? GlobalNotificationSettings) ?? GlobalNotificationSettings.defaultSettings - return (peerSettings, globalSettings) - } - |> deliverOnMainQueue).start(next: { peerSettings, globalSettings in - let soundSettings: NotificationSoundSettings? - if case .default = peerSettings.messageSound { - soundSettings = NotificationSoundSettings(value: nil) - } else { - soundSettings = NotificationSoundSettings(value: peerSettings.messageSound) - } - let controller = notificationMuteSettingsController(presentationData: presentationData, notificationSettings: globalSettings.effective.groupChats, soundSettings: soundSettings, openSoundSettings: { - let controller = notificationSoundSelectionController(context: context, isModal: true, currentSound: peerSettings.messageSound, defaultSound: globalSettings.effective.groupChats.sound, completion: { sound in - let _ = updatePeerNotificationSoundInteractive(account: context.account, peerId: peerId, sound: sound).start() - }) - presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }, updateSettings: { value in - changeMuteSettingsDisposable.set(updatePeerMuteSetting(account: context.account, peerId: peerId, muteInterval: value).start()) - }) - presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }) - }, openSharedMedia: { - if let controller = context.sharedContext.makePeerSharedMediaController(context: context, peerId: peerId) { - pushControllerImpl?(controller) - } - }, openGroupsInCommon: { - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) - |> take(1) - |> deliverOnMainQueue).start(next: { peer, _ in - guard let peer = peer else { - return - } - - pushControllerImpl?(groupsInCommonController(context: context, peerId: peer.id)) - }) - }, updatePeerBlocked: { value in - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) - |> take(1) - |> deliverOnMainQueue).start(next: { peer, _ in - guard let peer = peer else { - return - } - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - if let peer = peer as? TelegramUser, let _ = peer.botInfo { - updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: context.account, peerId: peer.id, isBlocked: value).start()) - if !value { - let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start() - openChatImpl?() - } - } else { - if value { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = ActionSheetController(presentationData: presentationData) - let dismissAction: () -> Void = { [weak controller] in - controller?.dismissAnimated() - } - var reportSpam = false - var deleteChat = false - controller.setItemGroups([ - ActionSheetItemGroup(items: [ - ActionSheetTextItem(title: presentationData.strings.UserInfo_BlockConfirmationTitle(peer.compactDisplayTitle).0), - /*ActionSheetCheckboxItem(title: presentationData.strings.Conversation_Moderate_Report, label: "", value: reportSpam, action: { [weak controller] checkValue in - reportSpam = checkValue - controller?.updateItem(groupIndex: 0, itemIndex: 1, { item in - if let item = item as? ActionSheetCheckboxItem { - return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action) - } - return item - }) - }), - ActionSheetCheckboxItem(title: presentationData.strings.ReportSpam_DeleteThisChat, label: "", value: deleteChat, action: { [weak controller] checkValue in - deleteChat = checkValue - controller?.updateItem(groupIndex: 0, itemIndex: 2, { item in - if let item = item as? ActionSheetCheckboxItem { - return ActionSheetCheckboxItem(title: item.title, label: item.label, value: !item.value, action: item.action) - } - return item - }) - }),*/ - ActionSheetButtonItem(title: presentationData.strings.UserInfo_BlockActionTitle(peer.compactDisplayTitle).0, color: .destructive, action: { - dismissAction() - updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: context.account, peerId: peer.id, isBlocked: true).start()) - if deleteChat { - let _ = removePeerChat(account: context.account, peerId: peerId, reportChatSpam: reportSpam).start() - popToRootImpl?() - } else if reportSpam { - let _ = reportPeer(account: context.account, peerId: peerId, reason: .spam, message: "").start() - } - - deleteSendMessageIntents(peerId: peerId) - }) - ]), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) - ]) - presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - } else { - let text: String - if value { - text = presentationData.strings.UserInfo_BlockConfirmation(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).0 - } else { - text = presentationData.strings.UserInfo_UnblockConfirmation(peer.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder)).0 - } - presentControllerImpl?(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_No, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Yes, action: { - updatePeerBlockedDisposable.set(requestUpdatePeerIsBlocked(account: context.account, peerId: peer.id, isBlocked: value).start()) - })]), nil) - } - } - }) - }, deleteContact: { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = ActionSheetController(presentationData: presentationData) - let dismissAction: () -> Void = { [weak controller] in - controller?.dismissAnimated() - } - controller.setItemGroups([ - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: presentationData.strings.UserInfo_DeleteContact, color: .destructive, action: { - dismissAction() - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) - |> deliverOnMainQueue).start(next: { peer, _ in - guard let peer = peer else { - return - } - let deleteContactFromDevice: Signal - if let contactDataManager = context.sharedContext.contactDataManager { - deleteContactFromDevice = contactDataManager.deleteContactWithAppSpecificReference(peerId: peer.id) - } else { - deleteContactFromDevice = .complete() - } - - var deleteSignal = deleteContactPeerInteractively(account: context.account, peerId: peer.id) - |> then(deleteContactFromDevice) - - let progressSignal = Signal { subscriber in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil)) - presentControllerImpl?(controller, nil) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - deleteSignal = deleteSignal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - - updatePeerBlockedDisposable.set((deleteSignal - |> deliverOnMainQueue).start(completed: { - dismissImpl?() - })) - - deleteSendMessageIntents(peerId: peerId) - }) - }) - ]), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) - ]) - presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - }, displayUsernameContextMenu: { text in - let shareController = ShareController(context: context, subject: .url("\(text)")) - presentControllerImpl?(shareController, nil) - }, displayCopyContextMenu: { tag, phone in - displayCopyContextMenuImpl?(tag, phone) - }, call: { - requestCallImpl(false) - }, openCallMenu: { number in - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) - |> deliverOnMainQueue).start(next: { peer, _ in - if let peer = peer as? TelegramUser, let peerPhoneNumber = peer.phone, formatPhoneNumber(number) == formatPhoneNumber(peerPhoneNumber) { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = ActionSheetController(presentationData: presentationData) - let dismissAction: () -> Void = { [weak controller] in - controller?.dismissAnimated() - } - controller.setItemGroups([ - ActionSheetItemGroup(items: [ - ActionSheetButtonItem(title: presentationData.strings.UserInfo_TelegramCall, action: { - dismissAction() - requestCallImpl(false) - }), - ActionSheetButtonItem(title: presentationData.strings.UserInfo_PhoneCall, action: { - dismissAction() - context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") - }), - ]), - ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })]) - ]) - presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) - } else { - context.sharedContext.applicationBindings.openUrl("tel:\(formatPhoneNumber(number).replacingOccurrences(of: " ", with: ""))") - } - }) - }, requestPhoneNumber: { - let _ = (requestPhoneNumber(account: context.account, peerId: peerId) - |> deliverOnMainQueue).start(completed: { - - }) - }, aboutLinkAction: { action, itemLink in - aboutLinkActionImpl?(action, itemLink) - }, displayAboutContextMenu: { text in - displayAboutContextMenuImpl?(text) - }, openEncryptionKey: { fingerprint in - let _ = (context.account.postbox.transaction { transaction -> Peer? in - if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { - if let userPeer = transaction.getPeer(peer.regularPeerId) { - return userPeer - } - } - return nil - } |> deliverOnMainQueue).start(next: { peer in - if let peer = peer { - pushControllerImpl?(SecretChatKeyController(context: context, fingerprint: fingerprint, peer: peer)) - } - }) - }, addBotToGroup: { - botAddToGroupImpl?() - }, shareBot: { - shareBotImpl?() - }, botSettings: { - let _ = (context.account.postbox.loadedPeerWithId(peerId) - |> deliverOnMainQueue).start(next: { peer in - let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/settings", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start() - openChatImpl?() - }) - }, botHelp: { - let _ = (context.account.postbox.loadedPeerWithId(peerId) - |> deliverOnMainQueue).start(next: { peer in - let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/help", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start() - openChatImpl?() - }) - }, botPrivacy: { - let _ = (context.account.postbox.loadedPeerWithId(peerId) - |> deliverOnMainQueue).start(next: { peer in - let _ = enqueueMessages(account: context.account, peerId: peer.id, messages: [.message(text: "/privacy", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start() - openChatImpl?() - }) - }, report: { - presentControllerImpl?(peerReportOptionsController(context: context, subject: .peer(peerId), passthrough: false, present: { c, a in - presentControllerImpl?(c, a) - }, push: { c in - pushControllerImpl?(c) - }, completion: { _, _ in }), nil) - }) - - let deviceContacts: Signal<[(DeviceContactStableId, DeviceContactBasicData)], NoError> = peerView.get() - |> map { peerView -> String in - if let peer = peerView.0.peers[peerId] as? TelegramUser { - return peer.phone ?? "" - } - return "" - } - |> distinctUntilChanged - |> mapToSignal { number -> Signal<[(DeviceContactStableId, DeviceContactBasicData)], NoError> in - if number.isEmpty { - return .single([]) - } else { - return context.sharedContext.contactDataManager?.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(number))) ?? .single([]) - } - } - - let globalNotificationsKey: PostboxViewKey = .preferences(keys: Set([PreferencesKeys.globalNotifications])) - let signal = combineLatest(context.sharedContext.presentationData, statePromise.get(), peerView.get(), deviceContacts, context.account.postbox.combinedView(keys: [.peerChatState(peerId: peerId), globalNotificationsKey])) - |> map { presentationData, state, view, deviceContacts, combinedView -> (ItemListControllerState, (ItemListNodeState, Any)) in - let peer = peerViewMainPeer(view.0) - - var globalNotificationSettings: GlobalNotificationSettings = .defaultSettings - if let preferencesView = combinedView.views[globalNotificationsKey] as? PreferencesView { - if let settings = preferencesView.values[PreferencesKeys.globalNotifications] as? GlobalNotificationSettings { - globalNotificationSettings = settings - } - } - - if let peer = peer { - let _ = cachedAvatarEntries.modify { value in - if value != nil { - return value - } else { - let promise = Promise<[AvatarGalleryEntry]>() - promise.set(fetchedAvatarGalleryEntries(account: context.account, peer: peer)) - return promise - } - } - } - var leftNavigationButton: ItemListNavigationButton? - let rightNavigationButton: ItemListNavigationButton - if let editingState = state.editingState { - leftNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Cancel), style: .regular, enabled: true, action: { - updateState { - $0.withUpdatedEditingState(nil) - } - }) - - var doneEnabled = true - if let editingName = editingState.editingName, editingName.isEmpty { - doneEnabled = false - } - - if state.savingData { - rightNavigationButton = ItemListNavigationButton(content: .none, style: .activity, enabled: doneEnabled, action: {}) - } else { - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Done), style: .bold, enabled: doneEnabled, action: { - var updateName: ItemListAvatarAndNameInfoItemName? - updateState { state in - if let editingState = state.editingState, let editingName = editingState.editingName { - if let user = peer { - if ItemListAvatarAndNameInfoItemName(user) != editingName { - updateName = editingName - } - } - } - if updateName != nil { - return state.withUpdatedSavingData(true) - } else { - return state.withUpdatedEditingState(nil) - } - } - - if let updateName = updateName, case let .personName(firstName, lastName, _) = updateName { - updatePeerNameDisposable.set((updateContactName(account: context.account, peerId: peerId, firstName: firstName, lastName: lastName) - |> deliverOnMainQueue).start(error: { _ in - updateState { state in - return state.withUpdatedSavingData(false) - } - }, completed: { - updateState { state in - return state.withUpdatedSavingData(false).withUpdatedEditingState(nil) - } - - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) - |> mapToSignal { peer, _ -> Signal in - guard let peer = peer as? TelegramUser, let phone = peer.phone, !phone.isEmpty else { - return .complete() - } - return (context.sharedContext.contactDataManager?.basicDataForNormalizedPhoneNumber(DeviceContactNormalizedPhoneNumber(rawValue: formatPhoneNumber(phone))) ?? .single([])) - |> take(1) - |> mapToSignal { records -> Signal in - var signals: [Signal] = [] - if let contactDataManager = context.sharedContext.contactDataManager { - for (id, basicData) in records { - signals.append(contactDataManager.appendContactData(DeviceContactExtendedData(basicData: DeviceContactBasicData(firstName: firstName, lastName: lastName, phoneNumbers: basicData.phoneNumbers), middleName: "", prefix: "", suffix: "", organization: "", jobTitle: "", department: "", emailAddresses: [], urls: [], addresses: [], birthdayDate: nil, socialProfiles: [], instantMessagingProfiles: [], note: ""), to: id)) - } - } - return combineLatest(signals) - |> mapToSignal { _ -> Signal in - return .complete() - } - } - }).start() - })) - } - }) - } - } else { - rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Common_Edit), style: .regular, enabled: true, action: { - if let user = peer { - updateState { state in - return state.withUpdatedEditingState(UserInfoEditingState(editingName: ItemListAvatarAndNameInfoItemName(user))) - } - } - }) - } - - let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.UserInfo_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: nil) - let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: userInfoEntries(account: context.account, presentationData: presentationData, view: view.0, cachedPeerData: view.1, deviceContacts: deviceContacts, mode: mode, state: state, peerChatState: (combinedView.views[.peerChatState(peerId: peerId)] as? PeerChatStateView)?.chatState, globalNotificationSettings: globalNotificationSettings), style: .plain) - - return (controllerState, (listState, arguments)) - } - |> afterDisposed { - actionsDisposable.dispose() - } - - let controller = ItemListController(context: context, state: signal) - - pushControllerImpl = { [weak controller] value in - (controller?.navigationController as? NavigationController)?.pushViewController(value) - } - dismissImpl = { [weak controller] in - guard let controller = controller else { - return - } - (controller.navigationController as? NavigationController)?.filterController(controller, animated: true) - } - presentControllerImpl = { [weak controller] value, presentationArguments in - controller?.present(value, in: .window(.root), with: presentationArguments, blockInteraction: true) - } - dismissInputImpl = { [weak controller] in - controller?.view.endEditing(true) - } - openChatImpl = { [weak controller] in - if let navigationController = (controller?.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) - } - } - shareContactImpl = { [weak controller] in - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) - |> deliverOnMainQueue).start(next: { peer, _ in - if let peer = peer as? TelegramUser, let phone = peer.phone { - let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil) - let shareController = ShareController(context: context, subject: .media(.standalone(media: contact))) - controller?.present(shareController, in: .window(.root)) - } - }) - } - shareMyContactImpl = { [weak controller] in - let _ = (getUserPeer(postbox: context.account.postbox, peerId: context.account.peerId) - |> deliverOnMainQueue).start(next: { peer, _ in - guard let peer = peer as? TelegramUser, let phone = peer.phone else { - return - } - let contact = TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil) - - let _ = (enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: contact), replyToMessageId: nil, localGroupingKey: nil)]) - |> deliverOnMainQueue).start(next: { [weak controller] _ in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - controller?.present(OverlayStatusController(theme: presentationData.theme, type: .success), in: .window(.root)) - }) - }) - } - startSecretChatImpl = { [weak controller] in - let _ = (context.account.postbox.transaction { transaction -> (Peer?, PeerId?) in - let peer = transaction.getPeer(peerId) - let filteredPeerIds = Array(transaction.getAssociatedPeerIds(peerId)).filter { $0.namespace == Namespaces.Peer.SecretChat } - var activeIndices: [ChatListIndex] = [] - for associatedId in filteredPeerIds { - if let state = (transaction.getPeer(associatedId) as? TelegramSecretChat)?.embeddedState { - switch state { - case .active, .handshake: - if let (_, index) = transaction.getPeerChatListIndex(associatedId) { - activeIndices.append(index) - } - default: - break - } - } - } - activeIndices.sort() - if let index = activeIndices.last { - return (peer, index.messageIndex.id.peerId) - } else { - return (peer, nil) - } - } |> deliverOnMainQueue).start(next: { peer, currentPeerId in - if let currentPeerId = currentPeerId { - if let navigationController = (controller?.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(currentPeerId))) - } - } else if let controller = controller { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let displayTitle = peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) ?? "" - controller.present(textAlertController(context: context, title: nil, text: presentationData.strings.UserInfo_StartSecretChatConfirmation(displayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}), TextAlertAction(type: .defaultAction, title: presentationData.strings.UserInfo_StartSecretChatStart, action: { - var createSignal = createSecretChat(account: context.account, peerId: peerId) - var cancelImpl: (() -> Void)? - let progressSignal = Signal { subscriber in - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - presentControllerImpl?(controller, nil) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.15, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - createSignal = createSignal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { - createSecretChatDisposable.set(nil) - } - - createSecretChatDisposable.set((createSignal |> deliverOnMainQueue).start(next: { [weak controller] peerId in - if let navigationController = (controller?.navigationController as? NavigationController) { - context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: context, chatLocation: .peer(peerId))) - } - }, error: { [weak controller] error in - if let controller = controller { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let text: String - switch error { - case .limitExceeded: - text = presentationData.strings.TwoStepAuth_FloodError - default: - text = presentationData.strings.Login_UnknownError - } - controller.present(textAlertController(context: context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root)) - } - })) - })]), in: .window(.root)) - } - }) - } - botAddToGroupImpl = { [weak controller] in - guard let controller = controller else { - return - } - context.sharedContext.openResolvedUrl(.groupBotStart(peerId: peerId, payload: ""), context: context, urlContext: .generic, navigationController: controller.navigationController as? NavigationController, openPeer: { id, navigation in - - }, sendFile: nil, - sendSticker: nil, - requestMessageActionUrlAuth: nil, - joinVoiceChat: nil, - present: { c, a in - presentControllerImpl?(c, a) - }, dismissInput: { - dismissInputImpl?() - }, contentContext: nil) - } - shareBotImpl = { [weak controller] in - let _ = (getUserPeer(postbox: context.account.postbox, peerId: peerId) - |> deliverOnMainQueue).start(next: { peer, _ in - if let peer = peer as? TelegramUser, let username = peer.username { - let shareController = ShareController(context: context, subject: .url("https://t.me/\(username)")) - controller?.present(shareController, in: .window(.root)) - } - }) - } - avatarGalleryTransitionArguments = { [weak controller] entry in - if let controller = controller { - var result: ((ASDisplayNode, CGRect, () -> (UIView?, UIView?)), CGRect)? - controller.forEachItemNode { itemNode in - if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode { - result = itemNode.avatarTransitionNode() - } - } - if let (node, _) = result { - return GalleryTransitionArguments(transitionNode: node, addToTransitionSurface: { _ in - }) - } - } - return nil - } - updateHiddenAvatarImpl = { [weak controller] in - if let controller = controller { - controller.forEachItemNode { itemNode in - if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode { - itemNode.updateAvatarHidden() - } - } - } - } - aboutLinkActionImpl = { [weak context, weak controller] action, itemLink in - if let controller = controller, let context = context { - context.sharedContext.handleTextLinkAction(context: context, peerId: peerId, navigateDisposable: navigateDisposable, controller: controller, action: action, itemLink: itemLink) - } - } - displayAboutContextMenuImpl = { [weak controller] text in - if let strongController = controller { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - var resultItemNode: ListViewItemNode? - let _ = strongController.frameForItemNode({ itemNode in - if let itemNode = itemNode as? ItemListTextWithLabelItemNode { - if let tag = itemNode.tag as? UserInfoEntryTag { - if tag == .about { - resultItemNode = itemNode - return true - } - } - } - return false - }) - if let resultItemNode = resultItemNode { - let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { - UIPasteboard.general.string = text - })]) - strongController.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in - if let strongController = controller, let resultItemNode = resultItemNode { - return (resultItemNode, resultItemNode.contentBounds.insetBy(dx: 0.0, dy: -2.0), strongController.displayNode, strongController.view.bounds) - } else { - return nil - } - })) - - } - } - } - - displayCopyContextMenuImpl = { [weak controller] tag, value in - if let strongController = controller { - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - var resultItemNode: ListViewItemNode? - let _ = strongController.frameForItemNode({ itemNode in - if let itemNode = itemNode as? ItemListTextWithLabelItemNode { - if let itemTag = itemNode.tag as? UserInfoEntryTag { - if itemTag == tag && itemNode.item?.text == value { - resultItemNode = itemNode - return true - } - } - } - return false - }) - if let resultItemNode = resultItemNode { - let contextMenuController = ContextMenuController(actions: [ContextMenuAction(content: .text(title: presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: presentationData.strings.Conversation_ContextMenuCopy), action: { - UIPasteboard.general.string = value - })]) - strongController.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in - if let strongController = controller, let resultItemNode = resultItemNode { - return (resultItemNode, resultItemNode.contentBounds.insetBy(dx: 0.0, dy: -2.0), strongController.displayNode, strongController.view.bounds) - } else { - return nil - } - })) - } - } - } - - popToRootImpl = { [weak controller] in - (controller?.navigationController as? NavigationController)?.popToRoot(animated: true) - } - - controller.didAppear = { [weak controller] firstTime in - guard let controller = controller, firstTime else { - return - } - - var resultItemNode: ItemListAvatarAndNameInfoItemNode? - let _ = controller.frameForItemNode({ itemNode in - if let itemNode = itemNode as? ItemListAvatarAndNameInfoItemNode { - resultItemNode = itemNode - return true - } - return false - }) - if let resultItemNode = resultItemNode, let callButtonFrame = resultItemNode.callButtonFrame { - let _ = (ApplicationSpecificNotice.getProfileCallTips(accountManager: context.sharedContext.accountManager) - |> deliverOnMainQueue).start(next: { [weak controller] counter in - guard let controller = controller else { - return - } - - var displayTip = false - if counter == 0 { - displayTip = true - } else if counter < 3 && arc4random_uniform(4) == 1 { - displayTip = true - } - if !displayTip { - return - } - let _ = ApplicationSpecificNotice.incrementProfileCallTips(accountManager: context.sharedContext.accountManager).start() - - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let text: String = presentationData.strings.UserInfo_TapToCall - - let tooltipController = TooltipController(content: .text(text), baseFontSize: presentationData.listsFontSize.baseDisplaySize, dismissByTapOutside: true) - controller.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { [weak resultItemNode] in - if let resultItemNode = resultItemNode { - return (resultItemNode, callButtonFrame) - } - return nil - })) - }) - } - } - - controller.navigationItem.backBarButtonItem = UIBarButtonItem(title: context.sharedContext.currentPresentationData.with{ $0 }.strings.Common_Back, style: .plain, target: nil, action: nil) - - return controller -} diff --git a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift index 4296059d10..ddaad650c8 100644 --- a/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift +++ b/submodules/PeersNearbyUI/Sources/PeersNearbyController.swift @@ -512,7 +512,7 @@ public func peersNearbyController(context: AccountContext) -> ViewController { cancelImpl = { checkCreationAvailabilityDisposable.set(nil) } - checkCreationAvailabilityDisposable.set((context.engine.peerNames.checkPublicChannelCreationAvailability(location: true) + checkCreationAvailabilityDisposable.set((context.engine.peers.checkPublicChannelCreationAvailability(location: true) |> afterDisposed { Queue.mainQueue().async { progressDisposable.dispose() diff --git a/submodules/Postbox/Sources/MediaBox.swift b/submodules/Postbox/Sources/MediaBox.swift index 97d8777d84..f0abfe8281 100644 --- a/submodules/Postbox/Sources/MediaBox.swift +++ b/submodules/Postbox/Sources/MediaBox.swift @@ -1110,15 +1110,17 @@ public final class MediaBox { } } - public func removeCachedResources(_ ids: Set) -> Signal { + public func removeCachedResources(_ ids: Set, force: Bool = false) -> Signal { return Signal { subscriber in self.dataQueue.async { for id in ids { - if self.fileContexts[id] != nil { - continue - } - if self.keepResourceContexts[id] != nil { - continue + if !force { + if self.fileContexts[id] != nil { + continue + } + if self.keepResourceContexts[id] != nil { + continue + } } let paths = self.storePathsForId(id.id) unlink(paths.complete) diff --git a/submodules/PresentationDataUtils/Sources/AlertTheme.swift b/submodules/PresentationDataUtils/Sources/AlertTheme.swift index a2d4ddb5a2..9fbfb15c0f 100644 --- a/submodules/PresentationDataUtils/Sources/AlertTheme.swift +++ b/submodules/PresentationDataUtils/Sources/AlertTheme.swift @@ -6,11 +6,15 @@ import SwiftSignalKit import TelegramPresentationData public func textAlertController(context: AccountContext, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController { - var presentationData = context.sharedContext.currentPresentationData.with { $0 } + return textAlertController(sharedContext: context.sharedContext, forceTheme: forceTheme, title: title, text: text, actions: actions, actionLayout: actionLayout, allowInputInset: allowInputInset, dismissOnOutsideTap: dismissOnOutsideTap) +} + +public func textAlertController(sharedContext: SharedAccountContext, forceTheme: PresentationTheme? = nil, title: String?, text: String, actions: [TextAlertAction], actionLayout: TextAlertContentActionLayout = .horizontal, allowInputInset: Bool = true, dismissOnOutsideTap: Bool = true) -> AlertController { + var presentationData = sharedContext.currentPresentationData.with { $0 } if let forceTheme = forceTheme { presentationData = presentationData.withUpdated(theme: forceTheme) } - return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: presentationData), themeSignal: context.sharedContext.presentationData |> map { + return textAlertController(alertContext: AlertControllerContext(theme: AlertControllerTheme(presentationData: presentationData), themeSignal: sharedContext.presentationData |> map { presentationData in var presentationData = presentationData if let forceTheme = forceTheme { diff --git a/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift b/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift index 7e72afbe0d..b9a2d40f90 100644 --- a/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift +++ b/submodules/SaveToCameraRoll/Sources/SaveToCameraRoll.swift @@ -96,7 +96,7 @@ public func saveToCameraRoll(context: AccountContext, postbox: Postbox, mediaRef return } - let tempVideoPath = NSTemporaryDirectory() + "\(arc4random64()).mp4" + let tempVideoPath = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max)).mp4" PHPhotoLibrary.shared().performChanges({ if isImage { if let fileData = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { diff --git a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift index 3e25abcabf..830ea6110b 100644 --- a/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift +++ b/submodules/SettingsUI/Sources/ChangePhoneNumberController.swift @@ -90,7 +90,7 @@ final class ChangePhoneNumberController: ViewController, MFMailComposeViewContro } } - loadServerCountryCodes(accountManager: self.context.sharedContext.accountManager, network: self.context.account.network, completion: { [weak self] in + loadServerCountryCodes(accountManager: self.context.sharedContext.accountManager, engine: self.context.engine, completion: { [weak self] in if let strongSelf = self { strongSelf.controllerNode.updateCountryCode() } diff --git a/submodules/SettingsUI/Sources/LogoutOptionsController.swift b/submodules/SettingsUI/Sources/LogoutOptionsController.swift index b898102e59..7a68f36c76 100644 --- a/submodules/SettingsUI/Sources/LogoutOptionsController.swift +++ b/submodules/SettingsUI/Sources/LogoutOptionsController.swift @@ -157,7 +157,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle dismissImpl?() }, contactSupport: { [weak navigationController] in let supportPeer = Promise() - supportPeer.set(context.engine.peerNames.supportPeerId()) + supportPeer.set(context.engine.peers.supportPeerId()) let presentationData = context.sharedContext.currentPresentationData.with { $0 } var faqUrl = presentationData.strings.Settings_FAQ_URL diff --git a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift index 427a7a4b75..0f79690bd9 100644 --- a/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift +++ b/submodules/SettingsUI/Sources/Search/SettingsSearchableItems.swift @@ -883,7 +883,7 @@ func settingsSearchableItems(context: AccountContext, notificationExceptionsList } let support = SettingsSearchableItem(id: .support(0), title: strings.Settings_Support, alternate: synonyms(strings.SettingsSearch_Synonyms_Support), icon: .support, breadcrumbs: [], present: { context, _, present in - let _ = (context.engine.peerNames.supportPeerId() + let _ = (context.engine.peers.supportPeerId() |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId { present(.push, context.sharedContext.makeChatController(context: context, chatLocation: .peer(peerId), subject: nil, botStart: nil, mode: .standard(previewing: false))) diff --git a/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift index 84d5b2a096..9ab2596ff6 100644 --- a/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/ArchivedStickerPacksController.swift @@ -268,7 +268,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv namespace = .masks } let stickerPacks = Promise<[ArchivedStickerPackItem]?>() - stickerPacks.set(.single(archived) |> then(archivedStickerPacks(account: context.account, namespace: namespace) |> map(Optional.init))) + stickerPacks.set(.single(archived) |> then(context.engine.stickers.archivedStickerPacks(namespace: namespace) |> map(Optional.init))) actionsDisposable.add(stickerPacks.get().start(next: { packs in updatedPacks(packs) @@ -302,17 +302,16 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv if !add { return } - let _ = (loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + let _ = (context.engine.stickers.loadedStickerPack(reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in switch result { case let .result(info, items, installed): if installed { return .complete() } else { - return addStickerPackInteractively(postbox: context.account.postbox, info: info, items: items) + return context.engine.stickers.addStickerPackInteractively(info: info, items: items) |> ignoreValues |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in - return .complete() } |> then(.single((info, items))) } @@ -336,7 +335,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv } } - presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in + presentControllerImpl?(UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return true }), nil) @@ -390,7 +389,7 @@ public func archivedStickerPacksController(context: AccountContext, mode: Archiv return .complete() } - removePackDisposables.set((removeArchivedStickerPack(account: context.account, info: info) |> then(applyPacks) |> deliverOnMainQueue).start(completed: { + removePackDisposables.set((context.engine.stickers.removeArchivedStickerPack(info: info) |> then(applyPacks) |> deliverOnMainQueue).start(completed: { updateState { state in var removingPackIds = state.removingPackIds removingPackIds.remove(info.id) diff --git a/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift index f98650abec..b460f6c795 100644 --- a/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/FeaturedStickerPacksController.swift @@ -180,14 +180,14 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr let arguments = FeaturedStickerPacksControllerArguments(account: context.account, openStickerPack: { info in presentStickerPackController?(info) }, addPack: { info in - let _ = (loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + let _ = (context.engine.stickers.loadedStickerPack(reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal in switch result { case let .result(info, items, installed): if installed { return .complete() } else { - return addStickerPackInteractively(postbox: context.account.postbox, info: info, items: items) + return context.engine.stickers.addStickerPackInteractively(info: info, items: items) } case .fetching: break @@ -254,7 +254,7 @@ public func featuredStickerPacksController(context: AccountContext) -> ViewContr if !unreadIds.isEmpty { alreadyReadIds.formUnion(Set(unreadIds)) - let _ = markFeaturedStickerPacksAsSeenInteractively(postbox: context.account.postbox, ids: unreadIds).start() + let _ = context.engine.stickers.markFeaturedStickerPacksAsSeenInteractively(ids: unreadIds).start() } } diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index 6129ce7dae..c5d62f21f0 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -532,7 +532,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta controller?.dismissAnimated() } let removeAction: (RemoveStickerPackOption) -> Void = { action in - let _ = (removeStickerPackInteractively(postbox: context.account.postbox, id: archivedItem.info.id, option: action) + let _ = (context.engine.stickers.removeStickerPackInteractively(id: archivedItem.info.id, option: action) |> deliverOnMainQueue).start(next: { indexAndItems in guard let (positionInList, items) = indexAndItems else { return @@ -548,9 +548,9 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } } - navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: action == .archive ? presentationData.strings.StickerPackActionInfo_ArchivedTitle : presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(archivedItem.info.title).0, undo: true, info: archivedItem.info, topItem: archivedItem.topItems.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in + navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: action == .archive ? presentationData.strings.StickerPackActionInfo_ArchivedTitle : presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(archivedItem.info.title).0, undo: true, info: archivedItem.info, topItem: archivedItem.topItems.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in if case .undo = action { - let _ = addStickerPackInteractively(postbox: context.account.postbox, info: archivedItem.info, items: items, positionInList: positionInList).start() + let _ = context.engine.stickers.addStickerPackInteractively(info: archivedItem.info, items: items, positionInList: positionInList).start() } return true })) @@ -581,7 +581,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta ]) presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }, openStickersBot: { - resolveDisposable.set((resolvePeerByName(account: context.account, name: "stickers") |> deliverOnMainQueue).start(next: { peerId in + resolveDisposable.set((context.engine.peers.resolvePeerByName(name: "stickers") |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId { navigateToChatControllerImpl?(peerId) } @@ -655,10 +655,10 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta switch mode { case .general, .modal: featured.set(context.account.viewTracker.featuredStickerPacks()) - archivedPromise.set(.single(archivedPacks) |> then(archivedStickerPacks(account: context.account) |> map(Optional.init))) + archivedPromise.set(.single(archivedPacks) |> then(context.engine.stickers.archivedStickerPacks() |> map(Optional.init))) case .masks: featured.set(.single([])) - archivedPromise.set(.single(nil) |> then(archivedStickerPacks(account: context.account, namespace: .masks) |> map(Optional.init))) + archivedPromise.set(.single(nil) |> then(context.engine.stickers.archivedStickerPacks(namespace: .masks) |> map(Optional.init))) } var previousPackCount: Int? @@ -717,7 +717,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } } - let _ = removeStickerPacksInteractively(postbox: context.account.postbox, ids: packIds, option: .delete).start() + let _ = context.engine.stickers.removeStickerPacksInteractively(ids: packIds, option: .delete).start() })) actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in @@ -742,7 +742,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } } - let _ = removeStickerPacksInteractively(postbox: context.account.postbox, ids: packIds, option: .archive).start() + let _ = context.engine.stickers.removeStickerPacksInteractively(ids: packIds, option: .archive).start() })) actionSheet.setItemGroups([ActionSheetItemGroup(items: items), ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, color: .accent, font: .bold, action: { [weak actionSheet] in @@ -954,13 +954,13 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } switch action { case .add: - navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in + navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return true })) case let .remove(positionInList): - navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in + navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, context: context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in if case .undo = action { - let _ = addStickerPackInteractively(postbox: context.account.postbox, info: info, items: items, positionInList: positionInList).start() + let _ = context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start() } return true })) diff --git a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift index ef219f330b..8c57830570 100644 --- a/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift +++ b/submodules/SettingsUI/Sources/Themes/CustomWallpaperPicker.swift @@ -130,11 +130,11 @@ func uploadCustomWallpaper(context: AccountContext, wallpaper: WallpaperGalleryE let thumbnailImage = generateScaledImage(image: croppedImage, size: thumbnailDimensions, scale: 1.0) if let data = croppedImage.jpegData(compressionQuality: 0.8), let thumbnailImage = thumbnailImage, let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) { - let thumbnailResource = LocalFileMediaResource(fileId: arc4random64()) + let thumbnailResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.sharedContext.accountManager.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData) context.account.postbox.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData) - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) diff --git a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift index 063e9c92c4..2b93ad1da8 100644 --- a/submodules/SettingsUI/Sources/Themes/EditThemeController.swift +++ b/submodules/SettingsUI/Sources/Themes/EditThemeController.swift @@ -443,7 +443,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll |> take(1)).start(next: { previewTheme, settings in let saveThemeTemplateFile: (String, LocalFileMediaResource, @escaping () -> Void) -> Void = { title, resource, completion in let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: resource.fileId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/x-tgtheme-ios", size: nil, attributes: [.FileName(fileName: "\(title).tgios-theme")]) - let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let _ = enqueueMessages(account: context.account, peerId: context.account.peerId, messages: [message]).start() @@ -478,7 +478,7 @@ public func editThemeController(context: AccountContext, mode: EditThemeControll let themeData: Data? let themeThumbnailData: Data? if let theme = theme, let themeString = encodePresentationTheme(theme), let data = themeString.data(using: .utf8) { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) themeResource = resource diff --git a/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift index 39f33e0555..39a9bd43b0 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeGridSearchContentNode.swift @@ -497,7 +497,7 @@ final class ThemeGridSearchContentNode: SearchDisplayControllerContentNode { guard let name = configuration.imageBotUsername else { return .single(nil) } - return resolvePeerByName(account: context.account, name: name) + return context.engine.peers.resolvePeerByName(name: name) |> mapToSignal { peerId -> Signal in if let peerId = peerId { return context.account.postbox.loadedPeerWithId(peerId) diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift index 923279b761..6ef7bd7077 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewController.swift @@ -245,7 +245,7 @@ public final class ThemePreviewController: ViewController { } case .media: if let strings = encodePresentationTheme(previewTheme), let data = strings.data(using: .utf8) { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) context.sharedContext.accountManager.mediaBox.storeResourceData(resource.id, data: data) theme = .single(.local(PresentationLocalTheme(title: previewTheme.name.string, resource: resource, resolvedWallpaper: nil))) diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 44376bb5dc..83039987af 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -407,7 +407,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let dimensions = CGSize(width: asset.pixelWidth, height: asset.pixelHeight) contentSize = dimensions displaySize = dimensions.dividedByScreenScale().integralFloor - signal = photoWallpaper(postbox: context.account.postbox, photoLibraryResource: PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64())) + signal = photoWallpaper(postbox: context.account.postbox, photoLibraryResource: PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: Int64.random(in: Int64.min ... Int64.max))) fetchSignal = .complete() statusSignal = .single(.Local) subtitleSignal = .single(nil) diff --git a/submodules/SettingsUI/Sources/UsernameSetupController.swift b/submodules/SettingsUI/Sources/UsernameSetupController.swift index abeabcbc0e..26789fa576 100644 --- a/submodules/SettingsUI/Sources/UsernameSetupController.swift +++ b/submodules/SettingsUI/Sources/UsernameSetupController.swift @@ -260,7 +260,7 @@ public func usernameSetupController(context: AccountContext) -> ViewController { return state.withUpdatedEditingPublicLinkText(text) } - checkAddressNameDisposable.set((context.engine.peerNames.validateAddressNameInteractive(domain: .account, name: text) + checkAddressNameDisposable.set((context.engine.peers.validateAddressNameInteractive(domain: .account, name: text) |> deliverOnMainQueue).start(next: { result in updateState { state in return state.withUpdatedAddressNameValidationStatus(result) @@ -325,7 +325,7 @@ public func usernameSetupController(context: AccountContext) -> ViewController { } if let updatedAddressNameValue = updatedAddressNameValue { - updateAddressNameDisposable.set((context.engine.peerNames.updateAddressName(domain: .account, name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) + updateAddressNameDisposable.set((context.engine.peers.updateAddressName(domain: .account, name: updatedAddressNameValue.isEmpty ? nil : updatedAddressNameValue) |> deliverOnMainQueue).start(error: { _ in updateState { state in return state.withUpdatedUpdatingAddressName(false) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 1867d75dad..785e24c330 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -519,9 +519,9 @@ public final class ShareController: ViewController { for peerId in peerIds { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: url + "\n\n" + text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: url + "\n\n" + text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } else { - messages.append(.message(text: url, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: url, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } @@ -529,58 +529,58 @@ public final class ShareController: ViewController { for peerId in peerIds { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } - messages.append(.message(text: string, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: string, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } case let .quote(string, url): for peerId in peerIds { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber]) attributedText.append(NSAttributedString(string: "\n\n\(url)")) let entities = generateChatInputTextEntities(attributedText) - messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } case let .image(representations): for peerId in peerIds { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } - messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } case let .media(mediaReference): for peerId in peerIds { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } - messages.append(.message(text: "", attributes: [], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: "", attributes: [], mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } case let .mapMedia(media): for peerId in peerIds { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } - messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages)) } case let .messages(messages): for peerId in peerIds { var messagesToEnqueue: [EnqueueMessage] = [] if !text.isEmpty { - messagesToEnqueue.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messagesToEnqueue.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) } for message in messages { - messagesToEnqueue.append(.forward(source: message.id, grouping: .auto, attributes: [])) + messagesToEnqueue.append(.forward(source: message.id, grouping: .auto, attributes: [], correlationId: nil)) } shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) } @@ -663,7 +663,7 @@ public final class ShareController: ViewController { case let .quote(text, url): collectableItems.append(CollectableExternalShareItem(url: "", text: "\"\(text)\"\n\n\(url)", author: nil, timestamp: nil, mediaReference: nil)) case let .image(representations): - let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) + let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: .standalone(media: media))) case let .media(mediaReference): collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: mediaReference)) diff --git a/submodules/ShareController/Sources/ShareSearchContainerNode.swift b/submodules/ShareController/Sources/ShareSearchContainerNode.swift index 2cba52dc8b..f585d685f3 100644 --- a/submodules/ShareController/Sources/ShareSearchContainerNode.swift +++ b/submodules/ShareController/Sources/ShareSearchContainerNode.swift @@ -249,7 +249,7 @@ final class ShareSearchContainerNode: ASDisplayNode, ShareContentContainerNode { let foundLocalPeers = context.account.postbox.searchPeers(query: query.lowercased()) let foundRemotePeers: Signal<([FoundPeer], [FoundPeer]), NoError> = .single(([], [])) |> then( - searchPeers(account: context.account, query: query) + context.engine.peers.searchPeers(query: query) |> delay(0.2, queue: Queue.concurrentDefaultQueue()) ) diff --git a/submodules/ShareItems/Sources/ShareItems.swift b/submodules/ShareItems/Sources/ShareItems.swift index 48d7040e80..a91ec1dec9 100644 --- a/submodules/ShareItems/Sources/ShareItems.swift +++ b/submodules/ShareItems/Sources/ShareItems.swift @@ -127,7 +127,7 @@ private func preparedShareItem(account: Account, to peerId: PeerId, value: [Stri let estimatedSize = TGMediaVideoConverter.estimatedSize(for: preset, duration: finalDuration, hasAudio: true) - let resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: asset.url.path, adjustments: resourceAdjustments) + let resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: asset.url.path, adjustments: resourceAdjustments) return standaloneUploadedFile(account: account, peerId: peerId, text: "", source: .resource(.standalone(resource: resource)), mimeType: "video/mp4", attributes: [.Video(duration: Int(finalDuration), size: PixelDimensions(width: Int32(finalDimensions.width), height: Int32(finalDimensions.height)), flags: flags)], hintFileIsLarge: estimatedSize > 10 * 1024 * 1024) |> mapError { _ -> Void in return Void() @@ -394,26 +394,26 @@ public func sentShareItems(account: Account, to peerIds: [PeerId], items: [Prepa } if ((mediaTypes.photo + mediaTypes.video) > 1) && (mediaTypes.music == 0 && mediaTypes.other == 0) { - groupingKey = arc4random64() + groupingKey = Int64.random(in: Int64.min ... Int64.max) } else if ((mediaTypes.photo + mediaTypes.video) == 0) && ((mediaTypes.music > 1 && mediaTypes.other == 0) || (mediaTypes.music == 0 && mediaTypes.other > 1)) { - groupingKey = arc4random64() + groupingKey = Int64.random(in: Int64.min ... Int64.max) } var mediaMessages: [EnqueueMessage] = [] for item in items { switch item { case let .text(text): - messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)) + messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)) case let .media(media): switch media { case let .media(reference): - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: reference, replyToMessageId: nil, localGroupingKey: groupingKey) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: reference, replyToMessageId: nil, localGroupingKey: groupingKey, correlationId: nil) messages.append(message) mediaMessages.append(message) } if let _ = groupingKey, mediaMessages.count % 10 == 0 { - groupingKey = arc4random64() + groupingKey = Int64.random(in: Int64.min ... Int64.max) } } } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift index ba352f796c..aa483d713b 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewController.swift @@ -82,7 +82,7 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese self.acceptsFocusWhenInOverlay = true self.statusBar.statusBarStyle = .Ignore - self.stickerPackContents.set(loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: stickerPack, forceActualized: true)) + self.stickerPackContents.set(context.engine.stickers.loadedStickerPack(reference: stickerPack, forceActualized: true)) self.presentationDataDisposable = (context.sharedContext.presentationData |> deliverOnMainQueue).start(next: { [weak self] presentationData in @@ -132,7 +132,7 @@ public final class StickerPackPreviewController: ViewController, StandalonePrese } let account = strongSelf.context.account - strongSelf.openMentionDisposable.set((resolvePeerByName(account: strongSelf.context.account, name: mention) + strongSelf.openMentionDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: mention) |> mapToSignal { peerId -> Signal in if let peerId = peerId { return account.postbox.loadedPeerWithId(peerId) diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift index e4198bd782..32f0b8cf59 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift @@ -521,7 +521,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol switch stickerPack { case let .result(info, items, installed): if installed { - let _ = (removeStickerPackInteractively(postbox: self.context.account.postbox, id: info.id, option: .delete) + let _ = (self.context.engine.stickers.removeStickerPackInteractively(id: info.id, option: .delete) |> deliverOnMainQueue).start(next: { [weak self] indexAndItems in guard let strongSelf = self, let (positionInList, _) = indexAndItems else { return @@ -532,7 +532,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol self.updateStickerPack(.result(info: info, items: items, installed: false), stickerSettings: stickerSettings) } } else { - let _ = addStickerPackInteractively(postbox: self.context.account.postbox, info: info, items: items).start() + let _ = self.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start() if !dismissOnAction { self.updateStickerPack(.result(info: info, items: items, installed: true), stickerSettings: stickerSettings) } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 56fdd49e00..628ac687b3 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -238,7 +238,7 @@ private final class StickerPackContainer: ASDisplayNode { return updatedOffset } - self.itemsDisposable = (loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: stickerPack, forceActualized: false) + self.itemsDisposable = (context.engine.stickers.loadedStickerPack(reference: stickerPack, forceActualized: false) |> deliverOnMainQueue).start(next: { [weak self] contents in guard let strongSelf = self else { return @@ -340,9 +340,9 @@ private final class StickerPackContainer: ASDisplayNode { return } if installed { - let _ = removeStickerPackInteractively(postbox: strongSelf.context.account.postbox, id: info.id, option: .delete).start() + let _ = strongSelf.context.engine.stickers.removeStickerPackInteractively(id: info.id, option: .delete).start() } else { - let _ = addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items).start() + let _ = strongSelf.context.engine.stickers.addStickerPackInteractively(info: info, items: items).start() } switch strongSelf.decideNextAction(strongSelf, installed ? .remove : .add) { diff --git a/submodules/SyncCore/Sources/OutgoingMessageInfoAttribute.swift b/submodules/SyncCore/Sources/OutgoingMessageInfoAttribute.swift index 42856b9faf..9426cf01ab 100644 --- a/submodules/SyncCore/Sources/OutgoingMessageInfoAttribute.swift +++ b/submodules/SyncCore/Sources/OutgoingMessageInfoAttribute.swift @@ -19,30 +19,38 @@ public class OutgoingMessageInfoAttribute: MessageAttribute { public let uniqueId: Int64 public let flags: OutgoingMessageInfoFlags public let acknowledged: Bool + public let correlationId: Int64? - public init(uniqueId: Int64, flags: OutgoingMessageInfoFlags, acknowledged: Bool) { + public init(uniqueId: Int64, flags: OutgoingMessageInfoFlags, acknowledged: Bool, correlationId: Int64?) { self.uniqueId = uniqueId self.flags = flags self.acknowledged = acknowledged + self.correlationId = correlationId } required public init(decoder: PostboxDecoder) { self.uniqueId = decoder.decodeInt64ForKey("u", orElse: 0) self.flags = OutgoingMessageInfoFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0)) self.acknowledged = decoder.decodeInt32ForKey("ack", orElse: 0) != 0 + self.correlationId = decoder.decodeOptionalInt64ForKey("cid") } public func encode(_ encoder: PostboxEncoder) { encoder.encodeInt64(self.uniqueId, forKey: "u") encoder.encodeInt32(self.flags.rawValue, forKey: "f") encoder.encodeInt32(self.acknowledged ? 1 : 0, forKey: "ack") + if let correlationId = self.correlationId { + encoder.encodeInt64(correlationId, forKey: "cid") + } else { + encoder.encodeNil(forKey: "cid") + } } public func withUpdatedFlags(_ flags: OutgoingMessageInfoFlags) -> OutgoingMessageInfoAttribute { - return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: flags, acknowledged: self.acknowledged) + return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: flags, acknowledged: self.acknowledged, correlationId: self.correlationId) } public func withUpdatedAcknowledged(_ acknowledged: Bool) -> OutgoingMessageInfoAttribute { - return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: self.flags, acknowledged: acknowledged) + return OutgoingMessageInfoAttribute(uniqueId: self.uniqueId, flags: self.flags, acknowledged: acknowledged, correlationId: self.correlationId) } } diff --git a/submodules/SyncCore/Sources/TelegramMediaFile.swift b/submodules/SyncCore/Sources/TelegramMediaFile.swift index 66f8ac0d75..0b5c8a68a0 100644 --- a/submodules/SyncCore/Sources/TelegramMediaFile.swift +++ b/submodules/SyncCore/Sources/TelegramMediaFile.swift @@ -425,6 +425,8 @@ public final class TelegramMediaFile: Media, Equatable, Codable { if case .Sticker = attribute { if let s = self.size, s < 300 * 1024 { return !isAnimatedSticker + } else if self.size == nil { + return !isAnimatedSticker } } } diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 0f27c3a3f9..4aa4ae1827 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -353,7 +353,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1098628881] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaVenue($0) } dict[-1494368259] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaContact($0) } dict[1262639204] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageGame($0) } - dict[-717976187] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaInvoice($0) } + dict[-672693723] = { return Api.InputBotInlineMessage.parse_inputBotInlineMessageMediaInvoice($0) } dict[2002815875] = { return Api.KeyboardButtonRow.parse_keyboardButtonRow($0) } dict[-673242758] = { return Api.StickerSet.parse_stickerSet($0) } dict[-1111085620] = { return Api.messages.ExportedChatInvites.parse_exportedChatInvites($0) } @@ -392,7 +392,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) } dict[-78455655] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) } dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) } - dict[-186607933] = { return Api.InputMedia.parse_inputMediaInvoice($0) } + dict[-646342540] = { return Api.InputMedia.parse_inputMediaInvoice($0) } dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) } dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) } dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) } diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index 1e7382eb4f..01f64f5520 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -9020,7 +9020,7 @@ public extension Api { case inputBotInlineMessageMediaVenue(flags: Int32, geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?) case inputBotInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?) case inputBotInlineMessageGame(flags: Int32, replyMarkup: Api.ReplyMarkup?) - case inputBotInlineMessageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.InputWebDocument?, invoice: Api.Invoice, payload: Buffer, provider: String, providerData: Api.DataJSON, startParam: String, replyMarkup: Api.ReplyMarkup?) + case inputBotInlineMessageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.InputWebDocument?, invoice: Api.Invoice, payload: Buffer, provider: String, providerData: Api.DataJSON, replyMarkup: Api.ReplyMarkup?) public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -9092,9 +9092,9 @@ public extension Api { serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} break - case .inputBotInlineMessageMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam, let replyMarkup): + case .inputBotInlineMessageMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let replyMarkup): if boxed { - buffer.appendInt32(-717976187) + buffer.appendInt32(-672693723) } serializeInt32(flags, buffer: buffer, boxed: false) serializeString(title, buffer: buffer, boxed: false) @@ -9104,7 +9104,6 @@ public extension Api { serializeBytes(payload, buffer: buffer, boxed: false) serializeString(provider, buffer: buffer, boxed: false) providerData.serialize(buffer, true) - serializeString(startParam, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)} break } @@ -9124,8 +9123,8 @@ public extension Api { return ("inputBotInlineMessageMediaContact", [("flags", flags), ("phoneNumber", phoneNumber), ("firstName", firstName), ("lastName", lastName), ("vcard", vcard), ("replyMarkup", replyMarkup)]) case .inputBotInlineMessageGame(let flags, let replyMarkup): return ("inputBotInlineMessageGame", [("flags", flags), ("replyMarkup", replyMarkup)]) - case .inputBotInlineMessageMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam, let replyMarkup): - return ("inputBotInlineMessageMediaInvoice", [("flags", flags), ("title", title), ("description", description), ("photo", photo), ("invoice", invoice), ("payload", payload), ("provider", provider), ("providerData", providerData), ("startParam", startParam), ("replyMarkup", replyMarkup)]) + case .inputBotInlineMessageMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let replyMarkup): + return ("inputBotInlineMessageMediaInvoice", [("flags", flags), ("title", title), ("description", description), ("photo", photo), ("invoice", invoice), ("payload", payload), ("provider", provider), ("providerData", providerData), ("replyMarkup", replyMarkup)]) } } @@ -9310,11 +9309,9 @@ public extension Api { if let signature = reader.readInt32() { _8 = Api.parse(reader, signature: signature) as? Api.DataJSON } - var _9: String? - _9 = parseString(reader) - var _10: Api.ReplyMarkup? + var _9: Api.ReplyMarkup? if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup + _9 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup } } let _c1 = _1 != nil let _c2 = _2 != nil @@ -9324,10 +9321,9 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 { - return Api.InputBotInlineMessage.inputBotInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, startParam: _9!, replyMarkup: _10) + let _c9 = (Int(_1!) & Int(1 << 2) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.InputBotInlineMessage.inputBotInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, replyMarkup: _9) } else { return nil @@ -9939,7 +9935,7 @@ public extension Api { case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaGame(id: Api.InputGame) - case inputMediaInvoice(flags: Int32, title: String, description: String, photo: Api.InputWebDocument?, invoice: Api.Invoice, payload: Buffer, provider: String, providerData: Api.DataJSON, startParam: String) + case inputMediaInvoice(flags: Int32, title: String, description: String, photo: Api.InputWebDocument?, invoice: Api.Invoice, payload: Buffer, provider: String, providerData: Api.DataJSON, startParam: String?) case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?) case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?) case inputMediaDice(emoticon: String) @@ -10052,7 +10048,7 @@ public extension Api { break case .inputMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam): if boxed { - buffer.appendInt32(-186607933) + buffer.appendInt32(-646342540) } serializeInt32(flags, buffer: buffer, boxed: false) serializeString(title, buffer: buffer, boxed: false) @@ -10062,7 +10058,7 @@ public extension Api { serializeBytes(payload, buffer: buffer, boxed: false) serializeString(provider, buffer: buffer, boxed: false) providerData.serialize(buffer, true) - serializeString(startParam, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)} break case .inputMediaGeoLive(let flags, let geoPoint, let heading, let period, let proximityNotificationRadius): if boxed { @@ -10373,7 +10369,7 @@ public extension Api { _8 = Api.parse(reader, signature: signature) as? Api.DataJSON } var _9: String? - _9 = parseString(reader) + if Int(_1!) & Int(1 << 1) != 0 {_9 = parseString(reader) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil @@ -10382,9 +10378,9 @@ public extension Api { let _c6 = _6 != nil let _c7 = _7 != nil let _c8 = _8 != nil - let _c9 = _9 != nil + let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { - return Api.InputMedia.inputMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, startParam: _9!) + return Api.InputMedia.inputMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, startParam: _9) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api4.swift b/submodules/TelegramApi/Sources/Api4.swift index 774fb67497..e8f1337a11 100644 --- a/submodules/TelegramApi/Sources/Api4.swift +++ b/submodules/TelegramApi/Sources/Api4.swift @@ -7868,15 +7868,17 @@ public extension Api { }) } - public static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, volume: Int32?, raiseHand: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + public static func editGroupCallParticipant(flags: Int32, call: Api.InputGroupCall, participant: Api.InputPeer, muted: Api.Bool?, volume: Int32?, raiseHand: Api.Bool?, videoMuted: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(-646583424) + buffer.appendInt32(-1362751260) serializeInt32(flags, buffer: buffer, boxed: false) call.serialize(buffer, true) participant.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {muted!.serialize(buffer, true)} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(volume!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 2) != 0 {raiseHand!.serialize(buffer, true)} - return (FunctionDescription(name: "phone.editGroupCallParticipant", parameters: [("flags", flags), ("call", call), ("participant", participant), ("volume", volume), ("raiseHand", raiseHand)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in + if Int(flags) & Int(1 << 3) != 0 {videoMuted!.serialize(buffer, true)} + return (FunctionDescription(name: "phone.editGroupCallParticipant", parameters: [("flags", flags), ("call", call), ("participant", participant), ("muted", muted), ("volume", volume), ("raiseHand", raiseHand), ("videoMuted", videoMuted)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in let reader = BufferReader(buffer) var result: Api.Updates? if let signature = reader.readInt32() { diff --git a/submodules/TelegramCallsUI/Sources/CallRatingController.swift b/submodules/TelegramCallsUI/Sources/CallRatingController.swift index ebf86e48f1..72ce0d4316 100644 --- a/submodules/TelegramCallsUI/Sources/CallRatingController.swift +++ b/submodules/TelegramCallsUI/Sources/CallRatingController.swift @@ -245,11 +245,11 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm let rate = rateCall(account: account, callId: callId, starsCount: Int32(starsCount), comment: comment, userInitiated: userInitiated) if includeLogs { - let id = arc4random64() + let id = Int64.random(in: Int64.min ... Int64.max) let name = "\(callId.id)_\(callId.accessHash).log.json" let path = callLogsPath(account: account) + "/" + name let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: path, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: name)]) - let message = EnqueueMessage.message(text: comment, attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + let message = EnqueueMessage.message(text: comment, attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) return rate |> then(enqueueMessages(account: account, peerId: peerId, messages: [message]) |> mapToSignal({ _ -> Signal in @@ -257,7 +257,7 @@ func rateCallAndSendLogs(account: Account, callId: CallId, starsCount: Int, comm })) } else if !comment.isEmpty { return rate - |> then(enqueueMessages(account: account, peerId: peerId, messages: [.message(text: comment, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]) + |> then(enqueueMessages(account: account, peerId: peerId, messages: [.message(text: comment, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) |> mapToSignal({ _ -> Signal in return .single(Void()) })) diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 66958f228b..3d7a7ba0a2 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -932,6 +932,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { activityTimestamp: strongSelf.temporaryActivityTimestamp, activityRank: strongSelf.temporaryActivityRank, muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + isVideoMuted: true, volume: nil, about: about )) @@ -1012,6 +1013,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { activityTimestamp: strongSelf.temporaryActivityTimestamp, activityRank: strongSelf.temporaryActivityRank, muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + isVideoMuted: true, volume: nil, about: about )) @@ -1174,6 +1176,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { activityTimestamp: strongSelf.temporaryActivityTimestamp, activityRank: strongSelf.temporaryActivityRank, muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canManageCall || !state.defaultParticipantsAreMuted.isMuted, mutedByYou: false), + isVideoMuted: true, volume: nil, about: about )) @@ -1748,6 +1751,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { activityTimestamp: strongSelf.temporaryActivityTimestamp, activityRank: strongSelf.temporaryActivityRank, muteState: strongSelf.temporaryMuteState ?? GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false), + isVideoMuted: true, volume: nil, about: about )) @@ -2344,7 +2348,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { if id == peerId { self.callContext?.setVolume(ssrc: ssrc, volume: Double(volume) / 10000.0) if sync { - self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: volume, raiseHand: nil) + self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, isVideoMuted: nil, volume: volume, raiseHand: nil) } break } @@ -2452,19 +2456,19 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { canThenUnmute = true } let muteState = isMuted ? GroupCallParticipantsContext.Participant.MuteState(canUnmute: canThenUnmute, mutedByYou: mutedByYou) : nil - self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, volume: nil, raiseHand: nil) + self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, isVideoMuted: nil, volume: nil, raiseHand: nil) return muteState } else { if peerId == self.joinAsPeerId { - self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: nil, raiseHand: nil) + self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, isVideoMuted: nil, volume: nil, raiseHand: nil) return nil } else if self.stateValue.canManageCall || self.stateValue.adminIds.contains(self.accountContext.account.peerId) { let muteState = GroupCallParticipantsContext.Participant.MuteState(canUnmute: true, mutedByYou: false) - self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, volume: nil, raiseHand: nil) + self.participantsContext?.updateMuteState(peerId: peerId, muteState: muteState, isVideoMuted: nil, volume: nil, raiseHand: nil) return muteState } else { self.setVolume(peerId: peerId, volume: 10000, sync: true) - self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, volume: nil, raiseHand: nil) + self.participantsContext?.updateMuteState(peerId: peerId, muteState: nil, isVideoMuted: nil, volume: nil, raiseHand: nil) return nil } } diff --git a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift index 207d79dc2b..7d58623c11 100644 --- a/submodules/TelegramCallsUI/Sources/VoiceChatController.swift +++ b/submodules/TelegramCallsUI/Sources/VoiceChatController.swift @@ -1108,7 +1108,7 @@ public final class VoiceChatController: ViewController { dismissController?() if let strongSelf = self { - let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: listenerLink, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]) + let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: listenerLink, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) |> deliverOnMainQueue).start(next: { [weak self] _ in if let strongSelf = self { strongSelf.presentUndoOverlay(content: .forward(savedMessages: false, text: strongSelf.presentationData.strings.UserInfo_LinkForwardTooltip_Chat_One(peer.displayTitle(strings: strongSelf.presentationData.strings, displayOrder: strongSelf.presentationData.nameDisplayOrder)).0), action: { _ in return true }) @@ -4708,7 +4708,7 @@ public final class VoiceChatController: ViewController { return } - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) self.call.account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil) @@ -4743,7 +4743,7 @@ public final class VoiceChatController: ViewController { return } - let photoResource = LocalFileMediaResource(fileId: arc4random64()) + let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil) @@ -4808,7 +4808,7 @@ public final class VoiceChatController: ViewController { if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult { resource = LocalFileMediaResource(fileId: liveUploadData.id) } else { - resource = LocalFileMediaResource(fileId: arc4random64()) + resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) } account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) diff --git a/submodules/TelegramCore/Sources/Account.swift b/submodules/TelegramCore/Sources/Account.swift index eb509bba7f..9c39c9e485 100644 --- a/submodules/TelegramCore/Sources/Account.swift +++ b/submodules/TelegramCore/Sources/Account.swift @@ -67,6 +67,11 @@ public class UnauthorizedAccount { public var updateLoginTokenEvents: Signal { return self.updateLoginTokenPipe.signal() } + + private let serviceNotificationPipe = ValuePipe() + public var serviceNotificationEvents: Signal { + return self.serviceNotificationPipe.signal() + } public var masterDatacenterId: Int32 { return Int32(self.network.mtProto.datacenterId) @@ -83,8 +88,11 @@ public class UnauthorizedAccount { self.postbox = postbox self.network = network let updateLoginTokenPipe = self.updateLoginTokenPipe + let serviceNotificationPipe = self.serviceNotificationPipe self.stateManager = UnauthorizedAccountStateManager(network: network, updateLoginToken: { updateLoginTokenPipe.putNext(Void()) + }, displayServiceNotification: { text in + serviceNotificationPipe.putNext(text) }) network.shouldKeepConnection.set(self.shouldBeServiceTaskMaster.get() diff --git a/submodules/TelegramCore/Sources/CachedChannelData.swift b/submodules/TelegramCore/Sources/ApiUtils/PeerGeoLocation.swift similarity index 100% rename from submodules/TelegramCore/Sources/CachedChannelData.swift rename to submodules/TelegramCore/Sources/ApiUtils/PeerGeoLocation.swift diff --git a/submodules/TelegramCore/Sources/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift similarity index 100% rename from submodules/TelegramCore/Sources/StoreMessage_Telegram.swift rename to submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift diff --git a/submodules/TelegramCore/Sources/TextEntitiesMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift similarity index 100% rename from submodules/TelegramCore/Sources/TextEntitiesMessageAttribute.swift rename to submodules/TelegramCore/Sources/ApiUtils/TextEntitiesMessageAttribute.swift diff --git a/submodules/TelegramCore/Sources/Authorization.swift b/submodules/TelegramCore/Sources/Authorization.swift index ed6ba8e60a..a921cdf250 100644 --- a/submodules/TelegramCore/Sources/Authorization.swift +++ b/submodules/TelegramCore/Sources/Authorization.swift @@ -467,7 +467,7 @@ public func signUpWithName(accountManager: AccountManager, account: Unauthorized |> castError(SignUpError.self) if let avatarData = avatarData { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) account.postbox.mediaBox.storeResourceData(resource.id, data: avatarData) return updatePeerPhotoInternal(postbox: account.postbox, network: account.network, stateManager: nil, accountPeerId: user.id, peer: .single(user), photo: uploadedPeerPhoto(postbox: account.postbox, network: account.network, resource: resource), video: avatarVideo, videoStartTimestamp: videoStartTimestamp, mapResourceToAvatarSizes: { _, _ in .single([:]) }) diff --git a/submodules/TelegramCore/Sources/CallSessionManager.swift b/submodules/TelegramCore/Sources/CallSessionManager.swift index 7ac37066ab..afd27413f2 100644 --- a/submodules/TelegramCore/Sources/CallSessionManager.swift +++ b/submodules/TelegramCore/Sources/CallSessionManager.swift @@ -246,7 +246,7 @@ private final class CallSessionContext { var isVideoPossible: Bool var state: CallSessionInternalState let subscribers = Bag<(CallSession) -> Void>() - let signalingSubscribers = Bag<(Data) -> Void>() + var signalingReceiver: (([Data]) -> Void)? let signalingDisposables = DisposableSet() @@ -254,7 +254,7 @@ private final class CallSessionContext { var isEmpty: Bool { if case .terminated = self.state { - return self.subscribers.isEmpty && self.signalingSubscribers.isEmpty + return self.subscribers.isEmpty } else { return false } @@ -303,6 +303,8 @@ private final class CallSessionManagerContext { private let ringingSubscribers = Bag<([CallSessionRingingState]) -> Void>() private var contexts: [CallSessionInternalId: CallSessionContext] = [:] private var contextIdByStableId: [CallSessionStableId: CallSessionInternalId] = [:] + + private var enqueuedSignalingData: [Int64: [Data]] = [:] private let disposables = DisposableSet() @@ -395,29 +397,31 @@ private final class CallSessionManagerContext { } } - func callSignalingData(internalId: CallSessionInternalId) -> Signal { + func beginReceivingCallSignalingData(internalId: CallSessionInternalId, _ receiver: @escaping ([Data]) -> Void) -> Disposable { let queue = self.queue - return Signal { [weak self] subscriber in - let disposable = MetaDisposable() - queue.async { - if let strongSelf = self, let context = strongSelf.contexts[internalId] { - let index = context.signalingSubscribers.add { next in - subscriber.putNext(next) + + let disposable = MetaDisposable() + queue.async { [weak self] in + if let strongSelf = self, let context = strongSelf.contexts[internalId] { + context.signalingReceiver = receiver + + for (listStableId, listInternalId) in strongSelf.contextIdByStableId { + if listInternalId == internalId { + strongSelf.deliverCallSignalingData(id: listStableId) + break } - disposable.set(ActionDisposable { - queue.async { - if let strongSelf = self, let context = strongSelf.contexts[internalId] { - context.signalingSubscribers.remove(index) - if context.isEmpty { - strongSelf.contexts.removeValue(forKey: internalId) - } - } - } - }) } + + disposable.set(ActionDisposable { + queue.async { + if let strongSelf = self, let context = strongSelf.contexts[internalId] { + context.signalingReceiver = nil + } + } + }) } - return disposable } + return disposable } private func ringingStatesValue() -> [CallSessionRingingState] { @@ -475,6 +479,7 @@ private final class CallSessionManagerContext { })) self.contextIdByStableId[stableId] = internalId self.contextUpdated(internalId: internalId) + self.deliverCallSignalingData(id: stableId) self.ringingStatesUpdated() return internalId } else { @@ -596,7 +601,7 @@ private final class CallSessionManagerContext { func accept(internalId: CallSessionInternalId) { if let context = self.contexts[internalId] { switch context.state { - case let .ringing(id, accessHash, gAHash, b, remoteVersions): + case let .ringing(id, accessHash, gAHash, b, _): let acceptVersions = self.versions.map({ $0.version }) context.state = .accepting(id: id, accessHash: accessHash, gAHash: gAHash, b: b, disposable: (acceptCallSession(postbox: self.postbox, network: self.network, stableId: id, accessHash: accessHash, b: b, maxLayer: self.maxLayer, versions: acceptVersions) |> deliverOn(self.queue)).start(next: { [weak self] result in if let strongSelf = self, let context = strongSelf.contexts[internalId] { @@ -652,7 +657,7 @@ private final class CallSessionManagerContext { switch call { case .phoneCallEmpty: break - case let .phoneCallAccepted(flags, id, _, _, _, _, gB, remoteProtocol): + case let .phoneCallAccepted(_, id, _, _, _, _, gB, remoteProtocol): let remoteVersions: [String] switch remoteProtocol { case let .phoneCallProtocol(_, _, _, versions): @@ -854,11 +859,22 @@ private final class CallSessionManagerContext { } func addCallSignalingData(id: Int64, data: Data) { + if self.enqueuedSignalingData[id] == nil { + self.enqueuedSignalingData[id] = [] + } + self.enqueuedSignalingData[id]?.append(data) + + self.deliverCallSignalingData(id: id) + } + + private func deliverCallSignalingData(id: Int64) { guard let internalId = self.contextIdByStableId[id], let context = self.contexts[internalId] else { return } - for f in context.signalingSubscribers.copyItems() { - f(data) + if let signalingReceiver = context.signalingReceiver { + if let data = self.enqueuedSignalingData.removeValue(forKey: id) { + signalingReceiver(data) + } } } @@ -902,6 +918,7 @@ private final class CallSessionManagerContext { context.state = .requested(id: id, accessHash: accessHash, a: a, gA: gA, config: config, remoteConfirmationTimestamp: remoteConfirmationTimestamp) strongSelf.contextIdByStableId[id] = internalId strongSelf.contextUpdated(internalId: internalId) + strongSelf.deliverCallSignalingData(id: id) case let .failed(error): context.state = .terminated(id: nil, accessHash: nil, reason: .error(error), reportRating: false, sendDebugLogs: false) strongSelf.contextUpdated(internalId: internalId) @@ -1044,16 +1061,14 @@ public final class CallSessionManager { } } - public func callSignalingData(internalId: CallSessionInternalId) -> Signal { - return Signal { [weak self] subscriber in - let disposable = MetaDisposable() - self?.withContext { context in - disposable.set(context.callSignalingData(internalId: internalId).start(next: { next in - subscriber.putNext(next) - })) - } - return disposable + public func beginReceivingCallSignalingData(internalId: CallSessionInternalId, _ receiver: @escaping ([Data]) -> Void) -> Disposable { + let disposable = MetaDisposable() + + self.withContext { context in + disposable.set(context.beginReceivingCallSignalingData(internalId: internalId, receiver)) } + + return disposable } } diff --git a/submodules/TelegramCore/Sources/DeleteAccount.swift b/submodules/TelegramCore/Sources/DeleteAccount.swift deleted file mode 100644 index 6ef229ca72..0000000000 --- a/submodules/TelegramCore/Sources/DeleteAccount.swift +++ /dev/null @@ -1,16 +0,0 @@ -import Foundation -import SwiftSignalKit -import Postbox -import TelegramApi - -public enum DeleteAccountError { - case generic -} - -public func deleteAccount(account: Account) -> Signal { - return account.network.request(Api.functions.account.deleteAccount(reason: "GDPR")) - |> mapError { _ -> DeleteAccountError in - return .generic - } - |> ignoreValues -} diff --git a/submodules/TelegramCore/Sources/DeleteMessages.swift b/submodules/TelegramCore/Sources/DeleteMessages.swift index d432d2f604..787659c8e4 100644 --- a/submodules/TelegramCore/Sources/DeleteMessages.swift +++ b/submodules/TelegramCore/Sources/DeleteMessages.swift @@ -35,7 +35,7 @@ public func deleteMessages(transaction: Transaction, mediaBox: MediaBox, ids: [M } } if !resourceIds.isEmpty { - let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + let _ = mediaBox.removeCachedResources(Set(resourceIds), force: true).start() } for id in ids { if id.peerId.namespace == Namespaces.Peer.CloudChannel && id.namespace == Namespaces.Message.Cloud { @@ -73,7 +73,7 @@ public func deleteAllMessagesWithForwardAuthor(transaction: Transaction, mediaBo addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) if !resourceIds.isEmpty { - let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + let _ = mediaBox.removeCachedResources(Set(resourceIds), force: true).start() } } @@ -85,7 +85,7 @@ public func clearHistory(transaction: Transaction, mediaBox: MediaBox, peerId: P return true }) if !resourceIds.isEmpty { - let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + let _ = mediaBox.removeCachedResources(Set(resourceIds), force: true).start() } } transaction.clearHistory(peerId, namespaces: namespaces, forEachMedia: { _ in diff --git a/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift b/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift index e5f8730294..64e3051ae1 100644 --- a/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift +++ b/submodules/TelegramCore/Sources/DeleteMessagesInteractively.swift @@ -80,7 +80,7 @@ func deleteMessagesInteractively(transaction: Transaction, stateManager: Account globallyUniqueIds.append(globallyUniqueId) } } - let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.deleteMessages(layer: layer, actionGloballyUniqueId: arc4random64(), globallyUniqueIds: globallyUniqueIds), state: state) + let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.deleteMessages(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), globallyUniqueIds: globallyUniqueIds), state: state) if updatedState != state { transaction.setPeerChatState(peerId, state: updatedState) } @@ -137,7 +137,7 @@ public func clearHistoryInteractively(postbox: Postbox, peerId: PeerId, type: In } if let layer = layer { - let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.clearHistory(layer: layer, actionGloballyUniqueId: arc4random64()), state: state) + let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.clearHistory(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max)), state: state) if updatedState != state { transaction.setPeerChatState(peerId, state: updatedState) } diff --git a/submodules/TelegramCore/Sources/Either.swift b/submodules/TelegramCore/Sources/Either.swift deleted file mode 100644 index 1478adbc58..0000000000 --- a/submodules/TelegramCore/Sources/Either.swift +++ /dev/null @@ -1,6 +0,0 @@ -import Foundation - -public enum Either { - case left(value: Left) - case right(value: Right) -} diff --git a/submodules/TelegramCore/Sources/EnqueueMessage.swift b/submodules/TelegramCore/Sources/EnqueueMessage.swift index 6c5fddddc3..7c66f0c083 100644 --- a/submodules/TelegramCore/Sources/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/EnqueueMessage.swift @@ -11,13 +11,13 @@ public enum EnqueueMessageGrouping { } public enum EnqueueMessage { - case message(text: String, attributes: [MessageAttribute], mediaReference: AnyMediaReference?, replyToMessageId: MessageId?, localGroupingKey: Int64?) - case forward(source: MessageId, grouping: EnqueueMessageGrouping, attributes: [MessageAttribute]) + case message(text: String, attributes: [MessageAttribute], mediaReference: AnyMediaReference?, replyToMessageId: MessageId?, localGroupingKey: Int64?, correlationId: Int64?) + case forward(source: MessageId, grouping: EnqueueMessageGrouping, attributes: [MessageAttribute], correlationId: Int64?) public func withUpdatedReplyToMessageId(_ replyToMessageId: MessageId?) -> EnqueueMessage { switch self { - case let .message(text, attributes, mediaReference, _, localGroupingKey): - return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey) + case let .message(text, attributes, mediaReference, _, localGroupingKey, correlationId): + return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId) case .forward: return self } @@ -25,21 +25,41 @@ public enum EnqueueMessage { public func withUpdatedAttributes(_ f: ([MessageAttribute]) -> [MessageAttribute]) -> EnqueueMessage { switch self { - case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey): - return .message(text: text, attributes: f(attributes), mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey) - case let .forward(source, grouping, attributes): - return .forward(source: source, grouping: grouping, attributes: f(attributes)) + case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId): + return .message(text: text, attributes: f(attributes), mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId) + case let .forward(source, grouping, attributes, correlationId): + return .forward(source: source, grouping: grouping, attributes: f(attributes), correlationId: correlationId) } } public func withUpdatedGroupingKey(_ f: (Int64?) -> Int64?) -> EnqueueMessage { switch self { - case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey): - return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: f(localGroupingKey)) + case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId): + return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: f(localGroupingKey), correlationId: correlationId) case .forward: return self } } + + public func withUpdatedCorrelationId(_ value: Int64?) -> EnqueueMessage { + switch self { + case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, _): + return .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: value) + case let .forward(source, grouping, attributes, _): + return .forward(source: source, grouping: grouping, attributes: attributes, correlationId: value) + } + } +} + +private extension EnqueueMessage { + var correlationId: Int64? { + switch self { + case let .message(_, _, _, _, _, correlationId): + return correlationId + case let .forward(_, _, _, correlationId): + return correlationId + } + } } func augmentMediaWithReference(_ mediaReference: AnyMediaReference) -> Media { @@ -62,9 +82,9 @@ func augmentMediaWithReference(_ mediaReference: AnyMediaReference) -> Media { private func convertForwardedMediaForSecretChat(_ media: Media) -> Media { if let file = media as? TelegramMediaFile { - return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: file.partialReference, resource: file.resource, previewRepresentations: file.previewRepresentations, videoThumbnails: file.videoThumbnails, immediateThumbnailData: file.immediateThumbnailData, mimeType: file.mimeType, size: file.size, attributes: file.attributes) + return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: file.partialReference, resource: file.resource, previewRepresentations: file.previewRepresentations, videoThumbnails: file.videoThumbnails, immediateThumbnailData: file.immediateThumbnailData, mimeType: file.mimeType, size: file.size, attributes: file.attributes) } else if let image = media as? TelegramMediaImage { - return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference, flags: []) + return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference, flags: []) } else { return media } @@ -139,7 +159,7 @@ private func opportunisticallyTransformOutgoingMedia(network: Network, postbox: var hasMedia = false loop: for message in messages { switch message { - case let .message(_, _, mediaReference, _, _): + case let .message(_, _, mediaReference, _, _, _): if mediaReference != nil { hasMedia = true break loop @@ -156,14 +176,14 @@ private func opportunisticallyTransformOutgoingMedia(network: Network, postbox: var signals: [Signal<(Bool, EnqueueMessage), NoError>] = [] for message in messages { switch message { - case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey): + case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId): if let mediaReference = mediaReference { signals.append(opportunisticallyTransformMessageWithMedia(network: network, postbox: postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, mediaReference: mediaReference, userInteractive: userInteractive) |> map { result -> (Bool, EnqueueMessage) in if let result = result { - return (true, .message(text: text, attributes: attributes, mediaReference: .standalone(media: result.media), replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey)) + return (true, .message(text: text, attributes: attributes, mediaReference: .standalone(media: result.media), replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)) } else { - return (false, .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey)) + return (false, .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: replyToMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId)) } }) } else { @@ -235,7 +255,7 @@ public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal< } } - messages.append(.message(text: message.text, attributes: filteredAttributes, mediaReference: message.media.first.flatMap(AnyMediaReference.standalone), replyToMessageId: replyToMessageId, localGroupingKey: message.groupingKey)) + messages.append(.message(text: message.text, attributes: filteredAttributes, mediaReference: message.media.first.flatMap(AnyMediaReference.standalone), replyToMessageId: replyToMessageId, localGroupingKey: message.groupingKey, correlationId: nil)) } } let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: messages.map { (false, $0) }) @@ -258,7 +278,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } } switch message { - case let .message(_, _, _, replyToMessageId, _): + case let .message(_, _, _, replyToMessageId, _, _): if let replyToMessageId = replyToMessageId, replyToMessageId.peerId != peerId, let replyMessage = transaction.getMessage(replyToMessageId) { var canBeForwarded = true if replyMessage.id.namespace != Namespaces.Message.Cloud { @@ -271,10 +291,10 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } } if canBeForwarded { - updatedMessages.append((true, .forward(source: replyToMessageId, grouping: .none, attributes: []))) + updatedMessages.append((true, .forward(source: replyToMessageId, grouping: .none, attributes: [], correlationId: nil))) } } - case let .forward(sourceId, _, _): + case let .forward(sourceId, _, _, _): if let sourceMessage = forwardedMessageToBeReuploaded(transaction: transaction, id: sourceId) { var mediaReference: AnyMediaReference? if sourceMessage.id.peerId.namespace == Namespaces.Peer.SecretChat { @@ -282,7 +302,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, mediaReference = .standalone(media: media) } } - updatedMessages.append((transformedMedia, .message(text: sourceMessage.text, attributes: sourceMessage.attributes, mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil))) + updatedMessages.append((transformedMedia, .message(text: sourceMessage.text, attributes: sourceMessage.attributes, mediaReference: mediaReference, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))) continue outer } } @@ -319,11 +339,11 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, if transformedMedia { infoFlags.insert(.transformedMedia) } - attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId, flags: infoFlags, acknowledged: false)) + attributes.append(OutgoingMessageInfoAttribute(uniqueId: randomId, flags: infoFlags, acknowledged: false, correlationId: message.correlationId)) globallyUniqueIds.append(randomId) switch message { - case let .message(text, requestedAttributes, mediaReference, replyToMessageId, localGroupingKey): + case let .message(text, requestedAttributes, mediaReference, replyToMessageId, localGroupingKey, correlationId): var peerAutoremoveTimeout: Int32? if let peer = peer as? TelegramSecretChat { var isAction = false @@ -495,7 +515,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, } storeMessages.append(StoreMessage(peerId: peerId, namespace: messageNamespace, globallyUniqueId: randomId, groupingKey: localGroupingKey, threadId: threadId, timestamp: effectiveTimestamp, flags: flags, tags: tags, globalTags: globalTags, localTags: localTags, forwardInfo: nil, authorId: authorId, text: text, attributes: attributes, media: mediaList)) - case let .forward(source, grouping, requestedAttributes): + case let .forward(source, grouping, requestedAttributes, correlationId): let sourceMessage = transaction.getMessage(source) if let sourceMessage = sourceMessage, let author = sourceMessage.author ?? sourceMessage.peers[sourceMessage.id.peerId] { if let peer = peer as? TelegramSecretChat { @@ -676,7 +696,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, if let generatedKey = localGroupingKeyBySourceKey[groupingKey] { localGroupingKey = generatedKey } else { - let generatedKey = arc4random64() + let generatedKey = Int64.random(in: Int64.min ... Int64.max) localGroupingKeyBySourceKey[groupingKey] = generatedKey localGroupingKey = generatedKey } diff --git a/submodules/TelegramCore/Sources/ForwardGame.swift b/submodules/TelegramCore/Sources/ForwardGame.swift index 6627125458..f41b02e57b 100644 --- a/submodules/TelegramCore/Sources/ForwardGame.swift +++ b/submodules/TelegramCore/Sources/ForwardGame.swift @@ -6,7 +6,7 @@ import SwiftSignalKit public func forwardGameWithScore(account: Account, messageId: MessageId, to peerId: PeerId) -> Signal { return account.postbox.transaction { transaction -> Signal in if let _ = transaction.getMessage(messageId), let fromPeer = transaction.getPeer(messageId.peerId), let fromInputPeer = apiInputPeer(fromPeer), let toPeer = transaction.getPeer(peerId), let toInputPeer = apiInputPeer(toPeer) { - return account.network.request(Api.functions.messages.forwardMessages(flags: 1 << 8, fromPeer: fromInputPeer, id: [messageId.id], randomId: [arc4random64()], toPeer: toInputPeer, scheduleDate: nil)) + return account.network.request(Api.functions.messages.forwardMessages(flags: 1 << 8, fromPeer: fromInputPeer, id: [messageId.id], randomId: [Int64.random(in: Int64.min ... Int64.max)], toPeer: toInputPeer, scheduleDate: nil)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramCore/Sources/GroupCalls.swift b/submodules/TelegramCore/Sources/GroupCalls.swift index c41510ab1b..ccbddd6e3d 100644 --- a/submodules/TelegramCore/Sources/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/GroupCalls.swift @@ -157,6 +157,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int jsonParams = data } } + let isVideoMuted = (flags & (1 << 14)) != 0 parsedParticipants.append(GroupCallParticipantsContext.Participant( peer: peer, ssrc: ssrc, @@ -167,6 +168,7 @@ public func getCurrentGroupCall(account: Account, callId: Int64, accessHash: Int activityTimestamp: activeDate.flatMap(Double.init), activityRank: nil, muteState: muteState, + isVideoMuted: isVideoMuted, volume: volume, about: about )) @@ -472,6 +474,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash jsonParams = data } } + let isVideoMuted = (flags & (1 << 14)) != 0 parsedParticipants.append(GroupCallParticipantsContext.Participant( peer: peer, ssrc: ssrc, @@ -482,6 +485,7 @@ public func getGroupCallParticipants(account: Account, callId: Int64, accessHash activityTimestamp: activeDate.flatMap(Double.init), activityRank: nil, muteState: muteState, + isVideoMuted: isVideoMuted, volume: volume, about: about )) @@ -716,6 +720,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal jsonParams = data } } + let isVideoMuted = (flags & (1 << 14)) != 0 if !state.participants.contains(where: { $0.peer.id == peer.id }) { state.participants.append(GroupCallParticipantsContext.Participant( peer: peer, @@ -727,6 +732,7 @@ public func joinGroupCall(account: Account, peerId: PeerId, joinAs: PeerId?, cal activityTimestamp: activeDate.flatMap(Double.init), activityRank: nil, muteState: muteState, + isVideoMuted: isVideoMuted, volume: volume, about: about )) @@ -880,6 +886,7 @@ public final class GroupCallParticipantsContext { public var activityTimestamp: Double? public var activityRank: Int? public var muteState: MuteState? + public var isVideoMuted: Bool public var volume: Int32? public var about: String? @@ -893,6 +900,7 @@ public final class GroupCallParticipantsContext { activityTimestamp: Double?, activityRank: Int?, muteState: MuteState?, + isVideoMuted: Bool, volume: Int32?, about: String? ) { @@ -905,6 +913,7 @@ public final class GroupCallParticipantsContext { self.activityTimestamp = activityTimestamp self.activityRank = activityRank self.muteState = muteState + self.isVideoMuted = isVideoMuted self.volume = volume self.about = about } @@ -945,6 +954,9 @@ public final class GroupCallParticipantsContext { if lhs.muteState != rhs.muteState { return false } + if lhs.isVideoMuted != rhs.isVideoMuted { + return false + } if lhs.volume != rhs.volume { return false } @@ -1128,6 +1140,7 @@ public final class GroupCallParticipantsContext { public var activityTimestamp: Double? public var raiseHandRating: Int64? public var muteState: Participant.MuteState? + public var isVideoMuted: Bool public var participationStatusChange: ParticipationStatusChange public var volume: Int32? public var about: String? @@ -1141,6 +1154,7 @@ public final class GroupCallParticipantsContext { activityTimestamp: Double?, raiseHandRating: Int64?, muteState: Participant.MuteState?, + isVideoMuted: Bool, participationStatusChange: ParticipationStatusChange, volume: Int32?, about: String?, @@ -1153,6 +1167,7 @@ public final class GroupCallParticipantsContext { self.activityTimestamp = activityTimestamp self.raiseHandRating = raiseHandRating self.muteState = muteState + self.isVideoMuted = isVideoMuted self.participationStatusChange = participationStatusChange self.volume = volume self.about = about @@ -1690,6 +1705,7 @@ public final class GroupCallParticipantsContext { activityTimestamp: activityTimestamp, activityRank: previousActivityRank, muteState: muteState, + isVideoMuted: participantUpdate.isVideoMuted, volume: volume, about: participantUpdate.about ) @@ -1767,7 +1783,7 @@ public final class GroupCallParticipantsContext { })) } - public func updateMuteState(peerId: PeerId, muteState: Participant.MuteState?, volume: Int32?, raiseHand: Bool?) { + public func updateMuteState(peerId: PeerId, muteState: Participant.MuteState?, isVideoMuted: Bool?, volume: Int32?, raiseHand: Bool?) { if let current = self.stateValue.overlayState.pendingMuteStateChanges[peerId] { if current.state == muteState { return @@ -1814,8 +1830,15 @@ public final class GroupCallParticipantsContext { if let volume = volume, volume > 0 { flags |= 1 << 1 } + var muted: Api.Bool? if let muteState = muteState, (!muteState.canUnmute || peerId == myPeerId || muteState.mutedByYou) { flags |= 1 << 0 + muted = .boolTrue + } + var videoMuted: Api.Bool? + if let isVideoMuted = isVideoMuted { + videoMuted = isVideoMuted ? .boolTrue : .boolFalse + flags |= 1 << 3 } let raiseHandApi: Api.Bool? if let raiseHand = raiseHand { @@ -1825,7 +1848,7 @@ public final class GroupCallParticipantsContext { raiseHandApi = nil } - return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), participant: inputPeer, volume: volume, raiseHand: raiseHandApi)) + return account.network.request(Api.functions.phone.editGroupCallParticipant(flags: flags, call: .inputGroupCall(id: id, accessHash: accessHash), participant: inputPeer, muted: muted, volume: volume, raiseHand: raiseHandApi, videoMuted: videoMuted)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -1866,11 +1889,11 @@ public final class GroupCallParticipantsContext { } public func raiseHand() { - self.updateMuteState(peerId: self.myPeerId, muteState: nil, volume: nil, raiseHand: true) + self.updateMuteState(peerId: self.myPeerId, muteState: nil, isVideoMuted: nil, volume: nil, raiseHand: true) } public func lowerHand() { - self.updateMuteState(peerId: self.myPeerId, muteState: nil, volume: nil, raiseHand: false) + self.updateMuteState(peerId: self.myPeerId, muteState: nil, isVideoMuted: nil, volume: nil, raiseHand: false) } public func updateShouldBeRecording(_ shouldBeRecording: Bool, title: String?) { @@ -1985,6 +2008,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { jsonParams = data } } + let isVideoMuted = (flags & (1 << 14)) != 0 self.init( peerId: peerId, @@ -1994,6 +2018,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate { activityTimestamp: activeDate.flatMap(Double.init), raiseHandRating: raiseHandRating, muteState: muteState, + isVideoMuted: isVideoMuted, participationStatusChange: participationStatusChange, volume: volume, about: about, @@ -2041,6 +2066,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate { jsonParams = data } } + let isVideoMuted = (flags & (1 << 14)) != 0 participantUpdates.append(GroupCallParticipantsContext.Update.StateUpdate.ParticipantUpdate( peerId: peerId, @@ -2050,6 +2076,7 @@ extension GroupCallParticipantsContext.Update.StateUpdate { activityTimestamp: activeDate.flatMap(Double.init), raiseHandRating: raiseHandRating, muteState: muteState, + isVideoMuted: isVideoMuted, participationStatusChange: participationStatusChange, volume: volume, about: about, diff --git a/submodules/TelegramCore/Sources/MacInternalUpdater.swift b/submodules/TelegramCore/Sources/MacInternalUpdater.swift index f185d80ce8..a54dd51ab4 100644 --- a/submodules/TelegramCore/Sources/MacInternalUpdater.swift +++ b/submodules/TelegramCore/Sources/MacInternalUpdater.swift @@ -12,7 +12,7 @@ public enum InternalUpdaterError { } public func requestUpdatesXml(account: Account, source: String) -> Signal { - return resolvePeerByName(account: account, name: source) + return TelegramEngine(account: account).peers.resolvePeerByName(name: source) |> castError(InternalUpdaterError.self) |> mapToSignal { peerId -> Signal in return account.postbox.transaction { transaction in @@ -79,7 +79,7 @@ public enum AppUpdateDownloadResult { } public func downloadAppUpdate(account: Account, source: String, messageId: Int32) -> Signal { - return resolvePeerByName(account: account, name: source) + return TelegramEngine(account: account).peers.resolvePeerByName(name: source) |> castError(InternalUpdaterError.self) |> mapToSignal { peerId -> Signal in return account.postbox.transaction { transaction in diff --git a/submodules/TelegramCore/Sources/MarkMessageContentAsConsumedInteractively.swift b/submodules/TelegramCore/Sources/MarkMessageContentAsConsumedInteractively.swift index c302433985..53a1202972 100644 --- a/submodules/TelegramCore/Sources/MarkMessageContentAsConsumedInteractively.swift +++ b/submodules/TelegramCore/Sources/MarkMessageContentAsConsumedInteractively.swift @@ -32,7 +32,7 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI var globallyUniqueIds: [Int64] = [] if let globallyUniqueId = message.globallyUniqueId { globallyUniqueIds.append(globallyUniqueId) - let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: message.id.peerId, operation: SecretChatOutgoingOperationContents.readMessagesContent(layer: layer, actionGloballyUniqueId: arc4random64(), globallyUniqueIds: globallyUniqueIds), state: state) + let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: message.id.peerId, operation: SecretChatOutgoingOperationContents.readMessagesContent(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), globallyUniqueIds: globallyUniqueIds), state: state) if updatedState != state { transaction.setPeerChatState(message.id.peerId, state: updatedState) } @@ -75,7 +75,7 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI } if let state = state, let layer = layer, let globallyUniqueId = message.globallyUniqueId { - let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: messageId.peerId, operation: .readMessagesContent(layer: layer, actionGloballyUniqueId: arc4random64(), globallyUniqueIds: [globallyUniqueId]), state: state) + let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: messageId.peerId, operation: .readMessagesContent(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), globallyUniqueIds: [globallyUniqueId]), state: state) if updatedState != state { transaction.setPeerChatState(messageId.peerId, state: updatedState) } @@ -106,7 +106,7 @@ public func markMessageContentAsConsumedInteractively(postbox: Postbox, messageI } if let state = state, let layer = layer, let globallyUniqueId = message.globallyUniqueId { - let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: messageId.peerId, operation: .readMessagesContent(layer: layer, actionGloballyUniqueId: arc4random64(), globallyUniqueIds: [globallyUniqueId]), state: state) + let updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: messageId.peerId, operation: .readMessagesContent(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), globallyUniqueIds: [globallyUniqueId]), state: state) if updatedState != state { transaction.setPeerChatState(messageId.peerId, state: updatedState) } diff --git a/submodules/TelegramCore/Sources/MultipartFetch.swift b/submodules/TelegramCore/Sources/MultipartFetch.swift index 5a1fe45827..b789090c65 100644 --- a/submodules/TelegramCore/Sources/MultipartFetch.swift +++ b/submodules/TelegramCore/Sources/MultipartFetch.swift @@ -445,7 +445,7 @@ private final class MultipartFetchManager { init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int32?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, partReady: @escaping (Int, Data) -> Void, reportCompleteSize: @escaping (Int) -> Void) { self.resource = resource self.parameters = parameters - self.consumerId = arc4random64() + self.consumerId = Int64.random(in: Int64.min ... Int64.max) self.completeSize = size if let size = size { diff --git a/submodules/TelegramCore/Sources/MultipartUpload.swift b/submodules/TelegramCore/Sources/MultipartUpload.swift index 359f815c11..7ac8cb2005 100644 --- a/submodules/TelegramCore/Sources/MultipartUpload.swift +++ b/submodules/TelegramCore/Sources/MultipartUpload.swift @@ -395,7 +395,7 @@ func multipartUpload(network: Network, postbox: Postbox, source: MultipartUpload let uploadInterface: Signal if useMultiplexedRequests { - uploadInterface = .single(.multiplexed(manager: network.multiplexedRequestManager, datacenterId: network.datacenterId, consumerId: arc4random64())) + uploadInterface = .single(.multiplexed(manager: network.multiplexedRequestManager, datacenterId: network.datacenterId, consumerId: Int64.random(in: Int64.min ... Int64.max))) } else { uploadInterface = network.upload(tag: tag) |> map { download -> UploadInterface in diff --git a/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift b/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift index c25e8dfb48..9b186b1fd0 100644 --- a/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift +++ b/submodules/TelegramCore/Sources/OutgoingMessageWithChatContextResult.swift @@ -4,7 +4,7 @@ import SwiftSignalKit import SyncCore -public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: ChatContextResultCollection, result: ChatContextResult, hideVia: Bool = false, scheduleTime: Int32? = nil) -> EnqueueMessage? { +public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: ChatContextResultCollection, result: ChatContextResult, hideVia: Bool = false, scheduleTime: Int32? = nil, correlationId: Int64? = nil) -> EnqueueMessage? { var attributes: [MessageAttribute] = [] attributes.append(OutgoingChatContextResultMessageAttribute(queryId: result.queryId, id: result.id, hideVia: hideVia)) if !hideVia { @@ -32,19 +32,19 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha return true } if let media: Media = internalReference.file ?? internalReference.image { - return .message(text: caption, attributes: filteredAttributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: filteredAttributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } else { - return .message(text: caption, attributes: filteredAttributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: filteredAttributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } } else { - return .message(text: "", attributes: attributes, mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: internalReference.title ?? "", description: internalReference.description ?? "", image: internalReference.image, file: internalReference.file)), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: "", attributes: attributes, mediaReference: .standalone(media: TelegramMediaGame(gameId: 0, accessHash: 0, name: "", title: internalReference.title ?? "", description: internalReference.description ?? "", image: internalReference.image, file: internalReference.file)), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } } else if let file = internalReference.file, internalReference.type == "gif" { - return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } else if let image = internalReference.image { - return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: image), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: image), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } else if let file = internalReference.file { - return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } else { return nil } @@ -56,9 +56,9 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha let thumbnailResource = thumbnail.resource let imageDimensions = thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128) let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []) - return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: tmpImage), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: tmpImage), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } else { - return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } } else if externalReference.type == "document" || externalReference.type == "gif" || externalReference.type == "audio" || externalReference.type == "voice" { var videoThumbnails: [TelegramMediaFile.VideoThumbnail] = [] @@ -118,9 +118,9 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha } let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: videoThumbnails, immediateThumbnailData: nil, mimeType: externalReference.content?.mimeType ?? "application/binary", size: nil, attributes: fileAttributes) - return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } else { - return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil) + return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } } case let .text(text, entities, disableUrlPreview, replyMarkup): @@ -130,21 +130,21 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha if let replyMarkup = replyMarkup { attributes.append(replyMarkup) } - return .message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil) + return .message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) case let .mapLocation(media, replyMarkup): if let replyMarkup = replyMarkup { attributes.append(replyMarkup) } - return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) case let .contact(media, replyMarkup): if let replyMarkup = replyMarkup { attributes.append(replyMarkup) } - return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) case let .invoice(media, replyMarkup): if let replyMarkup = replyMarkup { attributes.append(replyMarkup) } - return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) + return .message(text: "", attributes: attributes, mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: correlationId) } } diff --git a/submodules/TelegramCore/Sources/PeerSpecificStickerPack.swift b/submodules/TelegramCore/Sources/PeerSpecificStickerPack.swift index 0a9cea6eb7..acd61c4511 100644 --- a/submodules/TelegramCore/Sources/PeerSpecificStickerPack.swift +++ b/submodules/TelegramCore/Sources/PeerSpecificStickerPack.swift @@ -31,7 +31,7 @@ public func peerSpecificStickerPack(postbox: Postbox, network: Network, peerId: return signal |> mapToSignal { info, canInstall -> Signal in if let info = info.info { - return cachedStickerPack(postbox: postbox, network: network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceRemote: false) + return _internal_cachedStickerPack(postbox: postbox, network: network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceRemote: false) |> map { result -> PeerSpecificStickerPackData in if case let .result(info, items, _) = result { return PeerSpecificStickerPackData(packInfo: (info, items), canSetup: canInstall) diff --git a/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift index 0de3aa8a3a..08dc51b7df 100644 --- a/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessageUploadedContent.swift @@ -374,7 +374,7 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia])) } else { - updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: arc4random64(), flags: [.transformedMedia], acknowledged: false)) + updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil)) } return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) }) @@ -664,7 +664,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili let attribute = updatedAttributes[index] as! OutgoingMessageInfoAttribute updatedAttributes[index] = attribute.withUpdatedFlags(attribute.flags.union([.transformedMedia])) } else { - updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: arc4random64(), flags: [.transformedMedia], acknowledged: false)) + updatedAttributes.append(OutgoingMessageInfoAttribute(uniqueId: Int64.random(in: Int64.min ... Int64.max), flags: [.transformedMedia], acknowledged: false, correlationId: nil)) } return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: updatedAttributes, media: currentMessage.media)) }) diff --git a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift index 4bd4507122..dafc41c794 100644 --- a/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/ProcessSecretChatIncomingDecryptedOperations.swift @@ -190,7 +190,7 @@ func processSecretChatIncomingDecryptedOperations(encryptionProvider: Encryption let role = updatedState.role let fromSeqNo: Int32 = (topProcessedCanonicalIncomingOperationIndex + 1) * 2 + (role == .creator ? 0 : 1) let toSeqNo: Int32 = (canonicalIncomingIndex - 1) * 2 + (role == .creator ? 0 : 1) - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.resendOperations(layer: layer, actionGloballyUniqueId: arc4random64(), fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.resendOperations(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), state: updatedState) } else { assertionFailure() } @@ -204,7 +204,7 @@ func processSecretChatIncomingDecryptedOperations(encryptionProvider: Encryption let role = updatedState.role let fromSeqNo: Int32 = Int32(0 * 2) + (role == .creator ? Int32(0) : Int32(1)) let toSeqNo: Int32 = (canonicalIncomingIndex - 1) * 2 + (role == .creator ? 0 : 1) - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.resendOperations(layer: layer, actionGloballyUniqueId: arc4random64(), fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: SecretChatOutgoingOperationContents.resendOperations(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), fromSeqNo: fromSeqNo, toSeqNo: toSeqNo), state: updatedState) } else { assertionFailure() } @@ -230,15 +230,15 @@ func processSecretChatIncomingDecryptedOperations(encryptionProvider: Encryption if layerSupport >= 101 { let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: .layer101, locallyRequestedLayer: 101, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer101, actionGloballyUniqueId: arc4random64(), layerSupport: 101), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer101, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), layerSupport: 101), state: updatedState) } else if layerSupport >= 73 { let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: .layer73, locallyRequestedLayer: 73, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer73, actionGloballyUniqueId: arc4random64(), layerSupport: 101), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer73, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), layerSupport: 101), state: updatedState) } else if layerSupport >= 46 { let sequenceBasedLayerState = SecretChatSequenceBasedLayerState(layerNegotiationState: SecretChatLayerNegotiationState(activeLayer: .layer46, locallyRequestedLayer: 46, remotelyRequestedLayer: layerSupport), rekeyState: nil, baseIncomingOperationIndex: entry.tagLocalIndex, baseOutgoingOperationIndex: transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing), topProcessedCanonicalIncomingOperationIndex: nil) updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceBasedLayerState)) - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer46, actionGloballyUniqueId: arc4random64(), layerSupport: 101), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer46, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), layerSupport: 101), state: updatedState) } else { throw MessageParsingError.contentParsingError } @@ -711,7 +711,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 if let file = file { var representations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -737,7 +737,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -752,7 +752,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: PixelDimensions(width: w, height: h), flags: []), .FileName(fileName: "video.mov")] var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -792,7 +792,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes) parsedMedia.append(fileMedia) case let .decryptedMessageMediaWebPage(url): - parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url))) + parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: Int64.random(in: Int64.min ... Int64.max)), content: .Pending(0, url))) case let .decryptedMessageMediaGeoPoint(lat, long): parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)) case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId): @@ -913,7 +913,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 if let file = file { var representations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -940,7 +940,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -971,7 +971,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: PixelDimensions(width: w, height: h), flags: []), .FileName(fileName: "video.mov")] var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -1011,7 +1011,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes) parsedMedia.append(fileMedia) case let .decryptedMessageMediaWebPage(url): - parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url))) + parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: Int64.random(in: Int64.min ... Int64.max)), content: .Pending(0, url))) case let .decryptedMessageMediaGeoPoint(lat, long): parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)) case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId): @@ -1151,7 +1151,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 if let file = file { var representations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -1178,7 +1178,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -1209,7 +1209,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let parsedAttributes: [TelegramMediaFileAttribute] = [.Video(duration: Int(duration), size: PixelDimensions(width: w, height: h), flags: []), .FileName(fileName: "video.mov")] var previewRepresentations: [TelegramMediaImageRepresentation] = [] if thumb.size != 0 { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) previewRepresentations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: thumbW, height: thumbH), resource: resource, progressiveSizes: [], immediateThumbnailData: nil)) resources.append((resource, thumb.makeData())) } @@ -1249,7 +1249,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 let fileMedia = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.CloudFile, id: id), partialReference: nil, resource: CloudDocumentMediaResource(datacenterId: Int(dcId), fileId: id, accessHash: accessHash, size: Int(size), fileReference: nil, fileName: nil), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: parsedAttributes) parsedMedia.append(fileMedia) case let .decryptedMessageMediaWebPage(url): - parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: arc4random64()), content: .Pending(0, url))) + parsedMedia.append(TelegramMediaWebpage(webpageId: MediaId(namespace: Namespaces.Media.LocalWebpage, id: Int64.random(in: Int64.min ... Int64.max)), content: .Pending(0, url))) case let .decryptedMessageMediaGeoPoint(lat, long): parsedMedia.append(TelegramMediaMap(latitude: lat, longitude: long, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)) case let .decryptedMessageMediaContact(phoneNumber, firstName, lastName, userId): diff --git a/submodules/TelegramCore/Sources/Random.swift b/submodules/TelegramCore/Sources/Random.swift deleted file mode 100644 index 20bf912990..0000000000 --- a/submodules/TelegramCore/Sources/Random.swift +++ /dev/null @@ -1,7 +0,0 @@ -import Foundation - -public func arc4random64() -> Int64 { - var value: Int64 = 0 - arc4random_buf(&value, 8) - return value -} diff --git a/submodules/TelegramCore/Sources/RequestPhoneNumber.swift b/submodules/TelegramCore/Sources/RequestPhoneNumber.swift index 2842feaa2e..575f55a634 100644 --- a/submodules/TelegramCore/Sources/RequestPhoneNumber.swift +++ b/submodules/TelegramCore/Sources/RequestPhoneNumber.swift @@ -12,7 +12,7 @@ public func requestPhoneNumber(account: Account, peerId: PeerId) -> Signal map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramCore/Sources/RequestStartBot.swift b/submodules/TelegramCore/Sources/RequestStartBot.swift index 44e6b1118b..f6eebf7019 100644 --- a/submodules/TelegramCore/Sources/RequestStartBot.swift +++ b/submodules/TelegramCore/Sources/RequestStartBot.swift @@ -11,7 +11,7 @@ public func requestStartBot(account: Account, botPeerId: PeerId, payload: String return account.postbox.loadedPeerWithId(botPeerId) |> mapToSignal { botPeer -> Signal in if let inputUser = apiInputUser(botPeer) { - let r = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: arc4random64(), startParam: payload)) + let r = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: .inputPeerEmpty, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload)) |> mapToSignal { result -> Signal in account.stateManager.addUpdates(result) return .complete() @@ -26,7 +26,7 @@ public func requestStartBot(account: Account, botPeerId: PeerId, payload: String } } } else { - return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]) |> mapToSignal { _ -> Signal in + return enqueueMessages(account: account, peerId: botPeerId, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) |> mapToSignal { _ -> Signal in return .complete() } } @@ -48,7 +48,7 @@ public func requestStartBotInGroup(account: Account, botPeerId: PeerId, groupPee |> mapError { _ -> RequestStartBotInGroupError in return .generic } |> mapToSignal { botPeer, groupPeer -> Signal in if let botPeer = botPeer, let inputUser = apiInputUser(botPeer), let groupPeer = groupPeer, let inputGroup = apiInputPeer(groupPeer) { - let request = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: inputGroup, randomId: arc4random64(), startParam: payload ?? "")) + let request = account.network.request(Api.functions.messages.startBot(bot: inputUser, peer: inputGroup, randomId: Int64.random(in: Int64.min ... Int64.max), startParam: payload ?? "")) |> mapError { _ -> RequestStartBotInGroupError in return .generic } diff --git a/submodules/TelegramCore/Sources/SecretChats/SecretChatLayerNegotiation.swift b/submodules/TelegramCore/Sources/SecretChats/SecretChatLayerNegotiation.swift index ed5c0fbad3..474b62523c 100644 --- a/submodules/TelegramCore/Sources/SecretChats/SecretChatLayerNegotiation.swift +++ b/submodules/TelegramCore/Sources/SecretChats/SecretChatLayerNegotiation.swift @@ -23,11 +23,11 @@ func secretChatAddReportCurrentLayerSupportOperationAndUpdateRequestedLayer(tran switch state.embeddedState { case .basicLayer: var updatedState = state - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer8, actionGloballyUniqueId: arc4random64(), layerSupport: topSupportedLayer.rawValue), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: .layer8, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), layerSupport: topSupportedLayer.rawValue), state: updatedState) return updatedState case let .sequenceBasedLayer(sequenceState): var updatedState = state - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: sequenceState.layerNegotiationState.activeLayer.secretChatLayer, actionGloballyUniqueId: arc4random64(), layerSupport: topSupportedLayer.rawValue), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: sequenceState.layerNegotiationState.activeLayer.secretChatLayer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), layerSupport: topSupportedLayer.rawValue), state: updatedState) updatedState = updatedState.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedLayerNegotiationState(sequenceState.layerNegotiationState.withUpdatedLocallyRequestedLayer(topSupportedLayer.rawValue)))) return updatedState default: diff --git a/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift b/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift index d48e08b32f..d6feff50bf 100644 --- a/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift +++ b/submodules/TelegramCore/Sources/SecretChats/SecretChatRekeySession.swift @@ -16,12 +16,12 @@ func secretChatInitiateRekeySessionIfNeeded(transaction: Transaction, peerId: Pe let tagLocalIndex = transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing) let canonicalIndex = sequenceState.canonicalOutgoingOperationIndex(tagLocalIndex) if let key = state.keychain.latestKey(validForSequenceBasedCanonicalIndex: canonicalIndex), key.useCount >= keyUseCountThreshold { - let sessionId = arc4random64() + let sessionId = Int64.random(in: Int64.min ... Int64.max) let aBytes = malloc(256)! let _ = SecRandomCopyBytes(nil, 256, aBytes.assumingMemoryBound(to: UInt8.self)) let a = MemoryBuffer(memory: aBytes, capacity: 256, length: 256, freeWhenDone: true) - transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsRequestKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: sessionId, a: a), mutable: true, delivered: false)) + transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsRequestKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), rekeySessionId: sessionId, a: a), mutable: true, delivered: false)) return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(SecretChatRekeySessionState(id: sessionId, data: .requesting)))) } default: @@ -69,7 +69,7 @@ func secretChatAdvanceRekeySessionIfNeeded(encryptionProvider: EncryptionProvide assert(remoteKeyFingerprint == keyFingerprint) - transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsCommitKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, keyFingerprint: keyFingerprint), mutable: true, delivered: false)) + transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsCommitKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), rekeySessionId: rekeySession.id, keyFingerprint: keyFingerprint), mutable: true, delivered: false)) let keyValidityOperationIndex = transaction.operationLogGetNextEntryLocalIndex(peerId: peerId, tag: OperationLogTags.SecretOutgoing) let keyValidityOperationCanonicalIndex = sequenceState.canonicalOutgoingOperationIndex(keyValidityOperationIndex) @@ -92,7 +92,7 @@ func secretChatAdvanceRekeySessionIfNeeded(encryptionProvider: EncryptionProvide return SecretChatKey(fingerprint: keyFingerprint, key: key, validity: .sequenceBasedIndexRange(fromCanonicalIndex: keyValidityOperationCanonicalIndex), useCount: 0) })) - transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .noop(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64()), mutable: true, delivered: false)) + transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .noop(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max)), mutable: true, delivered: false)) return updatedState } else { @@ -107,7 +107,7 @@ func secretChatAdvanceRekeySessionIfNeeded(encryptionProvider: EncryptionProvide switch rekeySession.data { case .requesting, .requested: if rekeySessionId < rekeySession.id { - transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAbortSession(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id), mutable: true, delivered: false)) + transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAbortSession(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), rekeySessionId: rekeySession.id), mutable: true, delivered: false)) } else { acceptSession = false } @@ -123,7 +123,7 @@ func secretChatAdvanceRekeySessionIfNeeded(encryptionProvider: EncryptionProvide let rekeySession = SecretChatRekeySessionState(id: rekeySessionId, data: .accepting) - transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAcceptKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: arc4random64(), rekeySessionId: rekeySession.id, gA: gA, b: b), mutable: true, delivered: false)) + transaction.operationLogAddEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: .automatic, tagMergedIndex: .automatic, contents: SecretChatOutgoingOperation(contents: .pfsAcceptKey(layer: sequenceState.layerNegotiationState.activeLayer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), rekeySessionId: rekeySession.id, gA: gA, b: b), mutable: true, delivered: false)) return state.withUpdatedEmbeddedState(.sequenceBasedLayer(sequenceState.withUpdatedRekeyState(rekeySession))) } } diff --git a/submodules/TelegramCore/Sources/SetSecretChatMessageAutoremoveTimeoutInteractively.swift b/submodules/TelegramCore/Sources/SetSecretChatMessageAutoremoveTimeoutInteractively.swift index eb1473f89c..13906bc270 100644 --- a/submodules/TelegramCore/Sources/SetSecretChatMessageAutoremoveTimeoutInteractively.swift +++ b/submodules/TelegramCore/Sources/SetSecretChatMessageAutoremoveTimeoutInteractively.swift @@ -17,7 +17,7 @@ public func setSecretChatMessageAutoremoveTimeoutInteractively(account: Account, transaction.setPeerChatState(peerId, state: updatedState) } - let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.messageAutoremoveTimeoutUpdated(timeout == nil ? 0 : timeout!))), replyToMessageId: nil, localGroupingKey: nil))]) + let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.messageAutoremoveTimeoutUpdated(timeout == nil ? 0 : timeout!))), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))]) } } } @@ -32,7 +32,7 @@ public func addSecretChatMessageScreenshot(account: Account, peerId: PeerId) -> default: break } - let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil))]) + let _ = enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: [(true, .message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaAction(action: TelegramMediaActionType.historyScreenshot)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil))]) } } } diff --git a/submodules/TelegramCore/Sources/AutodownloadSettings.swift b/submodules/TelegramCore/Sources/Settings/AutodownloadSettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/AutodownloadSettings.swift rename to submodules/TelegramCore/Sources/Settings/AutodownloadSettings.swift diff --git a/submodules/TelegramCore/Sources/CacheStorageSettings.swift b/submodules/TelegramCore/Sources/Settings/CacheStorageSettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/CacheStorageSettings.swift rename to submodules/TelegramCore/Sources/Settings/CacheStorageSettings.swift diff --git a/submodules/TelegramCore/Sources/GlobalNotificationSettings.swift b/submodules/TelegramCore/Sources/Settings/GlobalNotificationSettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/GlobalNotificationSettings.swift rename to submodules/TelegramCore/Sources/Settings/GlobalNotificationSettings.swift diff --git a/submodules/TelegramCore/Sources/LimitsConfiguration.swift b/submodules/TelegramCore/Sources/Settings/LimitsConfiguration.swift similarity index 100% rename from submodules/TelegramCore/Sources/LimitsConfiguration.swift rename to submodules/TelegramCore/Sources/Settings/LimitsConfiguration.swift diff --git a/submodules/TelegramCore/Sources/LoggingSettings.swift b/submodules/TelegramCore/Sources/Settings/LoggingSettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/LoggingSettings.swift rename to submodules/TelegramCore/Sources/Settings/LoggingSettings.swift diff --git a/submodules/TelegramCore/Sources/NetworkSettings.swift b/submodules/TelegramCore/Sources/Settings/NetworkSettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/NetworkSettings.swift rename to submodules/TelegramCore/Sources/Settings/NetworkSettings.swift diff --git a/submodules/TelegramCore/Sources/PeerContactSettings.swift b/submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/PeerContactSettings.swift rename to submodules/TelegramCore/Sources/Settings/PeerContactSettings.swift diff --git a/submodules/TelegramCore/Sources/PrivacySettings.swift b/submodules/TelegramCore/Sources/Settings/PrivacySettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/PrivacySettings.swift rename to submodules/TelegramCore/Sources/Settings/PrivacySettings.swift diff --git a/submodules/TelegramCore/Sources/ProxySettings.swift b/submodules/TelegramCore/Sources/Settings/ProxySettings.swift similarity index 100% rename from submodules/TelegramCore/Sources/ProxySettings.swift rename to submodules/TelegramCore/Sources/Settings/ProxySettings.swift diff --git a/submodules/TelegramCore/Sources/VoipConfiguration.swift b/submodules/TelegramCore/Sources/Settings/VoipConfiguration.swift similarity index 100% rename from submodules/TelegramCore/Sources/VoipConfiguration.swift rename to submodules/TelegramCore/Sources/Settings/VoipConfiguration.swift diff --git a/submodules/TelegramCore/Sources/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/StandaloneSendMessage.swift index f43e7c9b91..bbba34b3a7 100644 --- a/submodules/TelegramCore/Sources/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/StandaloneSendMessage.swift @@ -74,7 +74,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M if peerId.namespace == Namespaces.Peer.SecretChat { return .complete() } else if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) { - var uniqueId: Int64 = arc4random64() + var uniqueId: Int64 = Int64.random(in: Int64.min ... Int64.max) //var forwardSourceInfoAttribute: ForwardSourceInfoAttribute? var messageEntities: [Api.MessageEntity]? var replyMessageId: Int32? diff --git a/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift b/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift index 2da2482ab3..16831f3f70 100644 --- a/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift +++ b/submodules/TelegramCore/Sources/StandaloneUploadedMedia.swift @@ -102,7 +102,7 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri |> mapToSignal { result -> Signal in switch result { case let .encryptedFile(id, accessHash, size, dcId, _): - return .single(.result(.media(.standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: Int32(data.count), datacenterId: Int(dcId), key: key), progressiveSizes: [], immediateThumbnailData: nil)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []))))) + return .single(.result(.media(.standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: Int32(data.count), datacenterId: Int(dcId), key: key), progressiveSizes: [], immediateThumbnailData: nil)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []))))) case .encryptedFileEmpty: return .fail(.generic) } @@ -195,7 +195,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin |> mapToSignal { result -> Signal in switch result { case let .encryptedFile(id, accessHash, size, dcId, _): - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: size, datacenterId: Int(dcId), key: key), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: attributes) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: size, datacenterId: Int(dcId), key: key), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int(size), attributes: attributes) return .single(.result(.media(.standalone(media: media)))) case .encryptedFileEmpty: diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index e57096a47b..3117d853df 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -2486,7 +2486,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) if !resourceIds.isEmpty { - let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + let _ = mediaBox.removeCachedResources(Set(resourceIds), force: true).start() } deletedMessageIds.append(contentsOf: ids.map { .global($0) }) case let .DeleteMessages(ids): @@ -2503,7 +2503,7 @@ func replayFinalState(accountManager: AccountManager, postbox: Postbox, accountP addMessageMediaResourceIdsToRemove(media: media, resourceIds: &resourceIds) }) if !resourceIds.isEmpty { - let _ = mediaBox.removeCachedResources(Set(resourceIds)).start() + let _ = mediaBox.removeCachedResources(Set(resourceIds), force: true).start() } case let .UpdatePeerChatInclusion(peerId, groupId, changedGroup): let currentInclusion = transaction.getPeerChatListInclusion(peerId) diff --git a/submodules/TelegramCore/Sources/AppChangelog.swift b/submodules/TelegramCore/Sources/State/AppChangelog.swift similarity index 100% rename from submodules/TelegramCore/Sources/AppChangelog.swift rename to submodules/TelegramCore/Sources/State/AppChangelog.swift diff --git a/submodules/TelegramCore/Sources/AppChangelogState.swift b/submodules/TelegramCore/Sources/State/AppChangelogState.swift similarity index 100% rename from submodules/TelegramCore/Sources/AppChangelogState.swift rename to submodules/TelegramCore/Sources/State/AppChangelogState.swift diff --git a/submodules/TelegramCore/Sources/AppConfiguration.swift b/submodules/TelegramCore/Sources/State/AppConfiguration.swift similarity index 86% rename from submodules/TelegramCore/Sources/AppConfiguration.swift rename to submodules/TelegramCore/Sources/State/AppConfiguration.swift index 827589c8ad..1599a52130 100644 --- a/submodules/TelegramCore/Sources/AppConfiguration.swift +++ b/submodules/TelegramCore/Sources/State/AppConfiguration.swift @@ -1,7 +1,7 @@ import Postbox import SyncCore -public func currentAppConfiguration(transaction: Transaction) -> AppConfiguration { +private func currentAppConfiguration(transaction: Transaction) -> AppConfiguration { if let entry = transaction.getPreferencesEntry(key: PreferencesKeys.appConfiguration) as? AppConfiguration { return entry } else { diff --git a/submodules/TelegramCore/Sources/AppUpdate.swift b/submodules/TelegramCore/Sources/State/AppUpdate.swift similarity index 100% rename from submodules/TelegramCore/Sources/AppUpdate.swift rename to submodules/TelegramCore/Sources/State/AppUpdate.swift diff --git a/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift index bb16cf50fa..1577d99a51 100644 --- a/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift +++ b/submodules/TelegramCore/Sources/State/ManagedAnimatedEmojiUpdates.swift @@ -5,7 +5,7 @@ import TelegramApi import MtProtoKit func managedAnimatedEmojiUpdates(postbox: Postbox, network: Network) -> Signal { - let poll = loadedStickerPack(postbox: postbox, network: network, reference: .animatedEmoji, forceActualized: true) + let poll = _internal_loadedStickerPack(postbox: postbox, network: network, reference: .animatedEmoji, forceActualized: true) |> mapToSignal { _ -> Signal in return .complete() } diff --git a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift index 43907d36b0..618a7b35a8 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSecretChatOutgoingOperations.swift @@ -258,7 +258,7 @@ private func initialHandshakeAccept(postbox: Postbox, network: Network, peerId: layer = sequenceState.layerNegotiationState.activeLayer.secretChatLayer } if let layer = layer { - updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: layer, actionGloballyUniqueId: arc4random64(), layerSupport: 46), state: updatedState) + updatedState = addSecretChatOutgoingOperation(transaction: transaction, peerId: peerId, operation: .reportLayerSupport(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), layerSupport: 46), state: updatedState) } transaction.setPeerChatState(peerId, state: updatedState) if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { @@ -1352,7 +1352,7 @@ private func markOutgoingOperationAsCompleted(transaction: Transaction, peerId: if let operation = entry?.contents as? SecretChatOutgoingOperation { return PeerOperationLogEntryUpdate(mergedIndex: .remove, contents: .update(operation.withUpdatedDelivered(true))) } else { - assertionFailure() + //assertionFailure() return PeerOperationLogEntryUpdate(mergedIndex: .remove, contents: .none) } }) @@ -1377,7 +1377,7 @@ private func replaceOutgoingOperationWithEmptyMessage(transaction: Transaction, if let layer = layer { transaction.operationLogUpdateEntry(peerId: peerId, tag: OperationLogTags.SecretOutgoing, tagLocalIndex: tagLocalIndex, { entry in if let _ = entry?.contents as? SecretChatOutgoingOperation { - return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .update(SecretChatOutgoingOperation(contents: SecretChatOutgoingOperationContents.deleteMessages(layer: layer, actionGloballyUniqueId: arc4random64(), globallyUniqueIds: [globallyUniqueId]), mutable: true, delivered: false))) + return PeerOperationLogEntryUpdate(mergedIndex: .none, contents: .update(SecretChatOutgoingOperation(contents: SecretChatOutgoingOperationContents.deleteMessages(layer: layer, actionGloballyUniqueId: Int64.random(in: Int64.min ... Int64.max), globallyUniqueIds: [globallyUniqueId]), mutable: true, delivered: false))) } else { assertionFailure() return PeerOperationLogEntryUpdate(mergedIndex: .remove, contents: .none) @@ -1519,7 +1519,7 @@ private func sendMessage(auxiliaryMethods: AccountAuxiliaryMethods, postbox: Pos } } } else { - replaceOutgoingOperationWithEmptyMessage(transaction: transaction, peerId: messageId.peerId, tagLocalIndex: tagLocalIndex, globallyUniqueId: arc4random64()) + replaceOutgoingOperationWithEmptyMessage(transaction: transaction, peerId: messageId.peerId, tagLocalIndex: tagLocalIndex, globallyUniqueId: Int64.random(in: Int64.min ... Int64.max)) deleteMessages(transaction: transaction, mediaBox: postbox.mediaBox, ids: [messageId]) return .complete() } @@ -1646,7 +1646,7 @@ private func sendBoxedDecryptedMessage(postbox: Postbox, network: Network, peer: if asService { let actionRandomId: Int64 if wasDelivered { - actionRandomId = arc4random64() + actionRandomId = Int64.random(in: Int64.min ... Int64.max) } else { actionRandomId = globallyUniqueId } diff --git a/submodules/TelegramCore/Sources/ManagedVoipConfigurationUpdates.swift b/submodules/TelegramCore/Sources/State/ManagedVoipConfigurationUpdates.swift similarity index 100% rename from submodules/TelegramCore/Sources/ManagedVoipConfigurationUpdates.swift rename to submodules/TelegramCore/Sources/State/ManagedVoipConfigurationUpdates.swift diff --git a/submodules/TelegramCore/Sources/StickerManagement.swift b/submodules/TelegramCore/Sources/State/StickerManagement.swift similarity index 100% rename from submodules/TelegramCore/Sources/StickerManagement.swift rename to submodules/TelegramCore/Sources/State/StickerManagement.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeAppLogEventsOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeAppLogEventsOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeAppLogEventsOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeAppLogEventsOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeChatInputStateOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeChatInputStateOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeChatInputStateOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeConsumeMessageContentsOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeConsumeMessageContentsOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeConsumeMessageContentsOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeConsumeMessageContentsOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeEmojiKeywordsOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeEmojiKeywordsOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeEmojiKeywordsOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeEmojiKeywordsOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeGroupedPeersOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeGroupedPeersOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeGroupedPeersOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeGroupedPeersOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeInstalledStickerPacksOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeInstalledStickerPacksOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeInstalledStickerPacksOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeInstalledStickerPacksOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeLocalizationUpdatesOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeLocalizationUpdatesOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeLocalizationUpdatesOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeLocalizationUpdatesOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeMarkAllUnseenPersonalMessagesOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizePeerReadState.swift b/submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizePeerReadState.swift rename to submodules/TelegramCore/Sources/State/SynchronizePeerReadState.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeRecentlyUsedMediaOperations.swift b/submodules/TelegramCore/Sources/State/SynchronizeRecentlyUsedMediaOperations.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeRecentlyUsedMediaOperations.swift rename to submodules/TelegramCore/Sources/State/SynchronizeRecentlyUsedMediaOperations.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeSavedGifsOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeSavedGifsOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeSavedGifsOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeSavedGifsOperation.swift diff --git a/submodules/TelegramCore/Sources/SynchronizeSavedStickersOperation.swift b/submodules/TelegramCore/Sources/State/SynchronizeSavedStickersOperation.swift similarity index 100% rename from submodules/TelegramCore/Sources/SynchronizeSavedStickersOperation.swift rename to submodules/TelegramCore/Sources/State/SynchronizeSavedStickersOperation.swift diff --git a/submodules/TelegramCore/Sources/UpdateMessageService.swift b/submodules/TelegramCore/Sources/State/UpdateMessageService.swift similarity index 100% rename from submodules/TelegramCore/Sources/UpdateMessageService.swift rename to submodules/TelegramCore/Sources/State/UpdateMessageService.swift diff --git a/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift new file mode 100644 index 0000000000..38aec73575 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TelegramEngineAccountData.swift @@ -0,0 +1,19 @@ +import SwiftSignalKit + +public extension TelegramEngine { + final class AccountData { + private let account: Account + + init(account: Account) { + self.account = account + } + + public func acceptTermsOfService(id: String) -> Signal { + return _internal_acceptTermsOfService(account: self.account, id: id) + } + + public func resetAccountDueTermsOfService() -> Signal { + return _internal_resetAccountDueTermsOfService(network: self.account.network) + } + } +} diff --git a/submodules/TelegramCore/Sources/TermsOfService.swift b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TermsOfService.swift similarity index 93% rename from submodules/TelegramCore/Sources/TermsOfService.swift rename to submodules/TelegramCore/Sources/TelegramEngine/AccountData/TermsOfService.swift index e3514b3fe9..b0c66a3537 100644 --- a/submodules/TelegramCore/Sources/TermsOfService.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/AccountData/TermsOfService.swift @@ -34,7 +34,7 @@ extension TermsOfServiceUpdate { } } -public func acceptTermsOfService(account: Account, id: String) -> Signal { +func _internal_acceptTermsOfService(account: Account, id: String) -> Signal { return account.network.request(Api.functions.help.acceptTermsOfService(id: .dataJSON(data: id))) |> `catch` { _ -> Signal in return .complete() @@ -45,7 +45,7 @@ public func acceptTermsOfService(account: Account, id: String) -> Signal Signal { +func _internal_resetAccountDueTermsOfService(network: Network) -> Signal { return network.request(Api.functions.account.deleteAccount(reason: "Decline ToS update")) |> retryRequest |> map { _ in return } diff --git a/submodules/TelegramCore/Sources/AuthTransfer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Auth/AuthTransfer.swift similarity index 97% rename from submodules/TelegramCore/Sources/AuthTransfer.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Auth/AuthTransfer.swift index 89068b6b9e..0e5e086488 100644 --- a/submodules/TelegramCore/Sources/AuthTransfer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Auth/AuthTransfer.swift @@ -21,7 +21,7 @@ public enum ExportAuthTransferTokenResult { case passwordRequested(UnauthorizedAccount) } -public func exportAuthTransferToken(accountManager: AccountManager, account: UnauthorizedAccount, otherAccountUserIds: [PeerId.Id], syncContacts: Bool) -> Signal { +func _internal_exportAuthTransferToken(accountManager: AccountManager, account: UnauthorizedAccount, otherAccountUserIds: [PeerId.Id], syncContacts: Bool) -> Signal { return account.network.request(Api.functions.auth.exportLoginToken(apiId: account.networkArguments.apiId, apiHash: account.networkArguments.apiHash, exceptIds: otherAccountUserIds.map({ $0._internalGetInt32Value() }))) |> map(Optional.init) |> `catch` { error -> Signal in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Auth/TelegramEngineAuth.swift b/submodules/TelegramCore/Sources/TelegramEngine/Auth/TelegramEngineAuth.swift new file mode 100644 index 0000000000..d9dbf133d0 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Auth/TelegramEngineAuth.swift @@ -0,0 +1,39 @@ +import SwiftSignalKit +import Postbox +import TelegramApi + +public extension TelegramEngineUnauthorized { + final class Auth { + private let account: UnauthorizedAccount + + init(account: UnauthorizedAccount) { + self.account = account + } + + public func exportAuthTransferToken(accountManager: AccountManager, otherAccountUserIds: [PeerId.Id], syncContacts: Bool) -> Signal { + return _internal_exportAuthTransferToken(accountManager: accountManager, account: self.account, otherAccountUserIds: otherAccountUserIds, syncContacts: syncContacts) + } + } +} + +public enum DeleteAccountError { + case generic +} + +public extension TelegramEngine { + final class Auth { + private let account: Account + + init(account: Account) { + self.account = account + } + + public func deleteAccount() -> Signal { + return self.account.network.request(Api.functions.account.deleteAccount(reason: "GDPR")) + |> mapError { _ -> DeleteAccountError in + return .generic + } + |> ignoreValues + } + } +} diff --git a/submodules/TelegramCore/Sources/Countries.swift b/submodules/TelegramCore/Sources/TelegramEngine/Localization/Countries.swift similarity index 97% rename from submodules/TelegramCore/Sources/Countries.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Localization/Countries.swift index 6ba3de2611..42fd67735d 100644 --- a/submodules/TelegramCore/Sources/Countries.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Localization/Countries.swift @@ -102,7 +102,7 @@ public final class CountriesList: PreferencesEntry, Equatable { } -public func getCountriesList(accountManager: AccountManager, network: Network, langCode: String?, forceUpdate: Bool = false) -> Signal<[Country], NoError> { +func _internal_getCountriesList(accountManager: AccountManager, network: Network, langCode: String?, forceUpdate: Bool = false) -> Signal<[Country], NoError> { let fetch: ([Country]?, Int32?) -> Signal<[Country], NoError> = { current, hash in return network.request(Api.functions.help.getCountriesList(langCode: langCode ?? "", hash: hash ?? 0)) |> retryRequest diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Localization/TelegramEngineLocalization.swift b/submodules/TelegramCore/Sources/TelegramEngine/Localization/TelegramEngineLocalization.swift new file mode 100644 index 0000000000..3f35e99178 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Localization/TelegramEngineLocalization.swift @@ -0,0 +1,30 @@ +import SwiftSignalKit +import Postbox + +public extension TelegramEngine { + final class Localization { + private let account: Account + + init(account: Account) { + self.account = account + } + + public func getCountriesList(accountManager: AccountManager, langCode: String?, forceUpdate: Bool = false) -> Signal<[Country], NoError> { + return _internal_getCountriesList(accountManager: accountManager, network: self.account.network, langCode: langCode, forceUpdate: forceUpdate) + } + } +} + +public extension TelegramEngineUnauthorized { + final class Localization { + private let account: UnauthorizedAccount + + init(account: UnauthorizedAccount) { + self.account = account + } + + public func getCountriesList(accountManager: AccountManager, langCode: String?, forceUpdate: Bool = false) -> Signal<[Country], NoError> { + return _internal_getCountriesList(accountManager: accountManager, network: self.account.network, langCode: langCode, forceUpdate: forceUpdate) + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/PeerManagement/TelegramEnginePeerManagement.swift b/submodules/TelegramCore/Sources/TelegramEngine/PeerManagement/TelegramEnginePeerManagement.swift new file mode 100644 index 0000000000..112d788c1d --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/PeerManagement/TelegramEnginePeerManagement.swift @@ -0,0 +1,13 @@ +import SwiftSignalKit + +public extension TelegramEngine { + final class PeerManagement { + private let account: Account + + init(account: Account) { + self.account = account + } + + + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/PeerNames/AddressNames.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift similarity index 100% rename from submodules/TelegramCore/Sources/TelegramEngine/PeerNames/AddressNames.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Peers/AddressNames.swift diff --git a/submodules/TelegramCore/Sources/TelegramEngine/PeerNames/FindChannelById.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/FindChannelById.swift similarity index 100% rename from submodules/TelegramCore/Sources/TelegramEngine/PeerNames/FindChannelById.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Peers/FindChannelById.swift diff --git a/submodules/TelegramCore/Sources/InactiveChannels.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift similarity index 94% rename from submodules/TelegramCore/Sources/InactiveChannels.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift index 9d36592941..718fd44925 100644 --- a/submodules/TelegramCore/Sources/InactiveChannels.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/InactiveChannels.swift @@ -19,7 +19,7 @@ public struct InactiveChannel : Equatable { } } -public func inactiveChannelList(network: Network) -> Signal<[InactiveChannel], NoError> { +func _internal_inactiveChannelList(network: Network) -> Signal<[InactiveChannel], NoError> { return network.request(Api.functions.channels.getInactiveChannels()) |> retryRequest |> map { result in diff --git a/submodules/TelegramCore/Sources/ResolvePeerByName.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ResolvePeerByName.swift similarity index 96% rename from submodules/TelegramCore/Sources/ResolvePeerByName.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Peers/ResolvePeerByName.swift index 2e4f7ec625..1b0c01f0b4 100644 --- a/submodules/TelegramCore/Sources/ResolvePeerByName.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ResolvePeerByName.swift @@ -18,7 +18,7 @@ public enum ResolvePeerByNameOptionRemote { case update } -public func resolvePeerByName(account: Account, name: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal { +func _internal_resolvePeerByName(account: Account, name: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal { var normalizedName = name if normalizedName.hasPrefix("@") { normalizedName = String(normalizedName[name.index(after: name.startIndex)...]) diff --git a/submodules/TelegramCore/Sources/SearchPeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift similarity index 97% rename from submodules/TelegramCore/Sources/SearchPeers.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift index 5276cffa85..0cd869c7d2 100644 --- a/submodules/TelegramCore/Sources/SearchPeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SearchPeers.swift @@ -20,7 +20,7 @@ public struct FoundPeer: Equatable { } } -public func searchPeers(account: Account, query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> { +func _internal_searchPeers(account: Account, query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> { let searchResult = account.network.request(Api.functions.contacts.search(q: query, limit: 20), automaticFloodWait: false) |> map(Optional.init) |> `catch` { _ in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/PeerNames/SupportPeerId.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/SupportPeerId.swift similarity index 100% rename from submodules/TelegramCore/Sources/TelegramEngine/PeerNames/SupportPeerId.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Peers/SupportPeerId.swift diff --git a/submodules/TelegramCore/Sources/TelegramEngine/PeerNames/TelegramEnginePeerNames.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift similarity index 80% rename from submodules/TelegramCore/Sources/TelegramEngine/PeerNames/TelegramEnginePeerNames.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 23f13ebec6..2a65d8f46e 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/PeerNames/TelegramEnginePeerNames.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -8,7 +8,7 @@ public enum AddressNameValidationStatus: Equatable { } public extension TelegramEngine { - final class PeerNames { + final class Peers { private let account: Account init(account: Account) { @@ -57,5 +57,17 @@ public extension TelegramEngine { public func supportPeerId() -> Signal { return _internal_supportPeerId(account: self.account) } + + public func inactiveChannelList() -> Signal<[InactiveChannel], NoError> { + return _internal_inactiveChannelList(network: self.account.network) + } + + public func resolvePeerByName(name: String, ageLimit: Int32 = 2 * 60 * 60 * 24) -> Signal { + return _internal_resolvePeerByName(account: self.account, name: name, ageLimit: ageLimit) + } + + public func searchPeers(query: String) -> Signal<([FoundPeer], [FoundPeer]), NoError> { + return _internal_searchPeers(account: self.account, query: query) + } } } diff --git a/submodules/TelegramCore/Sources/GrantSecureIdAccess.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/GrantSecureIdAccess.swift similarity index 100% rename from submodules/TelegramCore/Sources/GrantSecureIdAccess.swift rename to submodules/TelegramCore/Sources/TelegramEngine/SecureId/GrantSecureIdAccess.swift diff --git a/submodules/TelegramCore/Sources/RequestSecureIdForm.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/RequestSecureIdForm.swift similarity index 100% rename from submodules/TelegramCore/Sources/RequestSecureIdForm.swift rename to submodules/TelegramCore/Sources/TelegramEngine/SecureId/RequestSecureIdForm.swift diff --git a/submodules/TelegramCore/Sources/SaveSecureIdValue.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/SaveSecureIdValue.swift similarity index 100% rename from submodules/TelegramCore/Sources/SaveSecureIdValue.swift rename to submodules/TelegramCore/Sources/TelegramEngine/SecureId/SaveSecureIdValue.swift diff --git a/submodules/TelegramCore/Sources/UploadSecureIdFile.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/UploadSecureIdFile.swift similarity index 100% rename from submodules/TelegramCore/Sources/UploadSecureIdFile.swift rename to submodules/TelegramCore/Sources/TelegramEngine/SecureId/UploadSecureIdFile.swift diff --git a/submodules/TelegramCore/Sources/VerifySecureIdValue.swift b/submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift similarity index 100% rename from submodules/TelegramCore/Sources/VerifySecureIdValue.swift rename to submodules/TelegramCore/Sources/TelegramEngine/SecureId/VerifySecureIdValue.swift diff --git a/submodules/TelegramCore/Sources/ArchivedStickerPacks.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ArchivedStickerPacks.swift similarity index 87% rename from submodules/TelegramCore/Sources/ArchivedStickerPacks.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Stickers/ArchivedStickerPacks.swift index 5ffd9cb9b8..dac1149970 100644 --- a/submodules/TelegramCore/Sources/ArchivedStickerPacks.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ArchivedStickerPacks.swift @@ -29,7 +29,7 @@ public final class ArchivedStickerPackItem { } } -public func archivedStickerPacks(account: Account, namespace: ArchivedStickerPacksNamespace = .stickers) -> Signal<[ArchivedStickerPackItem], NoError> { +func _internal_archivedStickerPacks(account: Account, namespace: ArchivedStickerPacksNamespace = .stickers) -> Signal<[ArchivedStickerPackItem], NoError> { var flags: Int32 = 0 if case .masks = namespace { flags |= 1 << 0 @@ -50,7 +50,7 @@ public func archivedStickerPacks(account: Account, namespace: ArchivedStickerPac } } -public func removeArchivedStickerPack(account: Account, info: StickerPackCollectionInfo) -> Signal { +func _internal_removeArchivedStickerPack(account: Account, info: StickerPackCollectionInfo) -> Signal { return account.network.request(Api.functions.messages.uninstallStickerSet(stickerset: Api.InputStickerSet.inputStickerSetID(id: info.id.id, accessHash: info.accessHash))) |> `catch` { _ -> Signal in return .single(.boolFalse) diff --git a/submodules/TelegramCore/Sources/CachedStickerPack.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/CachedStickerPack.swift similarity index 98% rename from submodules/TelegramCore/Sources/CachedStickerPack.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Stickers/CachedStickerPack.swift index 3362bb657f..f9b31ebe8a 100644 --- a/submodules/TelegramCore/Sources/CachedStickerPack.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/CachedStickerPack.swift @@ -36,7 +36,7 @@ func cacheStickerPack(transaction: Transaction, info: StickerPackCollectionInfo, } } -public func cachedStickerPack(postbox: Postbox, network: Network, reference: StickerPackReference, forceRemote: Bool) -> Signal { +func _internal_cachedStickerPack(postbox: Postbox, network: Network, reference: StickerPackReference, forceRemote: Bool) -> Signal { return postbox.transaction { transaction -> CachedStickerPackResult? in if let (info, items, local) = cachedStickerPack(transaction: transaction, reference: reference) { if local { diff --git a/submodules/TelegramCore/Sources/EmojiKeywords.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/EmojiKeywords.swift similarity index 96% rename from submodules/TelegramCore/Sources/EmojiKeywords.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Stickers/EmojiKeywords.swift index 03f7d35eb8..7dd137a10f 100644 --- a/submodules/TelegramCore/Sources/EmojiKeywords.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/EmojiKeywords.swift @@ -11,7 +11,7 @@ private enum SearchEmojiKeywordsIntermediateResult { case completed([EmojiKeywordItem]) } -public func searchEmojiKeywords(postbox: Postbox, inputLanguageCode: String, query: String, completeMatch: Bool) -> Signal<[EmojiKeywordItem], NoError> { +func _internal_searchEmojiKeywords(postbox: Postbox, inputLanguageCode: String, query: String, completeMatch: Bool) -> Signal<[EmojiKeywordItem], NoError> { guard !query.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return .single([]) } diff --git a/submodules/TelegramCore/Sources/LoadedStickerPack.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/LoadedStickerPack.swift similarity index 93% rename from submodules/TelegramCore/Sources/LoadedStickerPack.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Stickers/LoadedStickerPack.swift index 53eb8104a4..f3349c812e 100644 --- a/submodules/TelegramCore/Sources/LoadedStickerPack.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/LoadedStickerPack.swift @@ -95,8 +95,8 @@ func updatedRemoteStickerPack(postbox: Postbox, network: Network, reference: Sti } } -public func loadedStickerPack(postbox: Postbox, network: Network, reference: StickerPackReference, forceActualized: Bool) -> Signal { - return cachedStickerPack(postbox: postbox, network: network, reference: reference, forceRemote: forceActualized) +func _internal_loadedStickerPack(postbox: Postbox, network: Network, reference: StickerPackReference, forceActualized: Bool) -> Signal { + return _internal_cachedStickerPack(postbox: postbox, network: network, reference: reference, forceRemote: forceActualized) |> map { result -> LoadedStickerPack in switch result { case .none: diff --git a/submodules/TelegramCore/Sources/SearchStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/SearchStickers.swift similarity index 95% rename from submodules/TelegramCore/Sources/SearchStickers.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Stickers/SearchStickers.swift index 4f2e712a6e..248e79ee64 100644 --- a/submodules/TelegramCore/Sources/SearchStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/SearchStickers.swift @@ -58,7 +58,7 @@ public struct SearchStickersScope: OptionSet { public static let remote = SearchStickersScope(rawValue: 1 << 1) } -public func randomGreetingSticker(account: Account) -> Signal { +func _internal_randomGreetingSticker(account: Account) -> Signal { return account.postbox.transaction { transaction -> FoundStickerItem? in var stickerItems: [FoundStickerItem] = [] for entry in transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudGreetingStickers) { @@ -70,7 +70,7 @@ public func randomGreetingSticker(account: Account) -> Signal Signal<[FoundStickerItem], NoError> { +func _internal_searchStickers(account: Account, query: String, scope: SearchStickersScope = [.installed, .remote]) -> Signal<[FoundStickerItem], NoError> { if scope.isEmpty { return .single([]) } @@ -265,7 +265,7 @@ public struct FoundStickerSets { } } -public func searchStickerSetsRemotely(network: Network, query: String) -> Signal { +func _internal_searchStickerSetsRemotely(network: Network, query: String) -> Signal { return network.request(Api.functions.messages.searchStickerSets(flags: 0, q: query, hash: 0)) |> mapError {_ in} |> mapToSignal { value in @@ -291,7 +291,7 @@ public func searchStickerSetsRemotely(network: Network, query: String) -> Signal } } -public func searchStickerSets(postbox: Postbox, query: String) -> Signal { +func _internal_searchStickerSets(postbox: Postbox, query: String) -> Signal { return postbox.transaction { transaction -> Signal in let infos = transaction.getItemCollectionsInfos(namespace: Namespaces.ItemCollection.CloudStickerPacks) @@ -324,12 +324,12 @@ public func searchStickerSets(postbox: Postbox, query: String) -> Signal switchToLatest } -public func searchGifs(account: Account, query: String, nextOffset: String = "") -> Signal { +func _internal_searchGifs(account: Account, query: String, nextOffset: String = "") -> Signal { return account.postbox.transaction { transaction -> String in let configuration = currentSearchBotsConfiguration(transaction: transaction) return configuration.gifBotUsername ?? "gif" } |> mapToSignal { - return resolvePeerByName(account: account, name: $0) + return _internal_resolvePeerByName(account: account, name: $0) } |> filter { $0 != nil } |> map { $0! } |> mapToSignal { peerId -> Signal in diff --git a/submodules/TelegramCore/Sources/StickerPack.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift similarity index 98% rename from submodules/TelegramCore/Sources/StickerPack.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift index 921b580e6e..61e247260f 100644 --- a/submodules/TelegramCore/Sources/StickerPack.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPack.swift @@ -58,7 +58,7 @@ extension StickerPackCollectionInfo { } } -public func stickerPacksAttachedToMedia(account: Account, media: AnyMediaReference) -> Signal<[StickerPackReference], NoError> { +func _internal_stickerPacksAttachedToMedia(account: Account, media: AnyMediaReference) -> Signal<[StickerPackReference], NoError> { let inputMedia: Api.InputStickeredMedia let resourceReference: MediaResourceReference if let imageReference = media.concrete(TelegramMediaImage.self), let reference = imageReference.media.reference, case let .cloud(imageId, accessHash, fileReference) = reference, let representation = largestImageRepresentation(imageReference.media.representations) { diff --git a/submodules/TelegramCore/Sources/StickerPackInteractiveOperations.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPackInteractiveOperations.swift similarity index 86% rename from submodules/TelegramCore/Sources/StickerPackInteractiveOperations.swift rename to submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPackInteractiveOperations.swift index d923975fe1..1a4d0f8c21 100644 --- a/submodules/TelegramCore/Sources/StickerPackInteractiveOperations.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/StickerPackInteractiveOperations.swift @@ -4,7 +4,7 @@ import SwiftSignalKit import SyncCore -public func addStickerPackInteractively(postbox: Postbox, info: StickerPackCollectionInfo, items: [ItemCollectionItem], positionInList: Int? = nil) -> Signal { +func _internal_addStickerPackInteractively(postbox: Postbox, info: StickerPackCollectionInfo, items: [ItemCollectionItem], positionInList: Int? = nil) -> Signal { return postbox.transaction { transaction -> Void in let namespace: SynchronizeInstalledStickerPacksOperationNamespace? switch info.id.namespace { @@ -44,11 +44,11 @@ public enum RemoveStickerPackOption { case archive } -public func removeStickerPackInteractively(postbox: Postbox, id: ItemCollectionId, option: RemoveStickerPackOption) -> Signal<(Int, [ItemCollectionItem])?, NoError> { - return removeStickerPacksInteractively(postbox: postbox, ids: [id], option: option) +func _internal_removeStickerPackInteractively(postbox: Postbox, id: ItemCollectionId, option: RemoveStickerPackOption) -> Signal<(Int, [ItemCollectionItem])?, NoError> { + return _internal_removeStickerPacksInteractively(postbox: postbox, ids: [id], option: option) } -public func removeStickerPacksInteractively(postbox: Postbox, ids: [ItemCollectionId], option: RemoveStickerPackOption) -> Signal<(Int, [ItemCollectionItem])?, NoError> { +func _internal_removeStickerPacksInteractively(postbox: Postbox, ids: [ItemCollectionId], option: RemoveStickerPackOption) -> Signal<(Int, [ItemCollectionItem])?, NoError> { return postbox.transaction { transaction -> (Int, [ItemCollectionItem])? in var commonNamespace: SynchronizeInstalledStickerPacksOperationNamespace? for id in ids { @@ -91,7 +91,7 @@ public func removeStickerPacksInteractively(postbox: Postbox, ids: [ItemCollecti } } -public func markFeaturedStickerPacksAsSeenInteractively(postbox: Postbox, ids: [ItemCollectionId]) -> Signal { +func _internal_markFeaturedStickerPacksAsSeenInteractively(postbox: Postbox, ids: [ItemCollectionId]) -> Signal { return postbox.transaction { transaction -> Void in let idsSet = Set(ids) var items = transaction.getOrderedListItems(collectionId: Namespaces.OrderedItemList.CloudFeaturedStickerPacks) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift new file mode 100644 index 0000000000..72b4e64009 --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/TelegramEngineStickers.swift @@ -0,0 +1,73 @@ +import SwiftSignalKit +import SyncCore +import Postbox + +public extension TelegramEngine { + final class Stickers { + private let account: Account + + init(account: Account) { + self.account = account + } + + public func archivedStickerPacks(namespace: ArchivedStickerPacksNamespace = .stickers) -> Signal<[ArchivedStickerPackItem], NoError> { + return _internal_archivedStickerPacks(account: account, namespace: namespace) + } + + public func removeArchivedStickerPack(info: StickerPackCollectionInfo) -> Signal { + return _internal_removeArchivedStickerPack(account: self.account, info: info) + } + + public func cachedStickerPack(reference: StickerPackReference, forceRemote: Bool) -> Signal { + return _internal_cachedStickerPack(postbox: self.account.postbox, network: self.account.network, reference: reference, forceRemote: forceRemote) + } + + public func loadedStickerPack(reference: StickerPackReference, forceActualized: Bool) -> Signal { + return _internal_loadedStickerPack(postbox: self.account.postbox, network: self.account.network, reference: reference, forceActualized: forceActualized) + } + + public func randomGreetingSticker() -> Signal { + return _internal_randomGreetingSticker(account: self.account) + } + + public func searchStickers(query: String, scope: SearchStickersScope = [.installed, .remote]) -> Signal<[FoundStickerItem], NoError> { + return _internal_searchStickers(account: self.account, query: query, scope: scope) + } + + public func searchStickerSetsRemotely(query: String) -> Signal { + return _internal_searchStickerSetsRemotely(network: self.account.network, query: query) + } + + public func searchStickerSets(query: String) -> Signal { + return _internal_searchStickerSets(postbox: self.account.postbox, query: query) + } + + public func searchGifs(query: String, nextOffset: String = "") -> Signal { + return _internal_searchGifs(account: self.account, query: query, nextOffset: nextOffset) + } + + public func addStickerPackInteractively(info: StickerPackCollectionInfo, items: [ItemCollectionItem], positionInList: Int? = nil) -> Signal { + return _internal_addStickerPackInteractively(postbox: self.account.postbox, info: info, items: items, positionInList: positionInList) + } + + public func removeStickerPackInteractively(id: ItemCollectionId, option: RemoveStickerPackOption) -> Signal<(Int, [ItemCollectionItem])?, NoError> { + return _internal_removeStickerPackInteractively(postbox: self.account.postbox, id: id, option: option) + } + + public func removeStickerPacksInteractively(ids: [ItemCollectionId], option: RemoveStickerPackOption) -> Signal<(Int, [ItemCollectionItem])?, NoError> { + return _internal_removeStickerPacksInteractively(postbox: self.account.postbox, ids: ids, option: option) + } + + public func markFeaturedStickerPacksAsSeenInteractively(ids: [ItemCollectionId]) -> Signal { + return _internal_markFeaturedStickerPacksAsSeenInteractively(postbox: self.account.postbox, ids: ids) + } + + public func searchEmojiKeywords(inputLanguageCode: String, query: String, completeMatch: Bool) -> Signal<[EmojiKeywordItem], NoError> { + return _internal_searchEmojiKeywords(postbox: self.account.postbox, inputLanguageCode: inputLanguageCode, query: query, completeMatch: completeMatch) + } + + public func stickerPacksAttachedToMedia(media: AnyMediaReference) -> Signal<[StickerPackReference], NoError> { + return _internal_stickerPacksAttachedToMedia(account: self.account, media: media) + } + } +} diff --git a/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift b/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift index 6e8daecad9..2389f19737 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/TelegramEngine.swift @@ -20,7 +20,43 @@ public final class TelegramEngine { return Payments(account: self.account) }() - public lazy var peerNames: PeerNames = { - return PeerNames(account: self.account) + public lazy var peers: Peers = { + return Peers(account: self.account) + }() + + public lazy var auth: Auth = { + return Auth(account: self.account) + }() + + public lazy var accountData: AccountData = { + return AccountData(account: self.account) + }() + + public lazy var stickers: Stickers = { + return Stickers(account: self.account) + }() + + public lazy var peerManagement: PeerManagement = { + return PeerManagement(account: self.account) + }() + + public lazy var localization: Localization = { + return Localization(account: self.account) + }() +} + +public final class TelegramEngineUnauthorized { + public let account: UnauthorizedAccount + + public init(account: UnauthorizedAccount) { + self.account = account + } + + public lazy var auth: Auth = { + return Auth(account: self.account) + }() + + public lazy var localization: Localization = { + return Localization(account: self.account) }() } diff --git a/submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift b/submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift index b9993cf1f9..57895597f9 100644 --- a/submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift +++ b/submodules/TelegramCore/Sources/UnauthorizedAccountStateManager.swift @@ -54,10 +54,12 @@ final class UnauthorizedAccountStateManager { private var updateService: UnauthorizedUpdateMessageService? private let updateServiceDisposable = MetaDisposable() private let updateLoginToken: () -> Void + private let displayServiceNotification: (String) -> Void - init(network: Network, updateLoginToken: @escaping () -> Void) { + init(network: Network, updateLoginToken: @escaping () -> Void, displayServiceNotification: @escaping (String) -> Void) { self.network = network self.updateLoginToken = updateLoginToken + self.displayServiceNotification = displayServiceNotification } deinit { @@ -69,11 +71,17 @@ final class UnauthorizedAccountStateManager { if self.updateService == nil { self.updateService = UnauthorizedUpdateMessageService() let updateLoginToken = self.updateLoginToken + let displayServiceNotification = self.displayServiceNotification self.updateServiceDisposable.set(self.updateService!.pipe.signal().start(next: { updates in for update in updates { switch update { case .updateLoginToken: updateLoginToken() + case let .updateServiceNotification(flags, _, _, message, _, _): + let popup = (flags & (1 << 0)) != 0 + if popup { + displayServiceNotification(message) + } default: break } diff --git a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift index ca6049a1ff..b9afca5cc0 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationThemeCodable.swift @@ -1882,9 +1882,9 @@ extension PresentationTheme: Codable { if let decoder = decoder as? PresentationThemeDecoding { let serviceBackgroundColor = decoder.serviceBackgroundColor ?? defaultServiceBackgroundColor decoder.referenceTheme = makeDefaultPresentationTheme(reference: referenceTheme, serviceBackgroundColor: serviceBackgroundColor) - index = decoder.reference?.index ?? arc4random64() + index = decoder.reference?.index ?? Int64.random(in: Int64.min ... Int64.max) } else { - index = arc4random64() + index = Int64.random(in: Int64.min ... Int64.max) } self.init(name: (try? values.decode(PresentationThemeName.self, forKey: .name)) ?? .custom("Untitled"), diff --git a/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift b/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift index 632299a592..b4aab4c33d 100644 --- a/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift +++ b/submodules/TelegramStringFormatting/Sources/CurrencyFormat.swift @@ -61,7 +61,9 @@ public func setupCurrencyNumberFormatter(currency: String) -> NumberFormatter { result.append("#") - result.append(entry.decimalSeparator) + if entry.decimalDigits != 0 { + result.append(entry.decimalSeparator) + } for _ in 0 ..< entry.decimalDigits { result.append("#") @@ -143,7 +145,9 @@ public func formatCurrencyAmount(_ amount: Int64, currency: String) -> String { } } result.append("\(integerPart)") - result.append(entry.decimalSeparator) + if !fractional.isEmpty { + result.append(entry.decimalSeparator) + } for i in 0 ..< fractional.count { result.append(fractional[fractional.count - i - 1]) } @@ -187,7 +191,9 @@ public func formatCurrencyAmountCustom(_ amount: Int64, currency: String) -> (St } } result.append("\(integerPart)") - result.append(entry.decimalSeparator) + if !fractional.isEmpty { + result.append(entry.decimalSeparator) + } for i in 0 ..< fractional.count { result.append(fractional[fractional.count - i - 1]) } @@ -203,3 +209,20 @@ public func formatCurrencyAmountCustom(_ amount: Int64, currency: String) -> (St return ("", "", false) } } + +public struct CurrencyFormat { + public var symbol: String + public var symbolOnLeft: Bool + public var decimalSeparator: String + public var decimalDigits: Int + + public init?(currency: String) { + guard let entry = currencyFormatterEntries[currency] else { + return nil + } + self.symbol = entry.symbol + self.symbolOnLeft = entry.symbolOnLeft + self.decimalSeparator = entry.decimalSeparator + self.decimalDigits = entry.decimalDigits + } +} diff --git a/submodules/TelegramUI/Resources/currencies.json b/submodules/TelegramUI/Resources/currencies.json index 5e332a88fa..93730660d1 100644 --- a/submodules/TelegramUI/Resources/currencies.json +++ b/submodules/TelegramUI/Resources/currencies.json @@ -267,7 +267,7 @@ "decimalSeparator": ",", "symbolOnLeft": true, "spaceBetweenAmountAndSymbol": true, - "decimalDigits": 2 + "decimalDigits": 0 }, "CNY": { "code": "CNY", @@ -1356,7 +1356,7 @@ "decimalSeparator": ",", "symbolOnLeft": false, "spaceBetweenAmountAndSymbol": true, - "decimalDigits": 1 + "decimalDigits": 0 }, "VUV": { "code": "VUV", diff --git a/submodules/TelegramUI/Sources/AccessoryPanelNode.swift b/submodules/TelegramUI/Sources/AccessoryPanelNode.swift index 067fe48fc8..28f7651ef3 100644 --- a/submodules/TelegramUI/Sources/AccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/AccessoryPanelNode.swift @@ -4,6 +4,7 @@ import AsyncDisplayKit import TelegramPresentationData class AccessoryPanelNode: ASDisplayNode { + var originalFrameBeforeDismissed: CGRect? var dismiss: (() -> Void)? var interfaceInteraction: ChatPanelInterfaceInteraction? diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index b1cbaba978..fa1f30b5f8 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -435,7 +435,7 @@ final class SharedApplicationContext { let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId) let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!) - TempBox.initializeShared(basePath: rootPath, processType: "app", launchSpecificId: arc4random64()) + TempBox.initializeShared(basePath: rootPath, processType: "app", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) let logsPath = rootPath + "/logs" let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) @@ -1974,7 +1974,7 @@ final class SharedApplicationContext { if let messageId = messageIdFromNotification(peerId: peerId, notification: response.notification) { let _ = applyMaxReadIndexInteractively(postbox: account.postbox, stateManager: account.stateManager, index: MessageIndex(id: messageId, timestamp: 0)).start() } - return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]) + return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) |> map { messageIds -> MessageId? in if messageIds.isEmpty { return nil diff --git a/submodules/TelegramUI/Sources/ApplicationContext.swift b/submodules/TelegramUI/Sources/ApplicationContext.swift index 4e4c9df927..3b5106cb76 100644 --- a/submodules/TelegramUI/Sources/ApplicationContext.swift +++ b/submodules/TelegramUI/Sources/ApplicationContext.swift @@ -36,6 +36,8 @@ final class UnauthorizedApplicationContext { let isReady = Promise() var authorizationCompleted: Bool = false + + private var serviceNotificationEventsDisposable: Disposable? init(apiId: Int32, apiHash: String, sharedContext: SharedAccountContextImpl, account: UnauthorizedAccount, otherAccountPhoneNumbers: ((String, AccountRecordId, Bool)?, [(String, AccountRecordId, Bool)])) { self.sharedContext = sharedContext @@ -71,6 +73,20 @@ final class UnauthorizedApplicationContext { }, { result in ApplicationSpecificNotice.setPermissionWarning(accountManager: sharedContext.accountManager, permission: .cellularData, value: 0) }) + + self.serviceNotificationEventsDisposable = (account.serviceNotificationEvents + |> deliverOnMainQueue).start(next: { [weak self] text in + if let strongSelf = self { + let presentationData = strongSelf.sharedContext.currentPresentationData.with { $0 } + let alertController = textAlertController(sharedContext: strongSelf.sharedContext, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + + (strongSelf.rootController.viewControllers.last as? ViewController)?.present(alertController, in: .window(.root)) + } + }) + } + + deinit { + self.serviceNotificationEventsDisposable?.dispose() } } @@ -445,11 +461,11 @@ final class AuthorizedApplicationContext { guard let strongSelf = self else { return } - let _ = (acceptTermsOfService(account: strongSelf.context.account, id: termsOfServiceUpdate.id) + let _ = (strongSelf.context.engine.accountData.acceptTermsOfService(id: termsOfServiceUpdate.id) |> deliverOnMainQueue).start(completed: { controller?.dismiss() if let strongSelf = self, let botName = botName { - strongSelf.termsOfServiceProceedToBotDisposable.set((resolvePeerByName(account: strongSelf.context.account, name: botName, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peerId in + strongSelf.termsOfServiceProceedToBotDisposable.set((strongSelf.context.engine.peers.resolvePeerByName(name: botName, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peerId in if let strongSelf = self, let peerId = peerId { self?.rootController.pushViewController(ChatControllerImpl(context: strongSelf.context, chatLocation: .peer(peerId))) } @@ -464,7 +480,7 @@ final class AuthorizedApplicationContext { } let accountId = strongSelf.context.account.id let accountManager = strongSelf.context.sharedContext.accountManager - let _ = (deleteAccount(account: strongSelf.context.account) + let _ = (strongSelf.context.engine.auth.deleteAccount() |> deliverOnMainQueue).start(error: { _ in guard let strongSelf = self else { return diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift index 6ae6070ef1..4854c1d83d 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceController.swift @@ -718,7 +718,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail var value = stat() if stat(result.fileURL.path, &value) == 0 { if let data = try? Data(contentsOf: result.fileURL) { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift index 3b44aa35aa..8bada79ec5 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift @@ -140,7 +140,7 @@ final class AuthorizationSequencePhoneEntryController: ViewController { self?.nextPressed() } - loadServerCountryCodes(accountManager: sharedContext.accountManager, network: account.network, completion: { [weak self] in + loadServerCountryCodes(accountManager: sharedContext.accountManager, engine: TelegramEngineUnauthorized(account: self.account), completion: { [weak self] in if let strongSelf = self { strongSelf.controllerNode.updateCountryCode() } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index d6320febd5..cf198be15b 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -438,14 +438,14 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { |> castError(ExportAuthTransferTokenError.self) |> take(1) |> mapToSignal { activeAccountsAndInfo -> Signal in - let (primary, activeAccounts, _) = activeAccountsAndInfo - var activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id }) - var activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.testingEnvironment }).map({ $0.peerId.id }) + let (_, activeAccounts, _) = activeAccountsAndInfo + let activeProductionUserIds = activeAccounts.map({ $0.1 }).filter({ !$0.testingEnvironment }).map({ $0.peerId.id }) + let activeTestingUserIds = activeAccounts.map({ $0.1 }).filter({ $0.testingEnvironment }).map({ $0.peerId.id }) let allProductionUserIds = activeProductionUserIds let allTestingUserIds = activeTestingUserIds - return exportAuthTransferToken(accountManager: sharedContext.accountManager, account: account, otherAccountUserIds: account.testingEnvironment ? allTestingUserIds : allProductionUserIds, syncContacts: true) + return TelegramEngineUnauthorized(account: account).auth.exportAuthTransferToken(accountManager: sharedContext.accountManager, otherAccountUserIds: account.testingEnvironment ? allTestingUserIds : allProductionUserIds, syncContacts: true) } self.exportTokenDisposable.set((tokenSignal diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 6a0a8535cc..f56d8216f1 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -829,7 +829,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break } } - let _ = combineLatest(queue: .mainQueue(), contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction), loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .animatedEmoji, forceActualized: false), ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager) + let _ = combineLatest(queue: .mainQueue(), contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction), strongSelf.context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false), ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager) ).start(next: { actions, animatedEmojiStickers, chatTextSelectionTips in guard let strongSelf = self, !actions.isEmpty else { return @@ -1004,7 +1004,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !entities.isEmpty { attributes.append(TextEntitiesMessageAttribute(entities: entities)) } - strongSelf.sendMessages([.message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: text, attributes: attributes, mediaReference: nil, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)]) }, sendSticker: { [weak self] fileReference, query, clearInput, sourceNode, sourceRect in guard let strongSelf = self else { return false @@ -1042,8 +1042,40 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let query = query { attributes.append(EmojiSearchQueryMessageAttribute(query: query)) } + + let correlationId = Int64.random(in: 0 ..< Int64.max) + + var replyPanel: ReplyAccessoryPanelNode? + if let accessoryPanelNode = strongSelf.chatDisplayNode.accessoryPanelNode as? ReplyAccessoryPanelNode { + replyPanel = accessoryPanelNode + } + + if strongSelf.chatDisplayNode.shouldAnimateMessageTransition { + if let sourceNode = sourceNode as? ChatMediaInputStickerGridItemNode { + strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .inputPanel(itemNode: sourceNode), replyPanel: replyPanel), initiated: { + guard let strongSelf = self else { + return + } + strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { current in + var current = current + current = current.updatedInputMode { current in + if case let .media(mode, maybeExpanded) = current, maybeExpanded != nil { + return .media(mode: mode, expanded: nil) + } + return current + } + + return current + }) + }) + } else if let sourceNode = sourceNode as? HorizontalStickerGridItemNode { + strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .mediaPanel(itemNode: sourceNode), replyPanel: replyPanel), initiated: {}) + } else if let sourceNode = sourceNode as? StickerPaneSearchStickerItemNode { + strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .stickerMediaInput(input: .inputPanelSearch(itemNode: sourceNode), replyPanel: replyPanel), initiated: {}) + } + } - strongSelf.sendMessages([.message(text: "", attributes: attributes, mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: "", attributes: attributes, mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: correlationId)]) return true }, sendGif: { [weak self] fileReference, sourceNode, sourceRect in if let strongSelf = self { @@ -1064,7 +1096,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } }) - strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: fileReference.abstract, replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)]) } return true }, sendBotContextResultAsGif: { [weak self] collection, result, sourceNode, sourceRect in @@ -1363,7 +1395,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G |> deliverOnMainQueue).start(next: { coordinate in if let strongSelf = self { if let coordinate = coordinate { - strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)), replyToMessageId: nil, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaMap(latitude: coordinate.latitude, longitude: coordinate.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: nil, liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) } else { strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.Login_UnknownError, actions: [TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {})]), in: .window(.root)) } @@ -1387,7 +1419,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId) |> deliverOnMainQueue).start(next: { peer in if let peer = peer as? TelegramUser, let phone = peer.phone, !phone.isEmpty { - strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)), replyToMessageId: nil, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaContact(firstName: peer.firstName ?? "", lastName: peer.lastName ?? "", phoneNumber: phone, peerId: peer.id, vCardData: nil)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) } }) } @@ -1427,7 +1459,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !entities.isEmpty { attributes.append(TextEntitiesMessageAttribute(entities: entities)) } - strongSelf.sendMessages([.message(text: command, attributes: attributes, mediaReference: nil, replyToMessageId: (postAsReply && messageId != nil) ? messageId! : nil, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: command, attributes: attributes, mediaReference: nil, replyToMessageId: (postAsReply && messageId != nil) ? messageId! : nil, localGroupingKey: nil, correlationId: nil)]) } }, openInstantPage: { [weak self] message, associatedData in if let strongSelf = self, strongSelf.isNodeLoaded, let navigationController = strongSelf.effectiveNavigationController, let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(message.id) { @@ -1712,7 +1744,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G items.append(ActionSheetButtonItem(title: strongSelf.presentationData.strings.ShareMenu_Send, color: .accent, action: { [weak actionSheet] in actionSheet?.dismissAnimated() if let strongSelf = self { - strongSelf.sendMessages([.message(text: command, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: command, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) } })) } @@ -4074,6 +4106,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G strongSelf.currentPinchController?.addRelativeContentOffset(CGPoint(x: 0.0, y: -offset), transition: transition) } } + + strongSelf.chatDisplayNode.messageTransitionNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode) } if case .pinnedMessages = self.presentationInterfaceState.subject { @@ -4480,7 +4514,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } else { isScheduledMessages = false } - strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut), listViewTransaction: { updateSizeAndInsets, _, _, _ in + strongSelf.chatDisplayNode.containerLayoutUpdated(validLayout, navigationBarHeight: strongSelf.navigationHeight, transition: .animated(duration: strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? 0.5 : 0.4, curve: strongSelf.chatDisplayNode.messageTransitionNode.hasScheduledTransitions ? .custom(0.33, 0.0, 0.0, 1.0) : .spring), listViewTransaction: { updateSizeAndInsets, _, _, _ in var options = transition.options let _ = options.insert(.Synchronous) let _ = options.insert(.LowLatency) @@ -4506,9 +4540,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var scrollToItem: ListViewScrollToItem? if isScheduledMessages, let insertedIndex = insertedIndex { - scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Default(duration: 0.2), directionHint: .Down) + scrollToItem = ListViewScrollToItem(index: insertedIndex, position: .visible, animated: true, curve: .Custom(duration: 0.5, 0.33, 0.0, 0.0, 1.0), directionHint: .Down) } else if transition.historyView.originalView.laterId == nil { - scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: 0.2), directionHint: .Up) + scrollToItem = ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Custom(duration: 0.5, 0.33, 0.0, 0.0, 1.0), directionHint: .Up) } var stationaryItemRange: (Int, Int)? @@ -4566,12 +4600,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G var forwardedMessages: [[EnqueueMessage]] = [] var forwardSourcePeerIds = Set() for message in transformedMessages { - if case let .forward(source, _, _) = message { + if case let .forward(source, _, _, _) = message { forwardSourcePeerIds.insert(source.peerId) var added = false if var last = forwardedMessages.last { - if let currentMessage = last.first, case let .forward(currentSource, _, _) = currentMessage, currentSource.peerId == source.peerId { + if let currentMessage = last.first, case let .forward(currentSource, _, _, _) = currentMessage, currentSource.peerId == source.peerId { last.append(message) added = true } @@ -4614,8 +4648,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } - self.chatDisplayNode.requestUpdateChatInterfaceState = { [weak self] animated, saveInterfaceState, f in - self?.updateChatPresentationInterfaceState(animated: animated, interactive: true, saveInterfaceState: saveInterfaceState, { $0.updatedInterfaceState(f) }) + self.chatDisplayNode.requestUpdateChatInterfaceState = { [weak self] transition, saveInterfaceState, f in + self?.updateChatPresentationInterfaceState(transition: transition, interactive: true, saveInterfaceState: saveInterfaceState, { $0.updatedInterfaceState(f) }) } self.chatDisplayNode.requestUpdateInterfaceState = { [weak self] transition, interactive, f in @@ -5371,7 +5405,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if !entities.isEmpty { attributes.append(TextEntitiesMessageAttribute(entities: entities)) } - strongSelf.sendMessages([.message(text: messageText, attributes: attributes, mediaReference: nil, replyToMessageId: replyMessageId, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: messageText, attributes: attributes, mediaReference: nil, replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)]) } } }, sendBotStart: { [weak self] payload in @@ -6112,7 +6146,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G |> switchToLatest |> deliverOnMainQueue).start(next: { [weak self] added in if let strongSelf = self { - strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(account: strongSelf.context.account, file: stickerFile, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites), elevatedLayout: false, action: { _ in return false }), in: .current) + strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .sticker(context: strongSelf.context, file: stickerFile, text: added ? strongSelf.presentationData.strings.Conversation_StickerAddedToFavorites : strongSelf.presentationData.strings.Conversation_StickerRemovedFromFavorites), elevatedLayout: false, action: { _ in return false }), in: .current) } }) } @@ -8308,7 +8342,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } private func editMessageMediaWithMessages(_ messages: [EnqueueMessage]) { - if let message = messages.first, case let .message(text, _, maybeMediaReference, _, _) = message, let mediaReference = maybeMediaReference { + if let message = messages.first, case let .message(text, _, maybeMediaReference, _, _, _) = message, let mediaReference = maybeMediaReference { self.updateChatPresentationInterfaceState(animated: true, interactive: true, { state in var state = state if let editMessageState = state.editMessageState, case let .media(options) = editMessageState.content, !options.isEmpty { @@ -8642,13 +8676,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if fileTypes.music != fileTypes.other { - groupingKey = arc4random64() + groupingKey = Int64.random(in: Int64.min ... Int64.max) } var messages: [EnqueueMessage] = [] for item in results { if let item = item { - let fileId = arc4random64() + let fileId = Int64.random(in: Int64.min ... Int64.max) let mimeType = guessMimeTypeByFileExtension((item.fileName as NSString).pathExtension) var previewRepresentations: [TelegramMediaImageRepresentation] = [] if mimeType.hasPrefix("image/") || mimeType == "application/pdf" { @@ -8661,11 +8695,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: item.fileSize, attributes: attributes) - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: replyMessageId, localGroupingKey: groupingKey) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: replyMessageId, localGroupingKey: groupingKey, correlationId: nil) messages.append(message) } if let _ = groupingKey, messages.count % 10 == 0 { - groupingKey = arc4random64() + groupingKey = Int64.random(in: Int64.min ... Int64.max) } } @@ -8899,7 +8933,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: replyMessageId, localGroupingKey: nil) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil) strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ if let strongSelf = self { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: false, { @@ -8954,7 +8988,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } }) - let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil) + let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil) enqueueMessages.append(message) } } @@ -9013,7 +9047,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } }) - let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil) + let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil) strongSelf.sendMessages([message]) } else { let contactController = strongSelf.context.sharedContext.makeDeviceContactInfoController(context: strongSelf.context, subject: .filter(peer: peerAndContactData.0, contactId: nil, contactData: contactData, completion: { peer, contactData in @@ -9031,7 +9065,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } }) - let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil) + let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil) strongSelf.sendMessages([message]) } }), completed: nil, cancelled: nil) @@ -9404,9 +9438,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } if let value = value { - self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, account: self.context.account, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in + self.present(UndoOverlayController(presentationData: self.presentationData, content: .dice(dice: dice, context: self.context, text: value, action: canSendMessagesToChat(self.presentationInterfaceState) ? self.presentationData.strings.Conversation_SendDice : nil), elevatedLayout: false, action: { [weak self] action in if let strongSelf = self, canSendMessagesToChat(strongSelf.presentationInterfaceState), action == .undo { - strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: dice.emoji)), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]) } return false }), in: .current) @@ -9427,9 +9461,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let defaultReplyMessageId = defaultReplyMessageId { switch message { - case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey): + case let .message(text, attributes, mediaReference, replyToMessageId, localGroupingKey, correlationId): if replyToMessageId == nil { - message = .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: defaultReplyMessageId, localGroupingKey: localGroupingKey) + message = .message(text: text, attributes: attributes, mediaReference: mediaReference, replyToMessageId: defaultReplyMessageId, localGroupingKey: localGroupingKey, correlationId: correlationId) } case .forward: break @@ -9589,7 +9623,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let size = image.size.aspectFitted(CGSize(width: 512.0, height: 512.0)) self.enqueueMediaMessageDisposable.set((convertToWebP(image: image, targetSize: size, targetBoundingSize: size, quality: 0.9) |> deliverOnMainQueue).start(next: { [weak self] data in if let strongSelf = self, !data.isEmpty { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) var fileAttributes: [TelegramMediaFileAttribute] = [] @@ -9597,8 +9631,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G fileAttributes.append(.Sticker(displayText: "", packReference: nil, maskData: nil)) fileAttributes.append(.ImageSize(size: PixelDimensions(size))) - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: data.count, attributes: fileAttributes) - let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "image/webp", size: data.count, attributes: fileAttributes) + let message = EnqueueMessage.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ @@ -9708,8 +9742,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G isScheduledMessages = true } - self.videoRecorder.set(.single(legacyInstantVideoController(theme: self.presentationData.theme, panelFrame: self.view.convert(currentInputPanelFrame, to: nil), context: self.context, peerId: peerId, slowmodeState: !isScheduledMessages ? self.presentationInterfaceState.slowmodeState : nil, hasSchedule: !isScheduledMessages && peerId.namespace != Namespaces.Peer.SecretChat, send: { [weak self] message in + self.videoRecorder.set(.single(legacyInstantVideoController(theme: self.presentationData.theme, panelFrame: self.view.convert(currentInputPanelFrame, to: nil), context: self.context, peerId: peerId, slowmodeState: !isScheduledMessages ? self.presentationInterfaceState.slowmodeState : nil, hasSchedule: !isScheduledMessages && peerId.namespace != Namespaces.Peer.SecretChat, send: { [weak self] videoController, message in if let strongSelf = self { + guard let message = message else { + strongSelf.videoRecorder.set(.single(nil)) + return + } + let replyMessageId = strongSelf.presentationInterfaceState.interfaceState.replyMessageId strongSelf.chatDisplayNode.setupSendActionOnViewUpdate({ if let strongSelf = self { @@ -9718,7 +9757,23 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } }) - let updatedMessage = message.withUpdatedReplyToMessageId(replyMessageId) + let correlationId = Int64.random(in: 0 ..< Int64.max) + let updatedMessage = message + .withUpdatedReplyToMessageId(replyMessageId) + .withUpdatedCorrelationId(correlationId) + + if strongSelf.chatDisplayNode.shouldAnimateMessageTransition, let extractedView = videoController.extractVideoSnapshot() { + strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .videoMessage(ChatMessageTransitionNode.Source.VideoMessage(view: extractedView)), initiated: { [weak videoController] in + videoController?.hideVideoSnapshot() + guard let strongSelf = self else { + return + } + strongSelf.videoRecorder.set(.single(nil)) + }) + } else { + strongSelf.videoRecorder.set(.single(nil)) + } + strongSelf.sendMessages([updatedMessage]) } }, displaySlowmodeTooltip: { [weak self] node, rect in @@ -9756,7 +9811,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G switch updatedAction { case .dismiss: self.chatDisplayNode.updateRecordedMediaDeleted(true) - break + self.audioRecorder.set(.single(nil)) case .preview: self.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInputTextPanelState { panelState in @@ -9774,7 +9829,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) } else if let waveform = data.waveform { - let resource = LocalFileMediaResource(fileId: arc4random64(), size: data.compressedData.count) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max), size: data.compressedData.count) strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData) @@ -9787,6 +9842,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } } }) + self.audioRecorder.set(.single(nil)) case .send: self.chatDisplayNode.updateRecordedMediaDeleted(false) let _ = (audioRecorderValue.takenRecordedData() @@ -9795,8 +9851,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if data.duration < 0.5 { strongSelf.recorderFeedback?.error() strongSelf.recorderFeedback = nil + strongSelf.audioRecorder.set(.single(nil)) } else { - let randomId = arc4random64() + let randomId = Int64.random(in: Int64.min ... Int64.max) let resource = LocalFileMediaResource(fileId: randomId) strongSelf.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data.compressedData) @@ -9813,8 +9870,21 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } }) + + let correlationId = Int64.random(in: 0 ..< Int64.max) + + if strongSelf.chatDisplayNode.shouldAnimateMessageTransition, let textInputPanelNode = strongSelf.chatDisplayNode.textInputPanelNode, let micButton = textInputPanelNode.micButton { + strongSelf.chatDisplayNode.messageTransitionNode.add(correlationId: correlationId, source: .audioMicInput(ChatMessageTransitionNode.Source.AudioMicInput(micButton: micButton)), initiated: { + guard let strongSelf = self else { + return + } + strongSelf.audioRecorder.set(.single(nil)) + }) + } else { + strongSelf.audioRecorder.set(.single(nil)) + } - strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.compressedData.count, attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)]) + strongSelf.sendMessages([.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.compressedData.count, attributes: [.Audio(isVoice: true, duration: Int(data.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: strongSelf.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: correlationId)]) strongSelf.recorderFeedback?.tap() strongSelf.recorderFeedback = nil @@ -9822,12 +9892,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) } - self.audioRecorder.set(.single(nil)) } else if let videoRecorderValue = self.videoRecorderValue { if case .send = updatedAction { self.chatDisplayNode.updateRecordedMediaDeleted(false) videoRecorderValue.completeVideo() - self.videoRecorder.set(.single(nil)) } else { if case .dismiss = updatedAction { self.chatDisplayNode.updateRecordedMediaDeleted(true) @@ -9910,7 +9978,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) - let messages: [EnqueueMessage] = [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)] + let messages: [EnqueueMessage] = [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: recordedMediaPreview.resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: Int(recordedMediaPreview.fileSize), attributes: [.Audio(isVoice: true, duration: Int(recordedMediaPreview.duration), title: nil, performer: nil, waveform: waveformBuffer)])), replyToMessageId: self.presentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)] let transformedMessages: [EnqueueMessage] if let silentPosting = silentPosting { @@ -10628,7 +10696,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }), in: .current) let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messages.map { message -> EnqueueMessage in - return .forward(source: message.id, grouping: .auto, attributes: []) + return .forward(source: message.id, grouping: .auto, attributes: [], correlationId: nil) }) |> deliverOnMainQueue).start(next: { messageIds in if let strongSelf = self { @@ -10855,7 +10923,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G disposable = MetaDisposable() self.resolvePeerByNameDisposable = disposable } - var resolveSignal = resolvePeerByName(account: self.context.account, name: name, ageLimit: 10) + var resolveSignal = self.context.engine.peers.resolvePeerByName(name: name, ageLimit: 10) var cancelImpl: (() -> Void)? let presentationData = self.presentationData @@ -10921,7 +10989,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let account = self.context.account var resolveSignal: Signal if let peerName = peerName { - resolveSignal = resolvePeerByName(account: self.context.account, name: peerName) + resolveSignal = self.context.engine.peers.resolvePeerByName(name: peerName) |> mapToSignal { peerId -> Signal in if let peerId = peerId { return account.postbox.loadedPeerWithId(peerId) @@ -10986,7 +11054,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G Queue.mainQueue().async { unblockingPeer.set(false) if let strongSelf = self, restartBot { - let _ = enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start() + let _ = enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start() } } })).start()) diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 3c4e4dbec0..6c3c6b2841 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -55,6 +55,7 @@ private struct ChatControllerNodeDerivedLayoutState { var inputContextPanelsFrame: CGRect var inputContextPanelsOverMainPanelFrame: CGRect var inputNodeHeight: CGFloat? + var inputNodeAdditionalHeight: CGFloat? var upperInputPositionBound: CGFloat? } @@ -329,7 +330,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private var inputPanelNode: ChatInputPanelNode? private weak var currentDismissedInputPanelNode: ASDisplayNode? private var secondaryInputPanelNode: ChatInputPanelNode? - private var accessoryPanelNode: AccessoryPanelNode? + private(set) var accessoryPanelNode: AccessoryPanelNode? private var inputContextPanelNode: ChatInputContextPanelNode? private let inputContextPanelContainer: ChatControllerTitlePanelNodeContainer private var overlayContextPanelNode: ChatInputContextPanelNode? @@ -337,7 +338,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private var inputNode: ChatInputNode? private var disappearingNode: ChatInputNode? - private var textInputPanelNode: ChatTextInputPanelNode? + private(set) var textInputPanelNode: ChatTextInputPanelNode? private var inputMediaNode: ChatMediaInputNode? let navigateButtons: ChatHistoryNavigationButtons @@ -377,7 +378,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } } - var requestUpdateChatInterfaceState: (Bool, Bool, (ChatInterfaceState) -> ChatInterfaceState) -> Void = { _, _, _ in } + var requestUpdateChatInterfaceState: (ContainedViewLayoutTransition, Bool, (ChatInterfaceState) -> ChatInterfaceState) -> Void = { _, _, _ in } var requestUpdateInterfaceState: (ContainedViewLayoutTransition, Bool, (ChatPresentationInterfaceState) -> ChatPresentationInterfaceState) -> Void = { _, _, _ in } var sendMessages: ([EnqueueMessage], Bool?, Int32?, Bool) -> Void = { _, _, _, _ in } var displayAttachmentMenu: () -> Void = { } @@ -393,6 +394,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private var expandedInputDimNode: ASDisplayNode? private var dropDimNode: ASDisplayNode? + + let messageTransitionNode: ChatMessageTransitionNode private var containerLayoutAndNavigationBarHeight: (ContainerViewLayout, CGFloat)? @@ -501,6 +504,8 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.navigationBarSeparatorNode = ASDisplayNode() self.navigationBarSeparatorNode.backgroundColor = chatPresentationInterfaceState.theme.rootController.navigationBar.separatorColor + + self.messageTransitionNode = ChatMessageTransitionNode(listNode: self.historyNode) super.init() @@ -593,6 +598,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { self.addSubnode(self.navigationBarBackroundNode) self.addSubnode(self.navigationBarSeparatorNode) + + self.addSubnode(self.messageTransitionNode) + if !self.context.sharedContext.immediateExperimentalUISettings.playerEmbedding { self.navigationBarBackroundNode.isHidden = true self.navigationBarSeparatorNode.isHidden = true @@ -721,7 +729,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { private func updateIsEmpty(_ emptyType: ChatHistoryNodeLoadState.EmptyType?, animated: Bool) { self.emptyType = emptyType if let emptyType = emptyType, self.emptyNode == nil { - let emptyNode = ChatEmptyNode(account: self.context.account, interaction: self.interfaceInteraction) + let emptyNode = ChatEmptyNode(context: self.context, interaction: self.interfaceInteraction) if let (size, insets) = self.validEmptyNodeLayout { emptyNode.updateLayout(interfaceState: self.chatPresentationInterfaceState, emptyType: emptyType, size: size, insets: insets, transition: .immediate) } @@ -774,6 +782,11 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } else { transition = protoTransition } + + var previousListBottomInset: CGFloat? + if !self.historyNode.frame.isEmpty { + previousListBottomInset = self.historyNode.insets.top + } self.scheduledLayoutTransitionRequest = nil if case .overlay = self.chatPresentationInterfaceState.mode { @@ -1150,7 +1163,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var dismissedInputPanelNode: ASDisplayNode? var dismissedSecondaryInputPanelNode: ASDisplayNode? - var dismissedAccessoryPanelNode: ASDisplayNode? + var dismissedAccessoryPanelNode: AccessoryPanelNode? var dismissedInputContextPanelNode: ChatInputContextPanelNode? var dismissedOverlayContextPanelNode: ChatInputContextPanelNode? @@ -1292,9 +1305,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { accessoryPanelNode.dismiss = { [weak self, weak accessoryPanelNode] in if let strongSelf = self, let accessoryPanelNode = accessoryPanelNode, strongSelf.accessoryPanelNode === accessoryPanelNode { if let _ = accessoryPanelNode as? ReplyAccessoryPanelNode { - strongSelf.requestUpdateChatInterfaceState(true, false, { $0.withUpdatedReplyMessageId(nil) }) + strongSelf.requestUpdateChatInterfaceState(.animated(duration: 0.4, curve: .spring), false, { $0.withUpdatedReplyMessageId(nil) }) } else if let _ = accessoryPanelNode as? ForwardAccessoryPanelNode { - strongSelf.requestUpdateChatInterfaceState(true, false, { $0.withUpdatedForwardMessageIds(nil) }) + strongSelf.requestUpdateChatInterfaceState(.animated(duration: 0.4, curve: .spring), false, { $0.withUpdatedForwardMessageIds(nil) }) } else if let _ = accessoryPanelNode as? EditAccessoryPanelNode { strongSelf.interfaceInteraction?.setupEditMessage(nil, { _ in }) } else if let _ = accessoryPanelNode as? WebpagePreviewAccessoryPanelNode { @@ -1743,6 +1756,9 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let inputPanelFrame = inputPanelFrame { transitionTargetY = inputPanelFrame.minY } + + dismissedAccessoryPanelNode.originalFrameBeforeDismissed = dismissedAccessoryPanelNode.frame + transition.updateFrame(node: dismissedAccessoryPanelNode, frame: CGRect(origin: CGPoint(x: 0.0, y: transitionTargetY), size: dismissedAccessoryPanelNode.frame.size), completion: { _ in frameCompleted = true completed() @@ -1863,8 +1879,13 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } self.updatePlainInputSeparator(transition: transition) - - self.derivedLayoutState = ChatControllerNodeDerivedLayoutState(inputContextPanelsFrame: inputContextPanelsFrame, inputContextPanelsOverMainPanelFrame: inputContextPanelsOverMainPanelFrame, inputNodeHeight: inputNodeHeightAndOverflow?.0, upperInputPositionBound: inputNodeHeightAndOverflow?.0 != nil ? self.upperInputPositionBound : nil) + + let listBottomInset = self.historyNode.insets.bottom + if let previousListBottomInset = previousListBottomInset, listBottomInset != previousListBottomInset { + self.historyNode.didScrollWithOffset?(listBottomInset - previousListBottomInset, transition, nil) + } + + self.derivedLayoutState = ChatControllerNodeDerivedLayoutState(inputContextPanelsFrame: inputContextPanelsFrame, inputContextPanelsOverMainPanelFrame: inputContextPanelsOverMainPanelFrame, inputNodeHeight: inputNodeHeightAndOverflow?.0, inputNodeAdditionalHeight: inputNodeHeightAndOverflow?.1, upperInputPositionBound: inputNodeHeightAndOverflow?.0 != nil ? self.upperInputPositionBound : nil) //self.notifyTransitionCompletionListeners(transition: transition) } @@ -2607,7 +2628,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { let trimmedInputText = effectiveInputText.string.trimmingCharacters(in: .whitespacesAndNewlines) let peerId = effectivePresentationInterfaceState.chatLocation.peerId if peerId.namespace != Namespaces.Peer.SecretChat, let interactiveEmojis = self.interactiveEmojis, interactiveEmojis.emojis.contains(trimmedInputText) { - messages.append(.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)) + messages.append(.message(text: "", attributes: [], mediaReference: AnyMediaReference.standalone(media: TelegramMediaDice(emoji: trimmedInputText)), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)) } else { let inputText = convertMarkdownToAttributes(effectiveInputText) @@ -2624,7 +2645,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } else { webpage = self.chatPresentationInterfaceState.urlPreview?.1 } - messages.append(.message(text: text.string, attributes: attributes, mediaReference: webpage.flatMap(AnyMediaReference.standalone), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil)) + messages.append(.message(text: text.string, attributes: attributes, mediaReference: webpage.flatMap(AnyMediaReference.standalone), replyToMessageId: self.chatPresentationInterfaceState.interfaceState.replyMessageId, localGroupingKey: nil, correlationId: nil)) } } @@ -2646,7 +2667,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let strongSelf = self, let textInputPanelNode = strongSelf.inputPanelNode as? ChatTextInputPanelNode { strongSelf.ignoreUpdateHeight = true textInputPanelNode.text = "" - strongSelf.requestUpdateChatInterfaceState(false, true, { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil).withUpdatedComposeDisableUrlPreview(nil) }) + strongSelf.requestUpdateChatInterfaceState(.immediate, true, { $0.withUpdatedReplyMessageId(nil).withUpdatedForwardMessageIds(nil).withUpdatedComposeDisableUrlPreview(nil) }) strongSelf.ignoreUpdateHeight = false } }) @@ -2654,7 +2675,22 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { if let forwardMessageIds = self.chatPresentationInterfaceState.interfaceState.forwardMessageIds { for id in forwardMessageIds { - messages.append(.forward(source: id, grouping: .auto, attributes: [])) + messages.append(.forward(source: id, grouping: .auto, attributes: [], correlationId: nil)) + } + } + + if !messages.isEmpty, case .message = messages[messages.count - 1] { + let correlationId = Int64.random(in: 0 ..< Int64.max) + messages[messages.count - 1] = messages[messages.count - 1].withUpdatedCorrelationId(correlationId) + + var replyPanel: ReplyAccessoryPanelNode? + if let accessoryPanelNode = self.accessoryPanelNode as? ReplyAccessoryPanelNode { + replyPanel = accessoryPanelNode + } + if self.shouldAnimateMessageTransition, let inputPanelNode = self.inputPanelNode as? ChatTextInputPanelNode, let textInput = inputPanelNode.makeSnapshotForTransition() { + let source: ChatMessageTransitionNode.Source = .textInput(textInput: textInput, replyPanel: replyPanel) + self.messageTransitionNode.add(correlationId: correlationId, source: source, initiated: { + }) } } @@ -2861,4 +2897,13 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { } self.historyNode.isHidden = isBlurred } + + var shouldAnimateMessageTransition: Bool { + switch self.historyNode.visibleContentOffset() { + case .known(0.0): + return true + default: + return false + } + } } diff --git a/submodules/TelegramUI/Sources/ChatEmptyNode.swift b/submodules/TelegramUI/Sources/ChatEmptyNode.swift index 76bf2723ba..18976ae21f 100644 --- a/submodules/TelegramUI/Sources/ChatEmptyNode.swift +++ b/submodules/TelegramUI/Sources/ChatEmptyNode.swift @@ -10,6 +10,7 @@ import TelegramPresentationData import AppBundle import LocalizedPeerData import TelegramStringFormatting +import AccountContext private protocol ChatEmptyNodeContent { func updateLayout(interfaceState: ChatPresentationInterfaceState, size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize @@ -67,7 +68,7 @@ private final class ChatEmptyNodeRegularChatContent: ASDisplayNode, ChatEmptyNod } private final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNodeContent, UIGestureRecognizerDelegate { - private let account: Account + private let context: AccountContext private let interaction: ChatPanelInterfaceInteraction? private let titleNode: ImmediateTextNode @@ -92,8 +93,8 @@ private final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNo } } - init(account: Account, interaction: ChatPanelInterfaceInteraction?) { - self.account = account + init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?) { + self.context = context self.interaction = interaction self.titleNode = ImmediateTextNode() @@ -162,7 +163,7 @@ private final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNo if let preloadedSticker = interfaceState.greetingData?.sticker { sticker = .single(preloadedSticker) } else { - sticker = randomGreetingSticker(account: self.account) + sticker = self.context.engine.stickers.randomGreetingSticker() |> map { item -> TelegramMediaFile? in return item?.file } @@ -195,7 +196,7 @@ private final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNo let index = ItemCollectionItemIndex(index: 0, id: 0) let collectionId = ItemCollectionId(namespace: 0, id: 0) let stickerPackItem = StickerPackItem(index: index, file: sticker, indexKeys: []) - let item = ChatMediaInputStickerGridItem(account: strongSelf.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {}) + let item = ChatMediaInputStickerGridItem(account: strongSelf.context.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {}) strongSelf.stickerItem = item strongSelf.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true) strongSelf.stickerNode.isVisibleInGrid = true @@ -234,7 +235,7 @@ private final class ChatEmptyNodeGreetingChatContent: ASDisplayNode, ChatEmptyNo } private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNodeContent, UIGestureRecognizerDelegate { - private let account: Account + private let context: AccountContext private let interaction: ChatPanelInterfaceInteraction? private let titleNode: ImmediateTextNode @@ -259,8 +260,8 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode } } - init(account: Account, interaction: ChatPanelInterfaceInteraction?) { - self.account = account + init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?) { + self.context = context self.interaction = interaction self.titleNode = ImmediateTextNode() @@ -339,7 +340,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode if let preloadedSticker = interfaceState.greetingData?.sticker { sticker = .single(preloadedSticker) } else { - sticker = randomGreetingSticker(account: self.account) + sticker = self.context.engine.stickers.randomGreetingSticker() |> map { item -> TelegramMediaFile? in return item?.file } @@ -372,7 +373,7 @@ private final class ChatEmptyNodeNearbyChatContent: ASDisplayNode, ChatEmptyNode let index = ItemCollectionItemIndex(index: 0, id: 0) let collectionId = ItemCollectionId(namespace: 0, id: 0) let stickerPackItem = StickerPackItem(index: index, file: sticker, indexKeys: []) - let item = ChatMediaInputStickerGridItem(account: strongSelf.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {}) + let item = ChatMediaInputStickerGridItem(account: strongSelf.context.account, collectionId: collectionId, stickerPackInfo: nil, index: ItemCollectionViewEntryIndex(collectionIndex: 0, collectionId: collectionId, itemIndex: index), stickerItem: stickerPackItem, canManagePeerSpecificPack: nil, interfaceInteraction: nil, inputNodeInteraction: inputNodeInteraction, hasAccessory: false, theme: interfaceState.theme, large: true, selected: {}) strongSelf.stickerItem = item strongSelf.stickerNode.updateLayout(item: item, size: stickerSize, isVisible: true, synchronousLoads: true) strongSelf.stickerNode.isVisibleInGrid = true @@ -783,7 +784,7 @@ private enum ChatEmptyNodeContentType { } final class ChatEmptyNode: ASDisplayNode { - private let account: Account + private let context: AccountContext private let interaction: ChatPanelInterfaceInteraction? private let backgroundNode: ASImageNode @@ -793,8 +794,8 @@ final class ChatEmptyNode: ASDisplayNode { private var content: (ChatEmptyNodeContentType, ASDisplayNode & ChatEmptyNodeContent)? - init(account: Account, interaction: ChatPanelInterfaceInteraction?) { - self.account = account + init(context: AccountContext, interaction: ChatPanelInterfaceInteraction?) { + self.context = context self.interaction = interaction self.backgroundNode = ASImageNode() @@ -827,7 +828,7 @@ final class ChatEmptyNode: ASDisplayNode { if case .replyThread = interfaceState.chatLocation { contentType = .regular } else if let peer = interfaceState.renderedPeer?.peer, !isScheduledMessages { - if peer.id == self.account.peerId { + if peer.id == self.context.account.peerId { contentType = .cloud } else if let _ = peer as? TelegramSecretChat { contentType = .secret @@ -872,9 +873,9 @@ final class ChatEmptyNode: ASDisplayNode { case .cloud: node = ChatEmptyNodeCloudChatContent() case .peerNearby: - node = ChatEmptyNodeNearbyChatContent(account: self.account, interaction: self.interaction) + node = ChatEmptyNodeNearbyChatContent(context: self.context, interaction: self.interaction) case .greeting: - node = ChatEmptyNodeGreetingChatContent(account: self.account, interaction: self.interaction) + node = ChatEmptyNodeGreetingChatContent(context: self.context, interaction: self.interaction) } self.content = (contentType, node) self.addSubnode(node) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 65f74069c1..df6b000234 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -790,7 +790,7 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } |> distinctUntilChanged - let animatedEmojiStickers = loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .animatedEmoji, forceActualized: false) + let animatedEmojiStickers = context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false) |> map { animatedEmoji -> [String: [StickerPackItem]] in var animatedEmojiStickers: [String: [StickerPackItem]] = [:] switch animatedEmoji { @@ -1780,7 +1780,6 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if let historyView = strongSelf.historyView { if historyView.filteredEntries.isEmpty { if let firstEntry = historyView.originalView.entries.first { - var isPeerJoined = false var emptyType = ChatHistoryNodeLoadState.EmptyType.generic for media in firstEntry.message.media { if let action = media as? TelegramMediaAction { @@ -1893,6 +1892,28 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { } else if transition.scrolledToSomeIndex { self?.scrolledToSomeIndex?() } + + if let currentSendAnimationCorrelationId = strongSelf.currentSendAnimationCorrelationId { + var foundItemNode: ChatMessageItemView? + strongSelf.forEachItemNode { itemNode in + if let itemNode = itemNode as? ChatMessageItemView, let item = itemNode.item { + for (message, _) in item.content { + for attribute in message.attributes { + if let attribute = attribute as? OutgoingMessageInfoAttribute { + if attribute.correlationId == currentSendAnimationCorrelationId { + foundItemNode = itemNode + } + } + } + } + } + } + + if let foundItemNode = foundItemNode { + strongSelf.currentSendAnimationCorrelationId = nil + strongSelf.animationCorrelationMessageFound?(foundItemNode, currentSendAnimationCorrelationId) + } + } strongSelf.hasActiveTransition = false strongSelf.dequeueHistoryViewTransitions() @@ -2245,4 +2266,11 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { }) self.selectionScrollDisplayLink?.isPaused = false } + + private var currentSendAnimationCorrelationId: Int64? + func setCurrentSendAnimationCorrelationId(_ value: Int64?) { + self.currentSendAnimationCorrelationId = value + } + + var animationCorrelationMessageFound: ((ChatMessageItemView, Int64?) -> Void)? } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 8716e00cf2..66b1a1a16e 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -473,9 +473,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState if let strongController = controller { strongController.dismiss() - let id = arc4random64() + let id = Int64.random(in: Int64.min ... Int64.max) let file = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: id), partialReference: nil, resource: LocalFileReferenceMediaResource(localFilePath: logPath, randomId: id), previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "application/text", size: nil, attributes: [.FileName(fileName: "CallStats.log")]) - let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil) + let message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: file), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start() } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift index 32ceb31d47..bd0f80f701 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextQueries.swift @@ -127,7 +127,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee case .installed: scope = [.installed] } - return searchStickers(account: context.account, query: query.basicEmoji.0, scope: scope) + return context.engine.stickers.searchStickers(query: query.basicEmoji.0, scope: scope) |> castError(ChatContextQueryError.self) } |> map { stickers -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in @@ -264,7 +264,7 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee } let chatPeer = peer - let contextBot = resolvePeerByName(account: context.account, name: addressName) + let contextBot = context.engine.peers.resolvePeerByName(name: addressName) |> mapToSignal { peerId -> Signal in if let peerId = peerId { return context.account.postbox.loadedPeerWithId(peerId) @@ -338,13 +338,13 @@ private func updatedContextQueryResultStateForQuery(context: AccountContext, pee return signal |> then(contextBot) case let .emojiSearch(query, languageCode, range): - var signal = searchEmojiKeywords(postbox: context.account.postbox, inputLanguageCode: languageCode, query: query, completeMatch: query.count < 2) + var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query, completeMatch: query.count < 2) if !languageCode.lowercased().hasPrefix("en") { signal = signal |> mapToSignal { keywords in return .single(keywords) |> then( - searchEmojiKeywords(postbox: context.account.postbox, inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3) + context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query, completeMatch: query.count < 3) |> map { englishKeywords in return keywords + englishKeywords } diff --git a/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift b/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift index e87bfc7406..51e936abd3 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputGifPane.swift @@ -8,6 +8,7 @@ import SyncCore import SwiftSignalKit import TelegramPresentationData import ContextUI +import AccountContext private func fixListScrolling(_ multiplexedNode: MultiplexedVideoNode) { let searchBarHeight: CGFloat = 56.0 @@ -35,7 +36,7 @@ final class ChatMediaInputGifPaneTrendingState { } final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { - private let account: Account + private let context: AccountContext private var theme: PresentationTheme private var strings: PresentationStrings private let controllerInteraction: ChatControllerInteraction @@ -70,8 +71,8 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { private var isLoadingMore: Bool = false private var nextOffset: String? - init(account: Account, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, paneDidScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void, fixPaneScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void, openGifContextMenu: @escaping (MultiplexedVideoNodeFile, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void) { - self.account = account + init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, controllerInteraction: ChatControllerInteraction, paneDidScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState, ContainedViewLayoutTransition) -> Void, fixPaneScroll: @escaping (ChatMediaInputPane, ChatMediaInputPaneScrollState) -> Void, openGifContextMenu: @escaping (MultiplexedVideoNodeFile, ASDisplayNode, CGRect, ContextGesture, Bool) -> Void) { + self.context = context self.theme = theme self.strings = strings self.controllerInteraction = controllerInteraction @@ -208,7 +209,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { func initializeIfNeeded() { if self.multiplexedNode == nil { - self.trendingPromise.set(paneGifSearchForQuery(account: account, query: "", offset: nil, incompleteResults: true, delayRequest: false, updateActivity: nil) + self.trendingPromise.set(paneGifSearchForQuery(context: self.context, query: "", offset: nil, incompleteResults: true, delayRequest: false, updateActivity: nil) |> map { items -> ChatMediaInputGifPaneTrendingState? in if let items = items { return ChatMediaInputGifPaneTrendingState(files: items.files, nextOffset: items.nextOffset) @@ -217,7 +218,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { } }) - let multiplexedNode = MultiplexedVideoNode(account: self.account, theme: self.theme, strings: self.strings) + let multiplexedNode = MultiplexedVideoNode(account: self.context.account, theme: self.theme, strings: self.strings) self.multiplexedNode = multiplexedNode if let layout = self.validLayout { multiplexedNode.frame = CGRect(origin: CGPoint(), size: layout.0) @@ -289,7 +290,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { let filesSignal: Signal<(MultiplexedVideoNodeFiles, String?), NoError> switch self.mode { case .recent: - filesSignal = combineLatest(self.trendingPromise.get(), self.account.postbox.combinedView(keys: [.orderedItemList(id: Namespaces.OrderedItemList.CloudRecentGifs)])) + filesSignal = combineLatest(self.trendingPromise.get(), self.context.account.postbox.combinedView(keys: [.orderedItemList(id: Namespaces.OrderedItemList.CloudRecentGifs)])) |> map { trending, view -> (MultiplexedVideoNodeFiles, String?) in var recentGifs: OrderedItemListView? if let orderedView = view.views[.orderedItemList(id: Namespaces.OrderedItemList.CloudRecentGifs)] { @@ -311,7 +312,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { } case .trending: if let searchOffset = searchOffset { - filesSignal = paneGifSearchForQuery(account: self.account, query: "", offset: searchOffset, incompleteResults: true, delayRequest: false, updateActivity: nil) + filesSignal = paneGifSearchForQuery(context: self.context, query: "", offset: searchOffset, incompleteResults: true, delayRequest: false, updateActivity: nil) |> map { result -> (MultiplexedVideoNodeFiles, String?) in let canLoadMore: Bool if let result = result { @@ -328,7 +329,7 @@ final class ChatMediaInputGifPane: ChatMediaInputPane, UIScrollViewDelegate { } } case let .emojiSearch(emoji): - filesSignal = paneGifSearchForQuery(account: self.account, query: emoji, offset: searchOffset, incompleteResults: true, staleCachedResults: searchOffset == nil, delayRequest: false, updateActivity: nil) + filesSignal = paneGifSearchForQuery(context: self.context, query: emoji, offset: searchOffset, incompleteResults: true, staleCachedResults: searchOffset == nil, delayRequest: false, updateActivity: nil) |> map { result -> (MultiplexedVideoNodeFiles, String?) in let canLoadMore: Bool if let result = result { diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index ac93046ed5..918ba77da0 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -515,7 +515,7 @@ final class ChatMediaInputNode: ChatInputNode { }, fixPaneScroll: { pane, state in fixPaneScrollImpl?(pane, state) }) - self.gifPane = ChatMediaInputGifPane(account: context.account, theme: theme, strings: strings, controllerInteraction: controllerInteraction, paneDidScroll: { pane, state, transition in + self.gifPane = ChatMediaInputGifPane(context: context, theme: theme, strings: strings, controllerInteraction: controllerInteraction, paneDidScroll: { pane, state, transition in paneDidScrollImpl?(pane, state, transition) }, fixPaneScroll: { pane, state in fixPaneScrollImpl?(pane, state) @@ -783,14 +783,14 @@ final class ChatMediaInputNode: ChatInputNode { guard let strongSelf = self, let info = info as? StickerPackCollectionInfo else { return } - let _ = (loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + let _ = (context.engine.stickers.loadedStickerPack(reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal in switch result { case let .result(info, items, installed): if installed { return .complete() } else { - return addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items) + return context.engine.stickers.addStickerPackInteractively(info: info, items: items) } case .fetching: break diff --git a/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift index e4aa206ec3..c65e85329d 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift @@ -174,8 +174,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { private var currentState: (Account, StickerPackItem, CGSize)? private var currentSize: CGSize? let imageNode: TransformImageNode - var animationNode: AnimatedStickerNode? - private var placeholderNode: StickerShimmerEffectNode? + private(set) var animationNode: AnimatedStickerNode? + private(set) var placeholderNode: StickerShimmerEffectNode? private var didSetUpAnimationNode = false private var item: ChatMediaInputStickerGridItem? @@ -308,9 +308,13 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { if let (_, _, mediaDimensions) = self.currentState { let imageSize = mediaDimensions.aspectFitted(boundingSize) self.imageNode.asyncLayout()(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets()))() - self.imageNode.frame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: (size.height - imageSize.height) / 2.0), size: imageSize) + if self.imageNode.supernode === self { + self.imageNode.frame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: (size.height - imageSize.height) / 2.0), size: imageSize) + } if let animationNode = self.animationNode { - animationNode.frame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: (size.height - imageSize.height) / 2.0), size: imageSize) + if animationNode.supernode === self { + animationNode.frame = CGRect(origin: CGPoint(x: floor((size.width - imageSize.width) / 2.0), y: (size.height - imageSize.height) / 2.0), size: imageSize) + } animationNode.updateLayout(size: imageSize) } } @@ -318,7 +322,9 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode { if let placeholderNode = self.placeholderNode { let placeholderFrame = CGRect(origin: CGPoint(x: floor((size.width - boundingSize.width) / 2.0), y: floor((size.height - boundingSize.height) / 2.0)), size: boundingSize) - placeholderNode.frame = placeholderFrame + if placeholderNode.supernode === self { + placeholderNode.frame = placeholderFrame + } let theme = item.theme placeholderNode.update(backgroundColor: theme.chat.inputMediaPanel.stickersBackgroundColor.withAlphaComponent(1.0), foregroundColor: theme.chat.inputMediaPanel.stickersSectionTextColor.blitOver(theme.chat.inputMediaPanel.stickersBackgroundColor, alpha: 0.15), shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.3), data: item.stickerItem.file.immediateThumbnailData, size: placeholderFrame.size) diff --git a/submodules/TelegramUI/Sources/ChatMediaInputTrendingPane.swift b/submodules/TelegramUI/Sources/ChatMediaInputTrendingPane.swift index dd111fbed5..d8b37e863b 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputTrendingPane.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputTrendingPane.swift @@ -245,23 +245,22 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { let interaction = TrendingPaneInteraction(installPack: { [weak self] info in if let strongSelf = self, let info = info as? StickerPackCollectionInfo { - let account = strongSelf.context.account - var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + let context = strongSelf.context + var installSignal = context.engine.stickers.loadedStickerPack(reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in switch result { case let .result(info, items, installed): if installed { return .complete() } else { - return preloadedStickerPackThumbnail(account: account, info: info, items: items) + return preloadedStickerPackThumbnail(account: context.account, info: info, items: items) |> filter { $0 } |> ignoreValues |> then( - addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items) + context.engine.stickers.addStickerPackInteractively(info: info, items: items) |> ignoreValues ) |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in - return .complete() } |> then(.single((info, items))) } @@ -273,8 +272,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { return .complete() } |> deliverOnMainQueue - - let context = strongSelf.context + var cancelImpl: (() -> Void)? let progressSignal = Signal { subscriber in let presentationData = context.sharedContext.currentPresentationData.with { $0 } @@ -318,7 +316,7 @@ final class ChatMediaInputTrendingPane: ChatMediaInputPane { } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in + strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, context: strongSelf.context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return true })) })) diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index fe7c0e93fa..e89fe77d1b 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -29,14 +29,25 @@ private let inlineBotNameFont = nameFont protocol GenericAnimatedStickerNode: ASDisplayNode { func setOverlayColor(_ color: UIColor?, animated: Bool) + + var currentFrameIndex: Int { get } + func setFrameIndex(_ frameIndex: Int) } extension AnimatedStickerNode: GenericAnimatedStickerNode { - + func setFrameIndex(_ frameIndex: Int) { + self.stop() + self.play(fromIndex: frameIndex) + } } extension SlotMachineAnimationNode: GenericAnimatedStickerNode { - + var currentFrameIndex: Int { + return 0 + } + + func setFrameIndex(_ frameIndex: Int) { + } } class ChatMessageShareButton: HighlightableButtonNode { @@ -141,11 +152,12 @@ class ChatMessageShareButton: HighlightableButtonNode { } class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { - private let contextSourceNode: ContextExtractedContentContainingNode + let contextSourceNode: ContextExtractedContentContainingNode private let containerNode: ContextControllerSourceNode let imageNode: TransformImageNode - private var placeholderNode: StickerShimmerEffectNode - private var animationNode: GenericAnimatedStickerNode? + private var enableSynchronousImageApply: Bool = false + private(set) var placeholderNode: StickerShimmerEffectNode + private(set) var animationNode: GenericAnimatedStickerNode? private var didSetUpAnimationNode = false private var isPlaying = false private var animateGreeting = false @@ -250,8 +262,12 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } if image != nil { if firstTime && !strongSelf.placeholderNode.isEmpty && !strongSelf.animateGreeting && !strongSelf.animatingGreeting { - strongSelf.animationNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - strongSelf.removePlaceholder(animated: true) + if strongSelf.enableSynchronousImageApply { + strongSelf.removePlaceholder(animated: false) + } else { + strongSelf.animationNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + strongSelf.removePlaceholder(animated: true) + } } else { strongSelf.removePlaceholder(animated: true) } @@ -436,15 +452,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } - override func setupItem(_ item: ChatMessageItem) { - super.setupItem(item) + override func setupItem(_ item: ChatMessageItem, synchronousLoad: Bool) { + super.setupItem(item, synchronousLoad: synchronousLoad) for media in item.message.media { if let telegramFile = media as? TelegramMediaFile { if self.telegramFile?.id != telegramFile.id { self.telegramFile = telegramFile let dimensions = telegramFile.dimensions ?? PixelDimensions(width: 512, height: 512) - self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0)), thumbnail: false)) + self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: telegramFile, small: false, size: dimensions.cgSize.aspectFitted(CGSize(width: 384.0, height: 384.0)), thumbnail: false, synchronousLoad: synchronousLoad), attemptSynchronously: synchronousLoad) self.updateVisibility() self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile)).start()) } @@ -485,7 +501,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let fitz = fitz { fitzModifier = EmojiFitzModifier(emoji: fitz) } - self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: emojiFile, small: false, size: dimensions.cgSize.aspectFilled(CGSize(width: 384.0, height: 384.0)), fitzModifier: fitzModifier, thumbnail: false)) + self.imageNode.setSignal(chatMessageAnimatedSticker(postbox: item.context.account.postbox, file: emojiFile, small: false, size: dimensions.cgSize.aspectFilled(CGSize(width: 384.0, height: 384.0)), fitzModifier: fitzModifier, thumbnail: false, synchronousLoad: synchronousLoad), attemptSynchronously: synchronousLoad) self.disposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .standalone(media: emojiFile)).start()) } self.updateVisibility() @@ -1060,7 +1076,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let animationNode = strongSelf.animationNode as? AnimatedStickerNode, strongSelf.animationNode?.supernode === strongSelf.contextSourceNode.contentNode { animationNode.updateLayout(size: updatedContentFrame.insetBy(dx: imageInset, dy: imageInset).size) } + + strongSelf.enableSynchronousImageApply = true imageApply() + strongSelf.enableSynchronousImageApply = false strongSelf.contextSourceNode.contentRect = strongSelf.imageNode.frame strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect @@ -1090,7 +1109,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { if let updatedReplyBackgroundNode = updatedReplyBackgroundNode { if strongSelf.replyBackgroundNode == nil { strongSelf.replyBackgroundNode = updatedReplyBackgroundNode - strongSelf.addSubnode(updatedReplyBackgroundNode) + strongSelf.contextSourceNode.contentNode.addSubnode(updatedReplyBackgroundNode) updatedReplyBackgroundNode.image = replyBackgroundImage } else { strongSelf.replyBackgroundNode?.image = replyBackgroundImage @@ -1118,7 +1137,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let replyInfoNode = replyInfoApply() if strongSelf.replyInfoNode == nil { strongSelf.replyInfoNode = replyInfoNode - strongSelf.addSubnode(replyInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode) } var viaBotSize = CGSize() if let viaBotNode = strongSelf.viaBotNode { @@ -1669,6 +1688,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } } + + override func cancelInsertionAnimations() { + self.layer.removeAllAnimations() + } override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { super.animateInsertion(currentTimestamp, duration: duration, short: short) @@ -1707,6 +1730,168 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { override func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) { self.contextSourceNode.contentNode.addSubnode(accessoryItemNode) } + + func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: ContainedViewLayoutTransition) { + guard let _ = self.item else { + return + } + + let localSourceContentFrame = self.contextSourceNode.contentNode.view.convert(textInput.contentView.frame.offsetBy(dx: self.contextSourceNode.contentRect.minX, dy: self.contextSourceNode.contentRect.minY), to: self.contextSourceNode.contentNode.view) + textInput.contentView.frame = localSourceContentFrame + + self.contextSourceNode.contentNode.view.addSubview(textInput.contentView) + + let sourceCenter = CGPoint( + x: localSourceContentFrame.minX + 11.2, + y: localSourceContentFrame.midY - 1.8 + ) + let localSourceCenter = CGPoint( + x: sourceCenter.x - localSourceContentFrame.minX, + y: sourceCenter.y - localSourceContentFrame.minY + ) + let localSourceOffset = CGPoint( + x: localSourceCenter.x - localSourceContentFrame.width / 2.0, + y: localSourceCenter.y - localSourceContentFrame.height / 2.0 + ) + + let sourceScale: CGFloat = 28.0 / self.imageNode.frame.height + + let offset = CGPoint( + x: sourceCenter.x - self.imageNode.frame.midX, + y: sourceCenter.y - self.imageNode.frame.midY + ) + + transition.animatePositionAdditive(node: self.imageNode, offset: offset) + transition.animateTransformScale(node: self.imageNode, from: sourceScale) + if let animationNode = self.animationNode { + transition.animatePositionAdditive(node: animationNode, offset: offset) + transition.animateTransformScale(node: animationNode, from: sourceScale) + } + transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) + transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + + let inverseScale = 1.0 / sourceScale + + transition.animatePositionAdditive(layer: textInput.contentView.layer, offset: CGPoint(), to: CGPoint( + x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), + y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) + ), removeOnCompletion: false) + transition.updateTransformScale(layer: textInput.contentView.layer, scale: 1.0 / sourceScale) + + textInput.contentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { _ in + textInput.contentView.removeFromSuperview() + }) + + self.imageNode.layer.animateAlpha(from: 0.0, to: self.imageNode.alpha, duration: 0.1) + if let animationNode = self.animationNode { + animationNode.layer.animateAlpha(from: 0.0, to: animationNode.alpha, duration: 0.1) + } + self.placeholderNode.layer.animateAlpha(from: 0.0, to: self.placeholderNode.alpha, duration: 0.1) + + self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.16) + } + + func animateContentFromStickerGridItem(stickerSource: ChatMessageTransitionNode.Sticker, transition: ContainedViewLayoutTransition) { + guard let _ = self.item else { + return + } + + let localSourceContentFrame = CGRect( + origin: CGPoint( + x: self.imageNode.frame.minX + self.imageNode.frame.size.width / 2.0 - stickerSource.imageNode.frame.size.width / 2.0, + y: self.imageNode.frame.minY + self.imageNode.frame.size.height / 2.0 - stickerSource.imageNode.frame.size.height / 2.0 + ), + size: stickerSource.imageNode.frame.size + ) + + var snapshotView: UIView? + if let animationNode = stickerSource.animationNode { + snapshotView = animationNode.view.snapshotContentTree() + } else { + snapshotView = stickerSource.imageNode.view.snapshotContentTree() + } + snapshotView?.frame = localSourceContentFrame + + if let snapshotView = snapshotView { + self.contextSourceNode.contentNode.view.addSubview(snapshotView) + } + + let sourceCenter = CGPoint( + x: localSourceContentFrame.midX, + y: localSourceContentFrame.midY + ) + let localSourceCenter = CGPoint( + x: sourceCenter.x - localSourceContentFrame.minX, + y: sourceCenter.y - localSourceContentFrame.minY + ) + let localSourceOffset = CGPoint( + x: localSourceCenter.x - localSourceContentFrame.width / 2.0, + y: localSourceCenter.y - localSourceContentFrame.height / 2.0 + ) + + let sourceScale: CGFloat = stickerSource.imageNode.frame.height / self.imageNode.frame.height + + let offset = CGPoint( + x: sourceCenter.x - self.imageNode.frame.midX, + y: sourceCenter.y - self.imageNode.frame.midY + ) + + transition.animatePositionAdditive(node: self.imageNode, offset: offset) + transition.animateTransformScale(node: self.imageNode, from: sourceScale) + if let animationNode = self.animationNode { + transition.animatePositionAdditive(node: animationNode, offset: offset) + transition.animateTransformScale(node: animationNode, from: sourceScale) + } + transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) + transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + + let inverseScale = 1.0 / sourceScale + + if let snapshotView = snapshotView { + transition.animatePositionAdditive(layer: snapshotView.layer, offset: CGPoint(), to: CGPoint( + x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), + y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) + ), removeOnCompletion: false) + transition.updateTransformScale(layer: snapshotView.layer, scale: 1.0 / sourceScale) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.08, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + + self.imageNode.layer.animateAlpha(from: 0.0, to: self.imageNode.alpha, duration: 0.05) + if let animationNode = self.animationNode { + animationNode.layer.animateAlpha(from: 0.0, to: animationNode.alpha, duration: 0.05) + } + self.placeholderNode.layer.animateAlpha(from: 0.0, to: self.placeholderNode.alpha, duration: 0.05) + } + + self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.16) + + if let animationNode = stickerSource.animationNode { + animationNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + animationNode.layer.animateAlpha(from: 0.0, to: animationNode.alpha, duration: 0.4) + } + + stickerSource.imageNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + stickerSource.imageNode.layer.animateAlpha(from: 0.0, to: stickerSource.imageNode.alpha, duration: 0.4) + + if let placeholderNode = stickerSource.placeholderNode { + placeholderNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + placeholderNode.layer.animateAlpha(from: 0.0, to: placeholderNode.alpha, duration: 0.4) + } + } + + func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: ContainedViewLayoutTransition) { + if let replyInfoNode = self.replyInfoNode { + let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) + + let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) + if let replyBackgroundNode = self.replyBackgroundNode { + transition.animatePositionAdditive(node: replyBackgroundNode, offset: offset) + replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + } + } + } } struct AnimatedEmojiSoundsConfiguration { diff --git a/submodules/TelegramUI/Sources/ChatMessageBackground.swift b/submodules/TelegramUI/Sources/ChatMessageBackground.swift index 36bcc4941c..0dab2ab676 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBackground.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBackground.swift @@ -233,6 +233,23 @@ class ChatMessageBackground: ASDisplayNode { self.imageNode.image = image self.outlineImageNode.image = outlineImage } + + func animateFrom(sourceView: UIView, transition: ContainedViewLayoutTransition) { + if transition.isAnimated { + self.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + self.outlineImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + + self.view.addSubview(sourceView) + + sourceView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceView] _ in + sourceView?.removeFromSuperview() + }) + + transition.animateFrame(node: self.imageNode, from: sourceView.frame) + transition.animateFrame(node: self.outlineImageNode, from: sourceView.frame) + transition.updateFrame(view: sourceView, frame: CGRect(origin: self.imageNode.frame.origin, size: CGSize(width: self.imageNode.frame.width - 7.0, height: self.imageNode.frame.height))) + } + } } final class ChatMessageShadowNode: ASDisplayNode { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift index 925d55c693..842574b8b2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleBackdrop.swift @@ -62,6 +62,8 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { private var essentialGraphics: PrincipalThemeEssentialGraphics? private var maskView: UIImageView? + + private var fixedMaskMode: Bool? var hasImage: Bool { return self.backgroundContent.contents != nil @@ -94,7 +96,9 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { } } - func setType(type: ChatMessageBackgroundType, theme: ChatPresentationThemeData, mediaBox: MediaBox, essentialGraphics: PrincipalThemeEssentialGraphics, maskMode: Bool) { + func setType(type: ChatMessageBackgroundType, theme: ChatPresentationThemeData, mediaBox: MediaBox, essentialGraphics: PrincipalThemeEssentialGraphics, maskMode inputMaskMode: Bool) { + let maskMode = self.fixedMaskMode ?? inputMaskMode + if self.currentType != type || self.theme != theme || self.currentMaskMode != maskMode || self.essentialGraphics !== essentialGraphics { self.currentType = type self.theme = theme @@ -140,19 +144,31 @@ final class ChatMessageBubbleBackdrop: ASDisplayNode { self.backgroundContent.frame = CGRect(origin: CGPoint(x: -rect.minX, y: -rect.minY), size: containerSize) } - func offset(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + func offset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { let transition: ContainedViewLayoutTransition = .animated(duration: duration, curve: animationCurve) - transition.animatePositionAdditive(node: self.backgroundContent, offset: CGPoint(x: 0.0, y: -value)) + transition.animatePositionAdditive(node: self.backgroundContent, offset: CGPoint(x: -value.x, y: -value.y)) } func offsetSpring(value: CGFloat, duration: Double, damping: CGFloat) { self.backgroundContent.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: value)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: duration, initialVelocity: 0.0, damping: damping, additive: true) } - func updateFrame(_ value: CGRect, transition: ContainedViewLayoutTransition) { + func updateFrame(_ value: CGRect, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void = {}) { if let maskView = self.maskView { - transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width + maskInset * 2.0, height: value.size.height + maskInset * 2.0))) + transition.updateFrame(view: maskView, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: value.size.width, height: value.size.height)).insetBy(dx: -maskInset, dy: -maskInset)) + } + transition.updateFrame(node: self, frame: value, completion: { _ in + completion() + }) + } + + func animateFrom(sourceView: UIView, mediaBox: MediaBox, transition: ContainedViewLayoutTransition) { + if transition.isAnimated { + let previousFrame = self.frame + self.updateFrame(CGRect(origin: previousFrame.origin, size: sourceView.frame.size), transition: .immediate) + self.updateFrame(previousFrame, transition: transition) + + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) } - transition.updateFrame(node: self, frame: value) } } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 664d8726f6..c310d8f781 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -245,7 +245,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode backgroundWallpaperNode.update(rect: mappedRect, within: containerSize) } - fileprivate func applyAbsoluteOffset(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + fileprivate func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { guard let backgroundWallpaperNode = self.backgroundWallpaperNode else { return } @@ -362,12 +362,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } } - private let mainContextSourceNode: ContextExtractedContentContainingNode + let mainContextSourceNode: ContextExtractedContentContainingNode private let mainContainerNode: ContextControllerSourceNode private let backgroundWallpaperNode: ChatMessageBubbleBackdrop private let backgroundNode: ChatMessageBackground private let shadowNode: ChatMessageShadowNode - private var transitionClippingNode: ASDisplayNode? + private var clippingNode: ASDisplayNode override var extractedBackgroundNode: ASDisplayNode? { return self.shadowNode @@ -430,6 +430,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode self.backgroundNode = ChatMessageBackground() self.shadowNode = ChatMessageShadowNode() + + self.clippingNode = ASDisplayNode() + self.clippingNode.clipsToBounds = true + self.messageAccessibilityArea = AccessibilityAreaNode() super.init(layerBacked: false) @@ -482,6 +486,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode self.mainContextSourceNode.contentNode.addSubnode(self.backgroundWallpaperNode) self.mainContextSourceNode.contentNode.addSubnode(self.backgroundNode) self.mainContextSourceNode.contentNode.addSubnode(self.contentContainersWrapperNode) + self.mainContextSourceNode.contentNode.addSubnode(self.clippingNode) self.addSubnode(self.messageAccessibilityArea) self.messageAccessibilityArea.activate = { [weak self] in @@ -553,6 +558,40 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + override func cancelInsertionAnimations() { + self.shadowNode.layer.removeAllAnimations() + + func process(node: ASDisplayNode) { + if node === self.accessoryItemNode { + return + } + + if node !== self { + switch node { + case let node as ContextExtractedContentContainingNode: + process(node: node.contentNode) + return + case _ as ContextControllerSourceNode, _ as ContextExtractedContentNode: + break + default: + node.layer.removeAllAnimations() + node.layer.allowsGroupOpacity = false + return + } + } + + guard let subnodes = node.subnodes else { + return + } + + for subnode in subnodes { + process(node: subnode) + } + } + + process(node: self) + } override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { super.animateInsertion(currentTimestamp, duration: duration, short: short) @@ -614,6 +653,57 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } } } + + func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: ContainedViewLayoutTransition) { + guard let item = self.item else { + return + } + let widthDifference = self.backgroundNode.frame.width - textInput.backgroundView.frame.width + + textInput.backgroundView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: textInput.backgroundView.bounds.size) + + transition.animateFrame(node: self.clippingNode, from: CGRect(origin: self.clippingNode.frame.origin, size: textInput.backgroundView.frame.size)) + + self.backgroundWallpaperNode.animateFrom(sourceView: textInput.backgroundView, mediaBox: item.context.account.postbox.mediaBox, transition: transition) + self.backgroundNode.animateFrom(sourceView: textInput.backgroundView, transition: transition) + + for contentNode in self.contentNodes { + if let contentNode = contentNode as? ChatMessageTextBubbleContentNode { + let localSourceContentFrame = self.mainContextSourceNode.contentNode.view.convert(textInput.contentView.frame.offsetBy(dx: self.mainContextSourceNode.contentRect.minX, dy: self.mainContextSourceNode.contentRect.minY), to: contentNode.view) + textInput.contentView.frame = localSourceContentFrame + contentNode.animateFrom(sourceView: textInput.contentView, widthDifference: widthDifference, transition: transition) + } + } + } + + func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: ContainedViewLayoutTransition) { + if let replyInfoNode = self.replyInfoNode { + let localRect = self.mainContextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) + let _ = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) + } + } + + func animateFromMicInput(micInputNode: UIView, transition: ContainedViewLayoutTransition) -> ContextExtractedContentContainingNode? { + for contentNode in self.contentNodes { + if let contentNode = contentNode as? ChatMessageFileBubbleContentNode { + if let statusContainerNode = contentNode.interactiveFileNode.statusContainerNode { + let scale = statusContainerNode.contentRect.height / 100.0 + micInputNode.transform = CGAffineTransform(scaleX: scale, y: scale) + micInputNode.center = CGPoint(x: statusContainerNode.contentRect.midX, y: statusContainerNode.contentRect.midY) + statusContainerNode.contentNode.view.addSubview(micInputNode) + + transition.updateAlpha(layer: micInputNode.layer, alpha: 0.0, completion: { [weak micInputNode] _ in + micInputNode?.removeFromSuperview() + }) + + transition.animateTransformScale(node: statusContainerNode.contentNode, from: 1.0 / scale) + + return statusContainerNode + } + } + } + return nil + } override func didLoad() { super.didLoad() @@ -2241,7 +2331,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if !nameNode.isNodeLoaded { nameNode.isUserInteractionEnabled = false } - strongSelf.mainContextSourceNode.contentNode.addSubnode(nameNode) + strongSelf.clippingNode.addSubnode(nameNode) } nameNode.frame = CGRect(origin: CGPoint(x: contentOrigin.x + layoutConstants.text.bubbleInsets.left, y: layoutConstants.bubble.contentInsets.top + nameNodeOriginY), size: nameNodeSizeApply.0) nameNode.displaysAsynchronously = !item.presentationData.isPreview @@ -2253,7 +2343,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } else { credibilityIconNode = ASImageNode() strongSelf.credibilityIconNode = credibilityIconNode - strongSelf.mainContextSourceNode.contentNode.addSubnode(credibilityIconNode) + strongSelf.clippingNode.addSubnode(credibilityIconNode) } credibilityIconNode.frame = CGRect(origin: CGPoint(x: nameNode.frame.maxX + 4.0, y: nameNode.frame.minY), size: credibilityIconImage.size) credibilityIconNode.image = credibilityIconImage @@ -2269,7 +2359,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if !adminBadgeNode.isNodeLoaded { adminBadgeNode.isUserInteractionEnabled = false } - strongSelf.mainContextSourceNode.contentNode.addSubnode(adminBadgeNode) + strongSelf.clippingNode.addSubnode(adminBadgeNode) adminBadgeNode.frame = adminBadgeFrame } else { let previousAdminBadgeFrame = adminBadgeNode.frame @@ -2291,7 +2381,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode strongSelf.forwardInfoNode = forwardInfoNode var animateFrame = true if forwardInfoNode.supernode == nil { - strongSelf.mainContextSourceNode.contentNode.addSubnode(forwardInfoNode) + strongSelf.clippingNode.addSubnode(forwardInfoNode) animateFrame = false forwardInfoNode.openPsa = { [weak strongSelf] type, sourceNode in guard let strongSelf = strongSelf, let item = strongSelf.item else { @@ -2518,12 +2608,15 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode updatedContentNodes.append(contentNode) let contextSourceNode: ContextExtractedContentContainingNode + let containerSupernode: ASDisplayNode if isAttachent { contextSourceNode = strongSelf.mainContextSourceNode + containerSupernode = strongSelf.clippingNode } else { contextSourceNode = strongSelf.contentContainers.first(where: { $0.contentMessageStableId == contentNodeMessage.stableId })?.sourceNode ?? strongSelf.mainContextSourceNode + containerSupernode = strongSelf.contentContainers.first(where: { $0.contentMessageStableId == contentNodeMessage.stableId })?.sourceNode ?? strongSelf.clippingNode } - contextSourceNode.contentNode.addSubnode(contentNode) + containerSupernode.addSubnode(contentNode) contentNode.visibility = strongSelf.visibility contentNode.updateIsTextSelectionActive = { [weak contextSourceNode] value in @@ -2599,7 +2692,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if mosaicStatusNode !== strongSelf.mosaicStatusNode { strongSelf.mosaicStatusNode?.removeFromSupernode() strongSelf.mosaicStatusNode = mosaicStatusNode - strongSelf.mainContextSourceNode.contentNode.addSubnode(mosaicStatusNode) + strongSelf.clippingNode.addSubnode(mosaicStatusNode) } let absoluteOrigin = mosaicStatusOrigin.offsetBy(dx: contentOrigin.x, dy: contentOrigin.y) mosaicStatusNode.frame = CGRect(origin: CGPoint(x: absoluteOrigin.x - layoutConstants.image.statusInsets.right - size.width, y: absoluteOrigin.y - layoutConstants.image.statusInsets.bottom - size.height), size: size) @@ -2628,7 +2721,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if case .System = animation, !strongSelf.mainContextSourceNode.isExtractedToContextPreview { if !strongSelf.backgroundNode.frame.equalTo(backgroundFrame) { strongSelf.backgroundFrameTransition = (strongSelf.backgroundNode.frame, backgroundFrame) - strongSelf.enableTransitionClippingNode() } if let shareButtonNode = strongSelf.shareButtonNode { let currentBackgroundFrame = strongSelf.backgroundNode.frame @@ -2643,15 +2735,20 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if let shareButtonNode = strongSelf.shareButtonNode { shareButtonNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX + 8.0, y: backgroundFrame.maxY - 30.0), size: CGSize(width: 29.0, height: 29.0)) } - strongSelf.disableTransitionClippingNode() if case .System = animation, strongSelf.mainContextSourceNode.isExtractedToContextPreview { transition.updateFrame(node: strongSelf.backgroundNode, frame: backgroundFrame) + + transition.updateFrame(node: strongSelf.clippingNode, frame: backgroundFrame) + transition.updateBounds(node: strongSelf.clippingNode, bounds: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size)) + strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: transition) strongSelf.backgroundWallpaperNode.updateFrame(backgroundFrame, transition: transition) strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: transition) } else { strongSelf.backgroundNode.frame = backgroundFrame + strongSelf.clippingNode.frame = backgroundFrame + strongSelf.clippingNode.bounds = CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size) strongSelf.backgroundNode.updateLayout(size: backgroundFrame.size, transition: .immediate) strongSelf.backgroundWallpaperNode.frame = backgroundFrame strongSelf.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: .immediate) @@ -2709,7 +2806,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode strongSelf.updateSearchTextHighlightState() - if let (awaitingAppliedReaction, f) = strongSelf.awaitingAppliedReaction { + /*if let (awaitingAppliedReaction, f) = strongSelf.awaitingAppliedReaction { var bounds = strongSelf.bounds let offset = bounds.origin.x bounds.origin.x = 0.0 @@ -2746,7 +2843,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } strongSelf.reactionRecognizer?.complete(into: targetNode, hideTarget: hideTarget)*/ f() - } + }*/ } override func updateAccessibilityData(_ accessibilityData: ChatMessageAccessibilityData) { @@ -2789,52 +2886,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } } - private func enableTransitionClippingNode() { - if self.transitionClippingNode == nil { - let node = ASDisplayNode() - node.clipsToBounds = true - var backgroundFrame = self.backgroundNode.frame - backgroundFrame = backgroundFrame.insetBy(dx: 0.0, dy: 1.0) - node.frame = backgroundFrame - node.bounds = CGRect(origin: CGPoint(x: backgroundFrame.origin.x, y: backgroundFrame.origin.y), size: backgroundFrame.size) - if let forwardInfoNode = self.forwardInfoNode { - node.addSubnode(forwardInfoNode) - } - if let replyInfoNode = self.replyInfoNode { - node.addSubnode(replyInfoNode) - } - if !self.contentContainers.isEmpty { - node.addSubnode(self.contentContainersWrapperNode) - } else { - for contentNode in self.contentNodes { - node.addSubnode(contentNode) - } - } - self.mainContextSourceNode.contentNode.addSubnode(node) - self.transitionClippingNode = node - } - } - - private func disableTransitionClippingNode() { - if let transitionClippingNode = self.transitionClippingNode { - if let forwardInfoNode = self.forwardInfoNode { - self.mainContextSourceNode.contentNode.addSubnode(forwardInfoNode) - } - if let replyInfoNode = self.replyInfoNode { - self.mainContextSourceNode.contentNode.addSubnode(replyInfoNode) - } - if !self.contentContainers.isEmpty { - self.mainContextSourceNode.contentNode.addSubnode(self.contentContainersWrapperNode) - } else { - for contentNode in self.contentNodes { - self.mainContextSourceNode.contentNode.addSubnode(contentNode) - } - } - transitionClippingNode.removeFromSupernode() - self.transitionClippingNode = nil - } - } - override func shouldAnimateHorizontalFrameTransition() -> Bool { if let _ = self.backgroundFrameTransition { return true @@ -2849,6 +2900,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode if let backgroundFrameTransition = self.backgroundFrameTransition { let backgroundFrame = CGRect.interpolator()(backgroundFrameTransition.0, backgroundFrameTransition.1, progress) as! CGRect self.backgroundNode.frame = backgroundFrame + + self.clippingNode.frame = backgroundFrame + self.clippingNode.bounds = CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: backgroundFrame.size) + self.backgroundNode.updateLayout(size: backgroundFrame.size, transition: .immediate) self.backgroundWallpaperNode.frame = backgroundFrame self.shadowNode.updateLayout(backgroundFrame: backgroundFrame, transition: .immediate) @@ -2875,18 +2930,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode shareButtonNode.frame = CGRect(origin: CGPoint(x: backgroundFrame.maxX + 8.0, y: backgroundFrame.maxY - 30.0), size: CGSize(width: 29.0, height: 29.0)) } - if let transitionClippingNode = self.transitionClippingNode { - var fixedBackgroundFrame = backgroundFrame - fixedBackgroundFrame = fixedBackgroundFrame.insetBy(dx: 0.0, dy: self.backgroundNode.type == ChatMessageBackgroundType.none ? 0.0 : 1.0) - - transitionClippingNode.frame = fixedBackgroundFrame - transitionClippingNode.bounds = CGRect(origin: CGPoint(x: fixedBackgroundFrame.origin.x, y: fixedBackgroundFrame.origin.y), size: fixedBackgroundFrame.size) - - if progress >= 1.0 - CGFloat.ulpOfOne { - self.disableTransitionClippingNode() - } - } - if CGFloat(1.0).isLessThanOrEqualTo(progress) { self.backgroundFrameTransition = nil } @@ -3687,13 +3730,13 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode self.backgroundWallpaperNode.update(rect: mappedRect, within: containerSize) } - override func applyAbsoluteOffset(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + override func applyAbsoluteOffset(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { if !self.mainContextSourceNode.isExtractedToContextPreview { - self.applyAbsoluteOffsetInternal(value: -value, animationCurve: animationCurve, duration: duration) + self.applyAbsoluteOffsetInternal(value: CGPoint(x: -value.x, y: -value.y), animationCurve: animationCurve, duration: duration) } } - private func applyAbsoluteOffsetInternal(value: CGFloat, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { + private func applyAbsoluteOffsetInternal(value: CGPoint, animationCurve: ContainedViewLayoutTransitionCurve, duration: Double) { self.backgroundWallpaperNode.offset(value: value, animationCurve: animationCurve, duration: duration) } diff --git a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift index 8f475534f7..becd445ba4 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift @@ -90,6 +90,9 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { } else { displayName = selectedContact.lastName } + if displayName.isEmpty { + displayName = item.presentationData.strings.Message_Contact + } let info: String if let previousContact = previousContact, previousContact.isEqual(to: selectedContact), let contactInfo = previousContactInfo { diff --git a/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift index 749a02796f..4e374f4aa3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageFileBubbleContentNode.swift @@ -9,7 +9,7 @@ import SyncCore import TelegramUIPreferences class ChatMessageFileBubbleContentNode: ChatMessageBubbleContentNode { - private let interactiveFileNode: ChatMessageInteractiveFileNode + let interactiveFileNode: ChatMessageInteractiveFileNode override var visibility: ListViewItemNodeVisibility { didSet { diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index 30c9380450..d77afd6a67 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -20,7 +20,7 @@ private let inlineBotPrefixFont = Font.regular(14.0) private let inlineBotNameFont = nameFont class ChatMessageInstantVideoItemNode: ChatMessageItemView { - private let contextSourceNode: ContextExtractedContentContainingNode + let contextSourceNode: ContextExtractedContentContainingNode private let containerNode: ContextControllerSourceNode private let interactiveVideoNode: ChatMessageInteractiveInstantVideoNode @@ -985,6 +985,10 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { } } } + + override func cancelInsertionAnimations() { + self.layer.removeAllAnimations() + } override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { super.animateInsertion(currentTimestamp, duration: duration, short: short) @@ -1003,7 +1007,12 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView { self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } - + + func animateFromSnapshot(snapshotView: UIView, transition: ContainedViewLayoutTransition) { + snapshotView.frame = self.interactiveVideoNode.view.convert(snapshotView.frame, from: self.contextSourceNode.contentNode.view) + self.interactiveVideoNode.animateFromSnapshot(snapshotView: snapshotView, transition: transition) + } + override func playMediaWithSound() -> ((Double?) -> Void, Bool, Bool, Bool, ASDisplayNode?)? { return self.interactiveVideoNode.playMediaWithSound() } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift index 718f66a8b3..072496df97 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveFileNode.swift @@ -17,6 +17,7 @@ import FileMediaResourceStatus import CheckNode import MusicAlbumArtResources import AudioBlob +import ContextUI private struct FetchControls { let fetch: () -> Void @@ -38,6 +39,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { private let consumableContentNode: ASImageNode private var iconNode: TransformImageNode? + private(set) var statusContainerNode: ContextExtractedContentContainingNode? private var statusNode: SemanticStatusNode? private var playbackAudioLevelView: VoiceBlobView? private var streamingStatusNode: SemanticStatusNode? @@ -129,6 +131,8 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { self.dateAndStatusNode = ChatMessageDateAndStatusNode() self.consumableContentNode = ASImageNode() + + self.statusContainerNode = ContextExtractedContentContainingNode() super.init() @@ -136,6 +140,9 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { self.addSubnode(self.descriptionNode) self.addSubnode(self.fetchingTextNode) self.addSubnode(self.fetchingCompactTextNode) + if let statusContainerNode = self.statusContainerNode { + self.addSubnode(statusContainerNode) + } } deinit { @@ -683,11 +690,20 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { strongSelf.waveformNode.displaysAsynchronously = !presentationData.isPreview strongSelf.statusNode?.displaysAsynchronously = !presentationData.isPreview - strongSelf.statusNode?.frame = progressFrame + strongSelf.statusNode?.frame = CGRect(origin: CGPoint(), size: progressFrame.size) + + strongSelf.statusContainerNode?.frame = progressFrame + strongSelf.statusContainerNode?.contentRect = CGRect(origin: CGPoint(), size: progressFrame.size) + strongSelf.statusContainerNode?.contentNode.frame = CGRect(origin: CGPoint(), size: progressFrame.size) + strongSelf.playbackAudioLevelView?.frame = progressFrame.insetBy(dx: -12.0, dy: -12.0) strongSelf.progressFrame = progressFrame strongSelf.streamingCacheStatusFrame = streamingCacheStatusFrame strongSelf.fileIconImage = fileIconImage + + strongSelf.statusContainerNode?.frame = progressFrame + strongSelf.statusContainerNode?.contentRect = CGRect(origin: CGPoint(), size: progressFrame.size) + strongSelf.statusContainerNode?.contentNode.frame = CGRect(origin: CGPoint(), size: progressFrame.size) if let updatedFetchControls = updatedFetchControls { let _ = strongSelf.fetchControls.swap(updatedFetchControls) @@ -937,8 +953,12 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode { } let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor) self.statusNode = statusNode - statusNode.frame = progressFrame - self.addSubnode(statusNode) + + self.statusContainerNode?.contentNode.insertSubnode(statusNode, at: 0) + self.statusContainerNode?.frame = progressFrame + self.statusContainerNode?.contentRect = CGRect(origin: CGPoint(), size: progressFrame.size) + self.statusContainerNode?.contentNode.frame = CGRect(origin: CGPoint(), size: progressFrame.size) + statusNode.frame = CGRect(origin: CGPoint(), size: progressFrame.size) } else if let statusNode = self.statusNode { statusNode.backgroundNodeColor = backgroundNodeColor } diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index b11555a80f..d65ffa39ca 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -39,7 +39,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { private var statusNode: RadialStatusNode? private var playbackStatusNode: InstantVideoRadialStatusNode? - private var videoFrame: CGRect? + private(set) var videoFrame: CGRect? private var item: ChatMessageBubbleContentItem? private var automaticDownload: Bool? @@ -832,5 +832,28 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { return nil } } + + func animateFromSnapshot(snapshotView: UIView, transition: ContainedViewLayoutTransition) { + guard let videoFrame = self.videoFrame else { + return + } + + let scale = videoFrame.height / snapshotView.frame.height + snapshotView.transform = CGAffineTransform(scaleX: scale, y: scale) + snapshotView.center = CGPoint(x: videoFrame.midX, y: videoFrame.midY) + + self.view.addSubview(snapshotView) + + transition.updateAlpha(layer: snapshotView.layer, alpha: 0.0, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + + transition.animateTransformScale(node: self, from: 1.0 / scale) + + self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.18) + if let durationNode = self.durationNode { + durationNode.layer.animateAlpha(from: 0.0, to: durationNode.alpha, duration: 0.15, delay: 0.18) + } + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageItem.swift b/submodules/TelegramUI/Sources/ChatMessageItem.swift index 3392533409..606da3ec2a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItem.swift @@ -434,7 +434,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { let configure = { let node = (viewClassName as! ChatMessageItemView.Type).init() - node.setupItem(self) + node.setupItem(self, synchronousLoad: synchronousLoads) let nodeLayout = node.asyncLayout() let (top, bottom, dateAtBottom) = self.mergedWithItems(top: previousItem, bottom: nextItem) @@ -500,7 +500,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible { public func updateNode(async: @escaping (@escaping () -> Void) -> Void, node: @escaping () -> ListViewItemNode, params: ListViewItemLayoutParams, previousItem: ListViewItem?, nextItem: ListViewItem?, animation: ListViewItemUpdateAnimation, completion: @escaping (ListViewItemNodeLayout, @escaping (ListViewItemApply) -> Void) -> Void) { Queue.mainQueue().async { if let nodeValue = node() as? ChatMessageItemView { - nodeValue.setupItem(self) + nodeValue.setupItem(self, synchronousLoad: false) let nodeLayout = nodeValue.asyncLayout() diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 02594b2cd2..a640ad2c7d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -705,7 +705,7 @@ public class ChatMessageItemView: ListViewItemNode { self.frame = CGRect() } - func setupItem(_ item: ChatMessageItem) { + func setupItem(_ item: ChatMessageItem, synchronousLoad: Bool) { self.item = item } @@ -729,6 +729,9 @@ public class ChatMessageItemView: ListViewItemNode { avatarNode.frame = CGRect(origin: CGPoint(x: leftInset + 3.0, y: self.apparentFrame.height - 38.0 - self.insets.top - 2.0 - UIScreenPixel), size: CGSize(width: 38.0, height: 38.0)) } } + + func cancelInsertionAnimations() { + } override public func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { if short { diff --git a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index 91fd6ee3a6..bdea98855b 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -229,4 +229,86 @@ class ChatMessageReplyInfoNode: ASDisplayNode { }) } } + + func animateFromInputPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, localRect: CGRect, transition: ContainedViewLayoutTransition) -> CGPoint { + if let titleNode = self.titleNode { + let offset = CGPoint( + x: localRect.minX + sourceReplyPanel.titleNode.frame.minX - titleNode.frame.minX, + y: localRect.minY + sourceReplyPanel.titleNode.frame.midY - titleNode.frame.midY + ) + + transition.animatePositionAdditive(node: titleNode, offset: offset) + + self.addSubnode(sourceReplyPanel.titleNode) + + titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + sourceReplyPanel.titleNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in + sourceReplyPanel?.titleNode.removeFromSupernode() + }) + + sourceReplyPanel.titleNode.frame = sourceReplyPanel.titleNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.animatePositionAdditive(node: sourceReplyPanel.titleNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + } + + if let textNode = self.textNode { + let offset = CGPoint( + x: localRect.minX + sourceReplyPanel.textNode.frame.minX - textNode.frame.minX, + y: localRect.minY + sourceReplyPanel.textNode.frame.midY - textNode.frame.midY + ) + + transition.animatePositionAdditive(node: textNode, offset: offset) + + self.addSubnode(sourceReplyPanel.textNode) + + textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + sourceReplyPanel.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in + sourceReplyPanel?.textNode.removeFromSupernode() + }) + + sourceReplyPanel.textNode.frame = sourceReplyPanel.textNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.animatePositionAdditive(node: sourceReplyPanel.textNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + } + + if let imageNode = self.imageNode { + let offset = CGPoint( + x: localRect.minX + sourceReplyPanel.imageNode.frame.midX - imageNode.frame.midX, + y: localRect.minY + sourceReplyPanel.imageNode.frame.midY - imageNode.frame.midY + ) + + transition.animatePositionAdditive(node: imageNode, offset: offset) + + self.addSubnode(sourceReplyPanel.imageNode) + + imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + sourceReplyPanel.imageNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in + sourceReplyPanel?.imageNode.removeFromSupernode() + }) + + sourceReplyPanel.imageNode.frame = sourceReplyPanel.imageNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.animatePositionAdditive(node: sourceReplyPanel.imageNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + } + + do { + let lineNode = self.lineNode + + let offset = CGPoint( + x: localRect.minX + sourceReplyPanel.lineNode.frame.minX - lineNode.frame.minX, + y: localRect.minY + sourceReplyPanel.lineNode.frame.minY - lineNode.frame.minY + ) + + transition.animatePositionAdditive(node: lineNode, offset: offset) + + self.addSubnode(sourceReplyPanel.lineNode) + + lineNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + sourceReplyPanel.lineNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak sourceReplyPanel] _ in + sourceReplyPanel?.lineNode.removeFromSupernode() + }) + + sourceReplyPanel.lineNode.frame = sourceReplyPanel.lineNode.frame.offsetBy(dx: localRect.minX - offset.x, dy: localRect.minY - offset.y) + transition.animatePositionAdditive(node: sourceReplyPanel.lineNode, offset: CGPoint(x: offset.x, y: offset.y), removeOnCompletion: false) + + return offset + } + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index 3a5ee9c215..3785c23349 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -19,7 +19,7 @@ private let inlineBotPrefixFont = Font.regular(14.0) private let inlineBotNameFont = nameFont class ChatMessageStickerItemNode: ChatMessageItemView { - private let contextSourceNode: ContextExtractedContentContainingNode + let contextSourceNode: ContextExtractedContentContainingNode private let containerNode: ContextControllerSourceNode let imageNode: TransformImageNode private var placeholderNode: StickerShimmerEffectNode @@ -49,6 +49,8 @@ class ChatMessageStickerItemNode: ChatMessageItemView { private var currentSwipeToReplyTranslation: CGFloat = 0.0 private var currentSwipeAction: ChatControllerInteractionSwipeAction? + + private var enableSynchronousImageApply: Bool = false required init() { self.contextSourceNode = ContextExtractedContentContainingNode() @@ -68,9 +70,13 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } if image != nil { if firstTime && !strongSelf.placeholderNode.isEmpty { - strongSelf.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self] _ in - self?.removePlaceholder(animated: false) - }) + if strongSelf.enableSynchronousImageApply { + strongSelf.removePlaceholder(animated: false) + } else { + strongSelf.imageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, completion: { [weak self] _ in + self?.removePlaceholder(animated: false) + }) + } } else { strongSelf.removePlaceholder(animated: true) } @@ -214,15 +220,15 @@ class ChatMessageStickerItemNode: ChatMessageItemView { self.view.addGestureRecognizer(replyRecognizer) } - override func setupItem(_ item: ChatMessageItem) { - super.setupItem(item) + override func setupItem(_ item: ChatMessageItem, synchronousLoad: Bool) { + super.setupItem(item, synchronousLoad: synchronousLoad) for media in item.message.media { if let telegramFile = media as? TelegramMediaFile { if self.telegramFile != telegramFile { - let signal = chatMessageSticker(account: item.context.account, file: telegramFile, small: false, onlyFullSize: self.telegramFile != nil) + let signal = chatMessageSticker(account: item.context.account, file: telegramFile, small: false, onlyFullSize: self.telegramFile != nil, synchronousLoad: synchronousLoad) self.telegramFile = telegramFile - self.imageNode.setSignal(signal) + self.imageNode.setSignal(signal, attemptSynchronously: synchronousLoad) self.fetchDisposable.set(freeMediaFileInteractiveFetched(account: item.context.account, fileReference: .message(message: MessageReference(item.message), media: telegramFile)).start()) } @@ -642,7 +648,9 @@ class ChatMessageStickerItemNode: ChatMessageItemView { strongSelf.updateAccessibilityData(accessibilityData) transition.updateFrame(node: strongSelf.imageNode, frame: updatedImageFrame) + strongSelf.enableSynchronousImageApply = true imageApply() + strongSelf.enableSynchronousImageApply = false if let immediateThumbnailData = telegramFile?.immediateThumbnailData { let foregroundColor = bubbleVariableColor(variableColor: item.presentationData.theme.theme.chat.message.stickerPlaceholderColor, wallpaper: item.presentationData.theme.wallpaper) @@ -684,7 +692,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { if let updatedReplyBackgroundNode = updatedReplyBackgroundNode { if strongSelf.replyBackgroundNode == nil { strongSelf.replyBackgroundNode = updatedReplyBackgroundNode - strongSelf.addSubnode(updatedReplyBackgroundNode) + strongSelf.contextSourceNode.contentNode.addSubnode(updatedReplyBackgroundNode) updatedReplyBackgroundNode.image = replyBackgroundImage } else { strongSelf.replyBackgroundNode?.image = replyBackgroundImage @@ -711,7 +719,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let replyInfoNode = replyInfoApply() if strongSelf.replyInfoNode == nil { strongSelf.replyInfoNode = replyInfoNode - strongSelf.addSubnode(replyInfoNode) + strongSelf.contextSourceNode.contentNode.addSubnode(replyInfoNode) } replyInfoNode.frame = replyInfoFrame strongSelf.replyBackgroundNode?.frame = replyBackgroundFrame ?? CGRect() @@ -1160,6 +1168,10 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } } } + + override func cancelInsertionAnimations() { + self.layer.removeAllAnimations() + } override func animateInsertion(_ currentTimestamp: Double, duration: Double, short: Bool) { super.animateInsertion(currentTimestamp, duration: duration, short: short) @@ -1186,4 +1198,152 @@ class ChatMessageStickerItemNode: ChatMessageItemView { override func addAccessoryItemNode(_ accessoryItemNode: ListViewAccessoryItemNode) { self.contextSourceNode.contentNode.addSubnode(accessoryItemNode) } + + func animateContentFromTextInputField(textInput: ChatMessageTransitionNode.Source.TextInput, transition: ContainedViewLayoutTransition) { + guard let _ = self.item else { + return + } + + let localSourceContentFrame = self.contextSourceNode.contentNode.view.convert(textInput.contentView.frame.offsetBy(dx: self.contextSourceNode.contentRect.minX, dy: self.contextSourceNode.contentRect.minY), to: self.contextSourceNode.contentNode.view) + textInput.contentView.frame = localSourceContentFrame + + self.contextSourceNode.contentNode.view.addSubview(textInput.contentView) + + let sourceCenter = CGPoint( + x: localSourceContentFrame.minX + 11.2, + y: localSourceContentFrame.midY - 1.8 + ) + let localSourceCenter = CGPoint( + x: sourceCenter.x - localSourceContentFrame.minX, + y: sourceCenter.y - localSourceContentFrame.minY + ) + let localSourceOffset = CGPoint( + x: localSourceCenter.x - localSourceContentFrame.width / 2.0, + y: localSourceCenter.y - localSourceContentFrame.height / 2.0 + ) + + let sourceScale: CGFloat = 28.0 / self.imageNode.frame.height + + let offset = CGPoint( + x: sourceCenter.x - self.imageNode.frame.midX, + y: sourceCenter.y - self.imageNode.frame.midY + ) + + transition.animatePositionAdditive(node: self.imageNode, offset: offset) + transition.animateTransformScale(node: self.imageNode, from: sourceScale) + transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) + transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + + let inverseScale = 1.0 / sourceScale + + transition.animatePositionAdditive(layer: textInput.contentView.layer, offset: CGPoint(), to: CGPoint( + x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), + y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) + ), removeOnCompletion: false) + transition.updateTransformScale(layer: textInput.contentView.layer, scale: 1.0 / sourceScale) + + textInput.contentView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { _ in + textInput.contentView.removeFromSuperview() + }) + + self.imageNode.layer.animateAlpha(from: 0.0, to: self.imageNode.alpha, duration: 0.1) + self.placeholderNode.layer.animateAlpha(from: 0.0, to: self.placeholderNode.alpha, duration: 0.1) + + self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.16) + } + + func animateContentFromStickerGridItem(stickerSource: ChatMessageTransitionNode.Sticker, transition: ContainedViewLayoutTransition) { + guard let _ = self.item else { + return + } + + let localSourceContentFrame = CGRect( + origin: CGPoint( + x: self.imageNode.frame.minX + self.imageNode.frame.size.width / 2.0 - stickerSource.imageNode.frame.size.width / 2.0, + y: self.imageNode.frame.minY + self.imageNode.frame.size.height / 2.0 - stickerSource.imageNode.frame.size.height / 2.0 + ), + size: stickerSource.imageNode.frame.size + ) + + var snapshotView: UIView? + if let animationNode = stickerSource.animationNode { + snapshotView = animationNode.view.snapshotContentTree() + } else { + snapshotView = stickerSource.imageNode.view.snapshotContentTree() + } + snapshotView?.frame = localSourceContentFrame + + if let snapshotView = snapshotView { + self.contextSourceNode.contentNode.view.addSubview(snapshotView) + } + + let sourceCenter = CGPoint( + x: localSourceContentFrame.midX, + y: localSourceContentFrame.midY + ) + let localSourceCenter = CGPoint( + x: sourceCenter.x - localSourceContentFrame.minX, + y: sourceCenter.y - localSourceContentFrame.minY + ) + let localSourceOffset = CGPoint( + x: localSourceCenter.x - localSourceContentFrame.width / 2.0, + y: localSourceCenter.y - localSourceContentFrame.height / 2.0 + ) + + let sourceScale: CGFloat = stickerSource.imageNode.frame.height / self.imageNode.frame.height + + let offset = CGPoint( + x: sourceCenter.x - self.imageNode.frame.midX, + y: sourceCenter.y - self.imageNode.frame.midY + ) + + transition.animatePositionAdditive(node: self.imageNode, offset: offset) + transition.animateTransformScale(node: self.imageNode, from: sourceScale) + transition.animatePositionAdditive(node: self.placeholderNode, offset: offset) + transition.animateTransformScale(node: self.placeholderNode, from: sourceScale) + + let inverseScale = 1.0 / sourceScale + + if let snapshotView = snapshotView { + transition.animatePositionAdditive(layer: snapshotView.layer, offset: CGPoint(), to: CGPoint( + x: -offset.x - localSourceOffset.x * (inverseScale - 1.0), + y: -offset.y - localSourceOffset.y * (inverseScale - 1.0) + ), removeOnCompletion: false) + transition.updateTransformScale(layer: snapshotView.layer, scale: 1.0 / sourceScale) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.06, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + + self.imageNode.layer.animateAlpha(from: 0.0, to: self.imageNode.alpha, duration: 0.03) + self.placeholderNode.layer.animateAlpha(from: 0.0, to: self.placeholderNode.alpha, duration: 0.03) + } + + self.dateAndStatusNode.layer.animateAlpha(from: 0.0, to: self.dateAndStatusNode.alpha, duration: 0.15, delay: 0.16) + + if let animationNode = stickerSource.animationNode { + animationNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + animationNode.layer.animateAlpha(from: 0.0, to: animationNode.alpha, duration: 0.4) + } + + stickerSource.imageNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + stickerSource.imageNode.layer.animateAlpha(from: 0.0, to: stickerSource.imageNode.alpha, duration: 0.4) + + if let placeholderNode = stickerSource.placeholderNode { + placeholderNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring) + placeholderNode.layer.animateAlpha(from: 0.0, to: placeholderNode.alpha, duration: 0.4) + } + } + + func animateReplyPanel(sourceReplyPanel: ChatMessageTransitionNode.ReplyPanel, transition: ContainedViewLayoutTransition) { + if let replyInfoNode = self.replyInfoNode { + let localRect = self.contextSourceNode.contentNode.view.convert(sourceReplyPanel.relativeSourceRect, to: replyInfoNode.view) + + let offset = replyInfoNode.animateFromInputPanel(sourceReplyPanel: sourceReplyPanel, localRect: localRect, transition: transition) + if let replyBackgroundNode = self.replyBackgroundNode { + transition.animatePositionAdditive(node: replyBackgroundNode, offset: offset) + replyBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) + } + } + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift index c018521b50..0fc5da0fe8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTextBubbleContentNode.swift @@ -640,4 +640,24 @@ class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { override func getStatusNode() -> ASDisplayNode? { return self.statusNode } + + func animateFrom(sourceView: UIView, widthDifference: CGFloat, transition: ContainedViewLayoutTransition) { + self.view.addSubview(sourceView) + + sourceView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.1, removeOnCompletion: false, completion: { [weak sourceView] _ in + sourceView?.removeFromSuperview() + }) + self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.08) + + let offset = CGPoint( + x: sourceView.frame.minX - (self.textNode.frame.minX - 0.0), + y: sourceView.frame.minY - (self.textNode.frame.minY - 3.0) + ) + + transition.animatePositionAdditive(node: self.textNode, offset: offset) + transition.updatePosition(layer: sourceView.layer, position: CGPoint(x: sourceView.layer.position.x - offset.x, y: sourceView.layer.position.y - offset.y)) + + self.statusNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + transition.animatePositionAdditive(node: self.statusNode, offset: CGPoint(x: -widthDifference, y: 0.0)) + } } diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift new file mode 100644 index 0000000000..518f584a25 --- /dev/null +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -0,0 +1,489 @@ +import Foundation +import UIKit +import AsyncDisplayKit +import Display +import ContextUI +import AnimatedStickerNode +import SwiftSignalKit + +private final class OverlayTransitionContainerNode: ViewControllerTracingNode { + override init() { + super.init() + } + + deinit { + } + + override func didLoad() { + super.didLoad() + } + + func updateLayout(layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return nil + } +} + +private final class OverlayTransitionContainerController: ViewController, StandalonePresentableController { + private let _ready = Promise() + override public var ready: Promise { + return self._ready + } + + private var controllerNode: OverlayTransitionContainerNode { + return self.displayNode as! OverlayTransitionContainerNode + } + + private var wasDismissed: Bool = false + + init() { + super.init(navigationBarPresentationData: nil) + + self.statusBar.statusBarStyle = .Ignore + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + } + + override public func loadDisplayNode() { + self.displayNode = OverlayTransitionContainerNode() + + self.displayNodeDidLoad() + + self._ready.set(.single(true)) + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + self.controllerNode.updateLayout(layout: layout, transition: transition) + } + + override public func viewDidAppear(_ animated: Bool) { + if self.ignoreAppearanceMethodInvocations() { + return + } + super.viewDidAppear(animated) + } + + override public func dismiss(completion: (() -> Void)? = nil) { + if !self.wasDismissed { + self.wasDismissed = true + self.presentingViewController?.dismiss(animated: false, completion: nil) + completion?() + } + } +} + +final class ChatMessageTransitionNode: ASDisplayNode { + final class ReplyPanel { + let titleNode: ASDisplayNode + let textNode: ASDisplayNode + let lineNode: ASDisplayNode + let imageNode: ASDisplayNode + let relativeSourceRect: CGRect + + init(titleNode: ASDisplayNode, textNode: ASDisplayNode, lineNode: ASDisplayNode, imageNode: ASDisplayNode, relativeSourceRect: CGRect) { + self.titleNode = titleNode + self.textNode = textNode + self.lineNode = lineNode + self.imageNode = imageNode + self.relativeSourceRect = relativeSourceRect + } + } + + final class Sticker { + let imageNode: TransformImageNode + let animationNode: GenericAnimatedStickerNode? + let placeholderNode: ASDisplayNode? + let relativeSourceRect: CGRect + + init(imageNode: TransformImageNode, animationNode: GenericAnimatedStickerNode?, placeholderNode: ASDisplayNode?, relativeSourceRect: CGRect) { + self.imageNode = imageNode + self.animationNode = animationNode + self.placeholderNode = placeholderNode + self.relativeSourceRect = relativeSourceRect + } + } + + enum Source { + final class TextInput { + let backgroundView: UIView + let contentView: UIView + let sourceRect: CGRect + + init(backgroundView: UIView, contentView: UIView, sourceRect: CGRect) { + self.backgroundView = backgroundView + self.contentView = contentView + self.sourceRect = sourceRect + } + } + + enum StickerInput { + case inputPanel(itemNode: ChatMediaInputStickerGridItemNode) + case mediaPanel(itemNode: HorizontalStickerGridItemNode) + case inputPanelSearch(itemNode: StickerPaneSearchStickerItemNode) + } + + final class AudioMicInput { + let micButton: ChatTextInputMediaRecordingButton + + init(micButton: ChatTextInputMediaRecordingButton) { + self.micButton = micButton + } + } + + final class VideoMessage { + let view: UIView + + init(view: UIView) { + self.view = view + } + } + + case textInput(textInput: TextInput, replyPanel: ReplyAccessoryPanelNode?) + case stickerMediaInput(input: StickerInput, replyPanel: ReplyAccessoryPanelNode?) + case audioMicInput(AudioMicInput) + case videoMessage(VideoMessage) + } + + private final class AnimatingItemNode: ASDisplayNode { + private let itemNode: ChatMessageItemView + private let contextSourceNode: ContextExtractedContentContainingNode + private let source: ChatMessageTransitionNode.Source + + private let scrollingContainer: ASDisplayNode + private let containerNode: ASDisplayNode + + weak var overlayController: OverlayTransitionContainerController? + + var animationEnded: (() -> Void)? + + init(itemNode: ChatMessageItemView, contextSourceNode: ContextExtractedContentContainingNode, source: ChatMessageTransitionNode.Source) { + self.itemNode = itemNode + self.scrollingContainer = ASDisplayNode() + self.containerNode = ASDisplayNode() + self.contextSourceNode = contextSourceNode + self.source = source + + super.init() + + self.addSubnode(self.scrollingContainer) + self.scrollingContainer.addSubnode(self.containerNode) + } + + deinit { + self.contextSourceNode.addSubnode(self.contextSourceNode.contentNode) + } + + func beginAnimation() { + let verticalDuration: Double = 0.5 + let horizontalDuration: Double = verticalDuration * 0.5 + let delay: Double = 0.0 + + switch self.source { + case let .textInput(textInput, replyPanel): + self.contextSourceNode.isExtractedToContextPreview = true + self.contextSourceNode.isExtractedToContextPreviewUpdated?(true) + + self.containerNode.addSubnode(self.contextSourceNode.contentNode) + + let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: nil) + let sourceAbsoluteRect = textInput.backgroundView.frame.offsetBy(dx: textInput.sourceRect.minX, dy: textInput.sourceRect.minY) + + var sourceReplyPanel: ReplyPanel? + if let replyPanel = replyPanel, let replyPanelParentView = replyPanel.view.superview { + var replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanel.originalFrameBeforeDismissed ?? replyPanel.frame, to: nil) + replySourceAbsoluteFrame.origin.x -= sourceAbsoluteRect.minX - self.contextSourceNode.contentRect.minX + replySourceAbsoluteFrame.origin.y -= sourceAbsoluteRect.minY - self.contextSourceNode.contentRect.minY + + sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame) + } + + self.itemNode.cancelInsertionAnimations() + + let transition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + + if let itemNode = self.itemNode as? ChatMessageBubbleItemNode { + itemNode.animateContentFromTextInputField(textInput: textInput, transition: transition) + if let sourceReplyPanel = sourceReplyPanel { + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + } + } else if let itemNode = self.itemNode as? ChatMessageAnimatedStickerItemNode { + itemNode.animateContentFromTextInputField(textInput: textInput, transition: transition) + if let sourceReplyPanel = sourceReplyPanel { + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + } + } else if let itemNode = self.itemNode as? ChatMessageStickerItemNode { + itemNode.animateContentFromTextInputField(textInput: textInput, transition: transition) + if let sourceReplyPanel = sourceReplyPanel { + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + } + } + + self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) + self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.minY - targetAbsoluteRect.minY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self] _ in + guard let strongSelf = self else { + return + } + strongSelf.endAnimation() + }) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), .custom(0.33, 0.0, 0.0, 1.0), horizontalDuration) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.minY - targetAbsoluteRect.minY), .custom(0.33, 0.0, 0.0, 1.0), verticalDuration) + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.minX - targetAbsoluteRect.minX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true) + case let .stickerMediaInput(stickerMediaInput, replyPanel): + self.itemNode.cancelInsertionAnimations() + + self.contextSourceNode.isExtractedToContextPreview = true + self.contextSourceNode.isExtractedToContextPreviewUpdated?(true) + + self.containerNode.addSubnode(self.contextSourceNode.contentNode) + + let stickerSource: Sticker + let sourceAbsoluteRect: CGRect + switch stickerMediaInput { + case let .inputPanel(sourceItemNode): + stickerSource = Sticker(imageNode: sourceItemNode.imageNode, animationNode: sourceItemNode.animationNode, placeholderNode: sourceItemNode.placeholderNode, relativeSourceRect: sourceItemNode.imageNode.frame) + sourceAbsoluteRect = sourceItemNode.view.convert(stickerSource.imageNode.frame, to: nil) + case let .mediaPanel(sourceItemNode): + stickerSource = Sticker(imageNode: sourceItemNode.imageNode, animationNode: sourceItemNode.animationNode, placeholderNode: sourceItemNode.placeholderNode, relativeSourceRect: sourceItemNode.imageNode.frame) + sourceAbsoluteRect = sourceItemNode.view.convert(stickerSource.imageNode.frame, to: nil) + case let .inputPanelSearch(sourceItemNode): + stickerSource = Sticker(imageNode: sourceItemNode.imageNode, animationNode: sourceItemNode.animationNode, placeholderNode: nil, relativeSourceRect: sourceItemNode.imageNode.frame) + sourceAbsoluteRect = sourceItemNode.view.convert(stickerSource.imageNode.frame, to: nil) + } + + let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: nil) + + var sourceReplyPanel: ReplyPanel? + if let replyPanel = replyPanel, let replyPanelParentView = replyPanel.view.superview { + var replySourceAbsoluteFrame = replyPanelParentView.convert(replyPanel.originalFrameBeforeDismissed ?? replyPanel.frame, to: nil) + replySourceAbsoluteFrame.origin.x -= sourceAbsoluteRect.midX - self.contextSourceNode.contentRect.midX + replySourceAbsoluteFrame.origin.y -= sourceAbsoluteRect.midY - self.contextSourceNode.contentRect.midY + + sourceReplyPanel = ReplyPanel(titleNode: replyPanel.titleNode, textNode: replyPanel.textNode, lineNode: replyPanel.lineNode, imageNode: replyPanel.imageNode, relativeSourceRect: replySourceAbsoluteFrame) + } + + self.itemNode.cancelInsertionAnimations() + + let transition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + + if let itemNode = self.itemNode as? ChatMessageAnimatedStickerItemNode { + itemNode.animateContentFromStickerGridItem(stickerSource: stickerSource, transition: transition) + if let sourceAnimationNode = stickerSource.animationNode { + itemNode.animationNode?.setFrameIndex(sourceAnimationNode.currentFrameIndex) + } + if let sourceReplyPanel = sourceReplyPanel { + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + } + } else if let itemNode = self.itemNode as? ChatMessageStickerItemNode { + itemNode.animateContentFromStickerGridItem(stickerSource: stickerSource, transition: transition) + if let sourceReplyPanel = sourceReplyPanel { + itemNode.animateReplyPanel(sourceReplyPanel: sourceReplyPanel, transition: transition) + } + } + + self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) + self.contextSourceNode.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self] _ in + guard let strongSelf = self else { + return + } + strongSelf.endAnimation() + }) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), .custom(0.33, 0.0, 0.0, 1.0), horizontalDuration) + self.contextSourceNode.applyAbsoluteOffset?(CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), .custom(0.33, 0.0, 0.0, 1.0), verticalDuration) + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true) + + switch stickerMediaInput { + case .inputPanel: + break + case let .mediaPanel(sourceItemNode): + sourceItemNode.isHidden = true + case let .inputPanelSearch(sourceItemNode): + sourceItemNode.isHidden = true + } + case let .audioMicInput(audioMicInput): + if let (container, localRect) = audioMicInput.micButton.contentContainer { + let snapshotView = container.snapshotView(afterScreenUpdates: false) + if let snapshotView = snapshotView { + let sourceAbsoluteRect = container.convert(localRect, to: nil) + snapshotView.frame = sourceAbsoluteRect + + container.isHidden = true + + let transition: ContainedViewLayoutTransition = .animated(duration: horizontalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + + if let itemNode = self.itemNode as? ChatMessageBubbleItemNode { + if let contextContainer = itemNode.animateFromMicInput(micInputNode: snapshotView, transition: transition) { + self.containerNode.addSubnode(contextContainer.contentNode) + + let targetAbsoluteRect = contextContainer.view.convert(contextContainer.contentRect, to: nil) + + self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -contextContainer.contentRect.minX, dy: -contextContainer.contentRect.minY) + contextContainer.updateAbsoluteRect?(self.containerNode.frame, UIScreen.main.bounds.size) + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true, completion: { [weak self, weak contextContainer, weak container] _ in + guard let strongSelf = self else { + return + } + if let contextContainer = contextContainer { + contextContainer.isExtractedToContextPreview = false + contextContainer.isExtractedToContextPreviewUpdated?(false) + contextContainer.addSubnode(contextContainer.contentNode) + } + + container?.isHidden = false + + strongSelf.endAnimation() + }) + + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true) + } + } + } + } + case let .videoMessage(videoMessage): + let transition: ContainedViewLayoutTransition = .animated(duration: verticalDuration, curve: .custom(0.33, 0.0, 0.0, 1.0)) + + if let itemNode = self.itemNode as? ChatMessageInstantVideoItemNode { + itemNode.cancelInsertionAnimations() + + self.contextSourceNode.isExtractedToContextPreview = true + self.contextSourceNode.isExtractedToContextPreviewUpdated?(true) + + self.containerNode.addSubnode(self.contextSourceNode.contentNode) + + let sourceAbsoluteRect = videoMessage.view.frame + let targetAbsoluteRect = self.contextSourceNode.view.convert(self.contextSourceNode.contentRect, to: nil) + + videoMessage.view.frame = videoMessage.view.frame.offsetBy(dx: targetAbsoluteRect.midX - sourceAbsoluteRect.midX, dy: targetAbsoluteRect.midY - sourceAbsoluteRect.midY) + + self.containerNode.frame = targetAbsoluteRect.offsetBy(dx: -self.contextSourceNode.contentRect.minX, dy: -self.contextSourceNode.contentRect.minY) + self.containerNode.layer.animatePosition(from: CGPoint(x: 0.0, y: sourceAbsoluteRect.midY - targetAbsoluteRect.midY), to: CGPoint(), duration: horizontalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, force: true) + + self.containerNode.layer.animatePosition(from: CGPoint(x: sourceAbsoluteRect.midX - targetAbsoluteRect.midX, y: 0.0), to: CGPoint(), duration: verticalDuration, delay: delay, mediaTimingFunction: CAMediaTimingFunction(controlPoints: 0.33, 0.0, 0.0, 1.0), additive: true, completion: { [weak self] _ in + guard let strongSelf = self else { + return + } + + strongSelf.endAnimation() + }) + + itemNode.animateFromSnapshot(snapshotView: videoMessage.view, transition: transition) + } + } + } + + private func endAnimation() { + self.contextSourceNode.isExtractedToContextPreview = false + self.contextSourceNode.isExtractedToContextPreviewUpdated?(false) + + self.animationEnded?() + } + + func addExternalOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, itemNode: ListViewItemNode?) { + var applyOffset = false + if let itemNode = itemNode { + if itemNode === self.itemNode { + applyOffset = true + } + } else { + applyOffset = true + } + if applyOffset { + self.scrollingContainer.bounds = self.scrollingContainer.bounds.offsetBy(dx: 0.0, dy: -offset) + transition.animateOffsetAdditive(node: self.scrollingContainer, offset: offset) + } + } + } + + private let listNode: ChatHistoryListNode + + private var currentPendingItem: (Int64, Source, () -> Void)? + + private var animatingItemNodes: [AnimatingItemNode] = [] + + var hasScheduledTransitions: Bool { + return self.currentPendingItem != nil + } + + init(listNode: ChatHistoryListNode) { + self.listNode = listNode + + super.init() + + self.listNode.animationCorrelationMessageFound = { [weak self] itemNode, correlationId in + guard let strongSelf = self, let (currentId, currentSource, initiated) = strongSelf.currentPendingItem else { + return + } + if currentId == correlationId { + strongSelf.currentPendingItem = nil + strongSelf.beginAnimation(itemNode: itemNode, source: currentSource) + initiated() + } + } + } + + func add(correlationId: Int64, source: Source, initiated: @escaping () -> Void) { + self.currentPendingItem = (correlationId, source, initiated) + self.listNode.setCurrentSendAnimationCorrelationId(correlationId) + } + + private func beginAnimation(itemNode: ChatMessageItemView, source: Source) { + var contextSourceNode: ContextExtractedContentContainingNode? + if let itemNode = itemNode as? ChatMessageBubbleItemNode { + contextSourceNode = itemNode.mainContextSourceNode + } else if let itemNode = itemNode as? ChatMessageStickerItemNode { + contextSourceNode = itemNode.contextSourceNode + } else if let itemNode = itemNode as? ChatMessageAnimatedStickerItemNode { + contextSourceNode = itemNode.contextSourceNode + } else if let itemNode = itemNode as? ChatMessageInstantVideoItemNode { + contextSourceNode = itemNode.contextSourceNode + } + + if let contextSourceNode = contextSourceNode { + let animatingItemNode = AnimatingItemNode(itemNode: itemNode, contextSourceNode: contextSourceNode, source: source) + self.animatingItemNodes.append(animatingItemNode) + switch source { + case .audioMicInput, .videoMessage: + let overlayController = OverlayTransitionContainerController() + overlayController.displayNode.addSubnode(animatingItemNode) + animatingItemNode.overlayController = overlayController + itemNode.item?.context.sharedContext.mainWindow?.presentInGlobalOverlay(overlayController) + default: + self.addSubnode(animatingItemNode) + } + + animatingItemNode.animationEnded = { [weak self, weak animatingItemNode] in + guard let strongSelf = self, let animatingItemNode = animatingItemNode else { + return + } + animatingItemNode.removeFromSupernode() + animatingItemNode.overlayController?.dismiss() + if let index = strongSelf.animatingItemNodes.firstIndex(where: { $0 === animatingItemNode }) { + strongSelf.animatingItemNodes.remove(at: index) + } + } + + animatingItemNode.frame = self.bounds + animatingItemNode.beginAnimation() + } + } + + override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { + return nil + } + + func addExternalOffset(offset: CGFloat, transition: ContainedViewLayoutTransition, itemNode: ListViewItemNode?) { + for animatingItemNode in self.animatingItemNodes { + animatingItemNode.addExternalOffset(offset: offset, transition: transition, itemNode: itemNode) + } + } +} diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 0ac557bebc..f9c5d31dc1 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -282,7 +282,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { } let resolveSignal: Signal if let peerName = peerName { - resolveSignal = resolvePeerByName(account: strongSelf.context.account, name: peerName) + resolveSignal = strongSelf.context.engine.peers.resolvePeerByName(name: peerName) |> mapToSignal { peerId -> Signal in if let peerId = peerId { return context.account.postbox.loadedPeerWithId(peerId) @@ -786,7 +786,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { private func openPeerMention(_ name: String) { let postbox = self.context.account.postbox - self.navigationActionDisposable.set((resolvePeerByName(account: self.context.account, name: name, ageLimit: 10) + self.navigationActionDisposable.set((self.context.engine.peers.resolvePeerByName(name: name, ageLimit: 10) |> take(1) |> mapToSignal { peerId -> Signal in if let peerId = peerId { diff --git a/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift b/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift index d476cb3509..144f3db74e 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputMediaRecordingButton.swift @@ -97,7 +97,7 @@ private final class ChatTextInputMediaRecordingButtonPresenterControllerNode: Vi private final class ChatTextInputMediaRecordingButtonPresenter : NSObject, TGModernConversationInputMicButtonPresentation { private let account: Account? private let presentController: (ViewController) -> Void - private let container: ChatTextInputMediaRecordingButtonPresenterContainer + let container: ChatTextInputMediaRecordingButtonPresenterContainer private var presentationController: ChatTextInputMediaRecordingButtonPresenterController? init(account: Account, presentController: @escaping (ViewController) -> Void) { @@ -176,6 +176,16 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto private(set) var cancelTranslation: CGFloat = 0.0 private var micLevelDisposable: MetaDisposable? + + private weak var currentPresenter: UIView? + + var contentContainer: (UIView, CGRect)? { + if let _ = self.currentPresenter { + return (self.micDecoration, self.micDecoration.bounds) + } else { + return nil + } + } var audioRecorder: ManagedAudioRecorder? { didSet { @@ -410,7 +420,9 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto } func micButtonPresenter() -> TGModernConversationInputMicButtonPresentation! { - return ChatTextInputMediaRecordingButtonPresenter(account: self.account!, presentController: self.presentController) + let presenter = ChatTextInputMediaRecordingButtonPresenter(account: self.account!, presentController: self.presentController) + self.currentPresenter = presenter.view() + return presenter } func micButtonDecoration() -> (UIView & TGModernConversationInputMicButtonDecoration)! { @@ -427,7 +439,8 @@ final class ChatTextInputMediaRecordingButton: TGModernConversationInputMicButto override func animateIn() { super.animateIn() - + + micDecoration.isHidden = false micDecoration.startAnimating() innerIconView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 00dfc33dcb..431367db0f 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -162,20 +162,30 @@ private func calclulateTextFieldMinHeight(_ presentationInterfaceState: ChatPres } private var currentTextInputBackgroundImage: (UIColor, UIColor, CGFloat, UIImage)? -private func textInputBackgroundImage(backgroundColor: UIColor, strokeColor: UIColor, diameter: CGFloat) -> UIImage? { - if let current = currentTextInputBackgroundImage { +private func textInputBackgroundImage(backgroundColor: UIColor?, inputBackgroundColor: UIColor?, strokeColor: UIColor, diameter: CGFloat) -> UIImage? { + if let backgroundColor = backgroundColor, let current = currentTextInputBackgroundImage { if current.0.isEqual(backgroundColor) && current.1.isEqual(strokeColor) && current.2.isEqual(to: diameter) { return current.3 } } let image = generateImage(CGSize(width: diameter, height: diameter), rotatedContext: { size, context in - context.setFillColor(backgroundColor.cgColor) - context.fill(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter)) - - context.setBlendMode(.clear) - context.setFillColor(UIColor.clear.cgColor) + if let backgroundColor = backgroundColor { + context.setFillColor(backgroundColor.cgColor) + context.fill(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter)) + } else { + context.clear(CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter)) + } + + if let inputBackgroundColor = inputBackgroundColor { + context.setBlendMode(.normal) + context.setFillColor(inputBackgroundColor.cgColor) + } else { + context.setBlendMode(.clear) + context.setFillColor(UIColor.clear.cgColor) + } context.fillEllipse(in: CGRect(x: 0.0, y: 0.0, width: diameter, height: diameter)) + context.setBlendMode(.normal) context.setStrokeColor(strokeColor.cgColor) let strokeWidth: CGFloat = 1.0 @@ -183,7 +193,9 @@ private func textInputBackgroundImage(backgroundColor: UIColor, strokeColor: UIC context.strokeEllipse(in: CGRect(x: strokeWidth / 2.0, y: strokeWidth / 2.0, width: diameter - strokeWidth, height: diameter - strokeWidth)) })?.stretchableImage(withLeftCapWidth: Int(diameter) / 2, topCapHeight: Int(diameter) / 2) if let image = image { - currentTextInputBackgroundImage = (backgroundColor, strokeColor, diameter, image) + if let backgroundColor = backgroundColor { + currentTextInputBackgroundImage = (backgroundColor, strokeColor, diameter, image) + } return image } else { return nil @@ -205,6 +217,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { var textInputNode: EditableTextNode? let textInputBackgroundNode: ASImageNode + private var transparentTextInputBackgroundImage: UIImage? let actionButtons: ChatTextInputActionButtonsNode var mediaRecordingAccessibilityArea: AccessibilityAreaNode? private let counterTextNode: ImmediateTextNode @@ -281,6 +294,10 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { self.actionButtons.micButton.account = self.context?.account } } + + var micButton: ChatTextInputMediaRecordingButton? { + return self.actionButtons.micButton + } private let statusDisposable = MetaDisposable() override var interfaceInteraction: ChatPanelInterfaceInteraction? { @@ -636,7 +653,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { let maxNumberOfLines = min(12, (Int(fieldMaxHeight - 11.0) - 33) / 22) - let updatedMaxHeight = (CGFloat(maxNumberOfLines) * 22.0 + 10.0) + let updatedMaxHeight = (CGFloat(maxNumberOfLines) * (22.0 + 2.0) + 10.0) textFieldHeight = max(textFieldMinHeight, min(updatedMaxHeight, unboundTextFieldHeight)) } else { @@ -779,7 +796,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { backgroundColor = interfaceState.theme.chat.inputPanel.panelBackgroundColor } - self.textInputBackgroundNode.image = textInputBackgroundImage(backgroundColor: backgroundColor, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight) + self.textInputBackgroundNode.image = textInputBackgroundImage(backgroundColor: backgroundColor, inputBackgroundColor: nil, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight) + self.transparentTextInputBackgroundImage = textInputBackgroundImage(backgroundColor: nil, inputBackgroundColor: interfaceState.theme.chat.inputPanel.inputBackgroundColor, strokeColor: interfaceState.theme.chat.inputPanel.inputStrokeColor, diameter: minimalInputHeight) self.searchLayoutClearImageNode.image = PresentationResourcesChat.chatInputTextFieldClearImage(interfaceState.theme) @@ -2152,5 +2170,33 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { } return nil } + + func makeSnapshotForTransition() -> ChatMessageTransitionNode.Source.TextInput? { + guard let backgroundImage = self.transparentTextInputBackgroundImage else { + return nil + } + guard let textInputNode = self.textInputNode else { + return nil + } + + let backgroundView = UIImageView(image: backgroundImage) + backgroundView.frame = self.textInputBackgroundNode.frame + + //let previousTintColor = textInputNode.view.tintColor + //textInputNode.view.tintColor = .clear + + guard let contentView = textInputNode.view.snapshotView(afterScreenUpdates: true) else { + //textInputNode.view.tintColor = previousTintColor + return nil + } + //textInputNode.view.tintColor = previousTintColor + contentView.frame = textInputNode.frame + + return ChatMessageTransitionNode.Source.TextInput( + backgroundView: backgroundView, + contentView: contentView, + sourceRect: self.view.convert(self.bounds, to: nil) + ) + } } diff --git a/submodules/TelegramUI/Sources/CreateChannelController.swift b/submodules/TelegramUI/Sources/CreateChannelController.swift index bc748a8251..acbcb9eacc 100644 --- a/submodules/TelegramUI/Sources/CreateChannelController.swift +++ b/submodules/TelegramUI/Sources/CreateChannelController.swift @@ -326,7 +326,7 @@ public func createChannelController(context: AccountContext) -> ViewController { let completedChannelPhotoImpl: (UIImage) -> Void = { image in if let data = image.jpegData(compressionQuality: 0.6) { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil) uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource)) @@ -341,7 +341,7 @@ public func createChannelController(context: AccountContext) -> ViewController { let completedChannelVideoImpl: (UIImage, Any?, TGVideoEditAdjustments?) -> Void = { image, asset, adjustments in if let data = image.jpegData(compressionQuality: 0.6) { - let photoResource = LocalFileMediaResource(fileId: arc4random64()) + let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil) updateState { state in @@ -407,7 +407,7 @@ public func createChannelController(context: AccountContext) -> ViewController { if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult { resource = LocalFileMediaResource(fileId: liveUploadData.id) } else { - resource = LocalFileMediaResource(fileId: arc4random64()) + resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) } context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) diff --git a/submodules/TelegramUI/Sources/CreateGroupController.swift b/submodules/TelegramUI/Sources/CreateGroupController.swift index 6acfdbc2e0..7e34633f85 100644 --- a/submodules/TelegramUI/Sources/CreateGroupController.swift +++ b/submodules/TelegramUI/Sources/CreateGroupController.swift @@ -398,7 +398,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] }) } - venuesPromise.set(nearbyVenues(account: context.account, latitude: latitude, longitude: longitude) + venuesPromise.set(nearbyVenues(context: context, latitude: latitude, longitude: longitude) |> map(Optional.init)) } @@ -573,7 +573,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] let completedGroupPhotoImpl: (UIImage) -> Void = { image in if let data = image.jpegData(compressionQuality: 0.6) { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil) uploadedAvatar.set(uploadedPeerPhoto(postbox: context.account.postbox, network: context.account.network, resource: resource)) @@ -588,7 +588,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] let completedGroupVideoImpl: (UIImage, Any?, TGVideoEditAdjustments?) -> Void = { image, asset, adjustments in if let data = image.jpegData(compressionQuality: 0.6) { - let photoResource = LocalFileMediaResource(fileId: arc4random64()) + let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil) updateState { state in @@ -655,7 +655,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId] if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult { resource = LocalFileMediaResource(fileId: liveUploadData.id) } else { - resource = LocalFileMediaResource(fileId: arc4random64()) + resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) } context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) diff --git a/submodules/TelegramUI/Sources/EmojiResources.swift b/submodules/TelegramUI/Sources/EmojiResources.swift index 730c085b42..4396a939f9 100644 --- a/submodules/TelegramUI/Sources/EmojiResources.swift +++ b/submodules/TelegramUI/Sources/EmojiResources.swift @@ -9,6 +9,7 @@ import WebPBinding import MediaResources import Emoji import AppBundle +import AccountContext public struct EmojiThumbnailResourceId: MediaResourceId { public let emoji: String @@ -284,17 +285,17 @@ private final class Buffer { var data = Data() } -func fetchEmojiSpriteResource(postbox: Postbox, network: Network, resource: EmojiSpriteResource) -> Signal { +func fetchEmojiSpriteResource(account: Account, resource: EmojiSpriteResource) -> Signal { let packName = "P\(resource.packId)_by_AEStickerBot" - return loadedStickerPack(postbox: postbox, network: network, reference: .name(packName), forceActualized: false) + return TelegramEngine(account: account).stickers.loadedStickerPack(reference: .name(packName), forceActualized: false) |> castError(MediaResourceDataFetchError.self) |> mapToSignal { result -> Signal in switch result { case let .result(_, items, _): if let sticker = items[Int(resource.stickerId)] as? StickerPackItem { return Signal { subscriber in - guard let fetchResource = postbox.mediaBox.fetchResource else { + guard let fetchResource = account.postbox.mediaBox.fetchResource else { return EmptyDisposable } diff --git a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift index 44b44075f2..b6a8681938 100644 --- a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift +++ b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift @@ -258,7 +258,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { } } if !addedRead.isEmpty { - let _ = markFeaturedStickerPacksAsSeenInteractively(postbox: strongSelf.context.account.postbox, ids: addedRead).start() + let _ = strongSelf.context.engine.stickers.markFeaturedStickerPacksAsSeenInteractively(ids: addedRead).start() } if bottomIndex >= strongSelf.gridNode.items.count - 15 { @@ -295,84 +295,9 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode { } let account = strongSelf.context.account if install { - let _ = addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: []).start() - /*var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) - |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in - switch result { - case let .result(info, items, installed): - if installed { - return .complete() - } else { - return preloadedStickerPackThumbnail(account: account, info: info, items: items) - |> filter { $0 } - |> ignoreValues - |> then( - addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items) - |> ignoreValues - ) - |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in - } - |> then(.single((info, items))) - } - case .fetching: - break - case .none: - break - } - return .complete() - } - |> deliverOnMainQueue - - let context = strongSelf.context - var cancelImpl: (() -> Void)? - let progressSignal = Signal { subscriber in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - self?.controller?.present(controller, in: .window(.root)) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(1.0, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - installSignal = installSignal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { - self?.installDisposable.set(nil) - } - - strongSelf.installDisposable.set(installSignal.start(next: { info, items in - guard let strongSelf = self else { - return - } - - /*var animateInAsReplacement = false - if let navigationController = strongSelf.controllerInteraction.navigationController() { - for controller in navigationController.overlayControllers { - if let controller = controller as? UndoOverlayController { - controller.dismissWithCommitActionAndReplacementAnimation() - animateInAsReplacement = true - } - } - } - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in - return true - }))*/ - }))*/ + let _ = strongSelf.context.engine.stickers.addStickerPackInteractively(info: info, items: []).start() } else { - let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete) + let _ = (strongSelf.context.engine.stickers.removeStickerPackInteractively(id: info.id, option: .delete) |> deliverOnMainQueue).start(next: { _ in }) } @@ -1165,85 +1090,9 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { } let account = strongSelf.context.account if install { - let _ = addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: []).start() - /*var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) - |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in - switch result { - case let .result(info, items, installed): - if installed { - return .complete() - } else { - return preloadedStickerPackThumbnail(account: account, info: info, items: items) - |> filter { $0 } - |> ignoreValues - |> then( - addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items) - |> ignoreValues - ) - |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in - return .complete() - } - |> then(.single((info, items))) - } - case .fetching: - break - case .none: - break - } - return .complete() - } - |> deliverOnMainQueue - - let context = strongSelf.context - var cancelImpl: (() -> Void)? - let progressSignal = Signal { subscriber in - let presentationData = context.sharedContext.currentPresentationData.with { $0 } - let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: { - cancelImpl?() - })) - self?.controller?.present(controller, in: .window(.root)) - return ActionDisposable { [weak controller] in - Queue.mainQueue().async() { - controller?.dismiss() - } - } - } - |> runOn(Queue.mainQueue()) - |> delay(0.12, queue: Queue.mainQueue()) - let progressDisposable = progressSignal.start() - - installSignal = installSignal - |> afterDisposed { - Queue.mainQueue().async { - progressDisposable.dispose() - } - } - cancelImpl = { - self?.installDisposable.set(nil) - } - - strongSelf.installDisposable.set(installSignal.start(next: { info, items in - guard let strongSelf = self else { - return - } - - var animateInAsReplacement = false - if let navigationController = strongSelf.controller?.navigationController as? NavigationController { - for controller in navigationController.overlayControllers { - if let controller = controller as? UndoOverlayController { - controller.dismissWithCommitActionAndReplacementAnimation() - animateInAsReplacement = true - } - } - } - - let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - /*strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in - return true - }))*/ - }))*/ + let _ = strongSelf.context.engine.stickers.addStickerPackInteractively(info: info, items: []).start() } else { - let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete) + let _ = (strongSelf.context.engine.stickers.removeStickerPackInteractively(id: info.id, option: .delete) |> deliverOnMainQueue).start(next: { _ in }) } @@ -1268,22 +1117,22 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { func updateText(_ text: String, languageCode: String?) { let signal: Signal<([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError> if !text.isEmpty { - let account = self.context.account + let context = self.context let stickers: Signal<[(String?, FoundStickerItem)], NoError> = Signal { subscriber in var signals: Signal<[Signal<(String?, [FoundStickerItem]), NoError>], NoError> = .single([]) let query = text.trimmingCharacters(in: .whitespacesAndNewlines) if query.isSingleEmoji { - signals = .single([searchStickers(account: account, query: text.basicEmoji.0) + signals = .single([context.engine.stickers.searchStickers(query: text.basicEmoji.0) |> map { (nil, $0) }]) } else if query.count > 1, let languageCode = languageCode, !languageCode.isEmpty && languageCode != "emoji" { - var signal = searchEmojiKeywords(postbox: account.postbox, inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3) + var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3) if !languageCode.lowercased().hasPrefix("en") { signal = signal |> mapToSignal { keywords in return .single(keywords) |> then( - searchEmojiKeywords(postbox: account.postbox, inputLanguageCode: "en-US", query: query.lowercased(), completeMatch: query.count < 3) + context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query.lowercased(), completeMatch: query.count < 3) |> map { englishKeywords in return keywords + englishKeywords } @@ -1296,7 +1145,7 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = [] let emoticons = keywords.flatMap { $0.emoticons } for emoji in emoticons { - signals.append(searchStickers(account: self.context.account, query: emoji.basicEmoji.0) + signals.append(context.engine.stickers.searchStickers(query: emoji.basicEmoji.0) |> take(1) |> map { (emoji, $0) }) } @@ -1320,8 +1169,8 @@ private final class FeaturedPaneSearchContentNode: ASDisplayNode { }) } - let local = searchStickerSets(postbox: context.account.postbox, query: text) - let remote = searchStickerSetsRemotely(network: context.account.network, query: text) + let local = context.engine.stickers.searchStickerSets(query: text) + let remote = context.engine.stickers.searchStickerSetsRemotely(query: text) |> delay(0.2, queue: Queue.mainQueue()) let rawPacks = local |> mapToSignal { result -> Signal<(FoundStickerSets, Bool, FoundStickerSets?), NoError> in diff --git a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift index 71a0c3e920..3af52d0feb 100644 --- a/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift +++ b/submodules/TelegramUI/Sources/FetchCachedRepresentations.swift @@ -168,7 +168,7 @@ private func fetchCachedStickerAJpegRepresentation(account: Account, resource: M return Signal({ subscriber in if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { if let image = WebP.convert(fromWebP: data) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) let colorData = NSMutableData() @@ -245,7 +245,7 @@ private func fetchCachedScaledImageRepresentation(resource: MediaResource, resou return Signal({ subscriber in if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { if let image = UIImage(data: data) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) let size: CGSize @@ -318,7 +318,7 @@ private func fetchCachedVideoFirstFrameRepresentation(account: Account, resource let fullSizeImage = try imageGenerator.copyCGImage(at: CMTime(seconds: 0.0, preferredTimescale: asset.duration.timescale), actualTime: nil) - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) if let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { @@ -352,7 +352,7 @@ private func fetchCachedScaledVideoFirstFrameRepresentation(account: Account, re return Signal({ subscriber in if let data = try? Data(contentsOf: URL(fileURLWithPath: firstFrame.path), options: [.mappedIfSafe]) { if let image = UIImage(data: data) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) let size = representation.size @@ -387,7 +387,7 @@ private func fetchCachedBlurredWallpaperRepresentation(resource: MediaResource, return Signal({ subscriber in if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { if let image = UIImage(data: data) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) if let colorImage = blurredImage(image, radius: 45.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { @@ -413,7 +413,7 @@ private func fetchCachedBlurredWallpaperRepresentation(resource: MediaResource, private func fetchCachedPatternWallpaperMaskRepresentation(resource: MediaResource, resourceData: MediaResourceData, representation: CachedPatternWallpaperMaskRepresentation) -> Signal { return Signal({ subscriber in if var data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) if let unzippedData = TGGUnzipData(data, 2 * 1024 * 1024) { @@ -477,7 +477,7 @@ private func fetchCachedPatternWallpaperRepresentation(resource: MediaResource, data = unzippedData } - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) var colors: [UIColor] = [] @@ -619,7 +619,7 @@ private func fetchCachedBlurredWallpaperRepresentation(account: Account, resourc return Signal({ subscriber in if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { if let image = UIImage(data: data) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) if let colorImage = blurredImage(image, radius: 45.0), let colorDestination = CGImageDestinationCreateWithURL(url as CFURL, kUTTypeJPEG, 1, nil) { @@ -652,7 +652,7 @@ private func fetchCachedAlbumArtworkRepresentation(account: Account, resource: M switch result { case let .artworkData(data): if let image = UIImage(data: data) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) var size = image.size @@ -694,7 +694,7 @@ private func fetchEmojiThumbnailRepresentation(account: Account, resource: Media return .never() } return Signal({ subscriber in - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) let nsString = (resource.emoji as NSString) @@ -803,7 +803,7 @@ private func fetchEmojiRepresentation(account: Account, resource: MediaResource, |> mapToSignal { data in return Signal({ subscriber in if let data = data, let image = UIImage(data: data) { - let path = NSTemporaryDirectory() + "\(arc4random64())" + let path = NSTemporaryDirectory() + "\(Int64.random(in: Int64.min ... Int64.max))" let url = URL(fileURLWithPath: path) let size = CGSize(width: 160.0, height: 160.0) diff --git a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift index 91890cc402..0a619d7e7b 100644 --- a/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift +++ b/submodules/TelegramUI/Sources/GifPaneSearchContentNode.swift @@ -25,17 +25,17 @@ class PaneGifSearchForQueryResult { } } -func paneGifSearchForQuery(account: Account, query: String, offset: String?, incompleteResults: Bool = false, staleCachedResults: Bool = false, delayRequest: Bool = true, updateActivity: ((Bool) -> Void)?) -> Signal { - let contextBot = account.postbox.transaction { transaction -> String in +func paneGifSearchForQuery(context: AccountContext, query: String, offset: String?, incompleteResults: Bool = false, staleCachedResults: Bool = false, delayRequest: Bool = true, updateActivity: ((Bool) -> Void)?) -> Signal { + let contextBot = context.account.postbox.transaction { transaction -> String in let configuration = currentSearchBotsConfiguration(transaction: transaction) return configuration.gifBotUsername ?? "gif" } |> mapToSignal { botName -> Signal in - return resolvePeerByName(account: account, name: botName) + return context.engine.peers.resolvePeerByName(name: botName) } |> mapToSignal { peerId -> Signal in if let peerId = peerId { - return account.postbox.loadedPeerWithId(peerId) + return context.account.postbox.loadedPeerWithId(peerId) |> map { peer -> Peer? in return peer } @@ -46,7 +46,7 @@ func paneGifSearchForQuery(account: Account, query: String, offset: String?, inc } |> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?, Bool, Bool), NoError> in if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder { - let results = requestContextResults(account: account, botId: user.id, query: query, peerId: account.peerId, offset: offset ?? "", incompleteResults: incompleteResults, staleCachedResults: staleCachedResults, limit: 1) + let results = requestContextResults(account: context.account, botId: user.id, query: query, peerId: context.account.peerId, offset: offset ?? "", incompleteResults: incompleteResults, staleCachedResults: staleCachedResults, limit: 1) |> map { results -> (ChatPresentationInputQueryResult?, Bool, Bool) in return (.contextRequestResult(user, results?.results), results != nil, results?.isStale ?? false) } @@ -203,7 +203,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { let signal: Signal<([MultiplexedVideoNodeFile], String?)?, NoError> if !text.isEmpty { - signal = paneGifSearchForQuery(account: self.context.account, query: text, offset: "", updateActivity: self.updateActivity) + signal = paneGifSearchForQuery(context: self.context, query: text, offset: "", updateActivity: self.updateActivity) |> map { result -> ([MultiplexedVideoNodeFile], String?)? in if let result = result { return (result.files, result.nextOffset) @@ -252,7 +252,7 @@ final class GifPaneSearchContentNode: ASDisplayNode & PaneSearchContentNode { self.isLoadingNextResults = true let signal: Signal<([MultiplexedVideoNodeFile], String?)?, NoError> - signal = paneGifSearchForQuery(account: self.context.account, query: text, offset: nextOffsetValue, updateActivity: self.updateActivity) + signal = paneGifSearchForQuery(context: self.context, query: text, offset: nextOffsetValue, updateActivity: self.updateActivity) |> map { result -> ([MultiplexedVideoNodeFile], String?)? in if let result = result { return (result.files, result.nextOffset) diff --git a/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift b/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift index 3f7d1c7fe6..c8072be6b2 100755 --- a/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift +++ b/submodules/TelegramUI/Sources/HorizontalStickerGridItem.swift @@ -49,9 +49,9 @@ final class HorizontalStickerGridItem: GridItem { final class HorizontalStickerGridItemNode: GridItemNode { private var currentState: (Account, HorizontalStickerGridItem, CGSize)? - private let imageNode: TransformImageNode - private var animationNode: AnimatedStickerNode? - private var placeholderNode: StickerShimmerEffectNode? + let imageNode: TransformImageNode + private(set) var animationNode: AnimatedStickerNode? + private(set) var placeholderNode: StickerShimmerEffectNode? private let stickerFetchedDisposable = MetaDisposable() diff --git a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift index c9cad730e7..6206d9cdb8 100644 --- a/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift +++ b/submodules/TelegramUI/Sources/LegacyInstantVideoController.swift @@ -35,8 +35,9 @@ final class InstantVideoController: LegacyController, StandalonePresentableContr private let micLevelValue = ValuePromise(0.0) private let durationValue = ValuePromise(0.0) let audioStatus: InstantVideoControllerRecordingStatus - - private var dismissedVideo = false + + private var completed = false + private var dismissed = false override init(presentation: LegacyControllerPresentation, theme: PresentationTheme?, strings: PresentationStrings? = nil, initialLayout: ContainerViewLayout? = nil) { self.audioStatus = InstantVideoControllerRecordingStatus(micLevel: self.micLevelValue.get(), duration: self.durationValue.get()) @@ -61,8 +62,8 @@ final class InstantVideoController: LegacyController, StandalonePresentableContr } captureController.onDismiss = { [weak self] _, isCancelled in guard let strongSelf = self else { return } - if !strongSelf.dismissedVideo { - self?.dismissedVideo = true + if !strongSelf.dismissed { + self?.dismissed = true self?.onDismiss?(isCancelled) } } @@ -73,18 +74,33 @@ final class InstantVideoController: LegacyController, StandalonePresentableContr } func dismissVideo() { - if let captureController = self.captureController, !self.dismissedVideo { - self.dismissedVideo = true - captureController.dismiss() + if let captureController = self.captureController, !self.dismissed { + self.dismissed = true + captureController.dismiss(true) } } + + func extractVideoSnapshot() -> UIView? { + self.captureController?.extractVideoContent() + } + + func hideVideoSnapshot() { + self.captureController?.hideVideoContent() + } func completeVideo() { - if let captureController = self.captureController, !self.dismissedVideo { - self.dismissedVideo = true + if let captureController = self.captureController, !self.completed { + self.completed = true captureController.complete() } } + + func dismissAnimated() { + if let captureController = self.captureController, !self.dismissed { + self.dismissed = true + captureController.dismiss(false) + } + } func stopVideo() -> Bool { if let captureController = self.captureController { @@ -111,7 +127,7 @@ func legacyInputMicPalette(from theme: PresentationTheme) -> TGModernConversatio return TGModernConversationInputMicPallete(dark: theme.overallDarkAppearance, buttonColor: inputPanelTheme.actionControlFillColor, iconColor: inputPanelTheme.actionControlForegroundColor, backgroundColor: inputPanelTheme.panelBackgroundColor, borderColor: inputPanelTheme.panelSeparatorColor, lock: inputPanelTheme.panelControlAccentColor, textColor: inputPanelTheme.primaryTextColor, secondaryTextColor: inputPanelTheme.secondaryTextColor, recording: inputPanelTheme.mediaRecordingDotColor) } -func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, context: AccountContext, peerId: PeerId, slowmodeState: ChatSlowmodeState?, hasSchedule: Bool, send: @escaping (EnqueueMessage) -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void) -> InstantVideoController { +func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, context: AccountContext, peerId: PeerId, slowmodeState: ChatSlowmodeState?, hasSchedule: Bool, send: @escaping (InstantVideoController, EnqueueMessage?) -> Void, displaySlowmodeTooltip: @escaping (ASDisplayNode, CGRect) -> Void, presentSchedulePicker: @escaping (@escaping (Int32) -> Void) -> Void) -> InstantVideoController { let isSecretChat = peerId.namespace == Namespaces.Peer.SecretChat let legacyController = InstantVideoController(presentation: .custom, theme: theme) @@ -147,8 +163,13 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, done?(time) } } - controller.finishedWithVideo = { videoUrl, previewImage, _, duration, dimensions, liveUploadData, adjustments, isSilent, scheduleTimestamp in + controller.finishedWithVideo = { [weak legacyController] videoUrl, previewImage, _, duration, dimensions, liveUploadData, adjustments, isSilent, scheduleTimestamp in + guard let legacyController = legacyController else { + return + } + guard let videoUrl = videoUrl else { + send(legacyController, nil) return } @@ -157,7 +178,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, var previewRepresentations: [TelegramMediaImageRepresentation] = [] if let previewImage = previewImage { - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) let thumbnailSize = finalDimensions.aspectFitted(CGSize(width: 320.0, height: 320.0)) let thumbnailImage = TGScaleImageToPixelSize(previewImage, thumbnailSize)! if let thumbnailData = thumbnailImage.jpegData(compressionQuality: 0.4) { @@ -188,7 +209,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, resource = LocalFileMediaResource(fileId: liveUploadData.id) context.account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) } else { - resource = LocalFileVideoMediaResource(randomId: arc4random64(), path: videoUrl.path, adjustments: resourceAdjustments) + resource = LocalFileVideoMediaResource(randomId: Int64.random(in: Int64.min ... Int64.max), path: videoUrl.path, adjustments: resourceAdjustments) } if let previewImage = previewImage { @@ -197,8 +218,8 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, } } - let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: Int(finalDuration), size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo])]) - var message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil) + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: Int64.random(in: Int64.min ... Int64.max)), partialReference: nil, resource: resource, previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.FileName(fileName: "video.mp4"), .Video(duration: Int(finalDuration), size: PixelDimensions(finalDimensions), flags: [.instantRoundVideo])]) + var message: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) let scheduleTime: Int32? = scheduleTimestamp > 0 ? scheduleTimestamp : nil @@ -220,7 +241,7 @@ func legacyInstantVideoController(theme: PresentationTheme, panelFrame: CGRect, return attributes } - send(message) + send(legacyController, message) } controller.didDismiss = { [weak legacyController] in if let legacyController = legacyController { diff --git a/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift b/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift index 40b9492a53..6fb6235cb3 100644 --- a/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift +++ b/submodules/TelegramUI/Sources/ManagedDiceAnimationNode.swift @@ -137,7 +137,7 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick let appConfiguration: AppConfiguration = preferencesView.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue return InteractiveEmojiConfiguration.with(appConfiguration: appConfiguration) }) - self.emojis.set(loadedStickerPack(postbox: context.account.postbox, network: context.account.network, reference: .dice(emoji), forceActualized: false) + self.emojis.set(context.engine.stickers.loadedStickerPack(reference: .dice(emoji), forceActualized: false) |> mapToSignal { stickerPack -> Signal<[TelegramMediaFile], NoError> in switch stickerPack { case let .result(_, items, _): @@ -206,4 +206,11 @@ final class ManagedDiceAnimationNode: ManagedAnimationNode, GenericAnimatedStick func setOverlayColor(_ color: UIColor?, animated: Bool) { } + + func setFrameIndex(_ frameIndex: Int) { + } + + var currentFrameIndex: Int { + return 0 + } } diff --git a/submodules/TelegramUI/Sources/NotificationContentContext.swift b/submodules/TelegramUI/Sources/NotificationContentContext.swift index aa037e1bf0..abaf8b3c3a 100644 --- a/submodules/TelegramUI/Sources/NotificationContentContext.swift +++ b/submodules/TelegramUI/Sources/NotificationContentContext.swift @@ -82,7 +82,7 @@ public final class NotificationViewControllerImpl { let rootPath = rootPathForBasePath(self.initializationData.appGroupPath) performAppGroupUpgrades(appGroupPath: self.initializationData.appGroupPath, rootPath: rootPath) - TempBox.initializeShared(basePath: rootPath, processType: "notification-content", launchSpecificId: arc4random64()) + TempBox.initializeShared(basePath: rootPath, processType: "notification-content", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) let logsPath = rootPath + "/notificationcontent-logs" let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 328df0ae33..01809d3e85 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -67,7 +67,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { params.dismissInput() let controllerParams = LocationViewParams(sendLiveLocation: { location in - let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: nil, localGroupingKey: nil) + let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) params.enqueueMessage(outMessage) }, stopLiveLocation: { messageId in params.context.liveLocationManager?.cancelLiveLocation(peerId: messageId?.peerId ?? params.message.id.peerId) @@ -92,13 +92,13 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { } switch action { case .add: - params.navigationController?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: params.context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in + params.navigationController?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, context: params.context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { _ in return true })) case let .remove(positionInList): - params.navigationController?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, account: params.context.account), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in + params.navigationController?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(info.title).0, undo: true, info: info, topItem: items.first, context: params.context), elevatedLayout: true, animateInAsReplacement: animateInAsReplacement, action: { action in if case .undo = action { - let _ = addStickerPackInteractively(postbox: params.context.account.postbox, info: info, items: items, positionInList: positionInList).start() + let _ = params.context.engine.stickers.addStickerPackInteractively(info: info, items: items, positionInList: positionInList).start() } return true })) diff --git a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift index d15ceb433b..282e5f2185 100644 --- a/submodules/TelegramUI/Sources/OpenResolvedUrl.swift +++ b/submodules/TelegramUI/Sources/OpenResolvedUrl.swift @@ -242,7 +242,7 @@ func openResolvedUrlImpl(_ resolvedUrl: ResolvedUrl, context: AccountContext, ur if let to = to { if to.hasPrefix("@") { - let _ = (resolvePeerByName(account: context.account, name: String(to[to.index(to.startIndex, offsetBy: 1)...])) + let _ = (context.engine.peers.resolvePeerByName(name: String(to[to.index(to.startIndex, offsetBy: 1)...])) |> deliverOnMainQueue).start(next: { peerId in if let peerId = peerId { let _ = (context.account.postbox.loadedPeerWithId(peerId) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index f0749cbb1b..88ff00608d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -2738,7 +2738,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD |> map(Optional.init) )) self.privacySettings.set(.single(nil) |> then(requestAccountPrivacySettings(account: context.account) |> map(Optional.init))) - self.archivedPacks.set(.single(nil) |> then(archivedStickerPacks(account: context.account) |> map(Optional.init))) + self.archivedPacks.set(.single(nil) |> then(context.engine.stickers.archivedStickerPacks() |> map(Optional.init))) self.hasPassport.set(.single(false) |> then(twoStepAuthData(context.account.network) |> map { value -> Bool in return value.hasSecretValues @@ -2839,7 +2839,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD self.preloadHistoryDisposable.set(self.context.account.addAdditionalPreloadHistoryPeerId(peerId: peerId)) self.preloadedSticker.set(.single(nil) - |> then(randomGreetingSticker(account: context.account) + |> then(context.engine.stickers.randomGreetingSticker() |> map { item in return item?.file })) @@ -3237,7 +3237,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD disposable = MetaDisposable() self.resolvePeerByNameDisposable = disposable } - var resolveSignal = resolvePeerByName(account: self.context.account, name: name, ageLimit: 10) + var resolveSignal = self.context.engine.peers.resolvePeerByName(name: name, ageLimit: 10) var cancelImpl: (() -> Void)? let presentationData = self.presentationData @@ -3301,7 +3301,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD let account = self.context.account var resolveSignal: Signal if let peerName = peerName { - resolveSignal = resolvePeerByName(account: self.context.account, name: peerName) + resolveSignal = self.context.engine.peers.resolvePeerByName(name: peerName) |> mapToSignal { peerId -> Signal in if let peerId = peerId { return account.postbox.loadedPeerWithId(peerId) @@ -4280,7 +4280,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD if let peer = peer as? TelegramUser, let _ = peer.botInfo { strongSelf.activeActionDisposable.set(requestUpdatePeerIsBlocked(account: strongSelf.context.account, peerId: peer.id, isBlocked: block).start()) if !block { - let _ = enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start() + let _ = enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: "/start", attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start() if let navigationController = strongSelf.controller?.navigationController as? NavigationController { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peer.id))) } @@ -4650,7 +4650,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD case .help: text = "/help" } - let _ = enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)]).start() + let _ = enqueueMessages(account: strongSelf.context.account, peerId: peer.id, messages: [.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil, correlationId: nil)]).start() if let navigationController = strongSelf.controller?.navigationController as? NavigationController { strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(strongSelf.peerId))) @@ -5032,7 +5032,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } self.scrollNode.view.setContentOffset(CGPoint(), animated: false) - let resource = LocalFileMediaResource(fileId: arc4random64()) + let resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) self.context.account.postbox.mediaBox.storeResourceData(resource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: resource, progressiveSizes: [], immediateThumbnailData: nil) @@ -5077,7 +5077,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD } self.scrollNode.view.setContentOffset(CGPoint(), animated: false) - let photoResource = LocalFileMediaResource(fileId: arc4random64()) + let photoResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) self.context.account.postbox.mediaBox.storeResourceData(photoResource.id, data: data) let representation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 640, height: 640), resource: photoResource, progressiveSizes: [], immediateThumbnailData: nil) @@ -5145,7 +5145,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD if let liveUploadData = result.liveUploadData as? LegacyLiveUploadInterfaceResult { resource = LocalFileMediaResource(fileId: liveUploadData.id) } else { - resource = LocalFileMediaResource(fileId: arc4random64()) + resource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) } account.postbox.mediaBox.storeResourceData(resource.id, data: data, synchronous: true) subscriber.putNext(resource) @@ -5426,7 +5426,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD self.controller?.push(watchSettingsController(context: self.context)) case .support: let supportPeer = Promise() - supportPeer.set(context.engine.peerNames.supportPeerId()) + supportPeer.set(context.engine.peers.supportPeerId()) self.controller?.present(textAlertController(context: self.context, title: nil, text: self.presentationData.strings.Settings_FAQ_Intro, actions: [ TextAlertAction(type: .genericAction, title: presentationData.strings.Settings_FAQ_Button, action: { [weak self] in @@ -5652,7 +5652,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD strongSelf.headerNode.navigationButtonContainer.performAction?(.selectionDone) let _ = (enqueueMessages(account: strongSelf.context.account, peerId: peerId, messages: messageIds.map { id -> EnqueueMessage in - return .forward(source: id, grouping: .auto, attributes: []) + return .forward(source: id, grouping: .auto, attributes: [], correlationId: nil) }) |> deliverOnMainQueue).start(next: { [weak self] messageIds in if let strongSelf = self { diff --git a/submodules/TelegramUI/Sources/PrefetchManager.swift b/submodules/TelegramUI/Sources/PrefetchManager.swift index c91153056f..9ea80fb61b 100644 --- a/submodules/TelegramUI/Sources/PrefetchManager.swift +++ b/submodules/TelegramUI/Sources/PrefetchManager.swift @@ -52,7 +52,7 @@ private final class PrefetchManagerImpl { return view.values[PreferencesKeys.appConfiguration] as? AppConfiguration ?? .defaultValue } - let orderedPreloadMedia = combineLatest(account.viewTracker.orderedPreloadMedia, loadedStickerPack(postbox: account.postbox, network: account.network, reference: .animatedEmoji, forceActualized: false), appConfiguration) + let orderedPreloadMedia = combineLatest(account.viewTracker.orderedPreloadMedia, TelegramEngine(account: account).stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false), appConfiguration) |> map { orderedPreloadMedia, stickerPack, appConfiguration -> [PrefetchMediaItem] in let emojiSounds = AnimatedEmojiSoundsConfiguration.with(appConfiguration: appConfiguration, account: account) let chatHistoryMediaItems = orderedPreloadMedia.map { PrefetchMediaItem.chatHistory($0) } diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index c07f07f6c1..d8da3768a1 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -48,10 +48,12 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.titleNode = ImmediateTextNode() self.titleNode.maximumNumberOfLines = 1 self.titleNode.displaysAsynchronously = false + self.titleNode.insets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0) self.textNode = ImmediateTextNode() self.textNode.maximumNumberOfLines = 1 self.textNode.displaysAsynchronously = false + self.textNode.insets = UIEdgeInsets(top: 3.0, left: 0.0, bottom: 3.0, right: 0.0) self.imageNode = TransformImageNode() self.imageNode.contentAnimations = [.subsequentUpdates] @@ -162,8 +164,8 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { isMedia = false } - strongSelf.titleNode.attributedText = NSAttributedString(string: authorName, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor) - strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: isMedia ? strongSelf.theme.chat.inputPanel.secondaryTextColor : strongSelf.theme.chat.inputPanel.primaryTextColor) + strongSelf.titleNode.attributedText = NSAttributedString(string: authorName, font: Font.medium(14.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor) + strongSelf.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(14.0), textColor: isMedia ? strongSelf.theme.chat.inputPanel.secondaryTextColor : strongSelf.theme.chat.inputPanel.primaryTextColor) let headerString: String if let message = message, message.flags.contains(.Incoming), let author = message.author { @@ -239,20 +241,28 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { self.closeButton.frame = closeButtonFrame self.actionArea.frame = CGRect(origin: CGPoint(x: leftInset, y: 2.0), size: CGSize(width: closeButtonFrame.minX - leftInset, height: bounds.height)) - - self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) + + if self.lineNode.supernode == self { + self.lineNode.frame = CGRect(origin: CGPoint(x: leftInset, y: 8.0), size: CGSize(width: 2.0, height: bounds.size.height - 10.0)) + } var imageTextInset: CGFloat = 0.0 if !self.imageNode.isHidden { imageTextInset = 9.0 + 35.0 } - self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0)) + if self.imageNode.supernode == self { + self.imageNode.frame = CGRect(origin: CGPoint(x: leftInset + 9.0, y: 8.0), size: CGSize(width: 35.0, height: 35.0)) + } let titleSize = self.titleNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height)) - self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 7.0), size: titleSize) + if self.titleNode.supernode == self { + self.titleNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset - self.titleNode.insets.left, y: 7.0 - self.titleNode.insets.top), size: titleSize) + } let textSize = self.textNode.updateLayout(CGSize(width: bounds.size.width - leftInset - textLineInset - rightInset - textRightInset - imageTextInset, height: bounds.size.height)) - self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset, y: 25.0), size: textSize) + if self.textNode.supernode == self { + self.textNode.frame = CGRect(origin: CGPoint(x: leftInset + textLineInset + imageTextInset - self.textNode.insets.left, y: 25.0 - self.textNode.insets.top), size: textSize) + } } @objc func closePressed() { diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift index f69336c477..e33b4a8d7f 100644 --- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift @@ -169,7 +169,7 @@ public class ShareRootControllerImpl { let rootPath = rootPathForBasePath(self.initializationData.appGroupPath) performAppGroupUpgrades(appGroupPath: self.initializationData.appGroupPath, rootPath: rootPath) - TempBox.initializeShared(basePath: rootPath, processType: "share", launchSpecificId: arc4random64()) + TempBox.initializeShared(basePath: rootPath, processType: "share", launchSpecificId: Int64.random(in: Int64.min ... Int64.max)) let logsPath = rootPath + "/share-logs" let _ = try? FileManager.default.createDirectory(atPath: logsPath, withIntermediateDirectories: true, attributes: nil) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 7f551fcd19..61bed30c32 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1121,7 +1121,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { if !found { let controllerParams = LocationViewParams(sendLiveLocation: { location in - let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: nil, localGroupingKey: nil) + let outMessage: EnqueueMessage = .message(text: "", attributes: [], mediaReference: .standalone(media: location), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil) // params.enqueueMessage(outMessage) }, stopLiveLocation: { messageId in if let messageId = messageId { @@ -1343,7 +1343,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { present(WalletSplashScreen(context: WalletContextImpl(context: context, tonContext: tonContext), mode: .created(walletInfo, nil), walletCreatedPreloadState: nil)) } case let .send(address, amount, comment): - present(walletSendScreen(context: WalletContextImpl(context: context, tonContext: tonContext), randomId: arc4random64(), walletInfo: walletInfo, address: address, amount: amount, comment: comment)) + present(walletSendScreen(context: WalletContextImpl(context: context, tonContext: tonContext), randomId: Int64.random(in: Int64.min ... Int64.max), walletInfo: walletInfo, address: address, amount: amount, comment: comment)) } }) diff --git a/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift b/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift index 6f585cf323..14c25e6392 100644 --- a/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift +++ b/submodules/TelegramUI/Sources/StickerPaneSearchContentNode.swift @@ -238,24 +238,23 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { guard let strongSelf = self else { return } - let account = strongSelf.context.account + let context = strongSelf.context if install { - var installSignal = loadedStickerPack(postbox: strongSelf.context.account.postbox, network: strongSelf.context.account.network, reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) + var installSignal = strongSelf.context.engine.stickers.loadedStickerPack(reference: .id(id: info.id.id, accessHash: info.accessHash), forceActualized: false) |> mapToSignal { result -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in switch result { case let .result(info, items, installed): if installed { return .complete() } else { - return preloadedStickerPackThumbnail(account: account, info: info, items: items) + return preloadedStickerPackThumbnail(account: context.account, info: info, items: items) |> filter { $0 } |> ignoreValues |> then( - addStickerPackInteractively(postbox: strongSelf.context.account.postbox, info: info, items: items) + context.engine.stickers.addStickerPackInteractively(info: info, items: items) |> ignoreValues ) |> mapToSignal { _ -> Signal<(StickerPackCollectionInfo, [ItemCollectionItem]), NoError> in - return .complete() } |> then(.single((info, items))) } @@ -312,12 +311,12 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { } let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 } - strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, account: strongSelf.context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in + strongSelf.controllerInteraction.navigationController()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_AddedTitle, text: presentationData.strings.StickerPackActionInfo_AddedText(info.title).0, undo: false, info: info, topItem: items.first, context: strongSelf.context), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { _ in return true })) })) } else { - let _ = (removeStickerPackInteractively(postbox: account.postbox, id: info.id, option: .delete) + let _ = (context.engine.stickers.removeStickerPackInteractively(id: info.id, option: .delete) |> deliverOnMainQueue).start(next: { _ in }) } @@ -343,22 +342,22 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { func updateText(_ text: String, languageCode: String?) { let signal: Signal<([(String?, FoundStickerItem)], FoundStickerSets, Bool, FoundStickerSets?)?, NoError> if !text.isEmpty { - let account = self.context.account + let context = self.context let stickers: Signal<[(String?, FoundStickerItem)], NoError> = Signal { subscriber in var signals: Signal<[Signal<(String?, [FoundStickerItem]), NoError>], NoError> = .single([]) let query = text.trimmingCharacters(in: .whitespacesAndNewlines) if query.isSingleEmoji { - signals = .single([searchStickers(account: account, query: text.basicEmoji.0) + signals = .single([context.engine.stickers.searchStickers(query: text.basicEmoji.0) |> map { (nil, $0) }]) } else if query.count > 1, let languageCode = languageCode, !languageCode.isEmpty && languageCode != "emoji" { - var signal = searchEmojiKeywords(postbox: account.postbox, inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3) + var signal = context.engine.stickers.searchEmojiKeywords(inputLanguageCode: languageCode, query: query.lowercased(), completeMatch: query.count < 3) if !languageCode.lowercased().hasPrefix("en") { signal = signal |> mapToSignal { keywords in return .single(keywords) |> then( - searchEmojiKeywords(postbox: account.postbox, inputLanguageCode: "en-US", query: query.lowercased(), completeMatch: query.count < 3) + context.engine.stickers.searchEmojiKeywords(inputLanguageCode: "en-US", query: query.lowercased(), completeMatch: query.count < 3) |> map { englishKeywords in return keywords + englishKeywords } @@ -371,7 +370,7 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { var signals: [Signal<(String?, [FoundStickerItem]), NoError>] = [] let emoticons = keywords.flatMap { $0.emoticons } for emoji in emoticons { - signals.append(searchStickers(account: self.context.account, query: emoji.basicEmoji.0) + signals.append(context.engine.stickers.searchStickers(query: emoji.basicEmoji.0) |> take(1) |> map { (emoji, $0) }) } @@ -395,8 +394,8 @@ final class StickerPaneSearchContentNode: ASDisplayNode, PaneSearchContentNode { }) } - let local = searchStickerSets(postbox: context.account.postbox, query: text) - let remote = searchStickerSetsRemotely(network: context.account.network, query: text) + let local = context.engine.stickers.searchStickerSets(query: text) + let remote = context.engine.stickers.searchStickerSetsRemotely(query: text) |> delay(0.2, queue: Queue.mainQueue()) let rawPacks = local |> mapToSignal { result -> Signal<(FoundStickerSets, Bool, FoundStickerSets?), NoError> in diff --git a/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift b/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift index 3d729acda4..31c9514759 100644 --- a/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift +++ b/submodules/TelegramUI/Sources/StickerPaneSearchStickerItem.swift @@ -107,8 +107,8 @@ private let textFont = Font.regular(20.0) final class StickerPaneSearchStickerItemNode: GridItemNode { private var currentState: (Account, FoundStickerItem, CGSize)? - private let imageNode: TransformImageNode - private var animationNode: AnimatedStickerNode? + let imageNode: TransformImageNode + private(set) var animationNode: AnimatedStickerNode? private let textNode: ASTextNode private let stickerFetchedDisposable = MetaDisposable() diff --git a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift index ecb2de7a00..1eced6852a 100644 --- a/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift +++ b/submodules/TelegramUI/Sources/TelegramAccountAuxiliaryMethods.swift @@ -36,7 +36,7 @@ public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(updatePeerC } else if let resource = resource as? OpenInAppIconResource { return fetchOpenInAppIconResource(resource: resource) } else if let resource = resource as? EmojiSpriteResource { - return fetchEmojiSpriteResource(postbox: account.postbox, network: account.network, resource: resource) + return fetchEmojiSpriteResource(account: account, resource: resource) } else if let resource = resource as? VenueIconResource { return fetchVenueIconResource(account: account, resource: resource) } diff --git a/submodules/TelegramUI/Sources/TextLinkHandling.swift b/submodules/TelegramUI/Sources/TextLinkHandling.swift index d4f67403ec..f9bc83d904 100644 --- a/submodules/TelegramUI/Sources/TextLinkHandling.swift +++ b/submodules/TelegramUI/Sources/TextLinkHandling.swift @@ -91,7 +91,7 @@ func handleTextLinkActionImpl(context: AccountContext, peerId: PeerId?, navigate } let openPeerMentionImpl: (String) -> Void = { mention in - navigateDisposable.set((resolvePeerByName(account: context.account, name: mention, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peerId in + navigateDisposable.set((context.engine.peers.resolvePeerByName(name: mention, ageLimit: 10) |> take(1) |> deliverOnMainQueue).start(next: { peerId in openResolvedPeerImpl(peerId, .default) })) } diff --git a/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift b/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift index 958ed9eb4b..39ae2767dd 100644 --- a/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift +++ b/submodules/TelegramUI/Sources/TransformOutgoingMessageMedia.swift @@ -59,7 +59,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me }*/ let imageDimensions = CGSize(width: image.size.width * image.scale, height: image.size.height * image.scale) - let thumbnailResource = LocalFileMediaResource(fileId: arc4random64()) + let thumbnailResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) postbox.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData) let scaledImageSize = CGSize(width: scaledImage.size.width * scaledImage.scale, height: scaledImage.size.height * scaledImage.scale) @@ -99,7 +99,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me } else if file.mimeType.hasPrefix("video/") { return Signal { subscriber in if let scaledImage = generateVideoFirstFrame(data.path, maxDimensions: CGSize(width: 320.0, height: 320.0)), let thumbnailData = scaledImage.jpegData(compressionQuality: 0.6) { - let thumbnailResource = LocalFileMediaResource(fileId: arc4random64()) + let thumbnailResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) postbox.mediaBox.storeResourceData(thumbnailResource.id, data: thumbnailData) let scaledImageSize = CGSize(width: scaledImage.size.width * scaledImage.scale, height: scaledImage.size.height * scaledImage.scale) @@ -158,7 +158,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me if let fullImage = UIImage(contentsOfFile: data.path), let smallestImage = generateScaledImage(image: fullImage, size: smallestSize, scale: 1.0), let smallestData = compressImageToJPEG(smallestImage, quality: 0.7) { var representations = image.representations - let thumbnailResource = LocalFileMediaResource(fileId: arc4random64()) + let thumbnailResource = LocalFileMediaResource(fileId: Int64.random(in: Int64.min ... Int64.max)) postbox.mediaBox.storeResourceData(thumbnailResource.id, data: smallestData) representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(smallestSize), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil)) let updatedImage = TelegramMediaImage(imageId: image.imageId, representations: representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference, flags: []) diff --git a/submodules/TelegramVoip/Sources/OngoingCallContext.swift b/submodules/TelegramVoip/Sources/OngoingCallContext.swift index 332f932042..98e71dc325 100644 --- a/submodules/TelegramVoip/Sources/OngoingCallContext.swift +++ b/submodules/TelegramVoip/Sources/OngoingCallContext.swift @@ -729,19 +729,20 @@ public final class OngoingCallContext { } }) } + + strongSelf.signalingDataDisposable = callSessionManager.beginReceivingCallSignalingData(internalId: internalId, { [weak self] dataList in + queue.async { + self?.withContext { context in + if let context = context as? OngoingCallThreadLocalContextWebrtc { + for data in dataList { + context.addSignaling(data) + } + } + } + } + }) } })) - - self.signalingDataDisposable = (callSessionManager.callSignalingData(internalId: internalId)).start(next: { [weak self] data in - print("data received") - queue.async { - self?.withContext { context in - if let context = context as? OngoingCallThreadLocalContextWebrtc { - context.addSignaling(data) - } - } - } - }) } deinit { diff --git a/submodules/TgVoipWebrtc/tgcalls b/submodules/TgVoipWebrtc/tgcalls index cff573909e..e48eeb2914 160000 --- a/submodules/TgVoipWebrtc/tgcalls +++ b/submodules/TgVoipWebrtc/tgcalls @@ -1 +1 @@ -Subproject commit cff573909eb35be75491b6967063b94d81ad0828 +Subproject commit e48eeb2914e49858964b8671d0cdaf916c58189b diff --git a/submodules/UndoUI/Sources/UndoOverlayController.swift b/submodules/UndoUI/Sources/UndoOverlayController.swift index 76d927a634..124e6d17a8 100644 --- a/submodules/UndoUI/Sources/UndoOverlayController.swift +++ b/submodules/UndoUI/Sources/UndoOverlayController.swift @@ -17,8 +17,8 @@ public enum UndoOverlayContent { case emoji(path: String, text: String) case swipeToReply(title: String, text: String) case actionSucceeded(title: String, text: String, cancel: String) - case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: ItemCollectionItem?, account: Account) - case dice(dice: TelegramMediaDice, account: Account, text: String, action: String?) + case stickersModified(title: String, text: String, undo: Bool, info: StickerPackCollectionInfo, topItem: ItemCollectionItem?, context: AccountContext) + case dice(dice: TelegramMediaDice, context: AccountContext, text: String, action: String?) case chatAddedToFolder(chatTitle: String, folderTitle: String) case chatRemovedFromFolder(chatTitle: String, folderTitle: String) case messagesUnpinned(title: String, text: String, undo: Bool, isHidden: Bool) @@ -35,7 +35,7 @@ public enum UndoOverlayContent { case voiceChatRecording(text: String) case voiceChatFlag(text: String) case voiceChatCanSpeak(text: String) - case sticker(account: Account, file: TelegramMediaFile, text: String) + case sticker(context: AccountContext, file: TelegramMediaFile, text: String) case copy(text: String) case mediaSaved(text: String) case paymentSent(currencyValue: String, itemTitle: String) diff --git a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift index cc0f619c84..acd0e56a21 100644 --- a/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift +++ b/submodules/UndoUI/Sources/UndoOverlayControllerNode.swift @@ -17,6 +17,7 @@ import SlotMachineAnimationNode import AnimationUI import StickerResources import AvatarNode +import AccountContext final class UndoOverlayControllerNode: ViewControllerTracingNode { private let elevatedLayout: Bool @@ -330,7 +331,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { self.textNode.maximumNumberOfLines = 2 displayUndo = false self.originalRemainingSeconds = 5 - case let .stickersModified(title, text, undo, info, topItem, account): + case let .stickersModified(title, text, undo, info, topItem, context): self.avatarNode = nil self.iconNode = nil self.iconCheckNode = nil @@ -377,14 +378,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let stillImageSize = representation.dimensions.cgSize.aspectFitted(imageBoundingSize) self.stickerImageSize = stillImageSize - updatedImageSignal = chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource) + updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: representation.resource) case let .animated(resource): self.stickerImageSize = imageBoundingSize - updatedImageSignal = chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true) + updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: resource, animated: true) } if let resourceReference = resourceReference { - updatedFetchSignal = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference) + updatedFetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: resourceReference) } } else { updatedImageSignal = .single({ _ in return nil }) @@ -415,10 +416,10 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { case let .animated(resource): let animatedStickerNode = AnimatedStickerNode() self.animatedStickerNode = animatedStickerNode - animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 80, height: 80, mode: .cached) + animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource), width: 80, height: 80, mode: .cached) } } - case let .dice(dice, account, text, action): + case let .dice(dice, context, text, action): self.avatarNode = nil self.iconNode = nil self.iconCheckNode = nil @@ -458,14 +459,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let animatedStickerNode = AnimatedStickerNode() self.animatedStickerNode = animatedStickerNode - let _ = (loadedStickerPack(postbox: account.postbox, network: account.network, reference: .dice(dice.emoji), forceActualized: false) + let _ = (context.engine.stickers.loadedStickerPack(reference: .dice(dice.emoji), forceActualized: false) |> deliverOnMainQueue).start(next: { stickerPack in if let value = dice.value { switch stickerPack { case let .result(_, items, _): let item = items[Int(value)] if let item = item as? StickerPackItem { - animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: item.file.resource), width: 120, height: 120, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) + animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: item.file.resource), width: 120, height: 120, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) } default: break @@ -613,7 +614,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { displayUndo = false self.originalRemainingSeconds = 3 - case let .sticker(account, file, text): + case let .sticker(context, file, text): self.avatarNode = nil self.iconNode = nil self.iconCheckNode = nil @@ -650,14 +651,14 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { let stillImageSize = representation.dimensions.cgSize.aspectFitted(imageBoundingSize) self.stickerImageSize = stillImageSize - updatedImageSignal = chatMessageStickerPackThumbnail(postbox: account.postbox, resource: representation.resource) + updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: representation.resource) case let .animated(resource): self.stickerImageSize = imageBoundingSize - updatedImageSignal = chatMessageStickerPackThumbnail(postbox: account.postbox, resource: resource, animated: true) + updatedImageSignal = chatMessageStickerPackThumbnail(postbox: context.account.postbox, resource: resource, animated: true) } if let resourceReference = resourceReference { - updatedFetchSignal = fetchedMediaResource(mediaBox: account.postbox.mediaBox, reference: resourceReference) + updatedFetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: resourceReference) } } else { updatedImageSignal = .single({ _ in return nil }) @@ -688,7 +689,7 @@ final class UndoOverlayControllerNode: ViewControllerTracingNode { case let .animated(resource): let animatedStickerNode = AnimatedStickerNode() self.animatedStickerNode = animatedStickerNode - animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: account, resource: resource), width: 80, height: 80, mode: .cached) + animatedStickerNode.setup(source: AnimatedStickerResourceSource(account: context.account, resource: resource), width: 80, height: 80, mode: .cached) } } case let .copy(text): diff --git a/submodules/UrlHandling/Sources/UrlHandling.swift b/submodules/UrlHandling/Sources/UrlHandling.swift index 55c1ce387d..a3d111b8f5 100644 --- a/submodules/UrlHandling/Sources/UrlHandling.swift +++ b/submodules/UrlHandling/Sources/UrlHandling.swift @@ -303,13 +303,13 @@ public func parseInternalUrl(query: String) -> ParsedInternalUrl? { return nil } -private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Signal { +private func resolveInternalUrl(context: AccountContext, url: ParsedInternalUrl) -> Signal { switch url { case let .peerName(name, parameter): - return resolvePeerByName(account: account, name: name) + return context.engine.peers.resolvePeerByName(name: name) |> take(1) |> mapToSignal { peerId -> Signal in - return account.postbox.transaction { transaction -> Peer? in + return context.account.postbox.transaction { transaction -> Peer? in if let peerId = peerId { return transaction.getPeer(peerId) } else { @@ -329,7 +329,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig return .single(.channelMessage(peerId: peer.id, messageId: MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id))) case let .replyThread(id, replyId): let replyThreadMessageId = MessageId(peerId: peer.id, namespace: Namespaces.Message.Cloud, id: id) - return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId, atMessageId: nil) + return fetchChannelReplyThreadMessage(account: context.account, messageId: replyThreadMessageId, atMessageId: nil) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -355,7 +355,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } } case let .peerId(peerId): - return account.postbox.transaction { transaction -> Peer? in + return context.account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(peerId) } |> mapToSignal { peer -> Signal in @@ -366,7 +366,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig } } case let .privateMessage(messageId, threadId): - return account.postbox.transaction { transaction -> Peer? in + return context.account.postbox.transaction { transaction -> Peer? in return transaction.getPeer(messageId.peerId) } |> mapToSignal { peer -> Signal in @@ -374,14 +374,14 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig if let peer = peer { foundPeer = .single(peer) } else { - foundPeer = TelegramEngine(account: account).peerNames.findChannelById(channelId: messageId.peerId.id._internalGetInt32Value()) + foundPeer = TelegramEngine(account: context.account).peers.findChannelById(channelId: messageId.peerId.id._internalGetInt32Value()) } return foundPeer |> mapToSignal { foundPeer -> Signal in if let foundPeer = foundPeer { if let threadId = threadId { let replyThreadMessageId = MessageId(peerId: foundPeer.id, namespace: Namespaces.Message.Cloud, id: threadId) - return fetchChannelReplyThreadMessage(account: account, messageId: replyThreadMessageId, atMessageId: nil) + return fetchChannelReplyThreadMessage(account: context.account, messageId: replyThreadMessageId, atMessageId: nil) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -409,7 +409,7 @@ private func resolveInternalUrl(account: Account, url: ParsedInternalUrl) -> Sig case let .proxy(host, port, username, password, secret): return .single(.proxy(host: host, port: port, username: username, password: password, secret: secret)) case let .internalInstantView(url): - return resolveInstantViewUrl(account: account, url: url) + return resolveInstantViewUrl(account: context.account, url: url) |> map(Optional.init) case let .confirmationCode(code): return .single(.confirmationCode(code)) @@ -567,7 +567,7 @@ public func resolveUrlImpl(context: AccountContext, peerId: PeerId?, url: String let basePrefix = scheme + basePath + "/" if url.lowercased().hasPrefix(basePrefix) { if let internalUrl = parseInternalUrl(query: String(url[basePrefix.endIndex...])) { - return resolveInternalUrl(account: context.account, url: internalUrl) + return resolveInternalUrl(context: context, url: internalUrl) |> map { resolved -> ResolvedUrl in if let resolved = resolved { return resolved diff --git a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift index aa7ca83050..968fbc8eb3 100644 --- a/submodules/WatchBridge/Sources/WatchRequestHandlers.swift +++ b/submodules/WatchBridge/Sources/WatchRequestHandlers.swift @@ -200,17 +200,17 @@ final class WatchSendMessageHandler: WatchRequestHandler { if args.replyToMid != 0, let peerId = peerId { replyMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: args.replyToMid) } - messageSignal = .single((.message(text: args.text, attributes: [], mediaReference: nil, replyToMessageId: replyMessageId, localGroupingKey: nil), peerId)) + messageSignal = .single((.message(text: args.text, attributes: [], mediaReference: nil, replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil), peerId)) } else if let args = subscription as? TGBridgeSendLocationMessageSubscription, let location = args.location { let peerId = makePeerIdFromBridgeIdentifier(args.peerId) let map = TelegramMediaMap(latitude: location.latitude, longitude: location.longitude, heading: nil, accuracyRadius: nil, geoPlace: nil, venue: makeVenue(from: location.venue), liveBroadcastingTimeout: nil, liveProximityNotificationRadius: nil) - messageSignal = .single((.message(text: "", attributes: [], mediaReference: .standalone(media: map), replyToMessageId: nil, localGroupingKey: nil), peerId)) + messageSignal = .single((.message(text: "", attributes: [], mediaReference: .standalone(media: map), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil), peerId)) } else if let args = subscription as? TGBridgeSendStickerMessageSubscription { let peerId = makePeerIdFromBridgeIdentifier(args.peerId) messageSignal = mediaForSticker(documentId: args.document.documentId, account: context.account) |> map({ media -> (EnqueueMessage?, PeerId?) in if let media = media { - return (.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil), peerId) + return (.message(text: "", attributes: [], mediaReference: .standalone(media: media), replyToMessageId: nil, localGroupingKey: nil, correlationId: nil), peerId) } else { return (nil, nil) } @@ -218,7 +218,7 @@ final class WatchSendMessageHandler: WatchRequestHandler { } else if let args = subscription as? TGBridgeSendForwardedMessageSubscription { let peerId = makePeerIdFromBridgeIdentifier(args.targetPeerId) if let forwardPeerId = makePeerIdFromBridgeIdentifier(args.peerId) { - messageSignal = .single((.forward(source: MessageId(peerId: forwardPeerId, namespace: Namespaces.Message.Cloud, id: args.messageId), grouping: .none, attributes: []), peerId)) + messageSignal = .single((.forward(source: MessageId(peerId: forwardPeerId, namespace: Namespaces.Message.Cloud, id: args.messageId), grouping: .none, attributes: [], correlationId: nil), peerId)) } } @@ -728,7 +728,7 @@ final class WatchAudioHandler: WatchRequestHandler { replyMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMid) } - let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.count, attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])), replyToMessageId: replyMessageId, localGroupingKey: nil)]).start() + let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: randomId), partialReference: nil, resource: resource, previewRepresentations: [], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "audio/ogg", size: data.count, attributes: [.Audio(isVoice: true, duration: Int(duration), title: nil, performer: nil, waveform: nil)])), replyToMessageId: replyMessageId, localGroupingKey: nil, correlationId: nil)]).start() } }) } else { @@ -749,7 +749,7 @@ final class WatchLocationHandler: WatchRequestHandler { |> take(1) |> mapToSignal({ context -> Signal<[ChatContextResultMessage], NoError> in if let context = context { - return resolvePeerByName(account: context.account, name: "foursquare") + return context.engine.peers.resolvePeerByName(name: "foursquare") |> take(1) |> mapToSignal { peerId -> Signal in guard let peerId = peerId else { diff --git a/submodules/WebSearchUI/Sources/WebSearchController.swift b/submodules/WebSearchUI/Sources/WebSearchController.swift index 2e872a9aa8..520985529b 100644 --- a/submodules/WebSearchUI/Sources/WebSearchController.swift +++ b/submodules/WebSearchUI/Sources/WebSearchController.swift @@ -445,7 +445,7 @@ public final class WebSearchController: ViewController { } let account = self.context.account - let contextBot = resolvePeerByName(account: account, name: name) + let contextBot = self.context.engine.peers.resolvePeerByName(name: name) |> mapToSignal { peerId -> Signal in if let peerId = peerId { return account.postbox.loadedPeerWithId(peerId)