Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2019-12-10 21:17:13 +04:00
commit eb406e4032
39 changed files with 572 additions and 60 deletions

View File

@ -5143,7 +5143,7 @@ Any member of this group will be able to see messages in the channel.";
"Settings.Devices" = "Devices";
"Settings.AddDevice" = "Scan QR";
"AuthSessions.DevicesTitle" = "Devices";
"AuthSessions.AddDevice" = "Add Device";
"AuthSessions.AddDevice" = "Scan QR";
"AuthSessions.AddDevice.ScanInfo" = "Scan a QR code to log into\nthis account on another device.";
"AuthSessions.AddDevice.ScanTitle" = "Scan QR Code";
"AuthSessions.AddDevice.InvalidQRCode" = "Invalid QR Code";

View File

@ -19,7 +19,7 @@ private let colorKeyRegex = try? NSRegularExpression(pattern: "\"k\":\\[[\\d\\.]
private func transformedWithTheme(data: Data, theme: PresentationTheme) -> Data {
if var string = String(data: data, encoding: .utf8) {
var colors: [UIColor] = [0x333333, 0xFFFFFF, 0x50A7EA, 0x212121].map { UIColor(rgb: $0) }
let replacementColors: [UIColor] = [theme.list.itemPrimaryTextColor.mixedWith(.white, alpha: 0.2), theme.list.plainBackgroundColor, theme.list.itemAccentColor, theme.list.itemPrimaryTextColor.mixedWith(.white, alpha: 0.12)]
let replacementColors: [UIColor] = [theme.list.itemPrimaryTextColor.mixedWith(.white, alpha: 0.2), theme.list.plainBackgroundColor, theme.list.itemAccentColor, theme.list.plainBackgroundColor]
func colorToString(_ color: UIColor) -> String {
var r: CGFloat = 0.0
@ -284,6 +284,8 @@ private final class AuthDataTransferSplashScreenNode: ViewControllerTracingNode
updateInHierarchy = { [weak self] value in
if value {
self?.animationNode?.play()
} else {
self?.animationNode?.reset()
}
}
}
@ -354,6 +356,11 @@ private final class AuthDataTransferSplashScreenNode: ViewControllerTracingNode
contentY += iconSize.height + iconSpacing
if let animationNode = self.animationNode {
transition.updateFrameAdditive(node: animationNode, frame: iconFrame)
if iconFrame.minY < 0.0 {
transition.updateAlpha(node: animationNode, alpha: 0.0)
} else {
transition.updateAlpha(node: animationNode, alpha: 1.0)
}
}
let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: contentY), size: titleSize)

View File

@ -23,6 +23,9 @@ static_library(
"//submodules/SwipeToDismissGesture:SwipeToDismissGesture",
"//submodules/CheckNode:CheckNode",
"//submodules/AppBundle:AppBundle",
"//submodules/StickerPackPreviewUI:StickerPackPreviewUI",
"//submodules/OverlayStatusController:OverlayStatusController",
"//submodules/PresentationDataUtils:PresentationDataUtils",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",

View File

@ -10,6 +10,10 @@ import TelegramPresentationData
import AccountContext
import RadialStatusNode
import PhotoResources
import AppBundle
import StickerPackPreviewUI
import OverlayStatusController
import PresentationDataUtils
enum ChatMediaGalleryThumbnail: Equatable {
case image(ImageMediaReference)
@ -156,6 +160,7 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
private let imageNode: TransformImageNode
fileprivate let _ready = Promise<Void>()
fileprivate let _title = Promise<String>()
fileprivate let _rightBarButtonItem = Promise<UIBarButtonItem?>()
private let statusNodeContainer: HighlightableButtonNode
private let statusNode: RadialStatusNode
private let footerContentNode: ChatItemGalleryFooterContentNode
@ -230,10 +235,53 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
} else {
self._ready.set(.single(Void()))
}
if imageReference.media.flags.contains(.hasStickers) {
let rightBarButtonItem = UIBarButtonItem(image: UIImage(bundleImageName: "Media Gallery/Stickers"), style: .plain, target: self, action: #selector(self.openStickersButtonPressed))
self._rightBarButtonItem.set(.single(rightBarButtonItem))
}
}
self.contextAndMedia = (self.context, imageReference.abstract)
}
@objc func openStickersButtonPressed() {
guard let (context, media) = self.contextAndMedia else {
return
}
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
guard let strongSelf = self else {
return EmptyDisposable
}
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
(strongSelf.baseNavigationController()?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
return ActionDisposable { [weak controller] in
Queue.mainQueue().async() {
controller?.dismiss()
}
}
}
|> runOn(Queue.mainQueue())
|> delay(0.15, queue: Queue.mainQueue())
let progressDisposable = progressSignal.start()
let signal = stickerPacksAttachedToMedia(account: context.account, media: media)
|> afterDisposed {
Queue.mainQueue().async {
progressDisposable.dispose()
}
}
let _ = (signal
|> deliverOnMainQueue).start(next: { [weak self] packs in
guard let strongSelf = self, !packs.isEmpty else {
return
}
let baseNavigationController = strongSelf.baseNavigationController()
baseNavigationController?.view.endEditing(true)
let controller = StickerPackScreen(context: context, stickerPacks: packs, sendSticker: nil)
(baseNavigationController?.topViewController as? ViewController)?.present(controller, in: .window(.root), with: nil)
})
}
func setFile(context: AccountContext, fileReference: FileMediaReference) {
if self.contextAndMedia == nil || !self.contextAndMedia!.1.media.isEqual(to: fileReference.media) {
if var largestSize = fileReference.media.dimensions {
@ -447,6 +495,10 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode {
return self._title.get()
}
override func rightBarButtonItem() -> Signal<UIBarButtonItem?, NoError> {
return self._rightBarButtonItem.get()
}
override func footerContent() -> Signal<GalleryFooterContentNode?, NoError> {
return .single(self.footerContentNode)
}

View File

@ -872,12 +872,12 @@ final class InstantPageControllerNode: ASDisplayNode, UIScrollViewDelegate {
private func longPressMedia(_ media: InstantPageMedia) {
let controller = ContextMenuController(actions: [ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.strings.Conversation_ContextMenuCopy), action: { [weak self] in
if let strongSelf = self, let image = media.media as? TelegramMediaImage {
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: nil, partialReference: nil)
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
let _ = copyToPasteboard(context: strongSelf.context, postbox: strongSelf.context.account.postbox, mediaReference: .standalone(media: media)).start()
}
}), ContextMenuAction(content: .text(title: self.strings.Conversation_LinkDialogSave, accessibilityLabel: self.strings.Conversation_LinkDialogSave), action: { [weak self] in
if let strongSelf = self, let image = media.media as? TelegramMediaImage {
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: nil, partialReference: nil)
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
let _ = saveToCameraRoll(context: strongSelf.context, postbox: strongSelf.context.account.postbox, mediaReference: .standalone(media: media)).start()
}
}), ContextMenuAction(content: .text(title: self.strings.Conversation_ContextMenuShare, accessibilityLabel: self.strings.Conversation_ContextMenuShare), action: { [weak self] in

View File

@ -120,7 +120,7 @@ public struct InstantPageGalleryEntry: Equatable {
if let dimensions = file.dimensions {
representations.append(TelegramMediaImageRepresentation(dimensions: dimensions, resource: file.resource))
}
let image = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: file.immediateThumbnailData, reference: nil, partialReference: nil)
let image = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: file.immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
return InstantImageGalleryItem(context: context, presentationData: presentationData, imageReference: .webPage(webPage: WebpageReference(webPage), media: image), caption: caption, credit: credit, location: self.location, openUrl: openUrl, openUrlOptions: openUrlOptions)
}
} else if let embedWebpage = self.media.media as? TelegramMediaWebpage, case let .Loaded(webpageContent) = embedWebpage.content {

View File

@ -47,7 +47,7 @@ final class InstantPagePlayableVideoNode: ASDisplayNode, InstantPageNode, Galler
var imageReference: ImageMediaReference?
if let file = media.media as? TelegramMediaFile, let presentation = smallestImageRepresentation(file.previewRepresentations) {
let image = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [presentation], immediateThumbnailData: nil, reference: nil, partialReference: nil)
let image = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [presentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
imageReference = ImageMediaReference.webPage(webPage: WebpageReference(webPage), media: image)
}

View File

@ -418,7 +418,7 @@ private func loadLegacyMessages(account: TemporaryAccount, basePath: String, acc
}
}
parsedMedia.append(TelegramMediaImage(imageId: mediaId, representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil))
parsedMedia.append(TelegramMediaImage(imageId: mediaId, representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: []))
} else if let item = item as? TGVideoMediaAttachment {
let mediaId = MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64())
var representations: [TelegramMediaImageRepresentation] = []

View File

@ -13,6 +13,7 @@ import AccountContext
import ImageCompression
import MimeTypes
import LocalMediaResources
import LegacyUI
public func guessMimeTypeByFileExtension(_ ext: String) -> String {
return TGMimeTypeMap.mimeType(forExtension: ext) ?? "application/binary"
@ -132,13 +133,15 @@ public func legacyAssetPickerItemGenerator() -> ((Any?, String?, [Any]?, String?
}
// let stickers = (dict["stickers"] as? [TGDocumentMediaAttachment]).map { document -> FileMediaReference in
// if let sticker = stickerFromLegacyDocument(document) {
// return FileMediaReference.standalone(media: sticker)
// }
// }
let stickers = (dict["stickers"] as? [TGDocumentMediaAttachment])?.compactMap { document -> FileMediaReference? in
if let sticker = stickerFromLegacyDocument(document) {
return FileMediaReference.standalone(media: sticker)
} else {
return nil
}
} ?? []
var result: [AnyHashable : Any] = [:]
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .image(image), thumbnail: thumbnail, caption: caption, stickers: []), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
result["item" as NSString] = LegacyAssetItemWrapper(item: .image(data: .image(image), thumbnail: thumbnail, caption: caption, stickers: stickers), timer: (dict["timer"] as? NSNumber)?.intValue, groupedId: (dict["groupedId"] as? NSNumber)?.int64Value)
return result
} else if (dict["type"] as! NSString) == "cloudPhoto" {
let asset = dict["asset"] as! TGMediaAsset
@ -318,9 +321,24 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
let resource = LocalFileReferenceMediaResource(localFilePath: tempFilePath, randomId: randomId)
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource))
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil)
var imageFlags: TelegramMediaImageFlags = []
var stickerFiles: [TelegramMediaFile] = []
if !stickers.isEmpty {
for fileReference in stickers {
stickerFiles.append(fileReference.media)
}
}
var attributes: [MessageAttribute] = []
if !stickerFiles.isEmpty {
attributes.append(EmbeddedMediaStickersMessageAttribute(files: stickerFiles))
imageFlags.insert(.hasStickers)
}
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: imageFlags)
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))
}
@ -337,7 +355,7 @@ public func legacyAssetPickerEnqueueMessages(account: Account, signals: [Any]) -
let resource = PhotoLibraryMediaResource(localIdentifier: asset.localIdentifier, uniqueId: arc4random64())
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(scaledSize), resource: resource))
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil)
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
var attributes: [MessageAttribute] = []
if let timer = item.timer, timer > 0 && timer <= 60 {
attributes.append(AutoremoveTimeoutMessageAttribute(timeout: Int32(timer), countdownBeginTime: nil))

View File

@ -8,7 +8,7 @@ import SwiftSignalKit
import Display
import StickerResources
func stickerFromLegacyDocument(_ documentAttachment: TGDocumentMediaAttachment) -> TelegramMediaFile? {
public func stickerFromLegacyDocument(_ documentAttachment: TGDocumentMediaAttachment) -> TelegramMediaFile? {
if documentAttachment.isSticker() {
for case let sticker as TGDocumentAttributeSticker in documentAttachment.attributes {
var attributes: [TelegramMediaFileAttribute] = []

View File

@ -115,7 +115,7 @@ final class ThemeGridSearchItemNode: GridItemNode {
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
}
if !representations.isEmpty {
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
updateImageSignal = mediaGridMessagePhoto(account: item.account, photoReference: .standalone(media: tmpImage), fullRepresentationSize: CGSize(width: 512, height: 512))
} else {
updateImageSignal = .complete()

View File

@ -418,7 +418,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode {
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource))
}
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
signal = chatMessagePhoto(postbox: context.account.postbox, photoReference: .standalone(media: tmpImage))
fetchSignal = fetchedMediaResource(mediaBox: context.account.postbox.mediaBox, reference: .media(media: .standalone(media: tmpImage), resource: imageResource))

View File

@ -480,7 +480,7 @@ public final class ShareController: ViewController {
if !text.isEmpty {
messages.append(.message(text: text, attributes: [], mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil))
}
messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil)), replyToMessageId: nil, localGroupingKey: nil))
messages.append(.message(text: "", attributes: [], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), replyToMessageId: nil, localGroupingKey: nil))
shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messages))
}
case let .media(mediaReference):
@ -586,7 +586,7 @@ public final class ShareController: ViewController {
case let .quote(text, url):
collectableItems.append(CollectableExternalShareItem(url: "", text: "\"\(text)\"\n\n\(url)", author: nil, timestamp: nil, mediaReference: nil))
case let .image(representations):
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil)
let media = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: .standalone(media: media)))
case let .media(mediaReference):
collectableItems.append(CollectableExternalShareItem(url: "", text: "", author: nil, timestamp: nil, mediaReference: mediaReference))
@ -770,7 +770,7 @@ public final class ShareController: ViewController {
}
private func saveToCameraRoll(representations: [ImageRepresentationWithReference]) {
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil)
let media = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
let context: AccountContext
if self.currentContext.account.id == self.currentAccount.id {
context = self.currentContext

View File

@ -0,0 +1,19 @@
load("//Config:buck_rule_macros.bzl", "framework")
framework(
name = "SpotlightSupport",
srcs = glob([
"Sources/**/*.swift",
]),
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared",
"//submodules/Postbox:Postbox#shared",
"//submodules/SyncCore:SyncCore#shared",
"//submodules/TelegramCore:TelegramCore#shared",
"//submodules/Display:Display#shared",
],
frameworks = [
"$SDKROOT/System/Library/Frameworks/Foundation.framework",
"$SDKROOT/System/Library/Frameworks/CoreSpotlight.framework",
],
)

