mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various fixes
This commit is contained in:
parent
9bcbd429aa
commit
425a95514c
@ -3,7 +3,7 @@
|
||||
@implementation Serialization
|
||||
|
||||
- (NSUInteger)currentLayer {
|
||||
return 117;
|
||||
return 118;
|
||||
}
|
||||
|
||||
- (id _Nullable)parseMessage:(NSData * _Nullable)data {
|
||||
|
@ -264,6 +264,7 @@
|
||||
<string>onionhttp</string>
|
||||
<string>ucbrowser</string>
|
||||
<string>dolphin</string>
|
||||
<string>instagram-stories</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
|
@ -98,6 +98,7 @@
|
||||
<string>onionhttp</string>
|
||||
<string>ucbrowser</string>
|
||||
<string>dolphin</string>
|
||||
<string>instagram-stories</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
|
@ -12,6 +12,7 @@ static_library(
|
||||
"//submodules/YuvConversion:YuvConversion",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/rlottie:RLottieBinding",
|
||||
"//submodules/MediaResources:MediaResources",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
|
||||
|
@ -13,6 +13,7 @@ swift_library(
|
||||
"//submodules/YuvConversion:YuvConversion",
|
||||
"//submodules/GZip:GZip",
|
||||
"//submodules/rlottie:RLottieBinding",
|
||||
"//submodules/MediaResources:MediaResources",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
@ -6,6 +6,7 @@ import AsyncDisplayKit
|
||||
import RLottieBinding
|
||||
import GZip
|
||||
import YuvConversion
|
||||
import MediaResources
|
||||
|
||||
private let sharedQueue = Queue()
|
||||
private let sharedStoreQueue = Queue.concurrentDefaultQueue()
|
||||
@ -438,7 +439,7 @@ private final class AnimatedStickerDirectFrameSourceCache {
|
||||
private var scratchBuffer: Data
|
||||
private var decodeBuffer: Data
|
||||
|
||||
init?(queue: Queue, pathPrefix: String, width: Int, height: Int, frameCount: Int) {
|
||||
init?(queue: Queue, pathPrefix: String, width: Int, height: Int, frameCount: Int, fitzModifier: EmojiFitzModifier?) {
|
||||
self.queue = queue
|
||||
self.storeQueue = sharedStoreQueue
|
||||
|
||||
@ -446,7 +447,13 @@ private final class AnimatedStickerDirectFrameSourceCache {
|
||||
self.width = width
|
||||
self.height = height
|
||||
|
||||
let path = "\(pathPrefix)_\(width):\(height).stickerframecache"
|
||||
let suffix : String
|
||||
if let fitzModifier = fitzModifier {
|
||||
suffix = "_fitz\(fitzModifier.rawValue)"
|
||||
} else {
|
||||
suffix = ""
|
||||
}
|
||||
let path = "\(pathPrefix)_\(width):\(height)\(suffix).stickerframecache"
|
||||
var file = ManagedFileImpl(queue: queue, path: path, mode: .readwrite)
|
||||
if let file = file {
|
||||
self.file = file
|
||||
@ -594,7 +601,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
return self.currentFrame % self.frameCount
|
||||
}
|
||||
|
||||
init?(queue: Queue, data: Data, width: Int, height: Int, cachePathPrefix: String?) {
|
||||
init?(queue: Queue, data: Data, width: Int, height: Int, cachePathPrefix: String?, fitzModifier: EmojiFitzModifier?) {
|
||||
self.queue = queue
|
||||
self.data = data
|
||||
self.width = width
|
||||
@ -602,7 +609,9 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
self.bytesPerRow = (4 * Int(width) + 15) & (~15)
|
||||
self.currentFrame = 0
|
||||
let rawData = TGGUnzipData(data, 8 * 1024 * 1024) ?? data
|
||||
guard let animation = LottieInstance(data: rawData, cacheKey: "") else {
|
||||
let decompressedData = transformedWithFitzModifier(data: rawData, fitzModifier: fitzModifier)
|
||||
|
||||
guard let animation = LottieInstance(data: decompressedData, cacheKey: "") else {
|
||||
return nil
|
||||
}
|
||||
self.animation = animation
|
||||
@ -611,7 +620,7 @@ private final class AnimatedStickerDirectFrameSource: AnimatedStickerFrameSource
|
||||
self.frameRate = Int(animation.frameRate)
|
||||
|
||||
self.cache = cachePathPrefix.flatMap { cachePathPrefix in
|
||||
AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount)
|
||||
AnimatedStickerDirectFrameSourceCache(queue: queue, pathPrefix: cachePathPrefix, width: width, height: height, frameCount: frameCount, fitzModifier: fitzModifier)
|
||||
}
|
||||
}
|
||||
|
||||
@ -698,11 +707,15 @@ public struct AnimatedStickerStatus: Equatable {
|
||||
}
|
||||
|
||||
public protocol AnimatedStickerNodeSource {
|
||||
var fitzModifier: EmojiFitzModifier? { get }
|
||||
|
||||
func cachedDataPath(width: Int, height: Int) -> Signal<(String, Bool), NoError>
|
||||
func directDataPath() -> Signal<String, NoError>
|
||||
}
|
||||
|
||||
public final class AnimatedStickerNodeLocalFileSource: AnimatedStickerNodeSource {
|
||||
public var fitzModifier: EmojiFitzModifier? = nil
|
||||
|
||||
public let path: String
|
||||
|
||||
public init(path: String) {
|
||||
@ -733,8 +746,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
private let timer = Atomic<SwiftSignalKit.Timer?>(value: nil)
|
||||
private let frameSource = Atomic<QueueLocalObject<AnimatedStickerFrameSourceWrapper>?>(value: nil)
|
||||
|
||||
private var directData: (Data, String, Int, Int, String?)?
|
||||
private var cachedData: (Data, Bool)?
|
||||
private var directData: (Data, String, Int, Int, String?, EmojiFitzModifier?)?
|
||||
private var cachedData: (Data, Bool, EmojiFitzModifier?)?
|
||||
|
||||
private var renderer: (AnimationRenderer & ASDisplayNode)?
|
||||
|
||||
@ -809,7 +822,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
if let directData = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
strongSelf.directData = (directData, path, width, height, cachePathPrefix)
|
||||
strongSelf.directData = (directData, path, width, height, cachePathPrefix, source.fitzModifier)
|
||||
}
|
||||
if case let .still(position) = playbackMode {
|
||||
strongSelf.seekTo(position)
|
||||
@ -830,9 +843,9 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: path), options: [.mappedRead]) {
|
||||
if let (_, currentComplete) = strongSelf.cachedData {
|
||||
if let (_, currentComplete, _) = strongSelf.cachedData {
|
||||
if !currentComplete {
|
||||
strongSelf.cachedData = (data, complete)
|
||||
strongSelf.cachedData = (data, complete, source.fitzModifier)
|
||||
strongSelf.frameSource.with { frameSource in
|
||||
frameSource?.with { frameSource in
|
||||
if let frameSource = frameSource.value as? AnimatedStickerCachedFrameSource {
|
||||
@ -842,7 +855,7 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
strongSelf.cachedData = (data, complete)
|
||||
strongSelf.cachedData = (data, complete, source.fitzModifier)
|
||||
if strongSelf.isPlaying {
|
||||
strongSelf.play()
|
||||
} else if strongSelf.canDisplayFirstFrame {
|
||||
@ -892,8 +905,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
if maybeFrameSource == nil {
|
||||
let notifyUpdated: (() -> Void)? = nil
|
||||
if let directData = directData {
|
||||
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
|
||||
} else if let (cachedData, cachedDataComplete) = cachedData {
|
||||
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4, fitzModifier: directData.5)
|
||||
} else if let (cachedData, cachedDataComplete, _) = cachedData {
|
||||
if #available(iOS 9.0, *) {
|
||||
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
|
||||
notifyUpdated?()
|
||||
@ -964,8 +977,8 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
var maybeFrameSource: AnimatedStickerFrameSource?
|
||||
let notifyUpdated: (() -> Void)? = nil
|
||||
if let directData = directData {
|
||||
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
|
||||
} else if let (cachedData, cachedDataComplete) = cachedData {
|
||||
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4, fitzModifier: directData.5)
|
||||
} else if let (cachedData, cachedDataComplete, _) = cachedData {
|
||||
if #available(iOS 9.0, *) {
|
||||
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {
|
||||
notifyUpdated?()
|
||||
@ -1054,11 +1067,11 @@ public final class AnimatedStickerNode: ASDisplayNode {
|
||||
} else {
|
||||
var maybeFrameSource: AnimatedStickerFrameSource?
|
||||
if let directData = directData {
|
||||
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4)
|
||||
maybeFrameSource = AnimatedStickerDirectFrameSource(queue: queue, data: directData.0, width: directData.2, height: directData.3, cachePathPrefix: directData.4, fitzModifier: directData.5)
|
||||
if case .end = position {
|
||||
maybeFrameSource?.skipToEnd()
|
||||
}
|
||||
} else if let (cachedData, cachedDataComplete) = cachedData {
|
||||
} else if let (cachedData, cachedDataComplete, _) = cachedData {
|
||||
if #available(iOS 9.0, *) {
|
||||
maybeFrameSource = AnimatedStickerCachedFrameSource(queue: queue, data: cachedData, complete: cachedDataComplete, notifyUpdated: {})
|
||||
}
|
||||
|
76
submodules/AnimatedStickerNode/Sources/FitzModifier.swift
Normal file
76
submodules/AnimatedStickerNode/Sources/FitzModifier.swift
Normal file
@ -0,0 +1,76 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import MediaResources
|
||||
|
||||
let colorKeyRegex = try? NSRegularExpression(pattern: "\"k\":\\[[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\]")
|
||||
|
||||
public func transformedWithFitzModifier(data: Data, fitzModifier: EmojiFitzModifier?) -> Data {
|
||||
if let fitzModifier = fitzModifier, var string = String(data: data, encoding: .utf8) {
|
||||
let colors: [UIColor] = [0xf77e41, 0xffb139, 0xffd140, 0xffdf79].map { UIColor(rgb: $0) }
|
||||
let replacementColors: [UIColor]
|
||||
switch fitzModifier {
|
||||
case .type12:
|
||||
replacementColors = [0xca907a, 0xedc5a5, 0xf7e3c3, 0xfbefd6].map { UIColor(rgb: $0) }
|
||||
case .type3:
|
||||
replacementColors = [0xaa7c60, 0xc8a987, 0xddc89f, 0xe6d6b2].map { UIColor(rgb: $0) }
|
||||
case .type4:
|
||||
replacementColors = [0x8c6148, 0xad8562, 0xc49e76, 0xd4b188].map { UIColor(rgb: $0) }
|
||||
case .type5:
|
||||
replacementColors = [0x6e3c2c, 0x925a34, 0xa16e46, 0xac7a52].map { UIColor(rgb: $0) }
|
||||
case .type6:
|
||||
replacementColors = [0x291c12, 0x472a22, 0x573b30, 0x68493c].map { UIColor(rgb: $0) }
|
||||
}
|
||||
|
||||
func colorToString(_ color: UIColor) -> String {
|
||||
var r: CGFloat = 0.0
|
||||
var g: CGFloat = 0.0
|
||||
var b: CGFloat = 0.0
|
||||
if color.getRed(&r, green: &g, blue: &b, alpha: nil) {
|
||||
return "\"k\":[\(r),\(g),\(b),1]"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func match(_ a: Double, _ b: Double, eps: Double) -> Bool {
|
||||
return abs(a - b) < eps
|
||||
}
|
||||
|
||||
var replacements: [(NSTextCheckingResult, String)] = []
|
||||
|
||||
if let colorKeyRegex = colorKeyRegex {
|
||||
let results = colorKeyRegex.matches(in: string, range: NSRange(string.startIndex..., in: string))
|
||||
for result in results.reversed() {
|
||||
if let range = Range(result.range, in: string) {
|
||||
let substring = String(string[range])
|
||||
let color = substring[substring.index(string.startIndex, offsetBy: "\"k\":[".count) ..< substring.index(before: substring.endIndex)]
|
||||
let components = color.split(separator: ",")
|
||||
if components.count == 4, let r = Double(components[0]), let g = Double(components[1]), let b = Double(components[2]), let a = Double(components[3]) {
|
||||
if match(a, 1.0, eps: 0.01) {
|
||||
for i in 0 ..< colors.count {
|
||||
let color = colors[i]
|
||||
var cr: CGFloat = 0.0
|
||||
var cg: CGFloat = 0.0
|
||||
var cb: CGFloat = 0.0
|
||||
if color.getRed(&cr, green: &cg, blue: &cb, alpha: nil) {
|
||||
if match(r, Double(cr), eps: 0.01) && match(g, Double(cg), eps: 0.01) && match(b, Double(cb), eps: 0.01) {
|
||||
replacements.append((result, colorToString(replacementColors[i])))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (result, text) in replacements {
|
||||
if let range = Range(result.range, in: string) {
|
||||
string = string.replacingCharacters(in: range, with: text)
|
||||
}
|
||||
}
|
||||
|
||||
return string.data(using: .utf8) ?? data
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@ static_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit#shared",
|
||||
"//submodules/Display:Display#shared",
|
||||
"//submodules/TelegramCore:TelegramCore#shared",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/SearchBarNode:SearchBarNode",
|
||||
|
@ -10,6 +10,7 @@ swift_library(
|
||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||
"//submodules/Display:Display",
|
||||
"//submodules/TelegramCore:TelegramCore",
|
||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
|
||||
"//submodules/SearchBarNode:SearchBarNode",
|
||||
|
@ -2,12 +2,14 @@ import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import SwiftSignalKit
|
||||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
import SearchBarNode
|
||||
import AppBundle
|
||||
import TelegramCore
|
||||
|
||||
private func loadCountryCodes() -> [(String, Int)] {
|
||||
private func loadCountryCodes() -> [Country] {
|
||||
guard let filePath = getAppBundle().path(forResource: "PhoneCountries", ofType: "txt") else {
|
||||
return []
|
||||
}
|
||||
@ -21,10 +23,12 @@ private func loadCountryCodes() -> [(String, Int)] {
|
||||
let delimiter = ";"
|
||||
let endOfLine = "\n"
|
||||
|
||||
var result: [(String, Int)] = []
|
||||
var result: [Country] = []
|
||||
|
||||
var currentLocation = data.startIndex
|
||||
|
||||
let locale = Locale(identifier: "en-US")
|
||||
|
||||
while true {
|
||||
guard let codeRange = data.range(of: delimiter, options: [], range: currentLocation ..< data.endIndex) else {
|
||||
break
|
||||
@ -40,8 +44,9 @@ private func loadCountryCodes() -> [(String, Int)] {
|
||||
|
||||
let maybeNameRange = data.range(of: endOfLine, options: [], range: idRange.upperBound ..< data.endIndex)
|
||||
|
||||
let countryName = locale.localizedString(forIdentifier: countryId) ?? ""
|
||||
if let countryCodeInt = Int(countryCode) {
|
||||
result.append((countryId, countryCodeInt))
|
||||
result.append(Country(code: countryId, defaultName: countryName, name: countryName, countryCodes: [Country.CountryCode(code: countryCode, prefixes: [], patterns: [])]))
|
||||
}
|
||||
|
||||
if let maybeNameRange = maybeNameRange {
|
||||
@ -54,7 +59,14 @@ private func loadCountryCodes() -> [(String, Int)] {
|
||||
return result
|
||||
}
|
||||
|
||||
private let countryCodes: [(String, Int)] = loadCountryCodes()
|
||||
private var countryCodes: [Country] = loadCountryCodes()
|
||||
|
||||
public func loadServerCountryCodes(network: Network) {
|
||||
let _ = (getCountriesList(network: network, langCode: "")
|
||||
|> deliverOnMainQueue).start(next: { countries in
|
||||
countryCodes = countries
|
||||
})
|
||||
}
|
||||
|
||||
private final class AuthorizationSequenceCountrySelectionNavigationContentNode: NavigationBarContentNode {
|
||||
private let theme: PresentationTheme
|
||||
@ -117,8 +129,8 @@ private final class AuthorizationSequenceCountrySelectionNavigationContentNode:
|
||||
|
||||
public final class AuthorizationSequenceCountrySelectionController: ViewController {
|
||||
public static func lookupCountryNameById(_ id: String, strings: PresentationStrings) -> String? {
|
||||
for (itemId, _) in countryCodes {
|
||||
if id == itemId {
|
||||
for country in countryCodes {
|
||||
if id == country.code {
|
||||
let locale = localeWithStrings(strings)
|
||||
if let countryName = locale.localizedString(forRegionCode: id) {
|
||||
return countryName
|
||||
@ -131,9 +143,22 @@ public final class AuthorizationSequenceCountrySelectionController: ViewControll
|
||||
}
|
||||
|
||||
public static func lookupCountryIdByCode(_ code: Int) -> String? {
|
||||
for (itemId, itemCode) in countryCodes {
|
||||
if itemCode == code {
|
||||
return itemId
|
||||
for country in countryCodes {
|
||||
for countryCode in country.countryCodes {
|
||||
if countryCode.code == "\(code)" {
|
||||
return country.code
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public static func lookupPatternByCode(_ code: Int) -> String? {
|
||||
for country in countryCodes {
|
||||
for countryCode in country.countryCodes {
|
||||
if countryCode.code == "\(code)" {
|
||||
return countryCode.patterns.first
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
@ -22,6 +22,7 @@ public struct Font {
|
||||
|
||||
public static let bold = Traits(rawValue: 1 << 0)
|
||||
public static let italic = Traits(rawValue: 1 << 1)
|
||||
public static let monospacedNumbers = Traits(rawValue: 1 << 2)
|
||||
}
|
||||
|
||||
public enum Weight {
|
||||
@ -43,6 +44,15 @@ public struct Font {
|
||||
symbolicTraits.insert(.traitItalic)
|
||||
}
|
||||
var updatedDescriptor: UIFontDescriptor? = descriptor.withSymbolicTraits(symbolicTraits)
|
||||
if traits.contains(.monospacedNumbers) {
|
||||
updatedDescriptor = descriptor.addingAttributes([
|
||||
UIFontDescriptor.AttributeName.featureSettings: [
|
||||
[UIFontDescriptor.FeatureKey.featureIdentifier:
|
||||
kNumberSpacingType,
|
||||
UIFontDescriptor.FeatureKey.typeIdentifier:
|
||||
kMonospacedNumbersSelector]
|
||||
]])
|
||||
}
|
||||
switch design {
|
||||
case .serif:
|
||||
updatedDescriptor = updatedDescriptor?.withDesign(.serif)
|
||||
|
@ -1461,13 +1461,13 @@
|
||||
NSMutableArray *actions = [[NSMutableArray alloc] init];
|
||||
NSString *text = [self itemIsLivePhoto] ? TGLocalized(@"MediaPicker.LivePhotoDescription") : TGLocalized(@"MediaPicker.VideoMuteDescription");
|
||||
[actions addObject:@{@"title":text}];
|
||||
_tooltipContainerView.menuView.forceArrowOnTop = true;
|
||||
_tooltipContainerView.menuView.forceArrowOnTop = false;
|
||||
_tooltipContainerView.menuView.multiline = true;
|
||||
[_tooltipContainerView.menuView setButtonsAndActions:actions watcherHandle:nil];
|
||||
_tooltipContainerView.menuView.buttonHighlightDisabled = true;
|
||||
[_tooltipContainerView.menuView sizeToFit];
|
||||
|
||||
CGRect iconViewFrame = CGRectMake(12, 188 + _safeAreaInset.top, 40, 40);
|
||||
CGRect iconViewFrame = CGRectMake(12, self.frame.size.height - 192.0 - _safeAreaInset.bottom, 40, 40);
|
||||
[_tooltipContainerView showMenuFromRect:iconViewFrame animated:false];
|
||||
}
|
||||
|
||||
|
@ -173,12 +173,12 @@ private final class ChannelOwnershipTransferPasswordFieldNode: ASDisplayNode, UI
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
|
||||
public final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
|
||||
private let strings: PresentationStrings
|
||||
|
||||
private let titleNode: ASTextNode
|
||||
private let textNode: ASTextNode
|
||||
let inputFieldNode: ChannelOwnershipTransferPasswordFieldNode
|
||||
fileprivate let inputFieldNode: ChannelOwnershipTransferPasswordFieldNode
|
||||
|
||||
private let actionNodesSeparator: ASDisplayNode
|
||||
private let actionNodes: [TextAlertContentActionNode]
|
||||
@ -190,18 +190,25 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
|
||||
|
||||
private let hapticFeedback = HapticFeedback()
|
||||
|
||||
var complete: (() -> Void)? {
|
||||
public var complete: (() -> Void)? {
|
||||
didSet {
|
||||
self.inputFieldNode.complete = self.complete
|
||||
}
|
||||
}
|
||||
|
||||
override var dismissOnOutsideTap: Bool {
|
||||
public var theme: PresentationTheme {
|
||||
didSet {
|
||||
self.inputFieldNode.updateTheme(self.theme)
|
||||
}
|
||||
}
|
||||
|
||||
public override var dismissOnOutsideTap: Bool {
|
||||
return self.isUserInteractionEnabled
|
||||
}
|
||||
|
||||
init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction]) {
|
||||
public init(theme: AlertControllerTheme, ptheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction]) {
|
||||
self.strings = strings
|
||||
self.theme = ptheme
|
||||
|
||||
self.titleNode = ASTextNode()
|
||||
self.titleNode.maximumNumberOfLines = 2
|
||||
@ -258,19 +265,19 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
|
||||
self.disposable.dispose()
|
||||
}
|
||||
|
||||
func dismissInput() {
|
||||
public func dismissInput() {
|
||||
self.inputFieldNode.deactivateInput()
|
||||
}
|
||||
|
||||
var password: String {
|
||||
public var password: String {
|
||||
return self.inputFieldNode.password
|
||||
}
|
||||
|
||||
func updateIsChecking(_ checking: Bool) {
|
||||
public func updateIsChecking(_ checking: Bool) {
|
||||
self.inputFieldNode.updateIsChecking(checking)
|
||||
}
|
||||
|
||||
override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
public override func updateTheme(_ theme: AlertControllerTheme) {
|
||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Channel_OwnershipTransfer_EnterPassword, font: Font.bold(17.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||
self.textNode.attributedText = NSAttributedString(string: self.strings.Channel_OwnershipTransfer_EnterPasswordText, font: Font.regular(13.0), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||
|
||||
@ -287,7 +294,7 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
|
||||
}
|
||||
}
|
||||
|
||||
override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
public override func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) -> CGSize {
|
||||
var size = size
|
||||
size.width = min(size.width, 270.0)
|
||||
let measureSize = CGSize(width: size.width - 16.0 * 2.0, height: CGFloat.greatestFiniteMagnitude)
|
||||
@ -400,7 +407,7 @@ private final class ChannelOwnershipTransferAlertContentNode: AlertContentNode {
|
||||
return resultSize
|
||||
}
|
||||
|
||||
func animateError() {
|
||||
public func animateError() {
|
||||
self.inputFieldNode.updateIsInvalid()
|
||||
self.inputFieldNode.layer.addShakeAnimation()
|
||||
self.hapticFeedback.error()
|
||||
|
@ -76,9 +76,24 @@ private func cleanSuffix(_ text: String) -> String {
|
||||
return result
|
||||
}
|
||||
|
||||
extension String {
|
||||
func applyPatternOnNumbers(pattern: String, replacementCharacter: Character) -> String {
|
||||
var pureNumber = self.replacingOccurrences( of: "[^0-9]", with: "", options: .regularExpression)
|
||||
for index in 0 ..< pattern.count {
|
||||
guard index < pureNumber.count else { return pureNumber }
|
||||
let stringIndex = String.Index(encodedOffset: index)
|
||||
let patternCharacter = pattern[stringIndex]
|
||||
guard patternCharacter != replacementCharacter else { continue }
|
||||
pureNumber.insert(patternCharacter, at: stringIndex)
|
||||
}
|
||||
return pureNumber
|
||||
}
|
||||
}
|
||||
|
||||
public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
public let countryCodeField: TextFieldNode
|
||||
public let numberField: TextFieldNode
|
||||
public let placeholderNode: ImmediateTextNode
|
||||
|
||||
public var previousCountryCodeText = "+"
|
||||
public var previousNumberText = ""
|
||||
@ -151,13 +166,33 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
|
||||
private let phoneFormatter = InteractivePhoneFormatter()
|
||||
|
||||
public var mask: NSAttributedString? {
|
||||
didSet {
|
||||
self.updatePlaceholder()
|
||||
}
|
||||
}
|
||||
|
||||
private func updatePlaceholder() {
|
||||
if let mask = self.mask {
|
||||
let mutableMask = NSMutableAttributedString(attributedString: mask)
|
||||
mutableMask.replaceCharacters(in: NSRange(location: 0, length: mask.string.count), with: mask.string.replacingOccurrences(of: "X", with: "-"))
|
||||
mutableMask.addAttribute(.foregroundColor, value: UIColor.clear, range: NSRange(location: 0, length: min(self.numberField.textField.text?.count ?? 0, mask.string.count)))
|
||||
self.placeholderNode.attributedText = mutableMask
|
||||
} else {
|
||||
self.placeholderNode.attributedText = NSAttributedString(string: "")
|
||||
}
|
||||
let _ = self.placeholderNode.updateLayout(CGSize(width: self.frame.size.width, height: CGFloat.greatestFiniteMagnitude))
|
||||
}
|
||||
|
||||
private let fontSize: CGFloat
|
||||
|
||||
public init(fontSize: CGFloat = 20.0) {
|
||||
self.fontSize = fontSize
|
||||
|
||||
let font = Font.with(size: fontSize, design: .regular, traits: [.monospacedNumbers])
|
||||
|
||||
self.countryCodeField = TextFieldNode()
|
||||
self.countryCodeField.textField.font = Font.regular(fontSize)
|
||||
self.countryCodeField.textField.font = font
|
||||
self.countryCodeField.textField.textAlignment = .center
|
||||
self.countryCodeField.textField.returnKeyType = .next
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
@ -167,18 +202,21 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
self.countryCodeField.textField.keyboardType = .numberPad
|
||||
}
|
||||
|
||||
self.placeholderNode = ImmediateTextNode()
|
||||
|
||||
self.numberField = TextFieldNode()
|
||||
self.numberField.textField.font = Font.regular(fontSize)
|
||||
self.numberField.textField.font = font
|
||||
if #available(iOSApplicationExtension 10.0, iOS 10.0, *) {
|
||||
self.numberField.textField.keyboardType = .asciiCapableNumberPad
|
||||
self.numberField.textField.textContentType = .telephoneNumber
|
||||
} else {
|
||||
self.numberField.textField.keyboardType = .numberPad
|
||||
}
|
||||
|
||||
self.numberField.textField.defaultTextAttributes = [NSAttributedString.Key.font: font, NSAttributedString.Key.kern: 2.0]
|
||||
super.init()
|
||||
|
||||
self.addSubnode(self.countryCodeField)
|
||||
self.addSubnode(self.placeholderNode)
|
||||
self.addSubnode(self.numberField)
|
||||
|
||||
self.numberField.textField.didDeleteBackwardWhileEmpty = { [weak self] in
|
||||
@ -223,8 +261,9 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
|
||||
private func updateNumber(_ inputText: String, tryRestoringInputPosition: Bool = true, forceNotifyCountryCodeUpdated: Bool = false) {
|
||||
let (regionPrefix, text) = self.phoneFormatter.updateText(inputText)
|
||||
|
||||
var realRegionPrefix: String
|
||||
let numberText: String
|
||||
var numberText: String
|
||||
if let regionPrefix = regionPrefix, !regionPrefix.isEmpty, regionPrefix != "+" {
|
||||
realRegionPrefix = cleanSuffix(regionPrefix)
|
||||
if !realRegionPrefix.hasPrefix("+") {
|
||||
@ -239,6 +278,10 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
numberText = ""
|
||||
}
|
||||
|
||||
if let mask = self.mask {
|
||||
numberText = numberText.applyPatternOnNumbers(pattern: mask.string, replacementCharacter: "X")
|
||||
}
|
||||
|
||||
var focusOnNumber = false
|
||||
if realRegionPrefix != self.countryCodeField.textField.text {
|
||||
self.countryCodeField.textField.text = realRegionPrefix
|
||||
@ -296,5 +339,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
||||
if focusOnNumber && !self.numberField.textField.isFirstResponder {
|
||||
self.numberField.textField.becomeFirstResponder()
|
||||
}
|
||||
|
||||
self.updatePlaceholder()
|
||||
}
|
||||
}
|
||||
|
@ -593,6 +593,27 @@ public final class ShareController: ViewController {
|
||||
}
|
||||
self.controllerNode.shareExternal = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if case let .messages(messages) = strongSelf.subject, let message = messages.first, let peer = message.peers[message.id.peerId] {
|
||||
let renderer = MessageStoryRenderer(context: strongSelf.currentContext, messages: messages)
|
||||
|
||||
let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)
|
||||
renderer.update(layout: layout) { image in
|
||||
if let data = image?.pngData() {
|
||||
let pasteboardItems: [[String: Any]] = [["com.instagram.sharedSticker.backgroundImage": data,
|
||||
"com.instagram.sharedSticker.contentURL": "https://t.me/\(peer.addressName ?? "")/\(message.id.id)"]]
|
||||
if #available(iOS 10.0, *) {
|
||||
UIPasteboard.general.setItems(pasteboardItems, options: [.expirationDate: Date().addingTimeInterval(5 * 60)])
|
||||
} else {
|
||||
// UIPasteboard.general.setItems(pasteboardItems)
|
||||
}
|
||||
strongSelf.sharedContext.applicationBindings.openUrl("instagram-stories://share")
|
||||
}
|
||||
}
|
||||
|
||||
return .complete()
|
||||
}
|
||||
|
||||
|
||||
var collectableItems: [CollectableExternalShareItem] = []
|
||||
switch strongSelf.subject {
|
||||
case let .url(text):
|
||||
@ -865,3 +886,137 @@ public final class ShareController: ViewController {
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MessageStoryRenderer {
|
||||
private let context: AccountContext
|
||||
private let presentationData: PresentationData
|
||||
private let messages: [Message]
|
||||
|
||||
let containerNode: ASDisplayNode
|
||||
private let instantChatBackgroundNode: WallpaperBackgroundNode
|
||||
private let messagesContainerNode: ASDisplayNode
|
||||
private var dateHeaderNode: ListViewItemHeaderNode?
|
||||
private var messageNodes: [ListViewItemNode]?
|
||||
private let addressNode: ImmediateTextNode
|
||||
|
||||
init(context: AccountContext, messages: [Message]) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.messages = messages
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.instantChatBackgroundNode = WallpaperBackgroundNode()
|
||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings()), mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
self.messagesContainerNode.clipsToBounds = true
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
let message = messages.first!
|
||||
let addressName = message.peers[message.id.peerId]?.addressName ?? ""
|
||||
|
||||
self.addressNode = ImmediateTextNode()
|
||||
self.addressNode.displaysAsynchronously = false
|
||||
self.addressNode.attributedText = NSAttributedString(string: "t.me/\(addressName)/\(message.id.id)", font: Font.medium(14.0), textColor: UIColor(rgb: 0xa8b7c4))
|
||||
// self.addressNode.textShadowColor = .black
|
||||
|
||||
self.containerNode.addSubnode(self.instantChatBackgroundNode)
|
||||
self.containerNode.addSubnode(self.messagesContainerNode)
|
||||
self.containerNode.addSubnode(self.addressNode)
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, completion: @escaping (UIImage?) -> Void) {
|
||||
self.updateMessagesLayout(layout: layout)
|
||||
|
||||
Queue.mainQueue().after(0.01) {
|
||||
UIGraphicsBeginImageContextWithOptions(layout.size, false, 3.0)
|
||||
self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: layout.size), afterScreenUpdates: true)
|
||||
let img = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
completion(img)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateMessagesLayout(layout: ContainerViewLayout) {
|
||||
let size = layout.size
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.instantChatBackgroundNode.updateLayout(size: size, transition: .immediate)
|
||||
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
let addressLayout = self.addressNode.updateLayout(size)
|
||||
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.messages.first?.timestamp ?? 0, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
|
||||
|
||||
var items: [ListViewItem] = []
|
||||
let sampleMessages: [Message] = self.messages
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
}
|
||||
|
||||
let inset: CGFloat = 16.0
|
||||
let width = layout.size.width - inset * 2.0
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
||||
if let messageNodes = self.messageNodes {
|
||||
for i in 0 ..< items.count {
|
||||
let itemNode = messageNodes[i]
|
||||
items[i].updateNode(async: { $0() }, node: {
|
||||
return itemNode
|
||||
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
|
||||
let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - layout.size.height) / 2.0)), size: CGSize(width: width, height: layout.size.height))
|
||||
|
||||
itemNode.contentSize = layout.contentSize
|
||||
itemNode.insets = layout.insets
|
||||
itemNode.frame = nodeFrame
|
||||
itemNode.isUserInteractionEnabled = false
|
||||
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
var messageNodes: [ListViewItemNode] = []
|
||||
for i in 0 ..< items.count {
|
||||
var itemNode: ListViewItemNode?
|
||||
items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
|
||||
itemNode = node
|
||||
apply().1(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
itemNode!.isUserInteractionEnabled = false
|
||||
messageNodes.append(itemNode!)
|
||||
self.messagesContainerNode.addSubnode(itemNode!)
|
||||
}
|
||||
self.messageNodes = messageNodes
|
||||
}
|
||||
|
||||
var bottomOffset: CGFloat = 0.0
|
||||
if let messageNodes = self.messageNodes {
|
||||
for itemNode in messageNodes {
|
||||
itemNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - itemNode.frame.height) / 2.0)), size: itemNode.frame.size)
|
||||
bottomOffset += itemNode.frame.maxY
|
||||
itemNode.updateFrame(itemNode.frame, within: layout.size)
|
||||
}
|
||||
}
|
||||
|
||||
self.addressNode.frame = CGRect(origin: CGPoint(x: inset + 16.0, y: bottomOffset + 3.0), size: CGSize(width: addressLayout.width, height: addressLayout.height + 3.0))
|
||||
|
||||
let dateHeaderNode: ListViewItemHeaderNode
|
||||
if let currentDateHeaderNode = self.dateHeaderNode {
|
||||
dateHeaderNode = currentDateHeaderNode
|
||||
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
|
||||
} else {
|
||||
dateHeaderNode = headerItem.node()
|
||||
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
self.messagesContainerNode.addSubnode(dateHeaderNode)
|
||||
self.dateHeaderNode = dateHeaderNode
|
||||
}
|
||||
|
||||
dateHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height))
|
||||
dateHeaderNode.updateLayout(size: self.containerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import Postbox
|
||||
public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
|
||||
case text
|
||||
case url(String)
|
||||
case callback(MemoryBuffer)
|
||||
case callback(requiresPassword: Bool, data: MemoryBuffer)
|
||||
case requestPhone
|
||||
case requestMap
|
||||
case switchInline(samePeer: Bool, query: String)
|
||||
@ -19,7 +19,7 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
|
||||
case 1:
|
||||
self = .url(decoder.decodeStringForKey("u", orElse: ""))
|
||||
case 2:
|
||||
self = .callback(decoder.decodeBytesForKey("d") ?? MemoryBuffer())
|
||||
self = .callback(requiresPassword: decoder.decodeInt32ForKey("p", orElse: 0) != 0, data: decoder.decodeBytesForKey("d") ?? MemoryBuffer())
|
||||
case 3:
|
||||
self = .requestPhone
|
||||
case 4:
|
||||
@ -46,8 +46,9 @@ public enum ReplyMarkupButtonAction: PostboxCoding, Equatable {
|
||||
case let .url(url):
|
||||
encoder.encodeInt32(1, forKey: "v")
|
||||
encoder.encodeString(url, forKey: "u")
|
||||
case let .callback(data):
|
||||
case let .callback(requiresPassword, data):
|
||||
encoder.encodeInt32(2, forKey: "v")
|
||||
encoder.encodeInt32(requiresPassword ? 1 : 0, forKey: "p")
|
||||
encoder.encodeBytes(data, forKey: "d")
|
||||
case .requestPhone:
|
||||
encoder.encodeInt32(3, forKey: "v")
|
||||
|
@ -12,79 +12,7 @@ import MediaResources
|
||||
import MobileCoreServices
|
||||
import MediaResources
|
||||
import YuvConversion
|
||||
|
||||
let colorKeyRegex = try? NSRegularExpression(pattern: "\"k\":\\[[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\,[\\d\\.]+\\]")
|
||||
|
||||
private func transformedWithFitzModifier(data: Data, fitzModifier: EmojiFitzModifier?) -> Data {
|
||||
if let fitzModifier = fitzModifier, var string = String(data: data, encoding: .utf8) {
|
||||
var colors: [UIColor] = [0xf77e41, 0xffb139, 0xffd140, 0xffdf79].map { UIColor(rgb: $0) }
|
||||
let replacementColors: [UIColor]
|
||||
switch fitzModifier {
|
||||
case .type12:
|
||||
replacementColors = [0xca907a, 0xedc5a5, 0xf7e3c3, 0xfbefd6].map { UIColor(rgb: $0) }
|
||||
case .type3:
|
||||
replacementColors = [0xaa7c60, 0xc8a987, 0xddc89f, 0xe6d6b2].map { UIColor(rgb: $0) }
|
||||
case .type4:
|
||||
replacementColors = [0x8c6148, 0xad8562, 0xc49e76, 0xd4b188].map { UIColor(rgb: $0) }
|
||||
case .type5:
|
||||
replacementColors = [0x6e3c2c, 0x925a34, 0xa16e46, 0xac7a52].map { UIColor(rgb: $0) }
|
||||
case .type6:
|
||||
replacementColors = [0x291c12, 0x472a22, 0x573b30, 0x68493c].map { UIColor(rgb: $0) }
|
||||
}
|
||||
|
||||
func colorToString(_ color: UIColor) -> String {
|
||||
var r: CGFloat = 0.0
|
||||
var g: CGFloat = 0.0
|
||||
var b: CGFloat = 0.0
|
||||
if color.getRed(&r, green: &g, blue: &b, alpha: nil) {
|
||||
return "\"k\":[\(r),\(g),\(b),1]"
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func match(_ a: Double, _ b: Double, eps: Double) -> Bool {
|
||||
return abs(a - b) < eps
|
||||
}
|
||||
|
||||
var replacements: [(NSTextCheckingResult, String)] = []
|
||||
|
||||
if let colorKeyRegex = colorKeyRegex {
|
||||
let results = colorKeyRegex.matches(in: string, range: NSRange(string.startIndex..., in: string))
|
||||
for result in results.reversed() {
|
||||
if let range = Range(result.range, in: string) {
|
||||
let substring = String(string[range])
|
||||
let color = substring[substring.index(string.startIndex, offsetBy: "\"k\":[".count) ..< substring.index(before: substring.endIndex)]
|
||||
let components = color.split(separator: ",")
|
||||
if components.count == 4, let r = Double(components[0]), let g = Double(components[1]), let b = Double(components[2]), let a = Double(components[3]) {
|
||||
if match(a, 1.0, eps: 0.01) {
|
||||
for i in 0 ..< colors.count {
|
||||
let color = colors[i]
|
||||
var cr: CGFloat = 0.0
|
||||
var cg: CGFloat = 0.0
|
||||
var cb: CGFloat = 0.0
|
||||
if color.getRed(&cr, green: &cg, blue: &cb, alpha: nil) {
|
||||
if match(r, Double(cr), eps: 0.01) && match(g, Double(cg), eps: 0.01) && match(b, Double(cb), eps: 0.01) {
|
||||
replacements.append((result, colorToString(replacementColors[i])))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (result, text) in replacements {
|
||||
if let range = Range(result.range, in: string) {
|
||||
string = string.replacingCharacters(in: range, with: text)
|
||||
}
|
||||
}
|
||||
|
||||
return string.data(using: .utf8) ?? data
|
||||
} else {
|
||||
return data
|
||||
}
|
||||
}
|
||||
import AnimatedStickerNode
|
||||
|
||||
public func fetchCompressedLottieFirstFrameAJpeg(data: Data, size: CGSize, fitzModifier: EmojiFitzModifier? = nil, cacheKey: String) -> Signal<TempBoxFile, NoError> {
|
||||
return Signal({ subscriber in
|
||||
|
@ -255,6 +255,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[889491791] = { return Api.Update.parse_updateDialogFilters($0) }
|
||||
dict[643940105] = { return Api.Update.parse_updatePhoneCallSignalingData($0) }
|
||||
dict[1708307556] = { return Api.Update.parse_updateChannelParticipant($0) }
|
||||
dict[1854571743] = { return Api.Update.parse_updateChannelMessageForwards($0) }
|
||||
dict[136574537] = { return Api.messages.VotesList.parse_votesList($0) }
|
||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||
dict[-373643672] = { return Api.FolderPeer.parse_folderPeer($0) }
|
||||
@ -273,7 +274,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-994444869] = { return Api.Error.parse_error($0) }
|
||||
dict[-1560655744] = { return Api.KeyboardButton.parse_keyboardButton($0) }
|
||||
dict[629866245] = { return Api.KeyboardButton.parse_keyboardButtonUrl($0) }
|
||||
dict[1748655686] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
|
||||
dict[-1318425559] = { return Api.KeyboardButton.parse_keyboardButtonRequestPhone($0) }
|
||||
dict[-59151553] = { return Api.KeyboardButton.parse_keyboardButtonRequestGeoLocation($0) }
|
||||
dict[90744648] = { return Api.KeyboardButton.parse_keyboardButtonSwitchInline($0) }
|
||||
@ -282,6 +282,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[280464681] = { return Api.KeyboardButton.parse_keyboardButtonUrlAuth($0) }
|
||||
dict[-802258988] = { return Api.KeyboardButton.parse_inputKeyboardButtonUrlAuth($0) }
|
||||
dict[-1144565411] = { return Api.KeyboardButton.parse_keyboardButtonRequestPoll($0) }
|
||||
dict[901503851] = { return Api.KeyboardButton.parse_keyboardButtonCallback($0) }
|
||||
dict[-748155807] = { return Api.ContactStatus.parse_contactStatus($0) }
|
||||
dict[1679398724] = { return Api.SecureFile.parse_secureFileEmpty($0) }
|
||||
dict[-534283678] = { return Api.SecureFile.parse_secureFile($0) }
|
||||
@ -387,6 +388,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1694474197] = { return Api.messages.Chats.parse_chats($0) }
|
||||
dict[-1663561404] = { return Api.messages.Chats.parse_chatsSlice($0) }
|
||||
dict[482797855] = { return Api.InputSingleMedia.parse_inputSingleMedia($0) }
|
||||
dict[1831138451] = { return Api.MessageViews.parse_messageViews($0) }
|
||||
dict[218751099] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowContacts($0) }
|
||||
dict[407582158] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowAll($0) }
|
||||
dict[320652927] = { return Api.InputPrivacyRule.parse_inputPrivacyValueAllowUsers($0) }
|
||||
@ -447,6 +449,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) }
|
||||
dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) }
|
||||
dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) }
|
||||
dict[-77073091] = { return Api.help.Country.parse_country($0) }
|
||||
dict[418631927] = { return Api.StatsGroupTopPoster.parse_statsGroupTopPoster($0) }
|
||||
dict[-2128640689] = { return Api.account.SentEmailCode.parse_sentEmailCode($0) }
|
||||
dict[-1038136962] = { return Api.EncryptedFile.parse_encryptedFileEmpty($0) }
|
||||
@ -519,6 +522,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-914167110] = { return Api.CdnPublicKey.parse_cdnPublicKey($0) }
|
||||
dict[53231223] = { return Api.InputGame.parse_inputGameID($0) }
|
||||
dict[-1020139510] = { return Api.InputGame.parse_inputGameShortName($0) }
|
||||
dict[1107543535] = { return Api.help.CountryCode.parse_countryCode($0) }
|
||||
dict[-1502174430] = { return Api.InputMessage.parse_inputMessageID($0) }
|
||||
dict[-1160215659] = { return Api.InputMessage.parse_inputMessageReplyTo($0) }
|
||||
dict[-2037963464] = { return Api.InputMessage.parse_inputMessagePinned($0) }
|
||||
@ -598,7 +602,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1820043071] = { return Api.User.parse_user($0) }
|
||||
dict[-2082087340] = { return Api.Message.parse_messageEmpty($0) }
|
||||
dict[-1642487306] = { return Api.Message.parse_messageService($0) }
|
||||
dict[1160515173] = { return Api.Message.parse_message($0) }
|
||||
dict[-181507201] = { return Api.Message.parse_message($0) }
|
||||
dict[831924812] = { return Api.StatsGroupTopInviter.parse_statsGroupTopInviter($0) }
|
||||
dict[186120336] = { return Api.messages.RecentStickers.parse_recentStickersNotModified($0) }
|
||||
dict[586395571] = { return Api.messages.RecentStickers.parse_recentStickers($0) }
|
||||
@ -683,6 +687,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[364538944] = { return Api.messages.Dialogs.parse_dialogs($0) }
|
||||
dict[1910543603] = { return Api.messages.Dialogs.parse_dialogsSlice($0) }
|
||||
dict[-253500010] = { return Api.messages.Dialogs.parse_dialogsNotModified($0) }
|
||||
dict[-1986399595] = { return Api.stats.MessageStats.parse_messageStats($0) }
|
||||
dict[-709641735] = { return Api.EmojiKeyword.parse_emojiKeyword($0) }
|
||||
dict[594408994] = { return Api.EmojiKeyword.parse_emojiKeywordDeleted($0) }
|
||||
dict[-290921362] = { return Api.upload.CdnFile.parse_cdnFileReuploadNeeded($0) }
|
||||
@ -789,6 +794,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1041346555] = { return Api.updates.ChannelDifference.parse_channelDifferenceEmpty($0) }
|
||||
dict[543450958] = { return Api.updates.ChannelDifference.parse_channelDifference($0) }
|
||||
dict[-1531132162] = { return Api.updates.ChannelDifference.parse_channelDifferenceTooLong($0) }
|
||||
dict[-1815339214] = { return Api.help.CountriesList.parse_countriesListNotModified($0) }
|
||||
dict[-2016381538] = { return Api.help.CountriesList.parse_countriesList($0) }
|
||||
dict[-309659827] = { return Api.channels.AdminLogResults.parse_adminLogResults($0) }
|
||||
dict[-264117680] = { return Api.ChatOnlines.parse_chatOnlines($0) }
|
||||
dict[488313413] = { return Api.InputAppEvent.parse_inputAppEvent($0) }
|
||||
@ -1106,6 +1113,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputSingleMedia:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageViews:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputPrivacyRule:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.DhConfig:
|
||||
@ -1148,6 +1157,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.DraftMessage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.Country:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.StatsGroupTopPoster:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.account.SentEmailCode:
|
||||
@ -1226,6 +1237,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputGame:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.CountryCode:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.InputMessage:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.PhoneCallProtocol:
|
||||
@ -1366,6 +1379,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.messages.Dialogs:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.stats.MessageStats:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.EmojiKeyword:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.upload.CdnFile:
|
||||
@ -1442,6 +1457,8 @@ public struct Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.updates.ChannelDifference:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.help.CountriesList:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.channels.AdminLogResults:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.ChatOnlines:
|
||||
|
@ -6038,6 +6038,7 @@ public extension Api {
|
||||
case updateDialogFilters
|
||||
case updatePhoneCallSignalingData(phoneCallId: Int64, data: Buffer)
|
||||
case updateChannelParticipant(flags: Int32, channelId: Int32, date: Int32, userId: Int32, prevParticipant: Api.ChannelParticipant?, newParticipant: Api.ChannelParticipant?, qts: Int32)
|
||||
case updateChannelMessageForwards(channelId: Int32, id: Int32, forwards: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -6730,6 +6731,14 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 1) != 0 {newParticipant!.serialize(buffer, true)}
|
||||
serializeInt32(qts, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .updateChannelMessageForwards(let channelId, let id, let forwards):
|
||||
if boxed {
|
||||
buffer.appendInt32(1854571743)
|
||||
}
|
||||
serializeInt32(channelId, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
serializeInt32(forwards, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -6899,6 +6908,8 @@ public extension Api {
|
||||
return ("updatePhoneCallSignalingData", [("phoneCallId", phoneCallId), ("data", data)])
|
||||
case .updateChannelParticipant(let flags, let channelId, let date, let userId, let prevParticipant, let newParticipant, let qts):
|
||||
return ("updateChannelParticipant", [("flags", flags), ("channelId", channelId), ("date", date), ("userId", userId), ("prevParticipant", prevParticipant), ("newParticipant", newParticipant), ("qts", qts)])
|
||||
case .updateChannelMessageForwards(let channelId, let id, let forwards):
|
||||
return ("updateChannelMessageForwards", [("channelId", channelId), ("id", id), ("forwards", forwards)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -8277,6 +8288,23 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateChannelMessageForwards(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Int32?
|
||||
_3 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateChannelMessageForwards(channelId: _1!, id: _2!, forwards: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum PopularContact: TypeConstructorDescription {
|
||||
@ -8732,7 +8760,6 @@ public extension Api {
|
||||
public enum KeyboardButton: TypeConstructorDescription {
|
||||
case keyboardButton(text: String)
|
||||
case keyboardButtonUrl(text: String, url: String)
|
||||
case keyboardButtonCallback(text: String, data: Buffer)
|
||||
case keyboardButtonRequestPhone(text: String)
|
||||
case keyboardButtonRequestGeoLocation(text: String)
|
||||
case keyboardButtonSwitchInline(flags: Int32, text: String, query: String)
|
||||
@ -8741,6 +8768,7 @@ public extension Api {
|
||||
case keyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, buttonId: Int32)
|
||||
case inputKeyboardButtonUrlAuth(flags: Int32, text: String, fwdText: String?, url: String, bot: Api.InputUser)
|
||||
case keyboardButtonRequestPoll(flags: Int32, quiz: Api.Bool?, text: String)
|
||||
case keyboardButtonCallback(flags: Int32, text: String, data: Buffer)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -8757,13 +8785,6 @@ public extension Api {
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
serializeString(url, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .keyboardButtonCallback(let text, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1748655686)
|
||||
}
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .keyboardButtonRequestPhone(let text):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1318425559)
|
||||
@ -8824,6 +8845,14 @@ public extension Api {
|
||||
if Int(flags) & Int(1 << 0) != 0 {quiz!.serialize(buffer, true)}
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .keyboardButtonCallback(let flags, let text, let data):
|
||||
if boxed {
|
||||
buffer.appendInt32(901503851)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(text, buffer: buffer, boxed: false)
|
||||
serializeBytes(data, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -8833,8 +8862,6 @@ public extension Api {
|
||||
return ("keyboardButton", [("text", text)])
|
||||
case .keyboardButtonUrl(let text, let url):
|
||||
return ("keyboardButtonUrl", [("text", text), ("url", url)])
|
||||
case .keyboardButtonCallback(let text, let data):
|
||||
return ("keyboardButtonCallback", [("text", text), ("data", data)])
|
||||
case .keyboardButtonRequestPhone(let text):
|
||||
return ("keyboardButtonRequestPhone", [("text", text)])
|
||||
case .keyboardButtonRequestGeoLocation(let text):
|
||||
@ -8851,6 +8878,8 @@ public extension Api {
|
||||
return ("inputKeyboardButtonUrlAuth", [("flags", flags), ("text", text), ("fwdText", fwdText), ("url", url), ("bot", bot)])
|
||||
case .keyboardButtonRequestPoll(let flags, let quiz, let text):
|
||||
return ("keyboardButtonRequestPoll", [("flags", flags), ("quiz", quiz), ("text", text)])
|
||||
case .keyboardButtonCallback(let flags, let text, let data):
|
||||
return ("keyboardButtonCallback", [("flags", flags), ("text", text), ("data", data)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -8879,20 +8908,6 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_keyboardButtonCallback(_ reader: BufferReader) -> KeyboardButton? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: Buffer?
|
||||
_2 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.KeyboardButton.keyboardButtonCallback(text: _1!, data: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_keyboardButtonRequestPhone(_ reader: BufferReader) -> KeyboardButton? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
@ -9021,6 +9036,23 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_keyboardButtonCallback(_ reader: BufferReader) -> KeyboardButton? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: Buffer?
|
||||
_3 = parseBytes(reader)
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.KeyboardButton.keyboardButtonCallback(flags: _1!, text: _2!, data: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum ContactStatus: TypeConstructorDescription {
|
||||
@ -11718,6 +11750,44 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum MessageViews: TypeConstructorDescription {
|
||||
case messageViews(views: Int32, forwards: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageViews(let views, let forwards):
|
||||
if boxed {
|
||||
buffer.appendInt32(1831138451)
|
||||
}
|
||||
serializeInt32(views, buffer: buffer, boxed: false)
|
||||
serializeInt32(forwards, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageViews(let views, let forwards):
|
||||
return ("messageViews", [("views", views), ("forwards", forwards)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageViews(_ reader: BufferReader) -> MessageViews? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.MessageViews.messageViews(views: _1!, forwards: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum InputPrivacyRule: TypeConstructorDescription {
|
||||
case inputPrivacyValueAllowContacts
|
||||
@ -17092,7 +17162,7 @@ public extension Api {
|
||||
public enum Message: TypeConstructorDescription {
|
||||
case messageEmpty(id: Int32)
|
||||
case messageService(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, replyToMsgId: Int32?, date: Int32, action: Api.MessageAction)
|
||||
case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?)
|
||||
case message(flags: Int32, id: Int32, fromId: Int32?, toId: Api.Peer, fwdFrom: Api.MessageFwdHeader?, viaBotId: Int32?, replyToMsgId: Int32?, date: Int32, message: String, media: Api.MessageMedia?, replyMarkup: Api.ReplyMarkup?, entities: [Api.MessageEntity]?, views: Int32?, forwards: Int32?, editDate: Int32?, postAuthor: String?, groupedId: Int64?, restrictionReason: [Api.RestrictionReason]?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
@ -17114,9 +17184,9 @@ public extension Api {
|
||||
serializeInt32(date, buffer: buffer, boxed: false)
|
||||
action.serialize(buffer, true)
|
||||
break
|
||||
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason):
|
||||
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let editDate, let postAuthor, let groupedId, let restrictionReason):
|
||||
if boxed {
|
||||
buffer.appendInt32(1160515173)
|
||||
buffer.appendInt32(-181507201)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
@ -17135,6 +17205,7 @@ public extension Api {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(views!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(forwards!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 15) != 0 {serializeInt32(editDate!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 16) != 0 {serializeString(postAuthor!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 17) != 0 {serializeInt64(groupedId!, buffer: buffer, boxed: false)}
|
||||
@ -17153,8 +17224,8 @@ public extension Api {
|
||||
return ("messageEmpty", [("id", id)])
|
||||
case .messageService(let flags, let id, let fromId, let toId, let replyToMsgId, let date, let action):
|
||||
return ("messageService", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("replyToMsgId", replyToMsgId), ("date", date), ("action", action)])
|
||||
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let editDate, let postAuthor, let groupedId, let restrictionReason):
|
||||
return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)])
|
||||
case .message(let flags, let id, let fromId, let toId, let fwdFrom, let viaBotId, let replyToMsgId, let date, let message, let media, let replyMarkup, let entities, let views, let forwards, let editDate, let postAuthor, let groupedId, let restrictionReason):
|
||||
return ("message", [("flags", flags), ("id", id), ("fromId", fromId), ("toId", toId), ("fwdFrom", fwdFrom), ("viaBotId", viaBotId), ("replyToMsgId", replyToMsgId), ("date", date), ("message", message), ("media", media), ("replyMarkup", replyMarkup), ("entities", entities), ("views", views), ("forwards", forwards), ("editDate", editDate), ("postAuthor", postAuthor), ("groupedId", groupedId), ("restrictionReason", restrictionReason)])
|
||||
}
|
||||
}
|
||||
|
||||
@ -17240,14 +17311,16 @@ public extension Api {
|
||||
var _13: Int32?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {_13 = reader.readInt32() }
|
||||
var _14: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_14 = reader.readInt32() }
|
||||
var _15: String?
|
||||
if Int(_1!) & Int(1 << 16) != 0 {_15 = parseString(reader) }
|
||||
var _16: Int64?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {_16 = reader.readInt64() }
|
||||
var _17: [Api.RestrictionReason]?
|
||||
if Int(_1!) & Int(1 << 10) != 0 {_14 = reader.readInt32() }
|
||||
var _15: Int32?
|
||||
if Int(_1!) & Int(1 << 15) != 0 {_15 = reader.readInt32() }
|
||||
var _16: String?
|
||||
if Int(_1!) & Int(1 << 16) != 0 {_16 = parseString(reader) }
|
||||
var _17: Int64?
|
||||
if Int(_1!) & Int(1 << 17) != 0 {_17 = reader.readInt64() }
|
||||
var _18: [Api.RestrictionReason]?
|
||||
if Int(_1!) & Int(1 << 22) != 0 {if let _ = reader.readInt32() {
|
||||
_17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
|
||||
_18 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RestrictionReason.self)
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
@ -17262,12 +17335,13 @@ public extension Api {
|
||||
let _c11 = (Int(_1!) & Int(1 << 6) == 0) || _11 != nil
|
||||
let _c12 = (Int(_1!) & Int(1 << 7) == 0) || _12 != nil
|
||||
let _c13 = (Int(_1!) & Int(1 << 10) == 0) || _13 != nil
|
||||
let _c14 = (Int(_1!) & Int(1 << 15) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 16) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 17) == 0) || _16 != nil
|
||||
let _c17 = (Int(_1!) & Int(1 << 22) == 0) || _17 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 {
|
||||
return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, editDate: _14, postAuthor: _15, groupedId: _16, restrictionReason: _17)
|
||||
let _c14 = (Int(_1!) & Int(1 << 10) == 0) || _14 != nil
|
||||
let _c15 = (Int(_1!) & Int(1 << 15) == 0) || _15 != nil
|
||||
let _c16 = (Int(_1!) & Int(1 << 16) == 0) || _16 != nil
|
||||
let _c17 = (Int(_1!) & Int(1 << 17) == 0) || _17 != nil
|
||||
let _c18 = (Int(_1!) & Int(1 << 22) == 0) || _18 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 {
|
||||
return Api.Message.message(flags: _1!, id: _2!, fromId: _3, toId: _4!, fwdFrom: _5, viaBotId: _6, replyToMsgId: _7, date: _8!, message: _9!, media: _10, replyMarkup: _11, entities: _12, views: _13, forwards: _14, editDate: _15, postAuthor: _16, groupedId: _17, restrictionReason: _18)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -810,6 +810,42 @@ public struct stats {
|
||||
}
|
||||
|
||||
}
|
||||
public enum MessageStats: TypeConstructorDescription {
|
||||
case messageStats(viewsGraph: Api.StatsGraph)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageStats(let viewsGraph):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1986399595)
|
||||
}
|
||||
viewsGraph.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageStats(let viewsGraph):
|
||||
return ("messageStats", [("viewsGraph", viewsGraph)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? {
|
||||
var _1: Api.StatsGraph?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StatsGraph
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.stats.MessageStats.messageStats(viewsGraph: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
@ -1965,6 +2001,58 @@ public struct help {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum Country: TypeConstructorDescription {
|
||||
case country(iso2: String, defaultName: String, name: String, countryCodes: [Api.help.CountryCode])
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .country(let iso2, let defaultName, let name, let countryCodes):
|
||||
if boxed {
|
||||
buffer.appendInt32(-77073091)
|
||||
}
|
||||
serializeString(iso2, buffer: buffer, boxed: false)
|
||||
serializeString(defaultName, buffer: buffer, boxed: false)
|
||||
serializeString(name, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(countryCodes.count))
|
||||
for item in countryCodes {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .country(let iso2, let defaultName, let name, let countryCodes):
|
||||
return ("country", [("iso2", iso2), ("defaultName", defaultName), ("name", name), ("countryCodes", countryCodes)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_country(_ reader: BufferReader) -> Country? {
|
||||
var _1: String?
|
||||
_1 = parseString(reader)
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: String?
|
||||
_3 = parseString(reader)
|
||||
var _4: [Api.help.CountryCode]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.CountryCode.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.help.Country.country(iso2: _1!, defaultName: _2!, name: _3!, countryCodes: _4!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum PromoData: TypeConstructorDescription {
|
||||
case promoDataEmpty(expires: Int32)
|
||||
@ -2117,6 +2205,64 @@ public struct help {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum CountryCode: TypeConstructorDescription {
|
||||
case countryCode(flags: Int32, countryCode: String, prefixes: [String]?, patterns: [String]?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .countryCode(let flags, let countryCode, let prefixes, let patterns):
|
||||
if boxed {
|
||||
buffer.appendInt32(1107543535)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(countryCode, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(prefixes!.count))
|
||||
for item in prefixes! {
|
||||
serializeString(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(patterns!.count))
|
||||
for item in patterns! {
|
||||
serializeString(item, buffer: buffer, boxed: false)
|
||||
}}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .countryCode(let flags, let countryCode, let prefixes, let patterns):
|
||||
return ("countryCode", [("flags", flags), ("countryCode", countryCode), ("prefixes", prefixes), ("patterns", patterns)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_countryCode(_ reader: BufferReader) -> CountryCode? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: String?
|
||||
_2 = parseString(reader)
|
||||
var _3: [String]?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
||||
} }
|
||||
var _4: [String]?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: -1255641564, elementType: String.self)
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.help.CountryCode.countryCode(flags: _1!, countryCode: _2!, prefixes: _3, patterns: _4)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public enum Support: TypeConstructorDescription {
|
||||
case support(phoneNumber: String, user: Api.User)
|
||||
@ -2316,5 +2462,61 @@ public struct help {
|
||||
}
|
||||
|
||||
}
|
||||
public enum CountriesList: TypeConstructorDescription {
|
||||
case countriesListNotModified
|
||||
case countriesList(countries: [Api.help.Country], hash: Int32)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .countriesListNotModified:
|
||||
if boxed {
|
||||
buffer.appendInt32(-1815339214)
|
||||
}
|
||||
|
||||
break
|
||||
case .countriesList(let countries, let hash):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2016381538)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(countries.count))
|
||||
for item in countries {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
serializeInt32(hash, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .countriesListNotModified:
|
||||
return ("countriesListNotModified", [])
|
||||
case .countriesList(let countries, let hash):
|
||||
return ("countriesList", [("countries", countries), ("hash", hash)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_countriesListNotModified(_ reader: BufferReader) -> CountriesList? {
|
||||
return Api.help.CountriesList.countriesListNotModified
|
||||
}
|
||||
public static func parse_countriesList(_ reader: BufferReader) -> CountriesList? {
|
||||
var _1: [Api.help.Country]?
|
||||
if let _ = reader.readInt32() {
|
||||
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.help.Country.self)
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.help.CountriesList.countriesList(countries: _1!, hash: _2!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2243,26 +2243,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func getMessagesViews(peer: Api.InputPeer, id: [Int32], increment: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-993483427)
|
||||
peer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
increment.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.getMessagesViews", parameters: [("peer", peer), ("id", id), ("increment", increment)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: [Int32]?
|
||||
if let _ = reader.readInt32() {
|
||||
result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func editChatAdmin(chatId: Int32, userId: Api.InputUser, isAdmin: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1444503762)
|
||||
@ -2429,23 +2409,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.BotCallbackAnswer>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2130010132)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("data", data)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.BotCallbackAnswer?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func setBotCallbackAnswer(flags: Int32, queryId: Int64, message: String?, url: String?, cacheTime: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-712043766)
|
||||
@ -3718,6 +3681,44 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getBotCallbackAnswer(flags: Int32, peer: Api.InputPeer, msgId: Int32, data: Buffer?, password: Api.InputCheckPasswordSRP?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.BotCallbackAnswer>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1824339449)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeBytes(data!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {password!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "messages.getBotCallbackAnswer", parameters: [("flags", flags), ("peer", peer), ("msgId", msgId), ("data", data), ("password", password)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.BotCallbackAnswer? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.BotCallbackAnswer?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.BotCallbackAnswer
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getMessagesViews(peer: Api.InputPeer, id: [Int32], increment: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Api.MessageViews]>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-39035462)
|
||||
peer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
increment.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "messages.getMessagesViews", parameters: [("peer", peer), ("id", id), ("increment", increment)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Api.MessageViews]? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: [Api.MessageViews]?
|
||||
if let _ = reader.readInt32() {
|
||||
result = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageViews.self)
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct channels {
|
||||
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
@ -4450,6 +4451,41 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getMessagePublicForwards(channel: Api.InputChannel, msgId: Int32, offsetRate: Int32, offsetPeer: Api.InputPeer, offsetId: Int32, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.Messages>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1445996571)
|
||||
channel.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
serializeInt32(offsetRate, buffer: buffer, boxed: false)
|
||||
offsetPeer.serialize(buffer, true)
|
||||
serializeInt32(offsetId, buffer: buffer, boxed: false)
|
||||
serializeInt32(limit, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "stats.getMessagePublicForwards", parameters: [("channel", channel), ("msgId", msgId), ("offsetRate", offsetRate), ("offsetPeer", offsetPeer), ("offsetId", offsetId), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.Messages? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.messages.Messages?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.messages.Messages
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getMessageStats(flags: Int32, channel: Api.InputChannel, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.stats.MessageStats>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1226791947)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
channel.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "stats.getMessageStats", parameters: [("flags", flags), ("channel", channel), ("msgId", msgId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.stats.MessageStats? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.stats.MessageStats?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.stats.MessageStats
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct auth {
|
||||
public static func checkPhone(phoneNumber: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.CheckedPhone>) {
|
||||
@ -5369,34 +5405,6 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.help.PromoData>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1063816159)
|
||||
|
||||
return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.help.PromoData?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.help.PromoData
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(505748629)
|
||||
peer.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func editUserInfo(userId: Api.InputUser, message: String, entities: [Api.MessageEntity]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.help.UserInfo>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1723407216)
|
||||
@ -5431,6 +5439,34 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func getPromoData() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.help.PromoData>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1063816159)
|
||||
|
||||
return (FunctionDescription(name: "help.getPromoData", parameters: []), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.PromoData? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.help.PromoData?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.help.PromoData
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func hidePromoData(peer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(505748629)
|
||||
peer.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "help.hidePromoData", parameters: [("peer", peer)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func dismissSuggestion(suggestion: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(125807007)
|
||||
@ -5444,6 +5480,21 @@ public extension Api {
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
public static func getCountriesList(langCode: String, hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.help.CountriesList>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1935116200)
|
||||
serializeString(langCode, buffer: buffer, boxed: false)
|
||||
serializeInt32(hash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "help.getCountriesList", parameters: [("langCode", langCode), ("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.help.CountriesList? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.help.CountriesList?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.help.CountriesList
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public struct updates {
|
||||
public static func getState() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.updates.State>) {
|
||||
@ -6802,11 +6853,14 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.photos.Photo>) {
|
||||
public static func uploadProfilePhoto(flags: Int32, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.photos.Photo>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1926525996)
|
||||
id.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
|
||||
buffer.appendInt32(-1980559511)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", flags), ("file", file), ("video", video), ("videoStartTs", videoStartTs)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.photos.Photo?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -6816,14 +6870,11 @@ public extension Api {
|
||||
})
|
||||
}
|
||||
|
||||
public static func uploadProfilePhoto(flags: Int32, file: Api.InputFile?, video: Api.InputFile?, videoStartTs: Double?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.photos.Photo>) {
|
||||
public static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.photos.Photo>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1980559511)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {file!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {video!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeDouble(videoStartTs!, buffer: buffer, boxed: false)}
|
||||
return (FunctionDescription(name: "photos.uploadProfilePhoto", parameters: [("flags", flags), ("file", file), ("video", video), ("videoStartTs", videoStartTs)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
|
||||
buffer.appendInt32(1926525996)
|
||||
id.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "photos.updateProfilePhoto", parameters: [("id", id)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.photos.Photo? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.photos.Photo?
|
||||
if let signature = reader.readInt32() {
|
||||
|
@ -1272,6 +1272,10 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
|
||||
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (layout, navigationBarHeight)
|
||||
|
||||
if !self.hasVideoNodes {
|
||||
self.isUIHidden = false
|
||||
}
|
||||
|
||||
var isUIHidden = self.isUIHidden
|
||||
switch self.callState?.state {
|
||||
case .terminated, .terminating:
|
||||
|
@ -591,7 +591,7 @@ public final class AccountViewTracker {
|
||||
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
|
||||
return account.network.request(Api.functions.messages.getMessagesViews(peer: inputPeer, id: messageIds.map { $0.id }, increment: .boolTrue))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<[Int32]?, NoError> in
|
||||
|> `catch` { _ -> Signal<[Api.MessageViews]?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> mapToSignal { viewCounts -> Signal<Void, NoError> in
|
||||
@ -599,20 +599,7 @@ public final class AccountViewTracker {
|
||||
return account.postbox.transaction { transaction -> Void in
|
||||
for i in 0 ..< messageIds.count {
|
||||
if i < viewCounts.count {
|
||||
let views = viewCounts[i]
|
||||
do {
|
||||
transaction.updateMessage(messageIds[i], update: { currentMessage in
|
||||
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
|
||||
var attributes = currentMessage.attributes
|
||||
loop: for j in 0 ..< attributes.count {
|
||||
if let attribute = attributes[j] as? ViewCountMessageAttribute {
|
||||
attributes[j] = ViewCountMessageAttribute(count: max(attribute.count, Int(views)))
|
||||
}
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, 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: attributes, media: currentMessage.media))
|
||||
})
|
||||
}
|
||||
/*if case let .messageViews(views, forwards) = viewCounts[i] {
|
||||
if case let .messageViews(views, forwards) = viewCounts[i] {
|
||||
transaction.updateMessage(messageIds[i], update: { currentMessage in
|
||||
let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init)
|
||||
var attributes = currentMessage.attributes
|
||||
@ -626,7 +613,7 @@ public final class AccountViewTracker {
|
||||
}
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, 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: attributes, media: currentMessage.media))
|
||||
})
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ public func checkOwnershipTranfserAvailability(postbox: Postbox, network: Networ
|
||||
}
|
||||
|> mapToSignal { updates -> Signal<Never, ChannelOwnershipTransferError> in
|
||||
accountStateManager.addUpdates(updates)
|
||||
return.complete()
|
||||
return .complete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
72
submodules/TelegramCore/Sources/Countries.swift
Normal file
72
submodules/TelegramCore/Sources/Countries.swift
Normal file
@ -0,0 +1,72 @@
|
||||
import Foundation
|
||||
import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
|
||||
import SyncCore
|
||||
|
||||
public struct Country {
|
||||
public struct CountryCode {
|
||||
public let code: String
|
||||
public let prefixes: [String]
|
||||
public let patterns: [String]
|
||||
|
||||
public init(code: String, prefixes: [String], patterns: [String]) {
|
||||
self.code = code
|
||||
self.prefixes = prefixes
|
||||
self.patterns = patterns
|
||||
}
|
||||
}
|
||||
|
||||
public let code: String
|
||||
public let defaultName: String
|
||||
public let name: String
|
||||
public let countryCodes: [CountryCode]
|
||||
|
||||
public init(code: String, defaultName: String, name: String, countryCodes: [CountryCode]) {
|
||||
self.code = code
|
||||
self.defaultName = defaultName
|
||||
self.name = name
|
||||
self.countryCodes = countryCodes
|
||||
}
|
||||
}
|
||||
|
||||
public func getCountriesList(network: Network, langCode: String) -> Signal<[Country], NoError> {
|
||||
return network.request(Api.functions.help.getCountriesList(langCode: langCode, hash: 0))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.help.CountriesList?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result in
|
||||
if let result = result {
|
||||
switch result {
|
||||
case let .countriesList(apiCountries, hash):
|
||||
return apiCountries.map { Country(apiCountry: $0) }
|
||||
case .countriesListNotModified:
|
||||
return []
|
||||
}
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Country.CountryCode {
|
||||
init(apiCountryCode: Api.help.CountryCode) {
|
||||
switch apiCountryCode {
|
||||
case let .countryCode(_, countryCode, apiPrefixes, apiPatterns):
|
||||
let prefixes: [String] = apiPrefixes.flatMap { $0 } ?? []
|
||||
let patterns: [String] = apiPatterns.flatMap { $0 } ?? []
|
||||
self.init(code: countryCode, prefixes: prefixes, patterns: patterns)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Country {
|
||||
init(apiCountry: Api.help.Country) {
|
||||
switch apiCountry {
|
||||
case let .country(iso2, defaultName, name, countryCodes):
|
||||
self.init(code: iso2, defaultName: defaultName, name: name, countryCodes: countryCodes.map { Country.CountryCode(apiCountryCode: $0) })
|
||||
}
|
||||
}
|
||||
}
|
@ -9,11 +9,11 @@ extension ReplyMarkupButton {
|
||||
switch apiButton {
|
||||
case let .keyboardButton(text):
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .text)
|
||||
case let .keyboardButtonCallback(text, data):
|
||||
case let .keyboardButtonCallback(flags, text, data):
|
||||
let memory = malloc(data.size)!
|
||||
memcpy(memory, data.data, data.size)
|
||||
let dataBuffer = MemoryBuffer(memory: memory, capacity: data.size, length: data.size, freeWhenDone: true)
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .callback(dataBuffer))
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .callback(requiresPassword: (flags & (1 << 0)) != 0, data: dataBuffer))
|
||||
case let .keyboardButtonRequestGeoLocation(text):
|
||||
self.init(title: text, titleWhenForwarded: nil, action: .requestMap)
|
||||
case let .keyboardButtonRequestPhone(text):
|
||||
|
@ -3,8 +3,6 @@ import Postbox
|
||||
import SwiftSignalKit
|
||||
import TelegramApi
|
||||
import MtProtoKit
|
||||
|
||||
|
||||
import SyncCore
|
||||
|
||||
public enum MessageActionCallbackResult {
|
||||
@ -14,8 +12,69 @@ public enum MessageActionCallbackResult {
|
||||
case url(String)
|
||||
}
|
||||
|
||||
public func requestMessageActionCallback(account: Account, messageId: MessageId, isGame:Bool, data: MemoryBuffer?) -> Signal<MessageActionCallbackResult, NoError> {
|
||||
public enum MessageActionCallbackError {
|
||||
case generic
|
||||
case twoStepAuthMissing
|
||||
case twoStepAuthTooFresh(Int32)
|
||||
case authSessionTooFresh(Int32)
|
||||
case limitExceeded
|
||||
case requestPassword
|
||||
case invalidPassword
|
||||
case restricted
|
||||
case userBlocked
|
||||
}
|
||||
|
||||
public func requestMessageActionCallbackPasswordCheck(account: Account, messageId: MessageId, isGame: Bool, data: MemoryBuffer?) -> Signal<Never, MessageActionCallbackError> {
|
||||
return account.postbox.loadedPeerWithId(messageId.peerId)
|
||||
|> castError(MessageActionCallbackError.self)
|
||||
|> take(1)
|
||||
|> mapToSignal { peer in
|
||||
if let inputPeer = apiInputPeer(peer) {
|
||||
var flags: Int32 = 0
|
||||
var dataBuffer: Buffer?
|
||||
if let data = data {
|
||||
flags |= Int32(1 << 0)
|
||||
dataBuffer = Buffer(data: data.makeData())
|
||||
}
|
||||
if isGame {
|
||||
flags |= Int32(1 << 1)
|
||||
}
|
||||
|
||||
return account.network.request(Api.functions.messages.getBotCallbackAnswer(flags: flags, peer: inputPeer, msgId: messageId.id, data: dataBuffer, password: .inputCheckPasswordEmpty))
|
||||
|> mapError { error -> MessageActionCallbackError in
|
||||
if error.errorDescription == "PASSWORD_HASH_INVALID" {
|
||||
return .requestPassword
|
||||
} else if error.errorDescription == "PASSWORD_MISSING" {
|
||||
return .twoStepAuthMissing
|
||||
} else if error.errorDescription.hasPrefix("PASSWORD_TOO_FRESH_") {
|
||||
let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "PASSWORD_TOO_FRESH_".count)...])
|
||||
if let value = Int32(timeout) {
|
||||
return .twoStepAuthTooFresh(value)
|
||||
}
|
||||
} else if error.errorDescription.hasPrefix("SESSION_TOO_FRESH_") {
|
||||
let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "SESSION_TOO_FRESH_".count)...])
|
||||
if let value = Int32(timeout) {
|
||||
return .authSessionTooFresh(value)
|
||||
}
|
||||
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||
return .restricted
|
||||
} else if error.errorDescription == "USER_BLOCKED" {
|
||||
return .userBlocked
|
||||
}
|
||||
return .generic
|
||||
}
|
||||
|> mapToSignal { _ -> Signal<Never, MessageActionCallbackError> in
|
||||
return .complete()
|
||||
}
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func requestMessageActionCallback(account: Account, messageId: MessageId, isGame :Bool, password: String?, data: MemoryBuffer?) -> Signal<MessageActionCallbackResult, MessageActionCallbackError> {
|
||||
return account.postbox.loadedPeerWithId(messageId.peerId)
|
||||
|> castError(MessageActionCallbackError.self)
|
||||
|> take(1)
|
||||
|> mapToSignal { peer in
|
||||
if let inputPeer = apiInputPeer(peer) {
|
||||
@ -28,28 +87,79 @@ public func requestMessageActionCallback(account: Account, messageId: MessageId,
|
||||
if isGame {
|
||||
flags |= Int32(1 << 1)
|
||||
}
|
||||
return account.network.request(Api.functions.messages.getBotCallbackAnswer(flags: flags, peer: inputPeer, msgId: messageId.id, data: dataBuffer))
|
||||
|> map(Optional.init)
|
||||
|> `catch` { _ -> Signal<Api.messages.BotCallbackAnswer?, NoError> in
|
||||
return .single(nil)
|
||||
}
|
||||
|> map { result -> MessageActionCallbackResult in
|
||||
guard let result = result else {
|
||||
return .none
|
||||
|
||||
let checkPassword: Signal<Api.InputCheckPasswordSRP?, MessageActionCallbackError>
|
||||
if let password = password, !password.isEmpty {
|
||||
flags |= Int32(1 << 2)
|
||||
|
||||
checkPassword = twoStepAuthData(account.network)
|
||||
|> mapError { error -> MessageActionCallbackError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
} else {
|
||||
return .generic
|
||||
}
|
||||
}
|
||||
switch result {
|
||||
case let .botCallbackAnswer(flags, message, url, cacheTime):
|
||||
if let message = message {
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
return .alert(message)
|
||||
} else {
|
||||
return .toast(message)
|
||||
}
|
||||
} else if let url = url {
|
||||
return .url(url)
|
||||
} else {
|
||||
return .none
|
||||
|> mapToSignal { authData -> Signal<Api.InputCheckPasswordSRP?, MessageActionCallbackError> in
|
||||
if let currentPasswordDerivation = authData.currentPasswordDerivation, let srpSessionData = authData.srpSessionData {
|
||||
guard let kdfResult = passwordKDF(encryptionProvider: account.network.encryptionProvider, password: password, derivation: currentPasswordDerivation, srpSessionData: srpSessionData) else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
return .single(.inputCheckPasswordSRP(srpId: kdfResult.id, A: Buffer(data: kdfResult.A), M1: Buffer(data: kdfResult.M1)))
|
||||
} else {
|
||||
return .fail(.twoStepAuthMissing)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
checkPassword = .single(nil)
|
||||
}
|
||||
|
||||
return checkPassword
|
||||
|> mapToSignal { password -> Signal<MessageActionCallbackResult, MessageActionCallbackError> in
|
||||
return account.network.request(Api.functions.messages.getBotCallbackAnswer(flags: flags, peer: inputPeer, msgId: messageId.id, data: dataBuffer, password: password))
|
||||
|> map(Optional.init)
|
||||
|> mapError { error -> MessageActionCallbackError in
|
||||
if error.errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||
return .limitExceeded
|
||||
} else if error.errorDescription == "PASSWORD_HASH_INVALID" {
|
||||
return .invalidPassword
|
||||
} else if error.errorDescription == "PASSWORD_MISSING" {
|
||||
return .twoStepAuthMissing
|
||||
} else if error.errorDescription.hasPrefix("PASSWORD_TOO_FRESH_") {
|
||||
let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "PASSWORD_TOO_FRESH_".count)...])
|
||||
if let value = Int32(timeout) {
|
||||
return .twoStepAuthTooFresh(value)
|
||||
}
|
||||
} else if error.errorDescription.hasPrefix("SESSION_TOO_FRESH_") {
|
||||
let timeout = String(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "SESSION_TOO_FRESH_".count)...])
|
||||
if let value = Int32(timeout) {
|
||||
return .authSessionTooFresh(value)
|
||||
}
|
||||
} else if error.errorDescription == "USER_PRIVACY_RESTRICTED" {
|
||||
return .restricted
|
||||
} else if error.errorDescription == "USER_BLOCKED" {
|
||||
return .userBlocked
|
||||
}
|
||||
return .generic
|
||||
}
|
||||
|> map { result -> MessageActionCallbackResult in
|
||||
guard let result = result else {
|
||||
return .none
|
||||
}
|
||||
switch result {
|
||||
case let .botCallbackAnswer(flags, message, url, cacheTime):
|
||||
if let message = message {
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
return .alert(message)
|
||||
} else {
|
||||
return .toast(message)
|
||||
}
|
||||
} else if let url = url {
|
||||
return .url(url)
|
||||
} else {
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 117
|
||||
return 118
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
@ -136,7 +136,7 @@ func apiMessagePeerId(_ messsage: Api.Message) -> PeerId? {
|
||||
|
||||
func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
switch message {
|
||||
case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _):
|
||||
case let .message(flags, _, fromId, toId, fwdHeader, viaBotId, _, _, _, media, _, entities, _, _, _, _, _, _):
|
||||
let peerId: PeerId
|
||||
switch toId {
|
||||
case let .peerUser(userId):
|
||||
@ -240,7 +240,7 @@ func apiMessagePeerIds(_ message: Api.Message) -> [PeerId] {
|
||||
|
||||
func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? {
|
||||
switch message {
|
||||
case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .message(flags, _, fromId, toId, _, _, replyToMsgId, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if let replyToMsgId = replyToMsgId {
|
||||
let peerId: PeerId
|
||||
switch toId {
|
||||
@ -398,7 +398,7 @@ func messageTextEntitiesFromApiEntities(_ entities: [Api.MessageEntity]) -> [Mes
|
||||
extension StoreMessage {
|
||||
convenience init?(apiMessage: Api.Message, namespace: MessageId.Namespace = Namespaces.Message.Cloud) {
|
||||
switch apiMessage {
|
||||
case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, editDate, postAuthor, groupingId, restrictionReason):
|
||||
case let .message(flags, id, fromId, toId, fwdFrom, viaBotId, replyToMsgId, date, message, media, replyMarkup, entities, views, forwards, editDate, postAuthor, groupingId, restrictionReason):
|
||||
let peerId: PeerId
|
||||
var authorId: PeerId?
|
||||
switch toId {
|
||||
@ -517,13 +517,15 @@ extension StoreMessage {
|
||||
attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId)))
|
||||
}
|
||||
|
||||
if let views = views, namespace != Namespaces.Message.ScheduledCloud {
|
||||
attributes.append(ViewCountMessageAttribute(count: Int(views)))
|
||||
}
|
||||
if namespace != Namespaces.Message.ScheduledCloud {
|
||||
if let views = views {
|
||||
attributes.append(ViewCountMessageAttribute(count: Int(views)))
|
||||
}
|
||||
|
||||
/*if let forwards = forwards, namespace != Namespaces.Message.ScheduledCloud {
|
||||
attributes.append(ForwardCountMessageAttribute(count: Int(forwards)))
|
||||
}*/
|
||||
if let forwards = forwards {
|
||||
attributes.append(ForwardCountMessageAttribute(count: Int(forwards)))
|
||||
}
|
||||
}
|
||||
|
||||
if let editDate = editDate {
|
||||
attributes.append(EditedMessageAttribute(date: editDate, isHidden: (flags & (1 << 21)) != 0))
|
||||
|
@ -58,7 +58,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
||||
self.putNext(groups)
|
||||
}
|
||||
case let .updateShortChatMessage(flags, id, fromId, chatId, message, pts, ptsCount, date, fwdFrom, viaBotId, replyToMsgId, entities):
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: fromId, toId: Api.Peer.peerChat(chatId: chatId), fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
|
||||
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||
if groups.count != 0 {
|
||||
@ -75,7 +75,7 @@ class UpdateMessageService: NSObject, MTMessageService {
|
||||
generatedToId = Api.Peer.peerUser(userId: self.peerId.id)
|
||||
}
|
||||
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
|
||||
let generatedMessage = Api.Message.message(flags: flags, id: id, fromId: generatedFromId, toId: generatedToId, fwdFrom: fwdFrom, viaBotId: viaBotId, replyToMsgId: replyToMsgId, date: date, message: message, media: Api.MessageMedia.messageMediaEmpty, replyMarkup: nil, entities: entities, views: nil, forwards: nil, editDate: nil, postAuthor: nil, groupedId: nil, restrictionReason: nil)
|
||||
let update = Api.Update.updateNewMessage(message: generatedMessage, pts: pts, ptsCount: ptsCount)
|
||||
let groups = groupUpdates([update], users: [], chats: [], date: date, seqRange: nil)
|
||||
if groups.count != 0 {
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -58,6 +58,8 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
||||
self.openUrl = openUrl
|
||||
self.back = back
|
||||
|
||||
loadServerCountryCodes(network: account.network)
|
||||
|
||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)))
|
||||
|
||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||
|
@ -121,26 +121,32 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
self.phoneInputNode.countryCodeField.textField.disableAutomaticKeyboardHandling = [.forward]
|
||||
self.phoneInputNode.numberField.textField.disableAutomaticKeyboardHandling = [.forward]
|
||||
|
||||
|
||||
self.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 15.0, bottom: 10.0, right: 0.0)
|
||||
self.countryButton.contentHorizontalAlignment = .left
|
||||
|
||||
self.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
||||
// self.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
||||
|
||||
self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside)
|
||||
|
||||
self.phoneInputNode.countryCodeUpdated = { [weak self] code, name in
|
||||
let font = Font.with(size: 20.0, design: .monospace, traits: [])
|
||||
if let strongSelf = self {
|
||||
if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
|
||||
let flagString = emojiFlagForISOCountryCode(name as NSString)
|
||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName
|
||||
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: [])
|
||||
|
||||
strongSelf.phoneInputNode.mask = AuthorizationSequenceCountrySelectionController.lookupPatternByCode(code).flatMap { NSAttributedString(string: $0, font: font, textColor: theme.list.itemPlaceholderTextColor) }
|
||||
} else if let code = Int(code), let (countryId, countryName) = countryCodeToIdAndName[code] {
|
||||
let flagString = emojiFlagForISOCountryCode(countryId as NSString)
|
||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName
|
||||
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemPrimaryTextColor, for: [])
|
||||
|
||||
strongSelf.phoneInputNode.mask = AuthorizationSequenceCountrySelectionController.lookupPatternByCode(code).flatMap { NSAttributedString(string: $0, font: font, textColor: theme.list.itemPlaceholderTextColor) }
|
||||
} else {
|
||||
strongSelf.countryButton.setTitle(strings.Login_SelectCountry_Title, with: Font.regular(20.0), with: theme.list.itemPlaceholderTextColor, for: [])
|
||||
|
||||
strongSelf.phoneInputNode.mask = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,12 +171,14 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
||||
|
||||
let countryCodeFrame = CGRect(origin: CGPoint(x: 18.0, y: size.height - 57.0), size: CGSize(width: 60.0, height: 57.0))
|
||||
let numberFrame = CGRect(origin: CGPoint(x: 96.0, y: size.height - 57.0), size: CGSize(width: size.width - 96.0 - 8.0, height: 57.0))
|
||||
let placeholderFrame = numberFrame.offsetBy(dx: -1.0, dy: 16.0)
|
||||
|
||||
let phoneInputFrame = countryCodeFrame.union(numberFrame)
|
||||
|
||||
self.phoneInputNode.frame = phoneInputFrame
|
||||
self.phoneInputNode.countryCodeField.frame = countryCodeFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
|
||||
self.phoneInputNode.numberField.frame = numberFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
|
||||
self.phoneInputNode.placeholderNode.frame = placeholderFrame.offsetBy(dx: -phoneInputFrame.minX, dy: -phoneInputFrame.minY)
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,6 +212,8 @@ private final class ContactSyncNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
||||
private let sharedContext: SharedAccountContext
|
||||
private var account: UnauthorizedAccount
|
||||
|
@ -176,11 +176,11 @@ final class ChatButtonKeyboardInputNode: ChatInputNode {
|
||||
self.controllerInteraction.shareAccountContact()
|
||||
case .openWebApp:
|
||||
if let message = self.message {
|
||||
self.controllerInteraction.requestMessageActionCallback(message.id, nil, true)
|
||||
self.controllerInteraction.requestMessageActionCallback(message.id, nil, true, false)
|
||||
}
|
||||
case let .callback(data):
|
||||
case let .callback(requiresPassword, data):
|
||||
if let message = self.message {
|
||||
self.controllerInteraction.requestMessageActionCallback(message.id, data, false)
|
||||
self.controllerInteraction.requestMessageActionCallback(message.id, data, false, requiresPassword)
|
||||
}
|
||||
case let .switchInline(samePeer, query):
|
||||
if let message = message {
|
||||
|
@ -793,7 +793,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.enqueueChatContextResult(collection, result, hideVia: true, closeMediaInput: true)
|
||||
|
||||
return true
|
||||
}, requestMessageActionCallback: { [weak self] messageId, data, isGame in
|
||||
}, requestMessageActionCallback: { [weak self] messageId, data, isGame, requiresPassword in
|
||||
if let strongSelf = self {
|
||||
guard !strongSelf.presentationInterfaceState.isScheduledMessages else {
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: strongSelf.presentationData.strings.ScheduledMessages_BotActionUnavailable, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
@ -819,52 +819,73 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
})
|
||||
|
||||
strongSelf.messageActionCallbackDisposable.set(((requestMessageActionCallback(account: strongSelf.context.account, messageId: messageId, isGame: isGame, data: data)
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
return $0.updatedTitlePanelContext {
|
||||
if let index = $0.firstIndex(where: {
|
||||
switch $0 {
|
||||
case .requestInProgress:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}) {
|
||||
var updatedContexts = $0
|
||||
updatedContexts.remove(at: index)
|
||||
return updatedContexts
|
||||
}
|
||||
return $0
|
||||
}
|
||||
let proceedWithResult: (MessageActionCallbackResult) -> Void = { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
switch result {
|
||||
case .none:
|
||||
break
|
||||
case let .alert(text):
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
case let .toast(text):
|
||||
let message: Signal<String?, NoError> = .single(text)
|
||||
let noMessage: Signal<String?, NoError> = .single(nil)
|
||||
let delayedNoMessage: Signal<String?, NoError> = noMessage |> delay(1.0, queue: Queue.mainQueue())
|
||||
strongSelf.botCallbackAlertMessage.set(message |> then(delayedNoMessage))
|
||||
case let .url(url):
|
||||
if isGame {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
||||
} else {
|
||||
strongSelf.openUrl(url, concealed: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let account = strongSelf.context.account
|
||||
if requiresPassword {
|
||||
strongSelf.messageActionCallbackDisposable.set((requestMessageActionCallbackPasswordCheck(account: account, messageId: messageId, isGame: isGame, data: data)
|
||||
|> deliverOnMainQueue).start(error: { error in
|
||||
let controller = ownershipTransferController(context: context, initialError: error, present: { c, a in
|
||||
strongSelf.present(c, in: .window(.root), with: a)
|
||||
}, commit: { password in
|
||||
return requestMessageActionCallback(account: account, messageId: messageId, isGame: isGame, password: password, data: data)
|
||||
}, completion: { result in
|
||||
proceedWithResult(result)
|
||||
})
|
||||
strongSelf.present(controller, in: .window(.root))
|
||||
}))
|
||||
} else {
|
||||
strongSelf.messageActionCallbackDisposable.set(((requestMessageActionCallback(account: account, messageId: messageId, isGame: isGame, password: nil, data: data)
|
||||
|> afterDisposed {
|
||||
Queue.mainQueue().async {
|
||||
if let strongSelf = self {
|
||||
strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, {
|
||||
return $0.updatedTitlePanelContext {
|
||||
if let index = $0.firstIndex(where: {
|
||||
switch $0 {
|
||||
case .requestInProgress:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}) {
|
||||
var updatedContexts = $0
|
||||
updatedContexts.remove(at: index)
|
||||
return updatedContexts
|
||||
}
|
||||
return $0
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
if let strongSelf = self {
|
||||
switch result {
|
||||
case .none:
|
||||
break
|
||||
case let .alert(text):
|
||||
strongSelf.present(textAlertController(context: strongSelf.context, title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||
case let .toast(text):
|
||||
let message: Signal<String?, NoError> = .single(text)
|
||||
let noMessage: Signal<String?, NoError> = .single(nil)
|
||||
let delayedNoMessage: Signal<String?, NoError> = noMessage |> delay(1.0, queue: Queue.mainQueue())
|
||||
strongSelf.botCallbackAlertMessage.set(message |> then(delayedNoMessage))
|
||||
case let .url(url):
|
||||
if isGame {
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
strongSelf.effectiveNavigationController?.pushViewController(GameController(context: strongSelf.context, url: url, message: message))
|
||||
} else {
|
||||
strongSelf.openUrl(url, concealed: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}))
|
||||
})
|
||||
|> deliverOnMainQueue).start(next: { result in
|
||||
proceedWithResult(result)
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}, requestMessageActionUrlAuth: { [weak self] defaultUrl, messageId, buttonId in
|
||||
|
@ -71,7 +71,7 @@ public final class ChatControllerInteraction {
|
||||
let sendSticker: (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool
|
||||
let sendGif: (FileMediaReference, ASDisplayNode, CGRect) -> Bool
|
||||
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool
|
||||
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool) -> Void
|
||||
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
|
||||
let requestMessageActionUrlAuth: (String, MessageId, Int32) -> Void
|
||||
let activateSwitchInline: (PeerId?, String) -> Void
|
||||
let openUrl: (String, Bool, Bool?, Message?) -> Void
|
||||
@ -138,7 +138,7 @@ public final class ChatControllerInteraction {
|
||||
var searchTextHighightState: (String, [MessageIndex])?
|
||||
var seenOneTimeAnimatedMedia = Set<MessageId>()
|
||||
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||
init(openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, Message?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void, openMessageContextActions: @escaping (Message, ASDisplayNode, CGRect, ContextGesture?) -> Void, navigateToMessage: @escaping (MessageId, MessageId) -> Void, tapMessage: ((Message) -> Void)?, clickThroughMessage: @escaping () -> Void, toggleMessagesSelection: @escaping ([MessageId], Bool) -> Void, sendCurrentMessage: @escaping (Bool) -> Void, sendMessage: @escaping (String) -> Void, sendSticker: @escaping (FileMediaReference, Bool, ASDisplayNode, CGRect) -> Bool, sendGif: @escaping (FileMediaReference, ASDisplayNode, CGRect) -> Bool, sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, ASDisplayNode, CGRect) -> Bool, requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void, requestMessageActionUrlAuth: @escaping (String, MessageId, Int32) -> Void, activateSwitchInline: @escaping (PeerId?, String) -> Void, openUrl: @escaping (String, Bool, Bool?, Message?) -> Void, shareCurrentLocation: @escaping () -> Void, shareAccountContact: @escaping () -> Void, sendBotCommand: @escaping (MessageId?, String) -> Void, openInstantPage: @escaping (Message, ChatMessageItemAssociatedData?) -> Void, openWallpaper: @escaping (Message) -> Void, openTheme: @escaping (Message) -> Void, openHashtag: @escaping (String?, String) -> Void, updateInputState: @escaping ((ChatTextInputState) -> ChatTextInputState) -> Void, updateInputMode: @escaping ((ChatInputMode) -> ChatInputMode) -> Void, openMessageShareMenu: @escaping (MessageId) -> Void, presentController: @escaping (ViewController, Any?) -> Void, navigationController: @escaping () -> NavigationController?, chatControllerNode: @escaping () -> ASDisplayNode?, reactionContainerNode: @escaping () -> ReactionSelectionParentNode?, presentGlobalOverlayController: @escaping (ViewController, Any?) -> Void, callPeer: @escaping (PeerId, Bool) -> Void, longTap: @escaping (ChatControllerInteractionLongTapAction, Message?) -> Void, openCheckoutOrReceipt: @escaping (MessageId) -> Void, openSearch: @escaping () -> Void, setupReply: @escaping (MessageId) -> Void, canSetupReply: @escaping (Message) -> ChatControllerInteractionSwipeAction, navigateToFirstDateMessage: @escaping(Int32) ->Void, requestRedeliveryOfFailedMessages: @escaping (MessageId) -> Void, addContact: @escaping (String) -> Void, rateCall: @escaping (Message, CallId) -> Void, requestSelectMessagePollOptions: @escaping (MessageId, [Data]) -> Void, requestOpenMessagePollResults: @escaping (MessageId, MediaId) -> Void, openAppStorePage: @escaping () -> Void, displayMessageTooltip: @escaping (MessageId, String, ASDisplayNode?, CGRect?) -> Void, seekToTimecode: @escaping (Message, Double, Bool) -> Void, scheduleCurrentMessage: @escaping () -> Void, sendScheduledMessagesNow: @escaping ([MessageId]) -> Void, editScheduledMessagesTime: @escaping ([MessageId]) -> Void, performTextSelectionAction: @escaping (UInt32, NSAttributedString, TextSelectionAction) -> Void, updateMessageLike: @escaping (MessageId, Bool) -> Void, openMessageReactions: @escaping (MessageId) -> Void, displaySwipeToReplyHint: @escaping () -> Void, dismissReplyMarkupMessage: @escaping (Message) -> Void, openMessagePollResults: @escaping (MessageId, Data) -> Void, openPollCreation: @escaping (Bool?) -> Void, displayPollSolution: @escaping (TelegramMediaPollResults.Solution, ASDisplayNode) -> Void, displayPsa: @escaping (String, ASDisplayNode) -> Void, displayDiceTooltip: @escaping (TelegramMediaDice) -> Void, animateDiceSuccess: @escaping () -> Void, greetingStickerNode: @escaping () -> (ASDisplayNode, ASDisplayNode, ASDisplayNode, () -> Void)?, openPeerContextMenu: @escaping (Peer, ASDisplayNode, CGRect, ContextGesture?) -> Void, requestMessageUpdate: @escaping (MessageId) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, automaticMediaDownloadSettings: MediaAutoDownloadSettings, pollActionState: ChatInterfacePollActionState, stickerSettings: ChatInterfaceStickerSettings) {
|
||||
self.openMessage = openMessage
|
||||
self.openPeer = openPeer
|
||||
self.openPeerMention = openPeerMention
|
||||
@ -215,7 +215,7 @@ public final class ChatControllerInteraction {
|
||||
|
||||
static var `default`: ChatControllerInteraction {
|
||||
return ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, chatControllerNode: {
|
||||
|
@ -68,7 +68,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
|
||||
private var highlightedState: Bool = false
|
||||
|
||||
private var heartbeatHaptic: HeartbeatHaptic?
|
||||
private var haptic: EmojiHaptic?
|
||||
|
||||
private var currentSwipeToReplyTranslation: CGFloat = 0.0
|
||||
|
||||
@ -217,7 +217,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
didSet {
|
||||
if self.visibilityStatus != oldValue {
|
||||
self.updateVisibility()
|
||||
self.heartbeatHaptic?.enabled = self.visibilityStatus
|
||||
self.haptic?.enabled = self.visibilityStatus
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,10 +228,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
if let telegramDice = self.telegramDice {
|
||||
if telegramDice.emoji == "🎲" {
|
||||
let animationNode = SlotMachineAnimationNode(context: item.context)
|
||||
self.animationNode = animationNode
|
||||
} else {
|
||||
// if telegramDice.emoji == "🎲" {
|
||||
// let animationNode = SlotMachineAnimationNode(context: item.context)
|
||||
// self.animationNode = animationNode
|
||||
// } else {
|
||||
let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
|
||||
if !item.message.effectivelyIncoming(item.context.account.peerId) {
|
||||
animationNode.success = { [weak self] in
|
||||
@ -241,7 +241,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
self.animationNode = animationNode
|
||||
}
|
||||
// }
|
||||
} else {
|
||||
let animationNode: AnimatedStickerNode
|
||||
if let (node, parentNode, listNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
|
||||
@ -1110,24 +1110,29 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
|
||||
let peach = 0x1F351
|
||||
|
||||
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) {
|
||||
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) || firstScalar.value == peach {
|
||||
return .optionalAction({
|
||||
let _ = startTime.start(next: { [weak self] time in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
|
||||
let heartbeatHaptic: HeartbeatHaptic
|
||||
if let current = strongSelf.heartbeatHaptic {
|
||||
heartbeatHaptic = current
|
||||
var haptic: EmojiHaptic
|
||||
if let current = strongSelf.haptic {
|
||||
haptic = current
|
||||
} else {
|
||||
heartbeatHaptic = HeartbeatHaptic()
|
||||
heartbeatHaptic.enabled = true
|
||||
strongSelf.heartbeatHaptic = heartbeatHaptic
|
||||
if beatingHearts.contains(firstScalar.value) {
|
||||
haptic = HeartbeatHaptic()
|
||||
} else {
|
||||
haptic = PeachHaptic()
|
||||
}
|
||||
haptic.enabled = true
|
||||
strongSelf.haptic = haptic
|
||||
}
|
||||
if !heartbeatHaptic.active {
|
||||
heartbeatHaptic.start(time: time)
|
||||
if !haptic.active {
|
||||
haptic.start(time: time)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -907,6 +907,10 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
}
|
||||
}
|
||||
|
||||
if item.presentationData.isPreview {
|
||||
needShareButton = false
|
||||
}
|
||||
|
||||
var tmpWidth: CGFloat
|
||||
if allowFullWidth {
|
||||
tmpWidth = baseWidth
|
||||
@ -1843,6 +1847,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
|
||||
strongSelf.contextSourceNode.contentNode.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
|
||||
|
||||
if let credibilityIconImage = currentCredibilityIconImage {
|
||||
let credibilityIconNode: ASImageNode
|
||||
|
@ -508,6 +508,7 @@ class ChatMessageDateAndStatusNode: ASDisplayNode {
|
||||
impressionIcon.removeFromSupernode()
|
||||
strongSelf.impressionIcon = nil
|
||||
}
|
||||
strongSelf.impressionIcon?.displaysAsynchronously = !presentationData.isPreview
|
||||
|
||||
strongSelf.dateNode.frame = CGRect(origin: CGPoint(x: leftInset + backgroundInsets.left + impressionWidth, y: backgroundInsets.top + 1.0 + offset), size: date.size)
|
||||
|
||||
|
@ -26,7 +26,7 @@ final class ChatMessageGameBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
self.addSubnode(self.contentNode)
|
||||
self.contentNode.openMedia = { [weak self] _ in
|
||||
if let strongSelf = self, let item = strongSelf.item {
|
||||
item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true)
|
||||
item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -782,9 +782,9 @@ public class ChatMessageItemView: ListViewItemNode {
|
||||
case .requestPhone:
|
||||
item.controllerInteraction.shareAccountContact()
|
||||
case .openWebApp:
|
||||
item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true)
|
||||
case let .callback(data):
|
||||
item.controllerInteraction.requestMessageActionCallback(item.message.id, data, false)
|
||||
item.controllerInteraction.requestMessageActionCallback(item.message.id, nil, true, false)
|
||||
case let .callback(requiresPassword, data):
|
||||
item.controllerInteraction.requestMessageActionCallback(item.message.id, data, false, requiresPassword)
|
||||
case let .switchInline(samePeer, query):
|
||||
var botPeer: Peer?
|
||||
|
||||
|
@ -216,7 +216,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, _ in
|
||||
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
|
||||
}, openMessageContextActions: { _, _, _, _ in
|
||||
}, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in
|
||||
}, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in
|
||||
self?.openUrl(url)
|
||||
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
|
||||
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
|
||||
|
@ -108,7 +108,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
var selectStickerImpl: ((FileMediaReference, ASDisplayNode, CGRect) -> Bool)?
|
||||
|
||||
self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
return false }, openPeer: { _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, node, rect in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, chatControllerNode: {
|
||||
|
@ -2,11 +2,18 @@ import Foundation
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
|
||||
final class HeartbeatHaptic {
|
||||
protocol EmojiHaptic {
|
||||
var enabled: Bool { get set }
|
||||
var active: Bool { get }
|
||||
|
||||
func start(time: Double)
|
||||
}
|
||||
|
||||
final class HeartbeatHaptic: EmojiHaptic {
|
||||
private var hapticFeedback = HapticFeedback()
|
||||
private var timer: SwiftSignalKit.Timer?
|
||||
private var time: Double = 0.0
|
||||
var enabled = false {
|
||||
var enabled: Bool = false {
|
||||
didSet {
|
||||
if !self.enabled {
|
||||
self.reset()
|
||||
|
@ -76,7 +76,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
return false
|
||||
}, sendBotContextResultAsGif: { _, _, _, _ in
|
||||
return false
|
||||
}, requestMessageActionCallback: { _, _, _ in
|
||||
}, requestMessageActionCallback: { _, _, _, _ in
|
||||
}, requestMessageActionUrlAuth: { _, _, _ in
|
||||
}, activateSwitchInline: { _, _ in
|
||||
}, openUrl: { _, _, _, _ in
|
||||
|
128
submodules/TelegramUI/Sources/OwnershipTransferController.swift
Normal file
128
submodules/TelegramUI/Sources/OwnershipTransferController.swift
Normal file
@ -0,0 +1,128 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import AsyncDisplayKit
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SyncCore
|
||||
import TelegramPresentationData
|
||||
import ActivityIndicator
|
||||
import TextFormat
|
||||
import AccountContext
|
||||
import AlertUI
|
||||
import PresentationDataUtils
|
||||
import PasswordSetupUI
|
||||
import Markdown
|
||||
import PeerInfoUI
|
||||
|
||||
private func commitOwnershipTransferController(context: AccountContext, present: @escaping (ViewController, Any?) -> Void, commit: @escaping (String) -> Signal<MessageActionCallbackResult, MessageActionCallbackError>, completion: @escaping (MessageActionCallbackResult) -> Void) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var dismissImpl: (() -> Void)?
|
||||
var proceedImpl: (() -> Void)?
|
||||
|
||||
var pushControllerImpl: ((ViewController) -> Void)?
|
||||
|
||||
let disposable = MetaDisposable()
|
||||
|
||||
let contentNode = ChannelOwnershipTransferAlertContentNode(theme: AlertControllerTheme(presentationData: presentationData), ptheme: presentationData.theme, strings: presentationData.strings, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {
|
||||
dismissImpl?()
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.OwnershipTransfer_Transfer, action: {
|
||||
proceedImpl?()
|
||||
})])
|
||||
|
||||
contentNode.complete = {
|
||||
proceedImpl?()
|
||||
}
|
||||
|
||||
let controller = AlertController(theme: AlertControllerTheme(presentationData: presentationData), contentNode: contentNode)
|
||||
let presentationDataDisposable = context.sharedContext.presentationData.start(next: { [weak controller, weak contentNode] presentationData in
|
||||
controller?.theme = AlertControllerTheme(presentationData: presentationData)
|
||||
contentNode?.theme = presentationData.theme
|
||||
})
|
||||
controller.dismissed = {
|
||||
presentationDataDisposable.dispose()
|
||||
disposable.dispose()
|
||||
}
|
||||
dismissImpl = { [weak controller, weak contentNode] in
|
||||
contentNode?.dismissInput()
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
proceedImpl = { [weak contentNode] in
|
||||
guard let contentNode = contentNode else {
|
||||
return
|
||||
}
|
||||
contentNode.updateIsChecking(true)
|
||||
|
||||
disposable.set((commit(contentNode.password) |> deliverOnMainQueue).start(next: { result in
|
||||
dismissImpl?()
|
||||
}, error: { [weak contentNode] error in
|
||||
var errorTextAndActions: (String, [TextAlertAction])?
|
||||
switch error {
|
||||
case .invalidPassword:
|
||||
contentNode?.animateError()
|
||||
case .limitExceeded:
|
||||
errorTextAndActions = (presentationData.strings.TwoStepAuth_FloodError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
case .userBlocked, .restricted:
|
||||
errorTextAndActions = (presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
default:
|
||||
errorTextAndActions = (presentationData.strings.Login_UnknownError, [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})])
|
||||
}
|
||||
contentNode?.updateIsChecking(false)
|
||||
|
||||
if let (text, actions) = errorTextAndActions {
|
||||
dismissImpl?()
|
||||
present(textAlertController(context: context, title: nil, text: text, actions: actions), nil)
|
||||
}
|
||||
}))
|
||||
}
|
||||
|
||||
pushControllerImpl = { [weak controller] c in
|
||||
controller?.push(c)
|
||||
}
|
||||
|
||||
return controller
|
||||
}
|
||||
|
||||
|
||||
func ownershipTransferController(context: AccountContext, initialError: MessageActionCallbackError, present: @escaping (ViewController, Any?) -> Void, commit: @escaping (String) -> Signal<MessageActionCallbackResult, MessageActionCallbackError>, completion: @escaping (MessageActionCallbackResult) -> Void) -> ViewController {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let theme = AlertControllerTheme(presentationData: presentationData)
|
||||
|
||||
var title: NSAttributedString? = NSAttributedString(string: presentationData.strings.OwnershipTransfer_SecurityCheck, font: Font.medium(presentationData.listsFontSize.itemListBaseFontSize), textColor: theme.primaryColor, paragraphAlignment: .center)
|
||||
|
||||
var text = presentationData.strings.OwnershipTransfer_SecurityRequirements
|
||||
var actions: [TextAlertAction] = []
|
||||
|
||||
switch initialError {
|
||||
case .requestPassword:
|
||||
return commitOwnershipTransferController(context: context, present: present, commit: commit, completion: completion)
|
||||
case .twoStepAuthTooFresh, .authSessionTooFresh:
|
||||
text = text + presentationData.strings.OwnershipTransfer_ComeBackLater
|
||||
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
|
||||
case .twoStepAuthMissing:
|
||||
actions = [TextAlertAction(type: .genericAction, title: presentationData.strings.OwnershipTransfer_SetupTwoStepAuth, action: {
|
||||
let controller = SetupTwoStepVerificationController(context: context, initialState: .automatic, stateUpdated: { update, shouldDismiss, controller in
|
||||
if shouldDismiss {
|
||||
controller.dismiss()
|
||||
}
|
||||
})
|
||||
present(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}), TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {})]
|
||||
case .userBlocked, .restricted:
|
||||
title = nil
|
||||
text = presentationData.strings.Group_OwnershipTransfer_ErrorPrivacyRestricted
|
||||
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
|
||||
default:
|
||||
title = nil
|
||||
text = presentationData.strings.Login_UnknownError
|
||||
actions = [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]
|
||||
}
|
||||
|
||||
let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: theme.primaryColor)
|
||||
let bold = MarkdownAttributeSet(font: Font.semibold(13.0), textColor: theme.primaryColor)
|
||||
let attributedText = parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in return nil }), textAlignment: .center)
|
||||
|
||||
return richTextAlertController(context: context, title: title, text: attributedText, actions: actions)
|
||||
}
|
73
submodules/TelegramUI/Sources/PeachHaptic.swift
Normal file
73
submodules/TelegramUI/Sources/PeachHaptic.swift
Normal file
@ -0,0 +1,73 @@
|
||||
import Foundation
|
||||
import Display
|
||||
import SwiftSignalKit
|
||||
|
||||
private let impactTime: Double = 0.4
|
||||
|
||||
final class PeachHaptic: EmojiHaptic {
|
||||
private var hapticFeedback = HapticFeedback()
|
||||
private var timer: SwiftSignalKit.Timer?
|
||||
private var time: Double = 0.0
|
||||
var enabled: Bool = false {
|
||||
didSet {
|
||||
if !self.enabled {
|
||||
self.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var active: Bool {
|
||||
return self.timer != nil
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
if let timer = self.timer {
|
||||
self.time = 0.0
|
||||
timer.invalidate()
|
||||
self.timer = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func beat(time: Double) {
|
||||
let epsilon = 0.1
|
||||
if fabs(impactTime - time) < epsilon {
|
||||
self.hapticFeedback.impact(.heavy)
|
||||
}
|
||||
}
|
||||
|
||||
func start(time: Double) {
|
||||
self.hapticFeedback.prepareImpact()
|
||||
|
||||
if time > impactTime {
|
||||
return
|
||||
}
|
||||
|
||||
let startTime: Double = 0.0
|
||||
|
||||
let block = { [weak self] in
|
||||
guard let strongSelf = self, strongSelf.enabled else {
|
||||
return
|
||||
}
|
||||
|
||||
strongSelf.time = startTime
|
||||
strongSelf.beat(time: startTime)
|
||||
strongSelf.timer = SwiftSignalKit.Timer(timeout: 0.2, repeat: true, completion: { [weak self] in
|
||||
guard let strongSelf = self, strongSelf.enabled else {
|
||||
return
|
||||
}
|
||||
strongSelf.time += 0.2
|
||||
strongSelf.beat(time: strongSelf.time)
|
||||
|
||||
if strongSelf.time > impactTime {
|
||||
strongSelf.reset()
|
||||
strongSelf.time = 0.0
|
||||
strongSelf.timer?.invalidate()
|
||||
strongSelf.timer = nil
|
||||
}
|
||||
}, queue: Queue.mainQueue())
|
||||
strongSelf.timer?.start()
|
||||
}
|
||||
|
||||
block()
|
||||
}
|
||||
}
|
@ -1825,7 +1825,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
||||
return false
|
||||
}, sendBotContextResultAsGif: { _, _, _, _ in
|
||||
return false
|
||||
}, requestMessageActionCallback: { _, _, _ in
|
||||
}, requestMessageActionCallback: { _, _, _, _ in
|
||||
}, requestMessageActionUrlAuth: { _, _, _ in
|
||||
}, activateSwitchInline: { _, _ in
|
||||
}, openUrl: { [weak self] url, concealed, external, _ in
|
||||
|
@ -327,7 +327,7 @@ public class PeerMediaCollectionController: TelegramBaseController {
|
||||
return false
|
||||
}, sendBotContextResultAsGif: { _, _, _, _ in
|
||||
return false
|
||||
}, requestMessageActionCallback: { _, _, _ in
|
||||
}, requestMessageActionCallback: { _, _, _, _ in
|
||||
}, requestMessageActionUrlAuth: { _, _, _ in
|
||||
}, activateSwitchInline: { _, _ in
|
||||
}, openUrl: { [weak self] url, _, external, _ in
|
||||
|
@ -1158,7 +1158,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
clickThroughMessage?()
|
||||
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _ in return false }, sendGif: { _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _ in
|
||||
return false
|
||||
}, requestMessageActionCallback: { _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
}, presentController: { _, _ in }, navigationController: {
|
||||
return nil
|
||||
}, chatControllerNode: {
|
||||
|
@ -127,6 +127,25 @@ public final class TelegramRootController: NavigationController {
|
||||
self.accountSettingsController = accountSettingsController
|
||||
self.rootTabController = tabBarController
|
||||
self.pushViewController(tabBarController, animated: false)
|
||||
|
||||
// Queue.mainQueue().after(2.0) {
|
||||
// let messageId = MessageId(peerId: PeerId(namespace: 2, id: 1488156064), namespace: 0, id: 528)
|
||||
// let _ = ((self.context.account.postbox.transaction { transaction in
|
||||
// return transaction.getMessage(messageId)
|
||||
// }) |> deliverOnMainQueue).start(next: { [weak self] message in
|
||||
// guard let strongSelf = self, let message = message else {
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// let layout = ContainerViewLayout(size: CGSize(width: 414.0, height: 896.0), metrics: LayoutMetrics(widthClass: .compact, heightClass: .compact), deviceMetrics: .iPhoneX, intrinsicInsets: UIEdgeInsets(), safeInsets: UIEdgeInsets(), statusBarHeight: 0.0, inputHeight: nil, inputHeightIsInteractivellyChanging: false, inVoiceOver: false)
|
||||
// let renderer = MessageStoryRenderer(context: strongSelf.context, message: message)
|
||||
// let image = renderer.update(layout: layout)
|
||||
//
|
||||
// let node = renderer.containerNode
|
||||
// node.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
// strongSelf.displayNode.addSubnode(node)
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
public func updateRootControllers(showCallsTab: Bool) {
|
||||
@ -174,3 +193,134 @@ public final class TelegramRootController: NavigationController {
|
||||
presentedLegacyShortcutCamera(context: self.context, saveCapturedMedia: false, saveEditedPhotos: false, mediaGrouping: true, parentController: controller)
|
||||
}
|
||||
}
|
||||
|
||||
class MessageStoryRenderer {
|
||||
private let context: AccountContext
|
||||
private let presentationData: PresentationData
|
||||
private let message: Message
|
||||
|
||||
let containerNode: ASDisplayNode
|
||||
private let instantChatBackgroundNode: WallpaperBackgroundNode
|
||||
private let messagesContainerNode: ASDisplayNode
|
||||
private var dateHeaderNode: ListViewItemHeaderNode?
|
||||
private var messageNodes: [ListViewItemNode]?
|
||||
private let addressNode: ImmediateTextNode
|
||||
|
||||
init(context: AccountContext, message: Message) {
|
||||
self.context = context
|
||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
self.message = message
|
||||
|
||||
self.containerNode = ASDisplayNode()
|
||||
|
||||
self.instantChatBackgroundNode = WallpaperBackgroundNode()
|
||||
self.instantChatBackgroundNode.displaysAsynchronously = false
|
||||
self.instantChatBackgroundNode.image = chatControllerBackgroundImage(theme: presentationData.theme, wallpaper: .builtin(WallpaperSettings()), mediaBox: context.sharedContext.accountManager.mediaBox, knockoutMode: context.sharedContext.immediateExperimentalUISettings.knockoutWallpaper)
|
||||
|
||||
self.messagesContainerNode = ASDisplayNode()
|
||||
self.messagesContainerNode.clipsToBounds = true
|
||||
self.messagesContainerNode.transform = CATransform3DMakeScale(1.0, -1.0, 1.0)
|
||||
|
||||
let peer = message.peers[message.id.peerId]!
|
||||
self.addressNode = ImmediateTextNode()
|
||||
self.addressNode.displaysAsynchronously = false
|
||||
self.addressNode.attributedText = NSAttributedString(string: "t.me/\(peer.addressName ?? "")", font: Font.medium(14.0), textColor: UIColor(rgb: 0xa8b7c4))
|
||||
// self.addressNode.textShadowColor = .black
|
||||
|
||||
self.containerNode.addSubnode(self.instantChatBackgroundNode)
|
||||
self.containerNode.addSubnode(self.messagesContainerNode)
|
||||
self.containerNode.addSubnode(self.addressNode)
|
||||
}
|
||||
|
||||
// func update(layout: ContainerViewLayout, completion: (UIImage?) -> Void) {
|
||||
// self.updateMessagesLayout(layout: layout)
|
||||
//
|
||||
// Queue.mainQueue().after(0.01) {
|
||||
// UIGraphicsBeginImageContextWithOptions(layout.size, false, 3.0)
|
||||
// self.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: layout.size), afterScreenUpdates: true)
|
||||
// let img = UIGraphicsGetImageFromCurrentImageContext()
|
||||
// UIGraphicsEndImageContext()
|
||||
// completion(img)
|
||||
// }
|
||||
// }
|
||||
|
||||
private func updateMessagesLayout(layout: ContainerViewLayout) {
|
||||
let size = layout.size
|
||||
self.containerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.instantChatBackgroundNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
self.instantChatBackgroundNode.updateLayout(size: size, transition: .immediate)
|
||||
self.messagesContainerNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
let addressLayout = self.addressNode.updateLayout(size)
|
||||
|
||||
let theme = self.presentationData.theme.withUpdated(preview: true)
|
||||
let headerItem = self.context.sharedContext.makeChatMessageDateHeaderItem(context: self.context, timestamp: self.message.timestamp, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder)
|
||||
|
||||
var items: [ListViewItem] = []
|
||||
let sampleMessages: [Message] = [self.message]
|
||||
|
||||
items = sampleMessages.reversed().map { message in
|
||||
self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, message: message, theme: theme, strings: self.presentationData.strings, wallpaper: self.presentationData.theme.chat.defaultWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil)
|
||||
}
|
||||
|
||||
let inset: CGFloat = 16.0
|
||||
let width = layout.size.width - inset * 2.0
|
||||
let params = ListViewItemLayoutParams(width: width, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right, availableHeight: layout.size.height)
|
||||
if let messageNodes = self.messageNodes {
|
||||
for i in 0 ..< items.count {
|
||||
let itemNode = messageNodes[i]
|
||||
items[i].updateNode(async: { $0() }, node: {
|
||||
return itemNode
|
||||
}, params: params, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], animation: .None, completion: { (layout, apply) in
|
||||
let nodeFrame = CGRect(origin: CGPoint(x: 0.0, y: floor((size.height - layout.size.height) / 2.0)), size: CGSize(width: width, height: layout.size.height))
|
||||
|
||||
itemNode.contentSize = layout.contentSize
|
||||
itemNode.insets = layout.insets
|
||||
itemNode.frame = nodeFrame
|
||||
itemNode.isUserInteractionEnabled = false
|
||||
|
||||
apply(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
}
|
||||
} else {
|
||||
var messageNodes: [ListViewItemNode] = []
|
||||
for i in 0 ..< items.count {
|
||||
var itemNode: ListViewItemNode?
|
||||
items[i].nodeConfiguredForParams(async: { $0() }, params: params, synchronousLoads: true, previousItem: i == 0 ? nil : items[i - 1], nextItem: i == (items.count - 1) ? nil : items[i + 1], completion: { node, apply in
|
||||
itemNode = node
|
||||
apply().1(ListViewItemApply(isOnScreen: true))
|
||||
})
|
||||
itemNode!.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
itemNode!.isUserInteractionEnabled = false
|
||||
messageNodes.append(itemNode!)
|
||||
self.messagesContainerNode.addSubnode(itemNode!)
|
||||
}
|
||||
self.messageNodes = messageNodes
|
||||
}
|
||||
|
||||
var bottomOffset: CGFloat = 0.0
|
||||
if let messageNodes = self.messageNodes {
|
||||
for itemNode in messageNodes {
|
||||
itemNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((size.height - itemNode.frame.height) / 2.0)), size: itemNode.frame.size)
|
||||
bottomOffset += itemNode.frame.maxY
|
||||
itemNode.updateFrame(itemNode.frame, within: layout.size)
|
||||
}
|
||||
}
|
||||
|
||||
self.addressNode.frame = CGRect(origin: CGPoint(x: inset + 16.0, y: bottomOffset + 3.0), size: CGSize(width: addressLayout.width, height: addressLayout.height + 3.0))
|
||||
|
||||
let dateHeaderNode: ListViewItemHeaderNode
|
||||
if let currentDateHeaderNode = self.dateHeaderNode {
|
||||
dateHeaderNode = currentDateHeaderNode
|
||||
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
|
||||
} else {
|
||||
dateHeaderNode = headerItem.node()
|
||||
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
|
||||
self.messagesContainerNode.addSubnode(dateHeaderNode)
|
||||
self.dateHeaderNode = dateHeaderNode
|
||||
}
|
||||
|
||||
dateHeaderNode.frame = CGRect(origin: CGPoint(x: 0.0, y: bottomOffset), size: CGSize(width: layout.size.width, height: headerItem.height))
|
||||
dateHeaderNode.updateLayout(size: self.containerNode.frame.size, leftInset: layout.safeInsets.left, rightInset: layout.safeInsets.right)
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit a7d9b717fdf7e8e441b47692dc5771684b2d7970
|
||||
Subproject commit 3195a121ef3897d888ae55be1d0665a416560023
|
2
third-party/webrtc/webrtc-ios
vendored
2
third-party/webrtc/webrtc-ios
vendored
@ -1 +1 @@
|
||||
Subproject commit cf2c8a8364b4cfda7ea9eb448671033351851130
|
||||
Subproject commit e4d49e73cd8206518e7b3dd75d54af0f0ef5b810
|
Loading…
x
Reference in New Issue
Block a user