mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-17 03:40:18 +00:00
Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios
This commit is contained in:
commit
4439ba625b
2
.bazelrc
2
.bazelrc
@ -7,6 +7,8 @@ build --per_file_copt="third-party/webrtc/.*\.cpp$","@-std=c++14"
|
|||||||
build --per_file_copt="third-party/webrtc/.*\.cc$","@-std=c++14"
|
build --per_file_copt="third-party/webrtc/.*\.cc$","@-std=c++14"
|
||||||
build --per_file_copt="third-party/webrtc/.*\.mm$","@-std=c++14"
|
build --per_file_copt="third-party/webrtc/.*\.mm$","@-std=c++14"
|
||||||
|
|
||||||
|
build --swiftcopt=-disallow-use-new-driver
|
||||||
|
|
||||||
build --features=debug_prefix_map_pwd_is_dot
|
build --features=debug_prefix_map_pwd_is_dot
|
||||||
build --features=swift.cacheable_swiftmodules
|
build --features=swift.cacheable_swiftmodules
|
||||||
build --features=swift.debug_prefix_map
|
build --features=swift.debug_prefix_map
|
||||||
|
|||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -16,7 +16,7 @@ url=https://github.com/ali-fareed/rules_swift.git
|
|||||||
url = https://github.com/telegramdesktop/libtgvoip.git
|
url = https://github.com/telegramdesktop/libtgvoip.git
|
||||||
[submodule "build-system/tulsi"]
|
[submodule "build-system/tulsi"]
|
||||||
path = build-system/tulsi
|
path = build-system/tulsi
|
||||||
url=https://github.com/bazelbuild/tulsi.git
|
url=https://github.com/ali-fareed/tulsi.git
|
||||||
[submodule "submodules/TgVoipWebrtc/tgcalls"]
|
[submodule "submodules/TgVoipWebrtc/tgcalls"]
|
||||||
path = submodules/TgVoipWebrtc/tgcalls
|
path = submodules/TgVoipWebrtc/tgcalls
|
||||||
url=../tgcalls.git
|
url=../tgcalls.git
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
61c0e29ede9b63175583b4609216b9c6083192c87d0e6ee0a42a5ff263b627dd
|
1c6f97bd3d230ccacfcdb9ff323624644fa53fc0ca4bbb6a5152826fa731a273
|
||||||
|
|||||||
@ -436,6 +436,11 @@ app_groups_fragment = """
|
|||||||
telegram_bundle_id=telegram_bundle_id
|
telegram_bundle_id=telegram_bundle_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
communication_notifications_fragment = """
|
||||||
|
<key>com.apple.developer.usernotifications.communication</key>
|
||||||
|
<true/>
|
||||||
|
"""
|
||||||
|
|
||||||
plist_fragment(
|
plist_fragment(
|
||||||
name = "TelegramEntitlements",
|
name = "TelegramEntitlements",
|
||||||
extension = "entitlements",
|
extension = "entitlements",
|
||||||
@ -447,7 +452,8 @@ plist_fragment(
|
|||||||
icloud_fragment,
|
icloud_fragment,
|
||||||
apple_pay_merchants_fragment,
|
apple_pay_merchants_fragment,
|
||||||
unrestricted_voip_fragment,
|
unrestricted_voip_fragment,
|
||||||
carplay_fragment
|
carplay_fragment,
|
||||||
|
communication_notifications_fragment,
|
||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -1338,7 +1344,7 @@ ios_extension(
|
|||||||
],
|
],
|
||||||
minimum_os_version = "9.0", # maintain the same minimum OS version across extensions
|
minimum_os_version = "9.0", # maintain the same minimum OS version across extensions
|
||||||
ipa_post_processor = ":SetMinOsVersionWidgetExtension",
|
ipa_post_processor = ":SetMinOsVersionWidgetExtension",
|
||||||
provides_main = True,
|
#provides_main = True,
|
||||||
provisioning_profile = select({
|
provisioning_profile = select({
|
||||||
":disableProvisioningProfilesSetting": None,
|
":disableProvisioningProfilesSetting": None,
|
||||||
"//conditions:default": "@build_configuration//provisioning:Widget.mobileprovision",
|
"//conditions:default": "@build_configuration//provisioning:Widget.mobileprovision",
|
||||||
@ -1595,6 +1601,13 @@ plist_fragment(
|
|||||||
<string>com.apple.usernotifications.service</string>
|
<string>com.apple.usernotifications.service</string>
|
||||||
<key>NSExtensionPrincipalClass</key>
|
<key>NSExtensionPrincipalClass</key>
|
||||||
<string>NotificationService</string>
|
<string>NotificationService</string>
|
||||||
|
<key>NSExtensionAttributes</key>
|
||||||
|
<dict>
|
||||||
|
<key>IntentsSupported</key>
|
||||||
|
<array>
|
||||||
|
<string>INSendMessageIntent</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
""".format(
|
""".format(
|
||||||
telegram_bundle_id = telegram_bundle_id,
|
telegram_bundle_id = telegram_bundle_id,
|
||||||
@ -1817,6 +1830,8 @@ plist_fragment(
|
|||||||
</dict>
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
|
<true/>
|
||||||
""".format(
|
""".format(
|
||||||
telegram_bundle_id = telegram_bundle_id,
|
telegram_bundle_id = telegram_bundle_id,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ swift_library(
|
|||||||
"//submodules/WebPBinding:WebPBinding",
|
"//submodules/WebPBinding:WebPBinding",
|
||||||
"//submodules/rlottie:RLottieBinding",
|
"//submodules/rlottie:RLottieBinding",
|
||||||
"//submodules/GZip:GZip",
|
"//submodules/GZip:GZip",
|
||||||
|
"//submodules/PersistentStringHash:PersistentStringHash",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import WebPBinding
|
|||||||
import RLottieBinding
|
import RLottieBinding
|
||||||
import GZip
|
import GZip
|
||||||
import UIKit
|
import UIKit
|
||||||
|
import Intents
|
||||||
|
import PersistentStringHash
|
||||||
|
|
||||||
private let queue = Queue()
|
private let queue = Queue()
|
||||||
|
|
||||||
@ -277,6 +279,166 @@ private func convertLottieImage(data: Data) -> UIImage? {
|
|||||||
return context.generateImage()
|
return context.generateImage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func testAvatarImage(size: CGSize) -> UIImage? {
|
||||||
|
UIGraphicsBeginImageContextWithOptions(size, false, 2.0)
|
||||||
|
let context = UIGraphicsGetCurrentContext()!
|
||||||
|
|
||||||
|
context.beginPath()
|
||||||
|
context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
||||||
|
context.clip()
|
||||||
|
|
||||||
|
context.setFillColor(UIColor.red.cgColor)
|
||||||
|
context.fill(CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
private func avatarRoundImage(size: CGSize, source: UIImage) -> UIImage? {
|
||||||
|
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
|
||||||
|
let context = UIGraphicsGetCurrentContext()
|
||||||
|
|
||||||
|
context?.beginPath()
|
||||||
|
context?.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
||||||
|
context?.clip()
|
||||||
|
|
||||||
|
source.draw(in: CGRect(origin: CGPoint(), size: size))
|
||||||
|
|
||||||
|
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
private extension UIColor {
|
||||||
|
convenience init(rgb: UInt32) {
|
||||||
|
self.init(red: CGFloat((rgb >> 16) & 0xff) / 255.0, green: CGFloat((rgb >> 8) & 0xff) / 255.0, blue: CGFloat(rgb & 0xff) / 255.0, alpha: 1.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let gradientColors: [NSArray] = [
|
||||||
|
[UIColor(rgb: 0xff516a).cgColor, UIColor(rgb: 0xff885e).cgColor],
|
||||||
|
[UIColor(rgb: 0xffa85c).cgColor, UIColor(rgb: 0xffcd6a).cgColor],
|
||||||
|
[UIColor(rgb: 0x665fff).cgColor, UIColor(rgb: 0x82b1ff).cgColor],
|
||||||
|
[UIColor(rgb: 0x54cb68).cgColor, UIColor(rgb: 0xa0de7e).cgColor],
|
||||||
|
[UIColor(rgb: 0x4acccd).cgColor, UIColor(rgb: 0x00fcfd).cgColor],
|
||||||
|
[UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor],
|
||||||
|
[UIColor(rgb: 0xd669ed).cgColor, UIColor(rgb: 0xe0a2f3).cgColor],
|
||||||
|
]
|
||||||
|
|
||||||
|
private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId: Int64, letters: [String]) -> UIImage? {
|
||||||
|
UIGraphicsBeginImageContextWithOptions(size, false, 2.0)
|
||||||
|
let context = UIGraphicsGetCurrentContext()
|
||||||
|
|
||||||
|
context?.beginPath()
|
||||||
|
context?.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
|
||||||
|
context?.clip()
|
||||||
|
|
||||||
|
let colorIndex = abs(Int(accountPeerId + peerId))
|
||||||
|
|
||||||
|
let colorsArray = gradientColors[colorIndex % gradientColors.count]
|
||||||
|
var locations: [CGFloat] = [1.0, 0.0]
|
||||||
|
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray, locations: &locations)!
|
||||||
|
|
||||||
|
context?.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions())
|
||||||
|
|
||||||
|
context?.setBlendMode(.normal)
|
||||||
|
|
||||||
|
let string = letters.count == 0 ? "" : (letters[0] + (letters.count == 1 ? "" : letters[1]))
|
||||||
|
let attributedString = NSAttributedString(string: string, attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 20.0), NSAttributedString.Key.foregroundColor: UIColor.white])
|
||||||
|
|
||||||
|
let line = CTLineCreateWithAttributedString(attributedString)
|
||||||
|
let lineBounds = CTLineGetBoundsWithOptions(line, .useGlyphPathBounds)
|
||||||
|
|
||||||
|
let lineOffset = CGPoint(x: string == "B" ? 1.0 : 0.0, y: 0.0)
|
||||||
|
let lineOrigin = CGPoint(x: floor(-lineBounds.origin.x + (size.width - lineBounds.size.width) / 2.0) + lineOffset.x, y: floor(-lineBounds.origin.y + (size.height - lineBounds.size.height) / 2.0))
|
||||||
|
|
||||||
|
context?.translateBy(x: size.width / 2.0, y: size.height / 2.0)
|
||||||
|
context?.scaleBy(x: 1.0, y: -1.0)
|
||||||
|
context?.translateBy(x: -size.width / 2.0, y: -size.height / 2.0)
|
||||||
|
|
||||||
|
context?.translateBy(x: lineOrigin.x, y: lineOrigin.y)
|
||||||
|
if let context = context {
|
||||||
|
CTLineDraw(line, context)
|
||||||
|
}
|
||||||
|
context?.translateBy(x: -lineOrigin.x, y: -lineOrigin.y)
|
||||||
|
|
||||||
|
let image = UIGraphicsGetImageFromCurrentImageContext()
|
||||||
|
UIGraphicsEndImageContext()
|
||||||
|
return image
|
||||||
|
}
|
||||||
|
|
||||||
|
private func avatarImage(path: String?, peerId: Int64, accountPeerId: Int64, letters: [String], size: CGSize) -> UIImage {
|
||||||
|
if let path = path, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) {
|
||||||
|
return roundImage
|
||||||
|
} else {
|
||||||
|
return avatarViewLettersImage(size: size, peerId: peerId, accountPeerId: accountPeerId, letters: letters)!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func storeTemporaryImage(path: String) -> String {
|
||||||
|
let imagesPath = NSTemporaryDirectory() + "/aps-data"
|
||||||
|
let _ = try? FileManager.default.createDirectory(at: URL(fileURLWithPath: imagesPath), withIntermediateDirectories: true, attributes: nil)
|
||||||
|
|
||||||
|
let tempPath = imagesPath + "\(path.persistentHashValue)"
|
||||||
|
if FileManager.default.fileExists(atPath: tempPath) {
|
||||||
|
return tempPath
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = try? FileManager.default.copyItem(at: URL(fileURLWithPath: path), to: URL(fileURLWithPath: tempPath))
|
||||||
|
|
||||||
|
return tempPath
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 15.0, *)
|
||||||
|
private func peerAvatar(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) -> INImage? {
|
||||||
|
if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let path = mediaBox.completedResourcePath(resource) {
|
||||||
|
let cachedPath = mediaBox.cachedRepresentationPathForId(resource.id.stringRepresentation, representationId: "intents.png", keepDuration: .shortLived)
|
||||||
|
if let _ = fileSize(cachedPath), let data = try? Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) {
|
||||||
|
do {
|
||||||
|
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let image = avatarImage(path: path, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0))
|
||||||
|
if let data = image.pngData() {
|
||||||
|
let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
//let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped)
|
||||||
|
//return INImage(imageData: data)
|
||||||
|
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cachedPath = mediaBox.cachedRepresentationPathForId("lettersAvatar-\(peer.displayLetters.joined(separator: ","))", representationId: "intents.png", keepDuration: .shortLived)
|
||||||
|
if let _ = fileSize(cachedPath) {
|
||||||
|
do {
|
||||||
|
//let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: [])
|
||||||
|
//return INImage(imageData: data)
|
||||||
|
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let image = avatarImage(path: nil, peerId: peer.id.toInt64(), accountPeerId: accountPeerId.toInt64(), letters: peer.displayLetters, size: CGSize(width: 50.0, height: 50.0))
|
||||||
|
if let data = image.pngData() {
|
||||||
|
let _ = try? data.write(to: URL(fileURLWithPath: cachedPath), options: .atomic)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
//let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped)
|
||||||
|
//return INImage(imageData: data)
|
||||||
|
return INImage(url: URL(fileURLWithPath: storeTemporaryImage(path: cachedPath)))
|
||||||
|
} catch {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
|
@available(iOSApplicationExtension 10.0, iOS 10.0, *)
|
||||||
private struct NotificationContent {
|
private struct NotificationContent {
|
||||||
var title: String?
|
var title: String?
|
||||||
@ -289,8 +451,33 @@ private struct NotificationContent {
|
|||||||
var userInfo: [AnyHashable: Any] = [:]
|
var userInfo: [AnyHashable: Any] = [:]
|
||||||
var attachments: [UNNotificationAttachment] = []
|
var attachments: [UNNotificationAttachment] = []
|
||||||
|
|
||||||
func asNotificationContent() -> UNNotificationContent {
|
var senderPerson: INPerson?
|
||||||
let content = UNMutableNotificationContent()
|
var senderImage: INImage?
|
||||||
|
|
||||||
|
mutating func addSenderInfo(mediaBox: MediaBox, accountPeerId: PeerId, peer: Peer) {
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
let image = peerAvatar(mediaBox: mediaBox, accountPeerId: accountPeerId, peer: peer)
|
||||||
|
|
||||||
|
self.senderImage = image
|
||||||
|
|
||||||
|
var personNameComponents = PersonNameComponents()
|
||||||
|
personNameComponents.nickname = peer.debugDisplayTitle
|
||||||
|
|
||||||
|
self.senderPerson = INPerson(
|
||||||
|
personHandle: INPersonHandle(value: "\(peer.id.toInt64())", type: .unknown),
|
||||||
|
nameComponents: personNameComponents,
|
||||||
|
displayName: peer.debugDisplayTitle,
|
||||||
|
image: image,
|
||||||
|
contactIdentifier: nil,
|
||||||
|
customIdentifier: nil,
|
||||||
|
isMe: false,
|
||||||
|
suggestionType: .none
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generate() -> UNNotificationContent {
|
||||||
|
var content = UNMutableNotificationContent()
|
||||||
|
|
||||||
if let title = self.title {
|
if let title = self.title {
|
||||||
content.title = title
|
content.title = title
|
||||||
@ -320,6 +507,46 @@ private struct NotificationContent {
|
|||||||
content.attachments = self.attachments
|
content.attachments = self.attachments
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if #available(iOS 15.0, *) {
|
||||||
|
if let senderPerson = self.senderPerson {
|
||||||
|
let mePerson = INPerson(
|
||||||
|
personHandle: INPersonHandle(value: "0", type: .unknown),
|
||||||
|
nameComponents: nil,
|
||||||
|
displayName: nil,
|
||||||
|
image: nil,
|
||||||
|
contactIdentifier: nil,
|
||||||
|
customIdentifier: nil,
|
||||||
|
isMe: true,
|
||||||
|
suggestionType: .none
|
||||||
|
)
|
||||||
|
|
||||||
|
let incomingCommunicationIntent = INSendMessageIntent(
|
||||||
|
recipients: [mePerson],
|
||||||
|
outgoingMessageType: .outgoingMessageText,
|
||||||
|
content: content.body,
|
||||||
|
speakableGroupName: INSpeakableString(spokenPhrase: "Sender Name"),
|
||||||
|
conversationIdentifier: "sampleConversationIdentifier",
|
||||||
|
serviceName: nil,
|
||||||
|
sender: senderPerson,
|
||||||
|
attachments: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
if let senderImage = self.senderImage {
|
||||||
|
incomingCommunicationIntent.setImage(senderImage, forParameterNamed: \.sender)
|
||||||
|
}
|
||||||
|
|
||||||
|
let interaction = INInteraction(intent: incomingCommunicationIntent, response: nil)
|
||||||
|
interaction.direction = .incoming
|
||||||
|
interaction.donate(completion: nil)
|
||||||
|
|
||||||
|
do {
|
||||||
|
content = try content.updating(from: incomingCommunicationIntent) as! UNMutableNotificationContent
|
||||||
|
} catch let e {
|
||||||
|
print("Exception: \(e)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,7 +561,7 @@ private final class NotificationServiceHandler {
|
|||||||
private let notificationKeyDisposable = MetaDisposable()
|
private let notificationKeyDisposable = MetaDisposable()
|
||||||
private let pollDisposable = MetaDisposable()
|
private let pollDisposable = MetaDisposable()
|
||||||
|
|
||||||
init?(queue: Queue, updateCurrentContent: @escaping (UNNotificationContent) -> Void, completed: @escaping () -> Void, payload: [AnyHashable: Any]) {
|
init?(queue: Queue, updateCurrentContent: @escaping (NotificationContent) -> Void, completed: @escaping () -> Void, payload: [AnyHashable: Any]) {
|
||||||
self.queue = queue
|
self.queue = queue
|
||||||
|
|
||||||
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
guard let appBundleIdentifier = Bundle.main.bundleIdentifier, let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
||||||
@ -434,6 +661,8 @@ private final class NotificationServiceHandler {
|
|||||||
var messageId: MessageId.Id?
|
var messageId: MessageId.Id?
|
||||||
var mediaAttachment: Media?
|
var mediaAttachment: Media?
|
||||||
|
|
||||||
|
var interactionAuthorId: PeerId?
|
||||||
|
|
||||||
if let messageIdString = payloadJson["msg_id"] as? String {
|
if let messageIdString = payloadJson["msg_id"] as? String {
|
||||||
messageId = Int32(messageIdString)
|
messageId = Int32(messageIdString)
|
||||||
}
|
}
|
||||||
@ -510,6 +739,7 @@ private final class NotificationServiceHandler {
|
|||||||
|
|
||||||
if let messageId = messageId {
|
if let messageId = messageId {
|
||||||
content.userInfo["msg_id"] = "\(messageId)"
|
content.userInfo["msg_id"] = "\(messageId)"
|
||||||
|
interactionAuthorId = peerId
|
||||||
}
|
}
|
||||||
|
|
||||||
if peerId.namespace == Namespaces.Peer.CloudUser {
|
if peerId.namespace == Namespaces.Peer.CloudUser {
|
||||||
@ -579,7 +809,7 @@ private final class NotificationServiceHandler {
|
|||||||
|
|
||||||
action = .poll(peerId: peerId, content: content)
|
action = .poll(peerId: peerId, content: content)
|
||||||
|
|
||||||
updateCurrentContent(content.asNotificationContent())
|
updateCurrentContent(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -587,9 +817,11 @@ private final class NotificationServiceHandler {
|
|||||||
switch action {
|
switch action {
|
||||||
case .logout:
|
case .logout:
|
||||||
completed()
|
completed()
|
||||||
case .poll(let peerId, var content):
|
case let .poll(peerId, initialContent):
|
||||||
if let stateManager = strongSelf.stateManager {
|
if let stateManager = strongSelf.stateManager {
|
||||||
let pollCompletion: () -> Void = {
|
let pollCompletion: (NotificationContent) -> Void = { content in
|
||||||
|
var content = content
|
||||||
|
|
||||||
queue.async {
|
queue.async {
|
||||||
guard let strongSelf = self, let stateManager = strongSelf.stateManager else {
|
guard let strongSelf = self, let stateManager = strongSelf.stateManager else {
|
||||||
completed()
|
completed()
|
||||||
@ -728,7 +960,7 @@ private final class NotificationServiceHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateCurrentContent(content.asNotificationContent())
|
updateCurrentContent(content)
|
||||||
|
|
||||||
completed()
|
completed()
|
||||||
})
|
})
|
||||||
@ -764,8 +996,31 @@ private final class NotificationServiceHandler {
|
|||||||
pollSignal = signal
|
pollSignal = signal
|
||||||
}
|
}
|
||||||
|
|
||||||
strongSelf.pollDisposable.set(pollSignal.start(completed: {
|
let pollWithUpdatedContent: Signal<NotificationContent, NoError>
|
||||||
pollCompletion()
|
if let interactionAuthorId = interactionAuthorId {
|
||||||
|
pollWithUpdatedContent = stateManager.postbox.transaction { transaction -> NotificationContent in
|
||||||
|
var content = initialContent
|
||||||
|
|
||||||
|
if let peer = transaction.getPeer(interactionAuthorId) {
|
||||||
|
content.addSenderInfo(mediaBox: stateManager.postbox.mediaBox, accountPeerId: stateManager.accountPeerId, peer: peer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
|> then(
|
||||||
|
pollSignal
|
||||||
|
|> map { _ -> NotificationContent in }
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
pollWithUpdatedContent = pollSignal
|
||||||
|
|> map { _ -> NotificationContent in }
|
||||||
|
}
|
||||||
|
|
||||||
|
var updatedContent = initialContent
|
||||||
|
strongSelf.pollDisposable.set(pollWithUpdatedContent.start(next: { content in
|
||||||
|
updatedContent = content
|
||||||
|
}, completed: {
|
||||||
|
pollCompletion(updatedContent)
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
completed()
|
completed()
|
||||||
@ -800,7 +1055,7 @@ private final class NotificationServiceHandler {
|
|||||||
var content = NotificationContent()
|
var content = NotificationContent()
|
||||||
content.badge = Int(value.0)
|
content.badge = Int(value.0)
|
||||||
|
|
||||||
updateCurrentContent(content.asNotificationContent())
|
updateCurrentContent(content)
|
||||||
|
|
||||||
completed()
|
completed()
|
||||||
})
|
})
|
||||||
@ -843,7 +1098,7 @@ private final class NotificationServiceHandler {
|
|||||||
var content = NotificationContent()
|
var content = NotificationContent()
|
||||||
content.badge = Int(value.0)
|
content.badge = Int(value.0)
|
||||||
|
|
||||||
updateCurrentContent(content.asNotificationContent())
|
updateCurrentContent(content)
|
||||||
|
|
||||||
completed()
|
completed()
|
||||||
})
|
})
|
||||||
@ -862,7 +1117,7 @@ private final class NotificationServiceHandler {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let content = NotificationContent()
|
let content = NotificationContent()
|
||||||
updateCurrentContent(content.asNotificationContent())
|
updateCurrentContent(content)
|
||||||
|
|
||||||
completed()
|
completed()
|
||||||
}
|
}
|
||||||
@ -891,7 +1146,8 @@ private final class BoxedNotificationServiceHandler {
|
|||||||
final class NotificationService: UNNotificationServiceExtension {
|
final class NotificationService: UNNotificationServiceExtension {
|
||||||
private var impl: QueueLocalObject<BoxedNotificationServiceHandler>?
|
private var impl: QueueLocalObject<BoxedNotificationServiceHandler>?
|
||||||
|
|
||||||
private let content = Atomic<UNNotificationContent?>(value: nil)
|
private var initialContent: UNNotificationContent?
|
||||||
|
private let content = Atomic<NotificationContent?>(value: nil)
|
||||||
private var contentHandler: ((UNNotificationContent) -> Void)?
|
private var contentHandler: ((UNNotificationContent) -> Void)?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
@ -899,7 +1155,7 @@ final class NotificationService: UNNotificationServiceExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||||
let _ = self.content.swap(request.content)
|
self.initialContent = request.content
|
||||||
self.contentHandler = contentHandler
|
self.contentHandler = contentHandler
|
||||||
|
|
||||||
self.impl = nil
|
self.impl = nil
|
||||||
@ -917,8 +1173,17 @@ final class NotificationService: UNNotificationServiceExtension {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
strongSelf.impl = nil
|
strongSelf.impl = nil
|
||||||
if let content = content.with({ $0 }), let contentHandler = strongSelf.contentHandler {
|
|
||||||
contentHandler(content)
|
if let contentHandler = strongSelf.contentHandler {
|
||||||
|
if let content = content.with({ $0 }) {
|
||||||
|
/*let request = UNNotificationRequest(identifier: UUID().uuidString, content: content.generate(), trigger: .none)
|
||||||
|
UNUserNotificationCenter.current().add(request)
|
||||||
|
contentHandler(UNMutableNotificationContent())*/
|
||||||
|
|
||||||
|
contentHandler(content.generate())
|
||||||
|
} else if let initialContent = strongSelf.initialContent {
|
||||||
|
contentHandler(initialContent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
payload: request.content.userInfo
|
payload: request.content.userInfo
|
||||||
@ -927,8 +1192,12 @@ final class NotificationService: UNNotificationServiceExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override func serviceExtensionTimeWillExpire() {
|
override func serviceExtensionTimeWillExpire() {
|
||||||
if let content = self.content.with({ $0 }), let contentHandler = self.contentHandler {
|
if let contentHandler = self.contentHandler {
|
||||||
contentHandler(content)
|
if let content = self.content.with({ $0 }) {
|
||||||
|
contentHandler(content.generate())
|
||||||
|
} else if let initialContent = self.initialContent {
|
||||||
|
contentHandler(initialContent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit c337e9318d17a004dc06c966bab02a8c7929ce87
|
Subproject commit fcbfcfad2d633b6c8be85954975db88bee3fa26c
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 81aa39ed9f58c416e7255adc0c8a6ba50c081030
|
Subproject commit 8c8f4661dba2bbe8578ae42b8ab7001d27357575
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 0bddcf7522cd4f3bcad4e1502e5189156a2eb615
|
Subproject commit 01d37ab862350cb33cbae25cf6622bf534df264f
|
||||||
@ -5,7 +5,7 @@ set -e
|
|||||||
BUILD_TELEGRAM_VERSION="1"
|
BUILD_TELEGRAM_VERSION="1"
|
||||||
|
|
||||||
MACOS_VERSION="11"
|
MACOS_VERSION="11"
|
||||||
XCODE_VERSION="12.5.1"
|
XCODE_VERSION="13.0"
|
||||||
GUEST_SHELL="bash"
|
GUEST_SHELL="bash"
|
||||||
|
|
||||||
VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)"
|
VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)"
|
||||||
|
|||||||
@ -19,6 +19,7 @@ swift_library(
|
|||||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/Postbox:Postbox",
|
"//submodules/Postbox:Postbox",
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
|
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import Postbox
|
|||||||
import TelegramUIPreferences
|
import TelegramUIPreferences
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
|
import MusicAlbumArtResources
|
||||||
|
|
||||||
public enum SharedMediaPlaybackDataType {
|
public enum SharedMediaPlaybackDataType {
|
||||||
case music
|
case music
|
||||||
@ -44,25 +45,13 @@ public struct SharedMediaPlaybackData: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public struct SharedMediaPlaybackAlbumArt: Equatable {
|
public struct SharedMediaPlaybackAlbumArt: Equatable {
|
||||||
public let thumbnailResource: TelegramMediaResource
|
public let thumbnailResource: ExternalMusicAlbumArtResource
|
||||||
public let fullSizeResource: TelegramMediaResource
|
public let fullSizeResource: ExternalMusicAlbumArtResource
|
||||||
|
|
||||||
public init(thumbnailResource: TelegramMediaResource, fullSizeResource: TelegramMediaResource) {
|
public init(thumbnailResource: ExternalMusicAlbumArtResource, fullSizeResource: ExternalMusicAlbumArtResource) {
|
||||||
self.thumbnailResource = thumbnailResource
|
self.thumbnailResource = thumbnailResource
|
||||||
self.fullSizeResource = fullSizeResource
|
self.fullSizeResource = fullSizeResource
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: SharedMediaPlaybackAlbumArt, rhs: SharedMediaPlaybackAlbumArt) -> Bool {
|
|
||||||
if !lhs.thumbnailResource.isEqual(to: rhs.thumbnailResource) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if !lhs.fullSizeResource.isEqual(to: rhs.fullSizeResource) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SharedMediaPlaybackDisplayData: Equatable {
|
public enum SharedMediaPlaybackDisplayData: Equatable {
|
||||||
|
|||||||
@ -13,7 +13,6 @@ swift_library(
|
|||||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||||
"//submodules/Display:Display",
|
"//submodules/Display:Display",
|
||||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/Postbox:Postbox",
|
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||||
"//submodules/MonotonicTime:MonotonicTime",
|
"//submodules/MonotonicTime:MonotonicTime",
|
||||||
|
|||||||
@ -865,7 +865,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError>
|
let foundRemotePeers: Signal<([FoundPeer], [FoundPeer], Bool), NoError>
|
||||||
let currentRemotePeersValue = currentRemotePeers.with { $0 } ?? ([], [])
|
let currentRemotePeersValue: ([FoundPeer], [FoundPeer]) = currentRemotePeers.with { $0 } ?? ([], [])
|
||||||
if let query = query {
|
if let query = query {
|
||||||
foundRemotePeers = (
|
foundRemotePeers = (
|
||||||
.single((currentRemotePeersValue.0, currentRemotePeersValue.1, true))
|
.single((currentRemotePeersValue.0, currentRemotePeersValue.1, true))
|
||||||
|
|||||||
@ -2,8 +2,8 @@ import UIKit
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import UIKitRuntimeUtils
|
import UIKitRuntimeUtils
|
||||||
|
import ObjCRuntimeUtils
|
||||||
|
|
||||||
private let infiniteScrollSize: CGFloat = 10000.0
|
|
||||||
private let insertionAnimationDuration: Double = 0.4
|
private let insertionAnimationDuration: Double = 0.4
|
||||||
|
|
||||||
private struct VisibleHeaderNodeId: Hashable {
|
private struct VisibleHeaderNodeId: Hashable {
|
||||||
@ -161,6 +161,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
private final var lastContentOffsetTimestamp: CFAbsoluteTime = 0.0
|
private final var lastContentOffsetTimestamp: CFAbsoluteTime = 0.0
|
||||||
private final var ignoreScrollingEvents: Bool = false
|
private final var ignoreScrollingEvents: Bool = false
|
||||||
|
|
||||||
|
private let infiniteScrollSize: CGFloat
|
||||||
|
|
||||||
private final var displayLink: CADisplayLink!
|
private final var displayLink: CADisplayLink!
|
||||||
private final var needsAnimations = false
|
private final var needsAnimations = false
|
||||||
|
|
||||||
@ -368,6 +370,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
|
|
||||||
self.scroller = ListViewScroller()
|
self.scroller = ListViewScroller()
|
||||||
|
|
||||||
|
self.infiniteScrollSize = 10000.0
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.isAccessibilityContainer = true
|
self.isAccessibilityContainer = true
|
||||||
@ -822,12 +826,22 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var generalAccumulatedDeltaY: CGFloat = 0.0
|
private var generalAccumulatedDeltaY: CGFloat = 0.0
|
||||||
|
private var previousDidScrollTimestamp: Double = 0.0
|
||||||
|
|
||||||
private func updateScrollViewDidScroll(_ scrollView: UIScrollView, synchronous: Bool) {
|
private func updateScrollViewDidScroll(_ scrollView: UIScrollView, synchronous: Bool) {
|
||||||
if self.ignoreScrollingEvents || scroller !== self.scroller {
|
if self.ignoreScrollingEvents || scroller !== self.scroller {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*let timestamp = CACurrentMediaTime()
|
||||||
|
if !self.previousDidScrollTimestamp.isZero {
|
||||||
|
let delta = timestamp - self.previousDidScrollTimestamp
|
||||||
|
if delta < 0.1 {
|
||||||
|
print("Scrolling delta: \(delta)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.previousDidScrollTimestamp = timestamp*/
|
||||||
|
|
||||||
//CATransaction.begin()
|
//CATransaction.begin()
|
||||||
//CATransaction.setDisableActions(true)
|
//CATransaction.setDisableActions(true)
|
||||||
|
|
||||||
@ -854,7 +868,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
self.trackingOffset += -deltaY
|
self.trackingOffset += -deltaY
|
||||||
}
|
}
|
||||||
|
|
||||||
self.enqueueUpdateVisibleItems(synchronous: synchronous)
|
self.enqueueUpdateVisibleItems(synchronous: false)
|
||||||
|
|
||||||
var useScrollDynamics = false
|
var useScrollDynamics = false
|
||||||
|
|
||||||
@ -1514,8 +1528,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
|
self.scroller.contentSize = CGSize(width: self.visibleSize.width, height: infiniteScrollSize * 2.0)
|
||||||
self.lastContentOffset = CGPoint(x: 0.0, y: infiniteScrollSize)
|
if abs(self.scroller.contentOffset.y - infiniteScrollSize) > infiniteScrollSize / 2.0 {
|
||||||
self.scroller.contentOffset = self.lastContentOffset
|
self.lastContentOffset = CGPoint(x: 0.0, y: infiniteScrollSize)
|
||||||
|
self.scroller.contentOffset = self.lastContentOffset
|
||||||
|
} else {
|
||||||
|
self.lastContentOffset = self.scroller.contentOffset
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self.ignoreScrollingEvents = wasIgnoringScrollingEvents
|
self.ignoreScrollingEvents = wasIgnoringScrollingEvents
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ swift_library(
|
|||||||
"//submodules/FileMediaResourceStatus:FileMediaResourceStatus",
|
"//submodules/FileMediaResourceStatus:FileMediaResourceStatus",
|
||||||
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||||
"//submodules/WallpaperResources:WallpaperResources",
|
"//submodules/WallpaperResources:WallpaperResources",
|
||||||
|
"//submodules/Postbox:Postbox",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -682,7 +682,7 @@ public final class ListMessageFileItemNode: ListMessageNode {
|
|||||||
case let .imageRepresentation(file, representation):
|
case let .imageRepresentation(file, representation):
|
||||||
updateIconImageSignal = chatWebpageSnippetFile(account: item.context.account, fileReference: .message(message: MessageReference(message), media: file), representation: representation)
|
updateIconImageSignal = chatWebpageSnippetFile(account: item.context.account, fileReference: .message(message: MessageReference(message), media: file), representation: representation)
|
||||||
case let .albumArt(file, albumArt):
|
case let .albumArt(file, albumArt):
|
||||||
updateIconImageSignal = playerAlbumArt(postbox: item.context.account.postbox, fileReference: .message(message: MessageReference(message), media: file), albumArt: albumArt, thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), emptyColor: item.presentationData.theme.theme.list.itemAccentColor)
|
updateIconImageSignal = playerAlbumArt(postbox: item.context.account.postbox, engine: item.context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: albumArt, thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), emptyColor: item.presentationData.theme.theme.list.itemAccentColor)
|
||||||
case let .roundVideo(file):
|
case let .roundVideo(file):
|
||||||
updateIconImageSignal = mediaGridMessageVideo(postbox: item.context.account.postbox, videoReference: FileMediaReference.message(message: MessageReference(message), media: file), autoFetchFullSizeThumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3))
|
updateIconImageSignal = mediaGridMessageVideo(postbox: item.context.account.postbox, videoReference: FileMediaReference.message(message: MessageReference(message), media: file), autoFetchFullSizeThumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -397,7 +397,7 @@ private final class MediaPlayerContext {
|
|||||||
self.audioRenderer = nil
|
self.audioRenderer = nil
|
||||||
|
|
||||||
var timebase: CMTimebase?
|
var timebase: CMTimebase?
|
||||||
CMTimebaseCreateWithMasterClock(allocator: nil, masterClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
CMTimebaseCreateWithSourceClock(allocator: nil, sourceClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
||||||
controlTimebase = MediaPlayerControlTimebase(timebase: timebase!, isAudio: false)
|
controlTimebase = MediaPlayerControlTimebase(timebase: timebase!, isAudio: false)
|
||||||
CMTimebaseSetTime(timebase!, time: seekResult.timestamp)
|
CMTimebaseSetTime(timebase!, time: seekResult.timestamp)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -95,13 +95,8 @@ private func rendererInputProc(refCon: UnsafeMutableRawPointer, ioActionFlags: U
|
|||||||
|
|
||||||
if !didSetRate {
|
if !didSetRate {
|
||||||
context.state = .playing(rate: rate, didSetRate: true)
|
context.state = .playing(rate: rate, didSetRate: true)
|
||||||
let masterClock: CMClockOrTimebase
|
let masterClock = CMTimebaseCopySource(context.timebase)
|
||||||
if #available(iOS 9.0, *) {
|
CMTimebaseSetRateAndAnchorTime(context.timebase, rate: rate, anchorTime: CMTimeMake(value: sampleIndex, timescale: 44100), immediateSourceTime: CMSyncGetTime(masterClock))
|
||||||
masterClock = CMTimebaseCopyMaster(context.timebase)
|
|
||||||
} else {
|
|
||||||
masterClock = CMTimebaseGetMaster(context.timebase)!
|
|
||||||
}
|
|
||||||
CMTimebaseSetRateAndAnchorTime(context.timebase, rate: rate, anchorTime: CMTimeMake(value: sampleIndex, timescale: 44100), immediateMasterTime: CMSyncGetTime(masterClock))
|
|
||||||
updatedRate = context.updatedRate
|
updatedRate = context.updatedRate
|
||||||
} else {
|
} else {
|
||||||
context.renderTimestampTick += 1
|
context.renderTimestampTick += 1
|
||||||
@ -802,7 +797,7 @@ public final class MediaPlayerAudioRenderer {
|
|||||||
self.audioClock = audioClock!
|
self.audioClock = audioClock!
|
||||||
|
|
||||||
var audioTimebase: CMTimebase?
|
var audioTimebase: CMTimebase?
|
||||||
CMTimebaseCreateWithMasterClock(allocator: nil, masterClock: audioClock!, timebaseOut: &audioTimebase)
|
CMTimebaseCreateWithSourceClock(allocator: nil, sourceClock: audioClock!, timebaseOut: &audioTimebase)
|
||||||
self.audioTimebase = audioTimebase!
|
self.audioTimebase = audioTimebase!
|
||||||
|
|
||||||
audioPlayerRendererQueue.async {
|
audioPlayerRendererQueue.async {
|
||||||
|
|||||||
@ -569,6 +569,14 @@ NSString *suffix = @"";
|
|||||||
return @"iPhone 12 Pro";
|
return @"iPhone 12 Pro";
|
||||||
if ([platform isEqualToString:@"iPhone13,4"])
|
if ([platform isEqualToString:@"iPhone13,4"])
|
||||||
return @"iPhone 12 Pro Max";
|
return @"iPhone 12 Pro Max";
|
||||||
|
if ([platform isEqualToString:@"iPhone14,2"])
|
||||||
|
return @"iPhone 13 Pro";
|
||||||
|
if ([platform isEqualToString:@"iPhone14,3"])
|
||||||
|
return @"iPhone 13 Pro Max";
|
||||||
|
if ([platform isEqualToString:@"iPhone14,4"])
|
||||||
|
return @"iPhone 13 Mini";
|
||||||
|
if ([platform isEqualToString:@"iPhone14,5"])
|
||||||
|
return @"iPhone 13";
|
||||||
|
|
||||||
if ([platform hasPrefix:@"iPod1"])
|
if ([platform hasPrefix:@"iPod1"])
|
||||||
return @"iPod touch 1G";
|
return @"iPod touch 1G";
|
||||||
|
|||||||
@ -11,7 +11,6 @@ swift_library(
|
|||||||
],
|
],
|
||||||
deps = [
|
deps = [
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
"//submodules/Postbox:Postbox",
|
|
||||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/UrlEscaping:UrlEscaping",
|
"//submodules/UrlEscaping:UrlEscaping",
|
||||||
],
|
],
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Postbox
|
|
||||||
import UrlEscaping
|
import UrlEscaping
|
||||||
|
|
||||||
public struct ExternalMusicAlbumArtResourceId {
|
public struct ExternalMusicAlbumArtResourceId {
|
||||||
@ -25,7 +24,7 @@ public struct ExternalMusicAlbumArtResourceId {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ExternalMusicAlbumArtResource: TelegramMediaResource {
|
public class ExternalMusicAlbumArtResource: Equatable {
|
||||||
public let title: String
|
public let title: String
|
||||||
public let performer: String
|
public let performer: String
|
||||||
public let isThumbnail: Bool
|
public let isThumbnail: Bool
|
||||||
@ -36,38 +35,28 @@ public class ExternalMusicAlbumArtResource: TelegramMediaResource {
|
|||||||
self.isThumbnail = isThumbnail
|
self.isThumbnail = isThumbnail
|
||||||
}
|
}
|
||||||
|
|
||||||
public required init(decoder: PostboxDecoder) {
|
public var id: EngineMediaResource.Id {
|
||||||
self.title = decoder.decodeStringForKey("t", orElse: "")
|
return EngineMediaResource.Id(ExternalMusicAlbumArtResourceId(title: self.title, performer: self.performer, isThumbnail: self.isThumbnail).uniqueId)
|
||||||
self.performer = decoder.decodeStringForKey("p", orElse: "")
|
|
||||||
self.isThumbnail = decoder.decodeInt32ForKey("th", orElse: 1) != 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(_ encoder: PostboxEncoder) {
|
public static func ==(lhs: ExternalMusicAlbumArtResource, rhs: ExternalMusicAlbumArtResource) -> Bool {
|
||||||
encoder.encodeString(self.title, forKey: "t")
|
if lhs.title != rhs.title {
|
||||||
encoder.encodeString(self.performer, forKey: "p")
|
|
||||||
encoder.encodeInt32(self.isThumbnail ? 1 : 0, forKey: "th")
|
|
||||||
}
|
|
||||||
|
|
||||||
public var id: MediaResourceId {
|
|
||||||
return MediaResourceId(ExternalMusicAlbumArtResourceId(title: self.title, performer: self.performer, isThumbnail: self.isThumbnail).uniqueId)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func isEqual(to: MediaResource) -> Bool {
|
|
||||||
if let to = to as? ExternalMusicAlbumArtResource {
|
|
||||||
return self.title == to.title && self.performer == to.performer && self.isThumbnail == to.isThumbnail
|
|
||||||
} else {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.performer != rhs.performer {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.isThumbnail != rhs.isThumbnail {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func fetchExternalMusicAlbumArtResource(account: Account, resource: ExternalMusicAlbumArtResource) -> Signal<MediaResourceDataFetchResult, MediaResourceDataFetchError> {
|
public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, resource: ExternalMusicAlbumArtResource) -> Signal<EngineMediaResource.Fetch.Result, EngineMediaResource.Fetch.Error> {
|
||||||
return Signal { subscriber in
|
return Signal { subscriber in
|
||||||
subscriber.putNext(.reset)
|
|
||||||
|
|
||||||
if resource.performer.isEmpty || resource.performer.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "unknown artist" || resource.title.isEmpty {
|
if resource.performer.isEmpty || resource.performer.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "unknown artist" || resource.title.isEmpty {
|
||||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true))
|
subscriber.putError(.generic)
|
||||||
subscriber.putCompletion()
|
|
||||||
return EmptyDisposable
|
return EmptyDisposable
|
||||||
} else {
|
} else {
|
||||||
let excludeWords: [String] = [
|
let excludeWords: [String] = [
|
||||||
@ -103,14 +92,12 @@ public func fetchExternalMusicAlbumArtResource(account: Account, resource: Exter
|
|||||||
let disposable = fetchHttpResource(url: metaUrl).start(next: { result in
|
let disposable = fetchHttpResource(url: metaUrl).start(next: { result in
|
||||||
if case let .dataPart(_, data, _, complete) = result, complete {
|
if case let .dataPart(_, data, _, complete) = result, complete {
|
||||||
guard let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
|
guard let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else {
|
||||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true))
|
subscriber.putError(.generic)
|
||||||
subscriber.putCompletion()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let results = dict["results"] as? [Any] else {
|
guard let results = dict["results"] as? [Any] else {
|
||||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true))
|
subscriber.putError(.generic)
|
||||||
subscriber.putCompletion()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,14 +118,12 @@ public func fetchExternalMusicAlbumArtResource(account: Account, resource: Exter
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard let result = matchingResult as? [String: Any] else {
|
guard let result = matchingResult as? [String: Any] else {
|
||||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true))
|
subscriber.putError(.generic)
|
||||||
subscriber.putCompletion()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard var artworkUrl = result["artworkUrl100"] as? String else {
|
guard var artworkUrl = result["artworkUrl100"] as? String else {
|
||||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true))
|
subscriber.putError(.generic)
|
||||||
subscriber.putCompletion()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,12 +132,13 @@ public func fetchExternalMusicAlbumArtResource(account: Account, resource: Exter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if artworkUrl.isEmpty {
|
if artworkUrl.isEmpty {
|
||||||
subscriber.putNext(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: true))
|
subscriber.putError(.generic)
|
||||||
subscriber.putCompletion()
|
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
fetchDisposable.set(fetchHttpResource(url: artworkUrl).start(next: { next in
|
fetchDisposable.set(engine.resources.httpData(url: artworkUrl).start(next: { next in
|
||||||
subscriber.putNext(next)
|
let file = EngineTempBox.shared.tempFile(fileName: "image.jpg")
|
||||||
|
let _ = try? next.write(to: URL(fileURLWithPath: file.path))
|
||||||
|
subscriber.putNext(.moveTempFile(file: file))
|
||||||
}, completed: {
|
}, completed: {
|
||||||
subscriber.putCompletion()
|
subscriber.putCompletion()
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -22,6 +22,8 @@ typedef enum {
|
|||||||
- (id _Nullable)associatedObjectForKey:(void const * _Nonnull)key;
|
- (id _Nullable)associatedObjectForKey:(void const * _Nonnull)key;
|
||||||
- (bool)checkObjectIsKindOfClass:(Class _Nonnull)targetClass;
|
- (bool)checkObjectIsKindOfClass:(Class _Nonnull)targetClass;
|
||||||
- (void)setClass:(Class _Nonnull)newClass;
|
- (void)setClass:(Class _Nonnull)newClass;
|
||||||
|
+ (NSArray<NSString *> * _Nonnull)getIvarList:(Class _Nonnull)classValue;
|
||||||
|
- (id _Nullable)getIvarValue:(NSString * _Nonnull)name;
|
||||||
|
|
||||||
- (NSNumber * _Nullable)floatValueForKeyPath:(NSString * _Nonnull)keyPath;
|
- (NSNumber * _Nullable)floatValueForKeyPath:(NSString * _Nonnull)keyPath;
|
||||||
|
|
||||||
|
|||||||
@ -111,6 +111,36 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
+ (NSArray<NSString *> * _Nonnull)getIvarList:(Class _Nonnull)classValue {
|
||||||
|
NSMutableArray<NSString *> *result = [[NSMutableArray alloc] init];
|
||||||
|
|
||||||
|
unsigned int varCount;
|
||||||
|
|
||||||
|
Ivar *vars = class_copyIvarList(classValue, &varCount);
|
||||||
|
|
||||||
|
for (int i = 0; i < varCount; i++) {
|
||||||
|
Ivar var = vars[i];
|
||||||
|
|
||||||
|
const char* name = ivar_getName(var);
|
||||||
|
const char* typeEncoding = ivar_getTypeEncoding(var);
|
||||||
|
|
||||||
|
[result addObject:[NSString stringWithFormat:@"%s: %s", name, typeEncoding]];
|
||||||
|
}
|
||||||
|
|
||||||
|
free(vars);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (id _Nullable)getIvarValue:(NSString * _Nonnull)name {
|
||||||
|
Ivar ivar = class_getInstanceVariable([self class], [name UTF8String]);
|
||||||
|
if (!ivar){
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
id value = object_getIvar(self, ivar);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
||||||
SEL _Nonnull makeSelectorFromString(NSString * _Nonnull string) {
|
SEL _Nonnull makeSelectorFromString(NSString * _Nonnull string) {
|
||||||
|
|||||||
@ -756,7 +756,7 @@ public func channelInfoController(context: AccountContext, peerId: PeerId) -> Vi
|
|||||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: true)!
|
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: hasPhotos, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: true)!
|
||||||
let _ = currentAvatarMixin.swap(mixin)
|
let _ = currentAvatarMixin.swap(mixin)
|
||||||
mixin.requestSearchController = { assetsController in
|
mixin.requestSearchController = { assetsController in
|
||||||
let controller = WebSearchController(context: context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: peer.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { result in
|
let controller = WebSearchController(context: context, peer: peer.flatMap(EnginePeer.init), chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: peer.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
completedImpl(result)
|
completedImpl(result)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -23,6 +23,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
|
||||||
"//submodules/WebPBinding:WebPBinding",
|
"//submodules/WebPBinding:WebPBinding",
|
||||||
"//submodules/AppBundle:AppBundle",
|
"//submodules/AppBundle:AppBundle",
|
||||||
|
"//submodules/MusicAlbumArtResources:MusicAlbumArtResources",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import FastBlur
|
|||||||
import TinyThumbnail
|
import TinyThumbnail
|
||||||
import ImageTransparency
|
import ImageTransparency
|
||||||
import AppBundle
|
import AppBundle
|
||||||
|
import MusicAlbumArtResources
|
||||||
|
|
||||||
private enum ResourceFileData {
|
private enum ResourceFileData {
|
||||||
case data(Data)
|
case data(Data)
|
||||||
@ -2476,102 +2477,80 @@ public func chatWebFileImage(account: Account, file: TelegramMediaWebFile) -> Si
|
|||||||
|
|
||||||
private let precomposedSmallAlbumArt = Atomic<UIImage?>(value: nil)
|
private let precomposedSmallAlbumArt = Atomic<UIImage?>(value: nil)
|
||||||
|
|
||||||
private func albumArtThumbnailData(postbox: Postbox, thumbnail: MediaResource, attemptSynchronously: Bool = false) -> Signal<Data?, NoError> {
|
public func albumArtThumbnailData(engine: TelegramEngine, thumbnail: ExternalMusicAlbumArtResource, attemptSynchronously: Bool = false) -> Signal<Data?, NoError> {
|
||||||
let thumbnailResource = postbox.mediaBox.resourceData(thumbnail, attemptSynchronously: attemptSynchronously)
|
return engine.resources.custom(
|
||||||
|
id: thumbnail.id.stringRepresentation,
|
||||||
let signal = thumbnailResource |> take(1) |> mapToSignal { maybeData -> Signal<Data?, NoError> in
|
fetch: EngineMediaResource.Fetch {
|
||||||
if maybeData.complete {
|
return fetchExternalMusicAlbumArtResource(engine: engine, resource: thumbnail)
|
||||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
},
|
||||||
return .single((loadedData))
|
attemptSynchronously: attemptSynchronously
|
||||||
|
)
|
||||||
|
|> mapToSignal { data in
|
||||||
|
if data.isComplete {
|
||||||
|
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [])
|
||||||
|
return .single(loadedData)
|
||||||
} else {
|
} else {
|
||||||
let fetchedThumbnail = postbox.mediaBox.fetchedResource(thumbnail, parameters: nil)
|
return .single(nil)
|
||||||
|
|
||||||
let thumbnail = Signal<Data?, NoError> { subscriber in
|
|
||||||
let fetchedDisposable = fetchedThumbnail.start()
|
|
||||||
let thumbnailDisposable = thumbnailResource.start(next: { next in
|
|
||||||
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
|
|
||||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
|
||||||
|
|
||||||
return ActionDisposable {
|
|
||||||
fetchedDisposable.dispose()
|
|
||||||
thumbnailDisposable.dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return thumbnail
|
|
||||||
}
|
}
|
||||||
} |> distinctUntilChanged(isEqual: { lhs, rhs in
|
}
|
||||||
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
if lhs == nil && rhs == nil {
|
if lhs == nil && rhs == nil {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return signal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func albumArtFullSizeDatas(postbox: Postbox, thumbnail: MediaResource, fullSize: MediaResource, autoFetchFullSize: Bool = true) -> Signal<Tuple3<Data?, Data?, Bool>, NoError> {
|
private func albumArtFullSizeDatas(engine: TelegramEngine, thumbnail: ExternalMusicAlbumArtResource, fullSize: ExternalMusicAlbumArtResource, autoFetchFullSize: Bool = true) -> Signal<Tuple3<Data?, Data?, Bool>, NoError> {
|
||||||
let fullSizeResource = postbox.mediaBox.resourceData(fullSize)
|
return engine.resources.custom(
|
||||||
let thumbnailResource = postbox.mediaBox.resourceData(thumbnail)
|
id: thumbnail.id.stringRepresentation,
|
||||||
|
fetch: nil,
|
||||||
let signal = fullSizeResource |> take(1) |> mapToSignal { maybeData -> Signal<Tuple3<Data?, Data?, Bool>, NoError> in
|
attemptSynchronously: false
|
||||||
if maybeData.complete {
|
)
|
||||||
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: maybeData.path), options: [])
|
|> take(1)
|
||||||
|
|> mapToSignal { data in
|
||||||
|
if data.isComplete {
|
||||||
|
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: data.path), options: [])
|
||||||
return .single(Tuple(nil, loadedData, true))
|
return .single(Tuple(nil, loadedData, true))
|
||||||
} else {
|
} else {
|
||||||
let fetchedThumbnail = postbox.mediaBox.fetchedResource(thumbnail, parameters: nil)
|
return combineLatest(
|
||||||
let fetchedFullSize = postbox.mediaBox.fetchedResource(fullSize, parameters: nil)
|
engine.resources.custom(
|
||||||
|
id: thumbnail.id.stringRepresentation,
|
||||||
let thumbnail = Signal<Data?, NoError> { subscriber in
|
fetch: EngineMediaResource.Fetch {
|
||||||
let fetchedDisposable = fetchedThumbnail.start()
|
return fetchExternalMusicAlbumArtResource(engine: engine, resource: thumbnail)
|
||||||
let thumbnailDisposable = thumbnailResource.start(next: { next in
|
},
|
||||||
subscriber.putNext(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []))
|
attemptSynchronously: false
|
||||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
),
|
||||||
|
engine.resources.custom(
|
||||||
return ActionDisposable {
|
id: fullSize.id.stringRepresentation,
|
||||||
fetchedDisposable.dispose()
|
fetch: autoFetchFullSize ? EngineMediaResource.Fetch {
|
||||||
thumbnailDisposable.dispose()
|
return fetchExternalMusicAlbumArtResource(engine: engine, resource: fullSize)
|
||||||
|
} : nil,
|
||||||
|
attemptSynchronously: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> mapToSignal { thumbnail, fullSize in
|
||||||
|
if fullSize.isComplete {
|
||||||
|
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: fullSize.path), options: [])
|
||||||
|
return .single(Tuple(nil, loadedData, true))
|
||||||
|
} else if thumbnail.isComplete {
|
||||||
|
let loadedData: Data? = try? Data(contentsOf: URL(fileURLWithPath: thumbnail.path), options: [])
|
||||||
|
return .single(Tuple(loadedData, nil, false))
|
||||||
|
} else {
|
||||||
|
return .single(Tuple(nil, nil, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let fullSizeData: Signal<Tuple2<Data?, Bool>, NoError>
|
|
||||||
|
|
||||||
if autoFetchFullSize {
|
|
||||||
fullSizeData = Signal<Tuple2<Data?, Bool>, NoError> { subscriber in
|
|
||||||
let fetchedFullSizeDisposable = fetchedFullSize.start()
|
|
||||||
let fullSizeDisposable = fullSizeResource.start(next: { next in
|
|
||||||
subscriber.putNext(Tuple(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete))
|
|
||||||
}, error: subscriber.putError, completed: subscriber.putCompletion)
|
|
||||||
|
|
||||||
return ActionDisposable {
|
|
||||||
fetchedFullSizeDisposable.dispose()
|
|
||||||
fullSizeDisposable.dispose()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fullSizeData = fullSizeResource
|
|
||||||
|> map { next -> Tuple2<Data?, Bool> in
|
|
||||||
return Tuple(next.size == 0 ? nil : try? Data(contentsOf: URL(fileURLWithPath: next.path), options: []), next.complete)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return thumbnail |> mapToSignal { thumbnailData in
|
|
||||||
return fullSizeData |> map { value in
|
|
||||||
return Tuple(thumbnailData, value._0, value._1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} |> distinctUntilChanged(isEqual: { lhs, rhs in
|
}
|
||||||
if (lhs._0 == nil && lhs._1 == nil) && (rhs._0 == nil && rhs._1 == nil) {
|
|> distinctUntilChanged(isEqual: { lhs, rhs in
|
||||||
return true
|
if (lhs._0 == nil && lhs._1 == nil) && (rhs._0 == nil && rhs._1 == nil) {
|
||||||
} else {
|
return true
|
||||||
return false
|
} else {
|
||||||
}
|
return false
|
||||||
})
|
}
|
||||||
|
})
|
||||||
return signal
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func drawAlbumArtPlaceholder(into c: CGContext, arguments: TransformImageArguments, thumbnail: Bool) {
|
private func drawAlbumArtPlaceholder(into c: CGContext, arguments: TransformImageArguments, thumbnail: Bool) {
|
||||||
@ -2613,7 +2592,7 @@ private func drawAlbumArtPlaceholder(into c: CGContext, arguments: TransformImag
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil, drawPlaceholderWhenEmpty: Bool = true, attemptSynchronously: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
public func playerAlbumArt(postbox: Postbox, engine: TelegramEngine, fileReference: FileMediaReference?, albumArt: SharedMediaPlaybackAlbumArt?, thumbnail: Bool, overlayColor: UIColor? = nil, emptyColor: UIColor? = nil, drawPlaceholderWhenEmpty: Bool = true, attemptSynchronously: Bool = false) -> Signal<(TransformImageArguments) -> DrawingContext?, NoError> {
|
||||||
var fileArtworkData: Signal<Data?, NoError> = .single(nil)
|
var fileArtworkData: Signal<Data?, NoError> = .single(nil)
|
||||||
if let fileReference = fileReference {
|
if let fileReference = fileReference {
|
||||||
let size = thumbnail ? CGSize(width: 48.0, height: 48.0) : CGSize(width: 320.0, height: 320.0)
|
let size = thumbnail ? CGSize(width: 48.0, height: 48.0) : CGSize(width: 320.0, height: 320.0)
|
||||||
@ -2654,12 +2633,12 @@ public func playerAlbumArt(postbox: Postbox, fileReference: FileMediaReference?,
|
|||||||
}
|
}
|
||||||
} else if let albumArt = albumArt {
|
} else if let albumArt = albumArt {
|
||||||
if thumbnail {
|
if thumbnail {
|
||||||
immediateArtworkData = albumArtThumbnailData(postbox: postbox, thumbnail: albumArt.thumbnailResource, attemptSynchronously: attemptSynchronously)
|
immediateArtworkData = albumArtThumbnailData(engine: engine, thumbnail: albumArt.thumbnailResource, attemptSynchronously: attemptSynchronously)
|
||||||
|> map { thumbnailData in
|
|> map { thumbnailData in
|
||||||
return Tuple(thumbnailData, nil, false)
|
return Tuple(thumbnailData, nil, false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
immediateArtworkData = albumArtFullSizeDatas(postbox: postbox, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource)
|
immediateArtworkData = albumArtFullSizeDatas(engine: engine, thumbnail: albumArt.thumbnailResource, fullSize: albumArt.fullSizeResource)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6142,7 +6142,7 @@ public final class VoiceChatController: ViewController {
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = WebSearchController(context: strongSelf.context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: peer.id.namespace == Namespaces.Peer.CloudUser ? nil : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in
|
let controller = WebSearchController(context: strongSelf.context, peer: EnginePeer(peer), chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: peer.id.namespace == Namespaces.Peer.CloudUser ? nil : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
self?.updateProfilePhoto(result)
|
self?.updateProfilePhoto(result)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -0,0 +1,15 @@
|
|||||||
|
import Foundation
|
||||||
|
import SwiftSignalKit
|
||||||
|
import Postbox
|
||||||
|
|
||||||
|
public extension TelegramEngine {
|
||||||
|
final class OrderedLists {
|
||||||
|
private let account: Account
|
||||||
|
|
||||||
|
init(account: Account) {
|
||||||
|
self.account = account
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -59,6 +59,12 @@ public final class EngineMediaResource: Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum FetchStatus: Equatable {
|
||||||
|
case Remote
|
||||||
|
case Local
|
||||||
|
case Fetching(isActive: Bool, progress: Float)
|
||||||
|
}
|
||||||
|
|
||||||
public struct Id: Equatable, Hashable {
|
public struct Id: Equatable, Hashable {
|
||||||
public var stringRepresentation: String
|
public var stringRepresentation: String
|
||||||
|
|
||||||
@ -96,6 +102,30 @@ public extension EngineMediaResource.ResourceData {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public extension EngineMediaResource.FetchStatus {
|
||||||
|
init(_ status: MediaResourceStatus) {
|
||||||
|
switch status {
|
||||||
|
case .Remote:
|
||||||
|
self = .Remote
|
||||||
|
case .Local:
|
||||||
|
self = .Local
|
||||||
|
case let .Fetching(isActive, progress):
|
||||||
|
self = .Fetching(isActive: isActive, progress: progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func _asStatus() -> MediaResourceStatus {
|
||||||
|
switch self {
|
||||||
|
case .Remote:
|
||||||
|
return .Remote
|
||||||
|
case .Local:
|
||||||
|
return .Local
|
||||||
|
case let .Fetching(isActive, progress):
|
||||||
|
return .Fetching(isActive: isActive, progress: progress)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public extension TelegramEngine {
|
public extension TelegramEngine {
|
||||||
final class Resources {
|
final class Resources {
|
||||||
private let account: Account
|
private let account: Account
|
||||||
@ -128,7 +158,12 @@ public extension TelegramEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func custom(id: String, fetch: EngineMediaResource.Fetch, cacheTimeout: EngineMediaResource.CacheTimeout = .default, attemptSynchronously: Bool = false) -> Signal<EngineMediaResource.ResourceData, NoError> {
|
public func custom(
|
||||||
|
id: String,
|
||||||
|
fetch: EngineMediaResource.Fetch?,
|
||||||
|
cacheTimeout: EngineMediaResource.CacheTimeout = .default,
|
||||||
|
attemptSynchronously: Bool = false
|
||||||
|
) -> Signal<EngineMediaResource.ResourceData, NoError> {
|
||||||
let mappedKeepDuration: CachedMediaRepresentationKeepDuration
|
let mappedKeepDuration: CachedMediaRepresentationKeepDuration
|
||||||
switch cacheTimeout {
|
switch cacheTimeout {
|
||||||
case .default:
|
case .default:
|
||||||
@ -141,18 +176,20 @@ public extension TelegramEngine {
|
|||||||
baseResourceId: nil,
|
baseResourceId: nil,
|
||||||
pathExtension: nil,
|
pathExtension: nil,
|
||||||
complete: true,
|
complete: true,
|
||||||
fetch: {
|
fetch: fetch.flatMap { fetch in
|
||||||
return Signal { subscriber in
|
return {
|
||||||
return fetch.signal().start(next: { result in
|
return Signal { subscriber in
|
||||||
let mappedResult: CachedMediaResourceRepresentationResult
|
return fetch.signal().start(next: { result in
|
||||||
switch result {
|
let mappedResult: CachedMediaResourceRepresentationResult
|
||||||
case let .moveTempFile(file):
|
switch result {
|
||||||
mappedResult = .tempFile(file)
|
case let .moveTempFile(file):
|
||||||
}
|
mappedResult = .tempFile(file)
|
||||||
subscriber.putNext(mappedResult)
|
}
|
||||||
}, completed: {
|
subscriber.putNext(mappedResult)
|
||||||
subscriber.putCompletion()
|
}, completed: {
|
||||||
})
|
subscriber.putCompletion()
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
keepDuration: mappedKeepDuration,
|
keepDuration: mappedKeepDuration,
|
||||||
|
|||||||
@ -75,6 +75,10 @@ public final class TelegramEngine {
|
|||||||
public lazy var data: EngineData = {
|
public lazy var data: EngineData = {
|
||||||
return EngineData(account: self.account)
|
return EngineData(account: self.account)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
public lazy var orderedLists: OrderedLists = {
|
||||||
|
return OrderedLists(account: self.account)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
public final class TelegramEngineUnauthorized {
|
public final class TelegramEngineUnauthorized {
|
||||||
|
|||||||
@ -123,7 +123,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
|
|||||||
self.imageNode.displaysAsynchronously = false
|
self.imageNode.displaysAsynchronously = false
|
||||||
|
|
||||||
var timebase: CMTimebase?
|
var timebase: CMTimebase?
|
||||||
CMTimebaseCreateWithMasterClock(allocator: nil, masterClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
CMTimebaseCreateWithSourceClock(allocator: nil, sourceClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
||||||
CMTimebaseSetRate(timebase!, rate: 0.0)
|
CMTimebaseSetRate(timebase!, rate: 0.0)
|
||||||
self.timebase = timebase!
|
self.timebase = timebase!
|
||||||
|
|
||||||
|
|||||||
@ -9617,7 +9617,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
|
|
||||||
configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: editingMedia ? nil : { [weak self, weak legacyController] in
|
configureLegacyAssetPicker(controller, context: strongSelf.context, peer: peer, chatLocation: strongSelf.chatLocation, initialCaption: inputText.string, hasSchedule: strongSelf.presentationInterfaceState.subject != .scheduledMessages && peer.id.namespace != Namespaces.Peer.SecretChat, presentWebSearch: editingMedia ? nil : { [weak self, weak legacyController] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, chatLocation: strongSelf.chatLocation, configuration: searchBotsConfiguration, mode: .media(completion: { results, selectionState, editingState, silentPosting in
|
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), chatLocation: strongSelf.chatLocation, configuration: searchBotsConfiguration, mode: .media(completion: { results, selectionState, editingState, silentPosting in
|
||||||
if let legacyController = legacyController {
|
if let legacyController = legacyController {
|
||||||
legacyController.dismiss()
|
legacyController.dismiss()
|
||||||
}
|
}
|
||||||
@ -9724,7 +9724,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] configuration in
|
|> deliverOnMainQueue).start(next: { [weak self] configuration in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: peer, chatLocation: strongSelf.chatLocation, configuration: configuration, mode: .media(completion: { [weak self] results, selectionState, editingState, silentPosting in
|
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, peer: EnginePeer(peer), chatLocation: strongSelf.chatLocation, configuration: configuration, mode: .media(completion: { [weak self] results, selectionState, editingState, silentPosting in
|
||||||
legacyEnqueueWebSearchMessages(selectionState, editingState, enqueueChatContextResult: { [weak self] result in
|
legacyEnqueueWebSearchMessages(selectionState, editingState, enqueueChatContextResult: { [weak self] result in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.enqueueChatContextResult(results, result, hideVia: true)
|
strongSelf.enqueueChatContextResult(results, result, hideVia: true)
|
||||||
|
|||||||
@ -926,7 +926,7 @@ final class ChatMessageInteractiveFileNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
image = playerAlbumArt(postbox: context.account.postbox, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated)
|
image = playerAlbumArt(postbox: context.account.postbox, engine: context.engine, fileReference: .message(message: MessageReference(message), media: file), albumArt: .init(thumbnailResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: true), fullSizeResource: ExternalMusicAlbumArtResource(title: title ?? "", performer: performer ?? "", isThumbnail: false)), thumbnail: true, overlayColor: UIColor(white: 0.0, alpha: 0.3), drawPlaceholderWhenEmpty: false, attemptSynchronously: !animated)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor)
|
let statusNode = SemanticStatusNode(backgroundNodeColor: backgroundNodeColor, foregroundNodeColor: foregroundNodeColor, image: image, overlayForegroundNodeColor: presentationData.theme.theme.chat.message.mediaOverlayControlColors.foregroundColor)
|
||||||
|
|||||||
@ -455,7 +455,7 @@ public func createChannelController(context: AccountContext) -> ViewController {
|
|||||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
||||||
let _ = currentAvatarMixin.swap(mixin)
|
let _ = currentAvatarMixin.swap(mixin)
|
||||||
mixin.requestSearchController = { assetsController in
|
mixin.requestSearchController = { assetsController in
|
||||||
let controller = WebSearchController(context: context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
let controller = WebSearchController(context: context, peer: peer.flatMap(EnginePeer.init), chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
completedChannelPhotoImpl(result)
|
completedChannelPhotoImpl(result)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -701,7 +701,7 @@ public func createGroupControllerImpl(context: AccountContext, peerIds: [PeerId]
|
|||||||
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
let mixin = TGMediaAvatarMenuMixin(context: legacyController.context, parentController: emptyController, hasSearchButton: true, hasDeleteButton: stateValue.with({ $0.avatar }) != nil, hasViewButton: false, personalPhoto: false, isVideo: false, saveEditedPhotos: false, saveCapturedMedia: false, signup: false)!
|
||||||
let _ = currentAvatarMixin.swap(mixin)
|
let _ = currentAvatarMixin.swap(mixin)
|
||||||
mixin.requestSearchController = { assetsController in
|
mixin.requestSearchController = { assetsController in
|
||||||
let controller = WebSearchController(context: context, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
let controller = WebSearchController(context: context, peer: peer.flatMap(EnginePeer.init), chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: title, completion: { result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
completedGroupPhotoImpl(result)
|
completedGroupPhotoImpl(result)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -165,7 +165,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
|
|||||||
self.imageNode.displaysAsynchronously = false
|
self.imageNode.displaysAsynchronously = false
|
||||||
|
|
||||||
var timebase: CMTimebase?
|
var timebase: CMTimebase?
|
||||||
CMTimebaseCreateWithMasterClock(allocator: nil, masterClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
CMTimebaseCreateWithSourceClock(allocator: nil, sourceClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
||||||
CMTimebaseSetRate(timebase!, rate: 0.0)
|
CMTimebaseSetRate(timebase!, rate: 0.0)
|
||||||
self.timebase = timebase!
|
self.timebase = timebase!
|
||||||
|
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import AccountContext
|
|||||||
import TelegramUniversalVideoContent
|
import TelegramUniversalVideoContent
|
||||||
import DeviceProximity
|
import DeviceProximity
|
||||||
import MediaResources
|
import MediaResources
|
||||||
|
import PhotoResources
|
||||||
|
|
||||||
enum SharedMediaPlayerGroup: Int {
|
enum SharedMediaPlayerGroup: Int {
|
||||||
case music = 0
|
case music = 0
|
||||||
@ -324,7 +325,11 @@ public final class MediaManagerImpl: NSObject, MediaManager {
|
|||||||
|> distinctUntilChanged(isEqual: { $0?.0 === $1?.0 && $0?.1 == $1?.1 })
|
|> distinctUntilChanged(isEqual: { $0?.0 === $1?.0 && $0?.1 == $1?.1 })
|
||||||
|> mapToSignal { value -> Signal<UIImage?, NoError> in
|
|> mapToSignal { value -> Signal<UIImage?, NoError> in
|
||||||
if let (account, value) = value {
|
if let (account, value) = value {
|
||||||
return Signal { subscriber in
|
return albumArtThumbnailData(engine: TelegramEngine(account: account), thumbnail: value.fullSizeResource)
|
||||||
|
|> map { data -> UIImage? in
|
||||||
|
return data.flatMap(UIImage.init(data:))
|
||||||
|
}
|
||||||
|
/*return Signal { subscriber in
|
||||||
let fetched = account.postbox.mediaBox.fetchedResource(value.fullSizeResource, parameters: nil).start()
|
let fetched = account.postbox.mediaBox.fetchedResource(value.fullSizeResource, parameters: nil).start()
|
||||||
let data = account.postbox.mediaBox.resourceData(value.fullSizeResource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)).start(next: { data in
|
let data = account.postbox.mediaBox.resourceData(value.fullSizeResource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)).start(next: { data in
|
||||||
if data.complete, let value = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
if data.complete, let value = try? Data(contentsOf: URL(fileURLWithPath: data.path)) {
|
||||||
@ -336,7 +341,7 @@ public final class MediaManagerImpl: NSObject, MediaManager {
|
|||||||
fetched.dispose()
|
fetched.dispose()
|
||||||
data.dispose()
|
data.dispose()
|
||||||
}
|
}
|
||||||
}
|
}*/
|
||||||
} else {
|
} else {
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -168,7 +168,7 @@ final class MultiplexedVideoNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
self.trackingNode.isLayerBacked = true
|
self.trackingNode.isLayerBacked = true
|
||||||
|
|
||||||
var timebase: CMTimebase?
|
var timebase: CMTimebase?
|
||||||
CMTimebaseCreateWithMasterClock(allocator: nil, masterClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
CMTimebaseCreateWithSourceClock(allocator: nil, sourceClock: CMClockGetHostTimeClock(), timebaseOut: &timebase)
|
||||||
CMTimebaseSetRate(timebase!, rate: 0.0)
|
CMTimebaseSetRate(timebase!, rate: 0.0)
|
||||||
self.timebase = timebase!
|
self.timebase = timebase!
|
||||||
|
|
||||||
|
|||||||
@ -154,7 +154,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
|
|
||||||
self.contentNode = ASDisplayNode()
|
self.contentNode = ASDisplayNode()
|
||||||
|
|
||||||
self.controlsNode = OverlayPlayerControlsNode(account: context.account, accountManager: context.sharedContext.accountManager, presentationData: self.presentationData, status: context.sharedContext.mediaManager.musicMediaPlayerState)
|
self.controlsNode = OverlayPlayerControlsNode(account: context.account, engine: context.engine, accountManager: context.sharedContext.accountManager, presentationData: self.presentationData, status: context.sharedContext.mediaManager.musicMediaPlayerState)
|
||||||
|
|
||||||
self.historyBackgroundNode = ASDisplayNode()
|
self.historyBackgroundNode = ASDisplayNode()
|
||||||
self.historyBackgroundNode.isLayerBacked = true
|
self.historyBackgroundNode.isLayerBacked = true
|
||||||
|
|||||||
@ -88,6 +88,7 @@ private func stringsForDisplayData(_ data: SharedMediaPlaybackDisplayData?, pres
|
|||||||
final class OverlayPlayerControlsNode: ASDisplayNode {
|
final class OverlayPlayerControlsNode: ASDisplayNode {
|
||||||
private let accountManager: AccountManager<TelegramAccountManagerTypes>
|
private let accountManager: AccountManager<TelegramAccountManagerTypes>
|
||||||
private let postbox: Postbox
|
private let postbox: Postbox
|
||||||
|
private let engine: TelegramEngine
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
|
|
||||||
private let backgroundNode: ASImageNode
|
private let backgroundNode: ASImageNode
|
||||||
@ -153,9 +154,10 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
|
|
||||||
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat)?
|
private var validLayout: (width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, maxHeight: CGFloat)?
|
||||||
|
|
||||||
init(account: Account, accountManager: AccountManager<TelegramAccountManagerTypes>, presentationData: PresentationData, status: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError>) {
|
init(account: Account, engine: TelegramEngine, accountManager: AccountManager<TelegramAccountManagerTypes>, presentationData: PresentationData, status: Signal<(Account, SharedMediaPlayerItemPlaybackStateOrLoading, MediaManagerPlayerType)?, NoError>) {
|
||||||
self.accountManager = accountManager
|
self.accountManager = accountManager
|
||||||
self.postbox = account.postbox
|
self.postbox = account.postbox
|
||||||
|
self.engine = engine
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
|
||||||
self.backgroundNode = ASImageNode()
|
self.backgroundNode = ASImageNode()
|
||||||
@ -610,9 +612,9 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
if self.currentAlbumArt != albumArt || !self.currentAlbumArtInitialized {
|
if self.currentAlbumArt != albumArt || !self.currentAlbumArtInitialized {
|
||||||
self.currentAlbumArtInitialized = true
|
self.currentAlbumArtInitialized = true
|
||||||
self.currentAlbumArt = albumArt
|
self.currentAlbumArt = albumArt
|
||||||
self.albumArtNode.setSignal(playerAlbumArt(postbox: self.postbox, fileReference: self.currentFileReference, albumArt: albumArt, thumbnail: true))
|
self.albumArtNode.setSignal(playerAlbumArt(postbox: self.postbox, engine: self.engine, fileReference: self.currentFileReference, albumArt: albumArt, thumbnail: true))
|
||||||
if let largeAlbumArtNode = self.largeAlbumArtNode {
|
if let largeAlbumArtNode = self.largeAlbumArtNode {
|
||||||
largeAlbumArtNode.setSignal(playerAlbumArt(postbox: self.postbox, fileReference: self.currentFileReference, albumArt: albumArt, thumbnail: false))
|
largeAlbumArtNode.setSignal(playerAlbumArt(postbox: self.postbox, engine: self.engine, fileReference: self.currentFileReference, albumArt: albumArt, thumbnail: false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -710,7 +712,7 @@ final class OverlayPlayerControlsNode: ASDisplayNode {
|
|||||||
self.largeAlbumArtNode = largeAlbumArtNode
|
self.largeAlbumArtNode = largeAlbumArtNode
|
||||||
self.addSubnode(largeAlbumArtNode)
|
self.addSubnode(largeAlbumArtNode)
|
||||||
if self.currentAlbumArtInitialized {
|
if self.currentAlbumArtInitialized {
|
||||||
largeAlbumArtNode.setSignal(playerAlbumArt(postbox: self.postbox, fileReference: self.currentFileReference, albumArt: self.currentAlbumArt, thumbnail: false))
|
largeAlbumArtNode.setSignal(playerAlbumArt(postbox: self.postbox, engine: self.engine, fileReference: self.currentFileReference, albumArt: self.currentAlbumArt, thumbnail: false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1067,6 +1067,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
|||||||
self.updateHeaderFlashing(animated: true)
|
self.updateHeaderFlashing(animated: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var previousDidScrollTimestamp: Double = 0.0
|
||||||
|
|
||||||
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
if let (size, sideInset, bottomInset, visibleHeight, _, _, presentationData) = self.currentParams {
|
if let (size, sideInset, bottomInset, visibleHeight, _, _, presentationData) = self.currentParams {
|
||||||
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: false)
|
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: false)
|
||||||
|
|||||||
@ -5280,7 +5280,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
|
|||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: peer, chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: strongSelf.isSettings ? nil : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in
|
let controller = WebSearchController(context: strongSelf.context, updatedPresentationData: strongSelf.controller?.updatedPresentationData, peer: EnginePeer(peer), chatLocation: nil, configuration: searchBotsConfiguration, mode: .avatar(initialQuery: strongSelf.isSettings ? nil : EnginePeer(peer).displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder), completion: { [weak self] result in
|
||||||
assetsController?.dismiss()
|
assetsController?.dismiss()
|
||||||
self?.updateProfilePhoto(result)
|
self?.updateProfilePhoto(result)
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -21,8 +21,6 @@ public let telegramAccountAuxiliaryMethods = AccountAuxiliaryMethods(fetchResour
|
|||||||
return fetchLocalFileGifMediaResource(resource: resource)
|
return fetchLocalFileGifMediaResource(resource: resource)
|
||||||
} else if let photoLibraryResource = resource as? PhotoLibraryMediaResource {
|
} else if let photoLibraryResource = resource as? PhotoLibraryMediaResource {
|
||||||
return fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier)
|
return fetchPhotoLibraryResource(localIdentifier: photoLibraryResource.localIdentifier)
|
||||||
} else if let resource = resource as? ExternalMusicAlbumArtResource {
|
|
||||||
return fetchExternalMusicAlbumArtResource(account: account, resource: resource)
|
|
||||||
} else if let resource = resource as? ICloudFileResource {
|
} else if let resource = resource as? ICloudFileResource {
|
||||||
return fetchICloudFileResource(resource: resource)
|
return fetchICloudFileResource(resource: resource)
|
||||||
} else if let resource = resource as? SecureIdLocalImageResource {
|
} else if let resource = resource as? SecureIdLocalImageResource {
|
||||||
|
|||||||
@ -13,7 +13,6 @@ swift_library(
|
|||||||
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||||
"//submodules/Display:Display",
|
"//submodules/Display:Display",
|
||||||
"//submodules/Postbox:Postbox",
|
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||||
"//submodules/TextFormat:TextFormat",
|
"//submodules/TextFormat:TextFormat",
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import UIKit
|
|||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import Postbox
|
|
||||||
import SSignalKit
|
import SSignalKit
|
||||||
import Display
|
import Display
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import UIKit
|
|||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import Postbox
|
|
||||||
import SSignalKit
|
import SSignalKit
|
||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
@ -12,6 +11,7 @@ import AccountContext
|
|||||||
import PhotoResources
|
import PhotoResources
|
||||||
import LegacyUI
|
import LegacyUI
|
||||||
import LegacyMediaPickerUI
|
import LegacyMediaPickerUI
|
||||||
|
import Postbox
|
||||||
|
|
||||||
class LegacyWebSearchItem: NSObject, TGMediaEditableItem, TGMediaSelectableItem {
|
class LegacyWebSearchItem: NSObject, TGMediaEditableItem, TGMediaSelectableItem {
|
||||||
var isVideo: Bool {
|
var isVideo: Bool {
|
||||||
@ -263,7 +263,7 @@ func legacyWebSearchItem(account: Account, result: ChatContextResult) -> LegacyW
|
|||||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil))
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil))
|
||||||
}
|
}
|
||||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: [], immediateThumbnailData: nil))
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: [], immediateThumbnailData: nil))
|
||||||
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
let tmpImage = TelegramMediaImage(imageId: EngineMedia.Id(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||||
thumbnailSignal = chatMessagePhotoDatas(postbox: account.postbox, photoReference: .standalone(media: tmpImage), autoFetchFullSize: false)
|
thumbnailSignal = chatMessagePhotoDatas(postbox: account.postbox, photoReference: .standalone(media: tmpImage), autoFetchFullSize: false)
|
||||||
|> mapToSignal { value -> Signal<UIImage, NoError> in
|
|> mapToSignal { value -> Signal<UIImage, NoError> in
|
||||||
let thumbnailData = value._0
|
let thumbnailData = value._0
|
||||||
@ -312,7 +312,7 @@ private func galleryItems(account: Account, results: [ChatContextResult], curren
|
|||||||
return (galleryItems, focusItem)
|
return (galleryItems, focusItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
func presentLegacyWebSearchGallery(context: AccountContext, peer: Peer?, chatLocation: ChatLocation?, presentationData: PresentationData, results: [ChatContextResult], current: ChatContextResult, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (ChatContextResult) -> UIView?, completed: @escaping (ChatContextResult) -> Void, presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?, present: (ViewController, Any?) -> Void) {
|
func presentLegacyWebSearchGallery(context: AccountContext, peer: EnginePeer?, chatLocation: ChatLocation?, presentationData: PresentationData, results: [ChatContextResult], current: ChatContextResult, selectionContext: TGMediaSelectionContext?, editingContext: TGMediaEditingContext, updateHiddenMedia: @escaping (String?) -> Void, initialLayout: ContainerViewLayout?, transitionHostView: @escaping () -> UIView?, transitionView: @escaping (ChatContextResult) -> UIView?, completed: @escaping (ChatContextResult) -> Void, presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?, present: (ViewController, Any?) -> Void) {
|
||||||
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
let legacyController = LegacyController(presentation: .custom, theme: presentationData.theme, initialLayout: nil)
|
||||||
legacyController.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
|
legacyController.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style
|
||||||
|
|
||||||
@ -335,7 +335,7 @@ func presentLegacyWebSearchGallery(context: AccountContext, peer: Peer?, chatLoc
|
|||||||
|
|
||||||
let (items, focusItem) = galleryItems(account: context.account, results: results, current: current, selectionContext: selectionContext, editingContext: editingContext)
|
let (items, focusItem) = galleryItems(account: context.account, results: results, current: current, selectionContext: selectionContext, editingContext: editingContext)
|
||||||
|
|
||||||
let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: false, allowCaptionEntities: true, hasTimer: false, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: false, hasCamera: false, recipientName: peer.flatMap(EnginePeer.init)?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))!
|
let model = TGMediaPickerGalleryModel(context: legacyController.context, items: items, focus: focusItem, selectionContext: selectionContext, editingContext: editingContext, hasCaptions: false, allowCaptionEntities: true, hasTimer: false, onlyCrop: false, inhibitDocumentCaptions: false, hasSelectionPanel: false, hasCamera: false, recipientName: peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder))!
|
||||||
model.stickersContext = paintStickersContext
|
model.stickersContext = paintStickersContext
|
||||||
if let peer = peer, let chatLocation = chatLocation {
|
if let peer = peer, let chatLocation = chatLocation {
|
||||||
model.suggestionContext = legacySuggestionContext(context: context, peerId: peer.id, chatLocation: chatLocation)
|
model.suggestionContext = legacySuggestionContext(context: context, peerId: peer.id, chatLocation: chatLocation)
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import Postbox
|
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Display
|
import Display
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
@ -10,7 +9,7 @@ import TelegramUIPreferences
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AccountContext
|
import AccountContext
|
||||||
|
|
||||||
public func requestContextResults(context: AccountContext, botId: PeerId, query: String, peerId: PeerId, offset: String = "", existingResults: ChatContextResultCollection? = nil, incompleteResults: Bool = false, staleCachedResults: Bool = false, limit: Int = 60) -> Signal<RequestChatContextResultsResult?, NoError> {
|
public func requestContextResults(context: AccountContext, botId: EnginePeer.Id, query: String, peerId: EnginePeer.Id, offset: String = "", existingResults: ChatContextResultCollection? = nil, incompleteResults: Bool = false, staleCachedResults: Bool = false, limit: Int = 60) -> Signal<RequestChatContextResultsResult?, NoError> {
|
||||||
return context.engine.messages.requestChatContextResults(botId: botId, peerId: peerId, query: query, offset: offset, incompleteResults: incompleteResults, staleCachedResults: staleCachedResults)
|
return context.engine.messages.requestChatContextResults(botId: botId, peerId: peerId, query: query, offset: offset, incompleteResults: incompleteResults, staleCachedResults: staleCachedResults)
|
||||||
|> `catch` { error -> Signal<RequestChatContextResultsResult?, NoError> in
|
|> `catch` { error -> Signal<RequestChatContextResultsResult?, NoError> in
|
||||||
return .single(nil)
|
return .single(nil)
|
||||||
@ -125,7 +124,7 @@ public final class WebSearchController: ViewController {
|
|||||||
|
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let mode: WebSearchControllerMode
|
private let mode: WebSearchControllerMode
|
||||||
private let peer: Peer?
|
private let peer: EnginePeer?
|
||||||
private let chatLocation: ChatLocation?
|
private let chatLocation: ChatLocation?
|
||||||
private let configuration: SearchBotsConfiguration
|
private let configuration: SearchBotsConfiguration
|
||||||
|
|
||||||
@ -156,7 +155,7 @@ public final class WebSearchController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: Peer?, chatLocation: ChatLocation?, configuration: SearchBotsConfiguration, mode: WebSearchControllerMode) {
|
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peer: EnginePeer?, chatLocation: ChatLocation?, configuration: SearchBotsConfiguration, mode: WebSearchControllerMode) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.mode = mode
|
self.mode = mode
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
@ -446,11 +445,11 @@ public final class WebSearchController: ViewController {
|
|||||||
|
|
||||||
let context = self.context
|
let context = self.context
|
||||||
let contextBot = self.context.engine.peers.resolvePeerByName(name: name)
|
let contextBot = self.context.engine.peers.resolvePeerByName(name: name)
|
||||||
|> mapToSignal { peer -> Signal<Peer?, NoError> in
|
|> mapToSignal { peer -> Signal<EnginePeer?, NoError> in
|
||||||
return .single(peer?._asPeer())
|
return .single(peer)
|
||||||
}
|
}
|
||||||
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> in
|
|> mapToSignal { peer -> Signal<(ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult?, NoError> in
|
||||||
if let user = peer as? TelegramUser, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
|
if case let .user(user) = peer, let botInfo = user.botInfo, let _ = botInfo.inlinePlaceholder {
|
||||||
let results = requestContextResults(context: context, botId: user.id, query: query, peerId: peerId, limit: 64)
|
let results = requestContextResults(context: context, botId: user.id, query: query, peerId: peerId, limit: 64)
|
||||||
|> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
|> map { results -> (ChatPresentationInputQueryResult?) -> ChatPresentationInputQueryResult? in
|
||||||
return { _ in
|
return { _ in
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Postbox
|
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Display
|
import Display
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
@ -118,7 +117,7 @@ private func preparedWebSearchRecentTransition(from fromEntries: [WebSearchRecen
|
|||||||
|
|
||||||
class WebSearchControllerNode: ASDisplayNode {
|
class WebSearchControllerNode: ASDisplayNode {
|
||||||
private let context: AccountContext
|
private let context: AccountContext
|
||||||
private let peer: Peer?
|
private let peer: EnginePeer?
|
||||||
private let chatLocation: ChatLocation?
|
private let chatLocation: ChatLocation?
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
private var strings: PresentationStrings
|
private var strings: PresentationStrings
|
||||||
@ -172,7 +171,7 @@ class WebSearchControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
var presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?
|
var presentStickers: ((@escaping (TelegramMediaFile, Bool, UIView, CGRect) -> Void) -> TGPhotoPaintStickersScreen?)?
|
||||||
|
|
||||||
init(context: AccountContext, presentationData: PresentationData, controllerInteraction: WebSearchControllerInteraction, peer: Peer?, chatLocation: ChatLocation?, mode: WebSearchMode) {
|
init(context: AccountContext, presentationData: PresentationData, controllerInteraction: WebSearchControllerInteraction, peer: EnginePeer?, chatLocation: ChatLocation?, mode: WebSearchMode) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.theme = presentationData.theme
|
self.theme = presentationData.theme
|
||||||
self.strings = presentationData.strings
|
self.strings = presentationData.strings
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import Display
|
import Display
|
||||||
import QuickLook
|
import QuickLook
|
||||||
import Postbox
|
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
@ -38,7 +37,7 @@ struct WebSearchGalleryEntry: Equatable {
|
|||||||
switch self.result {
|
switch self.result {
|
||||||
case let .externalReference(externalReference):
|
case let .externalReference(externalReference):
|
||||||
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = externalReference.thumbnail?.resource, let dimensions = content.dimensions {
|
if let content = externalReference.content, externalReference.type == "gif", let thumbnailResource = externalReference.thumbnail?.resource, let dimensions = content.dimensions {
|
||||||
let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
|
let fileReference = FileMediaReference.standalone(media: TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: 0), partialReference: nil, resource: content.resource, previewRepresentations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: thumbnailResource, progressiveSizes: [], immediateThumbnailData: nil)], videoThumbnails: [], immediateThumbnailData: nil, mimeType: "video/mp4", size: nil, attributes: [.Animated, .Video(duration: 0, size: dimensions, flags: [])]))
|
||||||
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, index: self.index, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: fileReference, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
return WebSearchVideoGalleryItem(context: context, presentationData: presentationData, index: self.index, result: self.result, content: NativeVideoContent(id: .contextResult(self.result.queryId, self.result.id), fileReference: fileReference, loopVideo: true, enableSound: false, fetchAutomatically: true), controllerInteraction: controllerInteraction)
|
||||||
}
|
}
|
||||||
case let .internalReference(internalReference):
|
case let .internalReference(internalReference):
|
||||||
@ -99,7 +98,7 @@ class WebSearchGalleryController: ViewController {
|
|||||||
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
|
private let replaceRootController: (ViewController, Promise<Bool>?) -> Void
|
||||||
private let baseNavigationController: NavigationController?
|
private let baseNavigationController: NavigationController?
|
||||||
|
|
||||||
init(context: AccountContext, peer: Peer?, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext, entries: [WebSearchGalleryEntry], centralIndex: Int, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, sendCurrent: @escaping (ChatContextResult) -> Void) {
|
init(context: AccountContext, peer: EnginePeer?, selectionState: TGMediaSelectionContext?, editingState: TGMediaEditingContext, entries: [WebSearchGalleryEntry], centralIndex: Int, replaceRootController: @escaping (ViewController, Promise<Bool>?) -> Void, baseNavigationController: NavigationController?, sendCurrent: @escaping (ChatContextResult) -> Void) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.replaceRootController = replaceRootController
|
self.replaceRootController = replaceRootController
|
||||||
self.baseNavigationController = baseNavigationController
|
self.baseNavigationController = baseNavigationController
|
||||||
@ -115,7 +114,7 @@ class WebSearchGalleryController: ViewController {
|
|||||||
self?.dismiss(forceAway: true)
|
self?.dismiss(forceAway: true)
|
||||||
}, selectionState: selectionState, editingState: editingState)
|
}, selectionState: selectionState, editingState: editingState)
|
||||||
|
|
||||||
if let title = peer.flatMap(EnginePeer.init)?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) {
|
if let title = peer?.displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder) {
|
||||||
let recipientNode = GalleryNavigationRecipientNode(color: .white, title: title)
|
let recipientNode = GalleryNavigationRecipientNode(color: .white, title: title)
|
||||||
let leftItem = UIBarButtonItem(customDisplayNode: recipientNode)
|
let leftItem = UIBarButtonItem(customDisplayNode: recipientNode)
|
||||||
self.navigationItem.leftBarButtonItem = leftItem
|
self.navigationItem.leftBarButtonItem = leftItem
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import Postbox
|
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import AsyncDisplayKit
|
|||||||
import Display
|
import Display
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Postbox
|
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import CheckNode
|
import CheckNode
|
||||||
import PhotoResources
|
import PhotoResources
|
||||||
@ -51,7 +50,7 @@ final class WebSearchItemNode: GridItemNode {
|
|||||||
|
|
||||||
private let fetchStatusDisposable = MetaDisposable()
|
private let fetchStatusDisposable = MetaDisposable()
|
||||||
private let fetchDisposable = MetaDisposable()
|
private let fetchDisposable = MetaDisposable()
|
||||||
private var resourceStatus: MediaResourceStatus?
|
private var resourceStatus: EngineMediaResource.FetchStatus?
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
self.imageNodeBackground = ASDisplayNode()
|
self.imageNodeBackground = ASDisplayNode()
|
||||||
@ -137,7 +136,7 @@ final class WebSearchItemNode: GridItemNode {
|
|||||||
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: [], immediateThumbnailData: nil))
|
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource, progressiveSizes: [], immediateThumbnailData: nil))
|
||||||
}
|
}
|
||||||
if !representations.isEmpty {
|
if !representations.isEmpty {
|
||||||
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
let tmpImage = TelegramMediaImage(imageId: EngineMedia.Id(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||||
updateImageSignal = mediaGridMessagePhoto(account: item.account, photoReference: .standalone(media: tmpImage))
|
updateImageSignal = mediaGridMessagePhoto(account: item.account, photoReference: .standalone(media: tmpImage))
|
||||||
} else {
|
} else {
|
||||||
updateImageSignal = .complete()
|
updateImageSignal = .complete()
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import Foundation
|
|||||||
import UIKit
|
import UIKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import Postbox
|
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import SearchBarNode
|
import SearchBarNode
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import UIKit
|
import UIKit
|
||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Postbox
|
|
||||||
import Display
|
import Display
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import AsyncDisplayKit
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramCore
|
import TelegramCore
|
||||||
import Display
|
import Display
|
||||||
import Postbox
|
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import RadialStatusNode
|
import RadialStatusNode
|
||||||
@ -81,7 +80,7 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
private let statusDisposable = MetaDisposable()
|
private let statusDisposable = MetaDisposable()
|
||||||
|
|
||||||
private let fetchDisposable = MetaDisposable()
|
private let fetchDisposable = MetaDisposable()
|
||||||
private var fetchStatus: MediaResourceStatus?
|
private var fetchStatus: EngineMediaResource.FetchStatus?
|
||||||
private var fetchControls: FetchControls?
|
private var fetchControls: FetchControls?
|
||||||
|
|
||||||
var playbackCompleted: (() -> Void)?
|
var playbackCompleted: (() -> Void)?
|
||||||
@ -153,10 +152,10 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
func setupItem(_ item: WebSearchVideoGalleryItem) {
|
func setupItem(_ item: WebSearchVideoGalleryItem) {
|
||||||
if self.item?.content.id != item.content.id {
|
if self.item?.content.id != item.content.id {
|
||||||
var isAnimated = false
|
var isAnimated = false
|
||||||
var mediaResource: MediaResource?
|
var mediaResource: EngineMediaResource?
|
||||||
if let content = item.content as? NativeVideoContent {
|
if let content = item.content as? NativeVideoContent {
|
||||||
isAnimated = content.fileReference.media.isAnimated
|
isAnimated = content.fileReference.media.isAnimated
|
||||||
mediaResource = content.fileReference.media.resource
|
mediaResource = EngineMediaResource(content.fileReference.media.resource)
|
||||||
}
|
}
|
||||||
|
|
||||||
if let videoNode = self.videoNode {
|
if let videoNode = self.videoNode {
|
||||||
@ -175,9 +174,12 @@ final class WebSearchVideoGalleryItemNode: ZoomableContentGalleryItemNode {
|
|||||||
videoNode.canAttachContent = true
|
videoNode.canAttachContent = true
|
||||||
|
|
||||||
self.requiresDownload = true
|
self.requiresDownload = true
|
||||||
var mediaFileStatus: Signal<MediaResourceStatus?, NoError> = .single(nil)
|
var mediaFileStatus: Signal<EngineMediaResource.FetchStatus?, NoError> = .single(nil)
|
||||||
if let mediaResource = mediaResource {
|
if let mediaResource = mediaResource {
|
||||||
mediaFileStatus = item.context.account.postbox.mediaBox.resourceStatus(mediaResource)
|
mediaFileStatus = item.context.account.postbox.mediaBox.resourceStatus(mediaResource._asResource())
|
||||||
|
|> map { status in
|
||||||
|
return EngineMediaResource.FetchStatus(status)
|
||||||
|
}
|
||||||
|> map(Optional.init)
|
|> map(Optional.init)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"app": "8.1.2",
|
"app": "8.1.2",
|
||||||
"bazel": "4.0.0",
|
"bazel": "4.0.0",
|
||||||
"xcode": "12.5.1"
|
"xcode": "13.0"
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user