View File

@ -349,7 +349,7 @@ private final class StickerPackContainer: ASDisplayNode {
let titleAreaInset: CGFloat = 50.0
var actionAreaHeight: CGFloat = 0.0
actionAreaHeight += insets.bottom
actionAreaHeight += insets.bottom + 12.0
transition.updateFrame(node: self.buttonNode, frame: CGRect(origin: CGPoint(x: buttonSideInset, y: layout.size.height - actionAreaHeight - buttonHeight), size: CGSize(width: layout.size.width - buttonSideInset * 2.0, height: buttonHeight)))
actionAreaHeight += buttonHeight
@ -550,7 +550,9 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
return
}
if index == strongSelf.selectedStickerPackIndex {
strongSelf.containerLayoutUpdated(layout, transition: transition)
let modalProgress = strongSelf.containers[strongSelf.selectedStickerPackIndex].modalProgress
strongSelf.modalProgressUpdated(modalProgress, transition)
strongSelf.containerLayoutUpdated(layout, transition: .immediate)
}
}, presentInGlobalOverlay: presentInGlobalOverlay,
sendSticker: sendSticker)
@ -610,15 +612,12 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
var scaledOffset: CGFloat = 0.0
scaledOffset = -CGFloat(indexOffset) * (1.0 - expandProgress) * (scaledInset * 2.0) + CGFloat(indexOffset) * scaledDistance
transition.updateFrame(node: container, frame: CGRect(origin: CGPoint(x: CGFloat(indexOffset) * layout.size.width + self.relativeToSelectedStickerPackTransition + scaledOffset, y: containerVerticalOffset), size: layout.size))
transition.updateSublayerTransformScale(node: container, scale: containerScale)
transition.updateFrame(node: container, frame: CGRect(origin: CGPoint(x: CGFloat(indexOffset) * layout.size.width + self.relativeToSelectedStickerPackTransition + scaledOffset, y: containerVerticalOffset), size: layout.size), beginWithCurrentState: true)
transition.updateSublayerTransformScaleAndOffset(node: container, scale: containerScale, offset: CGPoint(), beginWithCurrentState: true)
if container.validLayout?.0 != layout {
container.updateLayout(layout: layout, transition: transition)
}
}
let modalProgress = self.containers[self.selectedStickerPackIndex].modalProgress
self.modalProgressUpdated(modalProgress, transition)
}
@objc private func panGesture(_ recognizer: UIPanGestureRecognizer) {

View File

@ -0,0 +1,18 @@
import Foundation
import Postbox
public class EmbeddedMediaStickersMessageAttribute: MessageAttribute {
public let files: [TelegramMediaFile]
public init(files: [TelegramMediaFile]) {
self.files = files
}
required public init(decoder: PostboxDecoder) {
self.files = decoder.decodeObjectArrayWithDecoderForKey("files")
}
public func encode(_ encoder: PostboxEncoder) {
encoder.encodeObjectArray(self.files, forKey: "files")
}
}

View File

@ -39,6 +39,16 @@ public enum TelegramMediaImageReference: PostboxCoding, Equatable {
}
}
public struct TelegramMediaImageFlags: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let hasStickers = TelegramMediaImageFlags(rawValue: 1 << 0)
}
public final class TelegramMediaImage: Media, Equatable {
public let imageId: MediaId
public let representations: [TelegramMediaImageRepresentation]
@ -46,17 +56,19 @@ public final class TelegramMediaImage: Media, Equatable {
public let reference: TelegramMediaImageReference?
public let partialReference: PartialMediaReference?
public let peerIds: [PeerId] = []
public let flags: TelegramMediaImageFlags
public var id: MediaId? {
return self.imageId
}
public init(imageId: MediaId, representations: [TelegramMediaImageRepresentation], immediateThumbnailData: Data?, reference: TelegramMediaImageReference?, partialReference: PartialMediaReference?) {
public init(imageId: MediaId, representations: [TelegramMediaImageRepresentation], immediateThumbnailData: Data?, reference: TelegramMediaImageReference?, partialReference: PartialMediaReference?, flags: TelegramMediaImageFlags) {
self.imageId = imageId
self.representations = representations
self.immediateThumbnailData = immediateThumbnailData
self.reference = reference
self.partialReference = partialReference
self.flags = flags
}
public init(decoder: PostboxDecoder) {
@ -65,6 +77,7 @@ public final class TelegramMediaImage: Media, Equatable {
self.immediateThumbnailData = decoder.decodeDataForKey("itd")
self.reference = decoder.decodeObjectForKey("rf", decoder: { TelegramMediaImageReference(decoder: $0) }) as? TelegramMediaImageReference
self.partialReference = decoder.decodeAnyObjectForKey("prf", decoder: { PartialMediaReference(decoder: $0) }) as? PartialMediaReference
self.flags = TelegramMediaImageFlags(rawValue: decoder.decodeInt32ForKey("fl", orElse: 0))
}
public func encode(_ encoder: PostboxEncoder) {
@ -87,6 +100,7 @@ public final class TelegramMediaImage: Media, Equatable {
} else {
encoder.encodeNil(forKey: "prf")
}
encoder.encodeInt32(self.flags.rawValue, forKey: "fl")
}
public func representationForDisplayAtSize(_ size: PixelDimensions) -> TelegramMediaImageRepresentation? {
@ -130,6 +144,9 @@ public final class TelegramMediaImage: Media, Equatable {
if self.partialReference != other.partialReference {
return false
}
if self.flags != other.flags {
return false
}
return true
}
return false
@ -152,6 +169,9 @@ public final class TelegramMediaImage: Media, Equatable {
if self.partialReference != other.partialReference {
return false
}
if self.flags != other.flags {
return false
}
return true
}
return false
@ -162,7 +182,7 @@ public final class TelegramMediaImage: Media, Equatable {
}
public func withUpdatedPartialReference(_ partialReference: PartialMediaReference?) -> TelegramMediaImage {
return TelegramMediaImage(imageId: self.imageId, representations: self.representations, immediateThumbnailData: self.immediateThumbnailData, reference: self.reference, partialReference: partialReference)
return TelegramMediaImage(imageId: self.imageId, representations: self.representations, immediateThumbnailData: self.immediateThumbnailData, reference: self.reference, partialReference: partialReference, flags: self.flags)
}
}

View File

@ -149,6 +149,7 @@ private var declaredEncodables: Void = {
declareEncodable(RestrictedContentMessageAttribute.self, f: { RestrictedContentMessageAttribute(decoder: $0) })
declareEncodable(SendScheduledMessageImmediatelyAction.self, f: { SendScheduledMessageImmediatelyAction(decoder: $0) })
declareEncodable(WalletCollection.self, f: { WalletCollection(decoder: $0) })
declareEncodable(EmbeddedMediaStickersMessageAttribute.self, f: { EmbeddedMediaStickersMessageAttribute(decoder: $0) })
return
}()

View File

@ -55,7 +55,7 @@ private func convertForwardedMediaForSecretChat(_ media: Media) -> Media {
if let file = media as? TelegramMediaFile {
return TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: file.partialReference, resource: file.resource, previewRepresentations: file.previewRepresentations, immediateThumbnailData: file.immediateThumbnailData, mimeType: file.mimeType, size: file.size, attributes: file.attributes)
} else if let image = media as? TelegramMediaImage {
return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference)
return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: image.representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference, flags: [])
} else {
return media
}
@ -82,6 +82,8 @@ private func filterMessageAttributesForOutgoingMessage(_ attributes: [MessageAtt
return true
case _ as OutgoingScheduleInfoMessageAttribute:
return true
case _ as EmbeddedMediaStickersMessageAttribute:
return true
default:
return false
}

View File

@ -55,7 +55,7 @@ public func outgoingMessageWithChatContextResult(to peerId: PeerId, results: Cha
arc4random_buf(&randomId, 8)
let thumbnailResource = thumbnail.resource
let imageDimensions = thumbnail.dimensions ?? PixelDimensions(width: 128, height: 128)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: thumbnailResource)], immediateThumbnailData: nil, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: randomId), representations: [TelegramMediaImageRepresentation(dimensions: imageDimensions, resource: thumbnailResource)], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
return .message(text: caption, attributes: attributes, mediaReference: .standalone(media: tmpImage), replyToMessageId: nil, localGroupingKey: nil)
} else {
return .message(text: caption, attributes: attributes, mediaReference: nil, replyToMessageId: nil, localGroupingKey: nil)

View File

@ -350,6 +350,22 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
flags |= 1 << 1
ttlSeconds = autoremoveAttribute.timeout
}
var stickers: [Api.InputDocument]?
for attribute in attributes {
if let attribute = attribute as? EmbeddedMediaStickersMessageAttribute {
var stickersValue: [Api.InputDocument] = []
for file in attribute.files {
if let resource = file.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
stickersValue.append(Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)))
}
}
if !stickersValue.isEmpty {
stickers = stickersValue
flags |= 1 << 0
}
break
}
}
return postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
@ -357,10 +373,10 @@ private func uploadedMediaImageContent(network: Network, postbox: Postbox, trans
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let inputPeer = inputPeer {
if autoremoveAttribute != nil {
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil)))
}
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: nil, ttlSeconds: ttlSeconds)))
return network.request(Api.functions.messages.uploadMedia(peer: inputPeer, media: Api.InputMedia.inputMediaUploadedPhoto(flags: flags, file: file, stickers: stickers, ttlSeconds: ttlSeconds)))
|> mapError { _ -> PendingMessageUploadError in return .generic }
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
switch result {

View File

@ -712,7 +712,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
resources.append((resource, thumb.makeData()))
}
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size)))
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil)
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
parsedMedia.append(image)
}
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
@ -910,7 +910,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
resources.append((resource, thumb.makeData()))
}
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size)))
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil)
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
parsedMedia.append(image)
}
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):
@ -1144,7 +1144,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32
resources.append((resource, thumb.makeData()))
}
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: w, height: h), resource: file.resource(key: SecretFileEncryptionKey(aesKey: key.makeData(), aesIv: iv.makeData()), decryptedSize: size)))
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil)
let image = TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudSecretImage, id: file.id), representations: representations, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
parsedMedia.append(image)
}
case let .decryptedMessageMediaAudio(duration, mimeType, size, key, iv):

View File

@ -102,7 +102,7 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri
|> mapToSignal { result -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch result {
case let .encryptedFile(id, accessHash, size, dcId, _):
return .single(.result(.media(.standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: Int32(data.count), datacenterId: Int(dcId), key: key))], immediateThumbnailData: nil, reference: nil, partialReference: nil)))))
return .single(.result(.media(.standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: Int32(data.count), datacenterId: Int(dcId), key: key))], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])))))
case .encryptedFileEmpty:
return .fail(.generic)
}

View File

@ -1,8 +1,9 @@
import Foundation
import Postbox
import TelegramApi
import SwiftSignalKit
import SyncCore
import MtProtoKit
func telegramStickerPachThumbnailRepresentationFromApiSize(datacenterId: Int32, size: Api.PhotoSize) -> TelegramMediaImageRepresentation? {
switch size {
@ -49,3 +50,54 @@ extension StickerPackCollectionInfo {
}
}
}
public func stickerPacksAttachedToMedia(account: Account, media: AnyMediaReference) -> Signal<[StickerPackReference], NoError> {
let inputMedia: Api.InputStickeredMedia
let resourceReference: MediaResourceReference
if let imageReference = media.concrete(TelegramMediaImage.self), let reference = imageReference.media.reference, case let .cloud(imageId, accessHash, fileReference) = reference, let representation = largestImageRepresentation(imageReference.media.representations) {
inputMedia = .inputStickeredMediaPhoto(id: Api.InputPhoto.inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: fileReference ?? Data())))
resourceReference = imageReference.resourceReference(representation.resource)
} else if let fileReference = media.concrete(TelegramMediaFile.self), let resource = fileReference.media.resource as? CloudDocumentMediaResource {
inputMedia = .inputStickeredMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())))
resourceReference = fileReference.resourceReference(fileReference.media.resource)
} else {
return .single([])
}
return account.network.request(Api.functions.messages.getAttachedStickers(media: inputMedia))
|> `catch` { _ -> Signal<[Api.StickerSetCovered], MTRpcError> in
return revalidateMediaResourceReference(postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, info: TelegramCloudMediaResourceFetchInfo(reference: resourceReference, preferBackgroundReferenceRevalidation: false, continueInBackground: false), resource: resourceReference.resource)
|> mapError { _ -> MTRpcError in
return MTRpcError(errorCode: 500, errorDescription: "Internal")
}
|> mapToSignal { reference -> Signal<[Api.StickerSetCovered], MTRpcError> in
let inputMedia: Api.InputStickeredMedia
if let resource = reference.updatedResource as? TelegramCloudMediaResourceWithFileReference, let updatedReference = resource.fileReference {
if let imageReference = media.concrete(TelegramMediaImage.self), let reference = imageReference.media.reference, case let .cloud(imageId, accessHash, fileReference) = reference, let representation = largestImageRepresentation(imageReference.media.representations) {
inputMedia = .inputStickeredMediaPhoto(id: Api.InputPhoto.inputPhoto(id: imageId, accessHash: accessHash, fileReference: Buffer(data: updatedReference ?? Data())))
} else if let fileReference = media.concrete(TelegramMediaFile.self), let resource = fileReference.media.resource as? CloudDocumentMediaResource {
inputMedia = .inputStickeredMediaDocument(id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: updatedReference ?? Data())))
} else {
return .single([])
}
return account.network.request(Api.functions.messages.getAttachedStickers(media: inputMedia))
} else {
return .single([])
}
}
|> `catch` { _ -> Signal<[Api.StickerSetCovered], MTRpcError> in
return .single([])
}
}
|> map { result -> [StickerPackReference] in
return result.map { pack in
switch pack {
case let .stickerSetCovered(set, _), let .stickerSetMultiCovered(set, _):
let info = StickerPackCollectionInfo(apiSet: set, namespace: Namespaces.ItemCollection.CloudStickerPacks)
return .id(id: info.id.id, accessHash: info.accessHash)
}
}
}
|> `catch` { _ -> Signal<[StickerPackReference], NoError> in
return .single([])
}
}

View File

@ -32,9 +32,14 @@ func telegramMediaImageRepresentationsFromApiSizes(datacenterId: Int32, photoId:
func telegramMediaImageFromApiPhoto(_ photo: Api.Photo) -> TelegramMediaImage? {
switch photo {
case let .photo(_, id, accessHash, fileReference, _, sizes, dcId):
case let .photo(flags, id, accessHash, fileReference, _, sizes, dcId):
let (immediateThumbnailData, representations) = telegramMediaImageRepresentationsFromApiSizes(datacenterId: dcId, photoId: id, accessHash: accessHash, fileReference: fileReference.makeData(), sizes: sizes)
return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: id), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: .cloud(imageId: id, accessHash: accessHash, fileReference: fileReference.makeData()), partialReference: nil)
var imageFlags: TelegramMediaImageFlags = []
let hasStickers = (flags & (1 << 0)) != 0
if hasStickers {
imageFlags.insert(.hasStickers)
}
return TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.CloudImage, id: id), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: .cloud(imageId: id, accessHash: accessHash, fileReference: fileReference.makeData()), partialReference: nil, flags: imageFlags)
case .photoEmpty:
return nil
}

View File

@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "GalleryEmbeddedStickersIcon@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "GalleryEmbeddedStickersIcon@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -35,6 +35,7 @@ import AppLock
import PresentationDataUtils
import TelegramIntents
import AccountUtils
import CoreSpotlight
#if canImport(BackgroundTasks)
import BackgroundTasks
@ -1828,6 +1829,53 @@ final class SharedApplicationContext {
self.openUrl(url: url)
}
if userActivity.activityType == CSSearchableItemActionType {
if let uniqueIdentifier = userActivity.userInfo?[CSSearchableItemActivityIdentifier] as? String, uniqueIdentifier.hasPrefix("contact-") {
if let peerIdValue = Int64(String(uniqueIdentifier[uniqueIdentifier.index(uniqueIdentifier.startIndex, offsetBy: "contact-".count)...])) {
let peerId = PeerId(peerIdValue)
let signal = self.sharedContextPromise.get()
|> take(1)
|> mapToSignal { sharedApplicationContext -> Signal<(AccountRecordId?, [Account?]), NoError> in
return sharedApplicationContext.sharedContext.activeAccounts
|> take(1)
|> mapToSignal { primary, accounts, _ -> Signal<(AccountRecordId?, [Account?]), NoError> in
return combineLatest(accounts.map { _, account, _ -> Signal<Account?, NoError> in
return account.postbox.transaction { transaction -> Account? in
if transaction.getPeer(peerId) != nil {
return account
} else {
return nil
}
}
})
|> map { accounts -> (AccountRecordId?, [Account?]) in
return (primary?.id, accounts)
}
}
}
let _ = (signal
|> deliverOnMainQueue).start(next: { primary, accounts in
if let primary = primary {
for account in accounts {
if let account = account, account.id == primary {
self.openChatWhenReady(accountId: nil, peerId: peerId)
return
}
}
}
for account in accounts {
if let account = account {
self.openChatWhenReady(accountId: account.id, peerId: peerId)
return
}
}
})
}
}
}
return true
}

View File

@ -225,7 +225,7 @@ private final class ChatContextResultPeekNode: ASDisplayNode, PeekControllerCont
if updatedImageResource {
if let imageResource = imageResource {
let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: Int32(fittedImageDimensions.width * 2.0), height: Int32(fittedImageDimensions.height * 2.0)), resource: imageResource)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
updateImageSignal = chatMessagePhoto(postbox: self.account.postbox, photoReference: .standalone(media: tmpImage))
} else {
updateImageSignal = .complete()

View File

@ -220,7 +220,7 @@ struct ChatRecentActionsEntry: Comparable, Identifiable {
var photo: TelegramMediaImage?
if !new.isEmpty {
photo = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: new, immediateThumbnailData: nil, reference: nil, partialReference: nil)
photo = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: new, immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
}
let action = TelegramMediaActionType.photoUpdated(image: photo)

View File

@ -310,7 +310,7 @@ final class HorizontalListContextResultsChatInputPanelItemNode: ListViewItemNode
updateImageSignal = chatMessageSticker(account: item.account, file: stickerFile, small: false, fetched: true)
} else {
let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(CGSize(width: fittedImageDimensions.width * 2.0, height: fittedImageDimensions.height * 2.0)), resource: imageResource)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
updateImageSignal = chatMessagePhoto(postbox: item.account.postbox, photoReference: .standalone(media: tmpImage))
}
} else {

View File

@ -161,6 +161,20 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}
}
self.historyNode.endedInteractiveDragging = { [weak self] in
guard let strongSelf = self else {
return
}
switch strongSelf.historyNode.visibleContentOffset() {
case let .known(value):
if value <= -10.0 {
strongSelf.requestDismiss()
}
default:
break
}
}
self.controlsNode.updateIsExpanded = { [weak self] in
if let strongSelf = self, let validLayout = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(validLayout, transition: .animated(duration: 0.3, curve: .spring))
@ -242,7 +256,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
panRecognizer.delegate = self
panRecognizer.delaysTouchesBegan = false
panRecognizer.cancelsTouchesInView = true
self.view.addGestureRecognizer(panRecognizer)
//self.view.addGestureRecognizer(panRecognizer)
}
func updatePresentationData(_ presentationData: PresentationData) {
@ -305,18 +319,19 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
let result = super.hitTest(point, with: event)
if self.controlsNode.bounds.contains(self.view.convert(point, to: self.controlsNode.view)) {
if result == nil {
return self.historyNode.view
}
}
if !self.bounds.contains(point) {
return nil
}
if point.y < self.controlsNode.frame.minY {
return self.dimNode.view
}
let result = super.hitTest(point, with: event)
if self.controlsNode.frame.contains(point) {
// if result == self.historyNode.view {
// return self.view
// }
}
return result
}
@ -532,6 +547,20 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}
}
self.historyNode.endedInteractiveDragging = { [weak self] in
guard let strongSelf = self else {
return
}
switch strongSelf.historyNode.visibleContentOffset() {
case let .known(value):
if value <= -10.0 {
strongSelf.requestDismiss()
}
default:
break
}
}
self.historyNode.beganInteractiveDragging = { [weak self] in
self?.controlsNode.collapse()
}

View File

@ -145,6 +145,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
private let displayUpgradeProgress: (Float?) -> Void
private var spotlightDataContext: SpotlightDataContext?
private var widgetDataContext: WidgetDataContext?
public init(mainWindow: Window1?, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, rootPath: String, legacyBasePath: String?, legacyCache: LegacyCache?, apsNotificationToken: Signal<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) {
@ -634,10 +635,17 @@ public final class SharedAccountContextImpl: SharedAccountContext {
self.updateNotificationTokensRegistration()
self.widgetDataContext = WidgetDataContext(basePath: self.basePath, activeAccount: self.activeAccounts
|> map { primary, _, _ in
return primary
}, presentationData: self.presentationData)
if applicationBindings.isMainApp {
self.widgetDataContext = WidgetDataContext(basePath: self.basePath, activeAccount: self.activeAccounts
|> map { primary, _, _ in
return primary
}, presentationData: self.presentationData)
self.spotlightDataContext = SpotlightDataContext(accounts: self.activeAccounts |> map { _, accounts, _ in
return accounts.map { _, account, _ in
return account
}
})
}
}
deinit {

View File

@ -0,0 +1,193 @@
import Foundation
import SwiftSignalKit
import Postbox
import SyncCore
import TelegramCore
import Display
import CoreSpotlight
import MobileCoreServices
private let roundCorners = { () -> UIImage in
let diameter: CGFloat = 60.0
UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0.0)
let context = UIGraphicsGetCurrentContext()!
context.setBlendMode(.copy)
context.setFillColor(UIColor.black.cgColor)
context.fill(CGRect(origin: CGPoint(), size: CGSize(width: diameter, height: diameter)))
context.setFillColor(UIColor.clear.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: CGSize(width: diameter, height: diameter)))
let image = UIGraphicsGetImageFromCurrentImageContext()!.stretchableImage(withLeftCapWidth: Int(diameter / 2.0), topCapHeight: Int(diameter / 2.0))
UIGraphicsEndImageContext()
return image
}()
private struct SpotlightAccountContact: Equatable, Codable {
var id: Int64
var title: String
var avatarPath: String?
}
private func manageableSpotlightContacts(accounts: Signal<[Account], NoError>) -> Signal<[Int64: SpotlightAccountContact], NoError> {
let queue = Queue()
return accounts
|> mapToSignal { accounts -> Signal<[[SpotlightAccountContact]], NoError> in
return combineLatest(queue: queue, accounts.map { account -> Signal<[SpotlightAccountContact], NoError> in
return account.postbox.contactPeersView(accountPeerId: account.peerId, includePresences: false)
|> map { view -> [SpotlightAccountContact] in
var result: [SpotlightAccountContact] = []
for peer in view.peers {
if let user = peer as? TelegramUser {
result.append(SpotlightAccountContact(id: user.id.toInt64(), title: user.debugDisplayTitle, avatarPath: smallestImageRepresentation(user.photo).flatMap { representation in
return account.postbox.mediaBox.resourcePath(representation.resource)
}))
}
}
result.sort(by: { $0.id < $1.id })
return result
}
|> distinctUntilChanged
})
}
|> map { accountContacts -> [Int64: SpotlightAccountContact] in
var result: [Int64: SpotlightAccountContact] = [:]
for singleAccountContacts in accountContacts {
for contact in singleAccountContacts {
if result[contact.id] == nil {
result[contact.id] = contact
}
}
}
return result
}
}
private final class SpotlightContactContext {
private let indexQueue: Queue
private let disposable = MetaDisposable()
private var contact: SpotlightAccountContact?
init(indexQueue: Queue) {
self.indexQueue = indexQueue
}
deinit {
self.disposable.dispose()
}
func update(contact: SpotlightAccountContact) {
if self.contact == contact {
return
}
let photoUpdated = self.contact?.avatarPath != contact.avatarPath
self.contact = contact
let indexQueue = self.indexQueue
let indexSignal: Signal<Never, NoError> = Signal { subscriber in
indexQueue.async {
let attributeSet = CSSearchableItemAttributeSet(itemContentType: kUTTypeText as String)
attributeSet.title = contact.title
if let avatarPath = contact.avatarPath, let avatarData = try? Data(contentsOf: URL(fileURLWithPath: avatarPath)), let image = UIImage(data: avatarData) {
let size = CGSize(width: 120.0, height: 120.0)
let context = DrawingContext(size: size, scale: 1.0, clear: true)
context.withFlippedContext { c in
c.draw(image.cgImage!, in: CGRect(origin: CGPoint(), size: size))
c.setBlendMode(.destinationOut)
c.draw(roundCorners.cgImage!, in: CGRect(origin: CGPoint(), size: size))
}
if let resultImage = context.generateImage(), let resultData = resultImage.pngData() {
attributeSet.thumbnailData = resultData
}
}
let item = CSSearchableItem(uniqueIdentifier: "contact-\(contact.id)", domainIdentifier: "telegram-contacts", attributeSet: attributeSet)
Logger.shared.log("SpotlightDataContext", "index \(contact.id) title: \(contact.title)")
CSSearchableIndex.default().indexSearchableItems([item], completionHandler: { error in
if let error = error {
Logger.shared.log("CSSearchableIndex", "error: \(error)")
}
subscriber.putCompletion()
})
}
return EmptyDisposable
}
self.disposable.set(indexSignal.start())
}
}
private final class SpotlightDataContextImpl {
private let queue: Queue
private let indexQueue: Queue = Queue()
private var contactContexts: [Int64: SpotlightContactContext] = [:]
private var listDisposable: Disposable?
init(queue: Queue, accounts: Signal<[Account], NoError>) {
self.queue = queue
self.indexQueue.async {
Logger.shared.log("SpotlightDataContext", "deleteSearchableItems")
CSSearchableIndex.default().deleteSearchableItems(withDomainIdentifiers: ["telegram-contacts"], completionHandler: { _ in })
}
self.listDisposable = (manageableSpotlightContacts(accounts: accounts
|> map { accounts in
return accounts.sorted(by: { $0.id < $1.id })
}
|> distinctUntilChanged(isEqual: { lhs, rhs in
if lhs.count != rhs.count {
return false
}
for i in 0 ..< lhs.count {
if lhs[i] !== rhs[i] {
return false
}
}
return true
}))
|> deliverOn(self.queue)).start(next: { [weak self] contacts in
guard let strongSelf = self else {
return
}
strongSelf.updateContacts(contacts: contacts)
})
}
private func updateContacts(contacts: [Int64: SpotlightAccountContact]) {
var validIds = Set<Int64>()
for (_, contact) in contacts {
validIds.insert(contact.id)
let context: SpotlightContactContext
if let current = self.contactContexts[contact.id] {
context = current
} else {
context = SpotlightContactContext(indexQueue: self.indexQueue)
self.contactContexts[contact.id] = context
}
context.update(contact: contact)
}
var removeIds: [Int64] = []
for id in self.contactContexts.keys {
if !validIds.contains(id) {
removeIds.append(id)
}
}
for id in removeIds {
self.contactContexts.removeValue(forKey: id)
}
}
}
public final class SpotlightDataContext {
private let impl: QueueLocalObject<SpotlightDataContextImpl>
public init(accounts: Signal<[Account], NoError>) {
let queue = Queue()
self.impl = QueueLocalObject(queue: queue, generate: {
return SpotlightDataContextImpl(queue: queue, accounts: accounts)
})
}
}

View File

@ -161,7 +161,7 @@ public func transformOutgoingMessageMedia(postbox: Postbox, network: Network, me
let thumbnailResource = LocalFileMediaResource(fileId: arc4random64())
postbox.mediaBox.storeResourceData(thumbnailResource.id, data: smallestData)
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(smallestSize), resource: thumbnailResource))
let updatedImage = TelegramMediaImage(imageId: image.imageId, representations: representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference)
let updatedImage = TelegramMediaImage(imageId: image.imageId, representations: representations, immediateThumbnailData: image.immediateThumbnailData, reference: image.reference, partialReference: image.partialReference, flags: [])
return .single(.standalone(media: updatedImage))
}
}

View File

@ -252,7 +252,7 @@ final class VerticalListContextResultsChatInputPanelItemNode: ListViewItemNode {
updateIconImageSignal = chatMessageSticker(account: item.account, file: stickerFile, small: false, fetched: true)
} else {
let tmpRepresentation = TelegramMediaImageRepresentation(dimensions: PixelDimensions(width: 55, height: 55), resource: imageResource)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [tmpRepresentation], immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])
updateIconImageSignal = chatWebpageSnippetPhoto(account: item.account, photoReference: .standalone(media: tmpImage))
}
} else {

View File

@ -264,7 +264,7 @@ func legacyWebSearchItem(account: Account, result: ChatContextResult) -> LegacyW
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(thumbnailDimensions), resource: thumbnailResource))
}
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
thumbnailSignal = chatMessagePhotoDatas(postbox: account.postbox, photoReference: .standalone(media: tmpImage), autoFetchFullSize: false)
|> mapToSignal { value -> Signal<UIImage, NoError> in
let thumbnailData = value._0

View File

@ -138,7 +138,7 @@ final class WebSearchItemNode: GridItemNode {
representations.append(TelegramMediaImageRepresentation(dimensions: PixelDimensions(imageDimensions), resource: imageResource))
}
if !representations.isEmpty {
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil)
let tmpImage = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: representations, immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
updateImageSignal = mediaGridMessagePhoto(account: item.account, photoReference: .standalone(media: tmpImage))
} else {
updateImageSignal = .complete()