From e82e856e7476e0105498372f158cd4fc9e441433 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 18 Feb 2021 21:13:35 +0400 Subject: [PATCH 1/5] Fix aps --- Telegram/BUILD | 1 + 1 file changed, 1 insertion(+) diff --git a/Telegram/BUILD b/Telegram/BUILD index 5893fd8821..9051cf015a 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -358,6 +358,7 @@ plist_fragment( name = "TelegramEntitlements", extension = "entitlements", template = "".join([ + aps_fragment, app_groups_fragment, siri_fragment, associated_domains_fragment, From c81162f229da05d433846f1cbca0fc6a6c084003 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 18 Feb 2021 21:13:59 +0400 Subject: [PATCH 2/5] Set sqlite busy handler before performing key-related requests --- submodules/Postbox/Sources/SqliteValueBox.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/Postbox/Sources/SqliteValueBox.swift b/submodules/Postbox/Sources/SqliteValueBox.swift index 177517a796..0ae732a92a 100644 --- a/submodules/Postbox/Sources/SqliteValueBox.swift +++ b/submodules/Postbox/Sources/SqliteValueBox.swift @@ -298,6 +298,8 @@ public final class SqliteValueBox: ValueBox { preconditionFailure("Couldn't open database") } + sqlite3_busy_timeout(database.handle, 1000 * 10000) + var resultCode: Bool = true resultCode = database.execute("PRAGMA cipher_plaintext_header_size=32") @@ -408,8 +410,6 @@ public final class SqliteValueBox: ValueBox { } } - sqlite3_busy_timeout(database.handle, 1000 * 10000) - //database.execute("PRAGMA cache_size=-2097152") resultCode = database.execute("PRAGMA mmap_size=0") assert(resultCode) From 20d620d63031e8544e88b3f7368aec9da3a31417 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 18 Feb 2021 21:20:31 +0400 Subject: [PATCH 3/5] Roll back updating the gallery controller --- .../GalleryUI/Sources/GalleryController.swift | 40 +++++-------------- 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/submodules/GalleryUI/Sources/GalleryController.swift b/submodules/GalleryUI/Sources/GalleryController.swift index ab2d04e0f5..9f07705918 100644 --- a/submodules/GalleryUI/Sources/GalleryController.swift +++ b/submodules/GalleryUI/Sources/GalleryController.swift @@ -463,6 +463,7 @@ public class GalleryController: ViewController, StandalonePresentableController } } } + |> take(1) let semaphore: DispatchSemaphore? if synchronousLoad { @@ -486,23 +487,6 @@ public class GalleryController: ViewController, StandalonePresentableController strongSelf.configuration = configuration let entries = view.entries - - if let centralEntryStableId = strongSelf.centralEntryStableId { - var found = false - for i in 0 ..< entries.count { - let message = entries[i].message - if message.stableId == centralEntryStableId { - found = true - break - } - } - - if !found { - strongSelf.dismiss(forceAway: true) - return - } - } - var centralEntryStableId: UInt32? loop: for i in 0 ..< entries.count { let message = entries[i].message @@ -531,16 +515,14 @@ public class GalleryController: ViewController, StandalonePresentableController strongSelf.entries = entries.reversed() strongSelf.hasLeftEntries = view.hasLater strongSelf.hasRightEntries = view.hasEarlier - if strongSelf.centralEntryStableId == nil, let centralEntryStableId = centralEntryStableId { + if let centralEntryStableId = centralEntryStableId { strongSelf.centralEntryStableId = centralEntryStableId } } else { strongSelf.entries = entries strongSelf.hasLeftEntries = view.hasEarlier strongSelf.hasRightEntries = view.hasLater - if strongSelf.centralEntryStableId == nil { - strongSelf.centralEntryStableId = centralEntryStableId - } + strongSelf.centralEntryStableId = centralEntryStableId } if strongSelf.isViewLoaded { var items: [GalleryItem] = [] @@ -565,17 +547,13 @@ public class GalleryController: ViewController, StandalonePresentableController strongSelf.galleryNode.pager.replaceItems(items, centralItemIndex: centralItemIndex) if strongSelf.temporaryDoNotWaitForReady { - if !strongSelf.didSetReady { - strongSelf.didSetReady = true - strongSelf._ready.set(.single(true)) - } + strongSelf.didSetReady = true + strongSelf._ready.set(.single(true)) } else { - if !strongSelf.didSetReady { - let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in - strongSelf?.didSetReady = true - } - strongSelf._ready.set(ready |> map { true }) + let ready = strongSelf.galleryNode.pager.ready() |> timeout(2.0, queue: Queue.mainQueue(), alternate: .single(Void())) |> afterNext { [weak strongSelf] _ in + strongSelf?.didSetReady = true } + strongSelf._ready.set(ready |> map { true }) } } } @@ -590,7 +568,7 @@ public class GalleryController: ViewController, StandalonePresentableController return (true, nil) } semaphore?.signal() - if process || true { + if process { Queue.mainQueue().async { f() } From a46bfa8265267f225afd8f1dc7e1a525ed532cd2 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 18 Feb 2021 23:21:58 +0400 Subject: [PATCH 4/5] Dismiss context menu when message gets deleted --- .../ContextUI/Sources/ContextController.swift | 28 +++++++++++++++++-- .../Sources/PeerAutoremoveSetupScreen.swift | 2 +- .../TelegramUI/Sources/ChatController.swift | 6 ++-- ...essageContextControllerContentSource.swift | 21 +++++++++++++- 4 files changed, 50 insertions(+), 7 deletions(-) diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index a40ea194b5..9d3b6f94b8 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -1510,11 +1510,18 @@ public protocol ContextExtractedContentSource: class { var keepInPlace: Bool { get } var ignoreContentTouches: Bool { get } var blurBackground: Bool { get } + var shouldBeDismissed: Signal { get } func takeView() -> ContextControllerTakeViewInfo? func putBack() -> ContextControllerPutBackViewInfo? } +public extension ContextExtractedContentSource { + var shouldBeDismissed: Signal { + return .single(false) + } +} + public protocol ContextControllerContentSource: class { var controller: ViewController { get } var navigationController: NavigationController? { get } @@ -1555,6 +1562,8 @@ public final class ContextController: ViewController, StandalonePresentableContr public var reactionSelected: ((ReactionContextItem.Reaction) -> Void)? + private var shouldBeDismissedDisposable: Disposable? + public init(account: Account, presentationData: PresentationData, source: ContextContentSource, items: Signal<[ContextMenuItem], NoError>, reactionItems: [ReactionContextItem], recognizer: TapLongTapOrDoubleTapGestureRecognizer? = nil, gesture: ContextGesture? = nil, displayTextSelectionTip: Bool = false) { self.account = account self.presentationData = presentationData @@ -1567,8 +1576,19 @@ public final class ContextController: ViewController, StandalonePresentableContr super.init(navigationBarPresentationData: nil) - if case let .extracted(extractedSource) = source, !extractedSource.blurBackground { - self.statusBar.statusBarStyle = .Ignore + if case let .extracted(extractedSource) = source { + if !extractedSource.blurBackground { + self.statusBar.statusBarStyle = .Ignore + } + self.shouldBeDismissedDisposable = (extractedSource.shouldBeDismissed + |> filter { $0 } + |> take(1) + |> deliverOnMainQueue).start(next: { [weak self] _ in + guard let strongSelf = self else { + return + } + strongSelf.dismiss(result: .default, completion: {}) + }) } else { self.statusBar.statusBarStyle = .Hide } @@ -1579,6 +1599,10 @@ public final class ContextController: ViewController, StandalonePresentableContr fatalError("init(coder:) has not been implemented") } + deinit { + self.shouldBeDismissedDisposable?.dispose() + } + override public func loadDisplayNode() { self.displayNode = ContextControllerNode(account: self.account, controller: self, presentationData: self.presentationData, source: self.source, items: self.items, reactionItems: self.reactionItems, beginDismiss: { [weak self] result in self?.dismiss(result: result, completion: nil) diff --git a/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift b/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift index f2fcf945ef..99ffac0867 100644 --- a/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift +++ b/submodules/PeerInfoUI/Sources/PeerAutoremoveSetupScreen.swift @@ -121,7 +121,7 @@ private func peerAutoremoveSetupEntries(peer: Peer?, presentationData: Presentat 24 * 60 * 60 * 7 ] if isDebug || true { - availableValues[1] = 60 + availableValues[1] = 5 availableValues[2] = 5 * 60 } entries.append(.timeValue(resolvedValue, availableValues)) diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index ae61cbfa6c..31f5ca6e77 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -844,7 +844,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let _ = ApplicationSpecificNotice.incrementChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager).start() } - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message, selectAll: selectAll)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, gesture: gesture, displayTextSelectionTip: displayTextSelectionTip) + let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: selectAll)), items: .single(actions), reactionItems: reactionItems, recognizer: recognizer, gesture: gesture, displayTextSelectionTip: displayTextSelectionTip) strongSelf.currentContextController = controller controller.reactionSelected = { [weak controller] value in guard let strongSelf = self, let message = updatedMessages.first else { @@ -1870,7 +1870,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }))) - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: message, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil) + let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: message, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil) strongSelf.currentContextController = controller strongSelf.forEachController({ controller in if let controller = controller as? TooltipScreen { @@ -1947,7 +1947,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G f(.dismissWithoutContent) }))) - let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, message: topMessage, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil) + let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, postbox: strongSelf.context.account.postbox, message: topMessage, selectAll: true)), items: .single(actions), reactionItems: [], recognizer: nil) strongSelf.currentContextController = controller strongSelf.forEachController({ controller in if let controller = controller as? TooltipScreen { diff --git a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift index 9740f7e645..20ea32ca77 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift @@ -3,6 +3,7 @@ import UIKit import Display import ContextUI import Postbox +import SwiftSignalKit final class ChatMessageContextExtractedContentSource: ContextExtractedContentSource { let keepInPlace: Bool = false @@ -10,11 +11,29 @@ final class ChatMessageContextExtractedContentSource: ContextExtractedContentSou let blurBackground: Bool = true private weak var chatNode: ChatControllerNode? + private let postbox: Postbox private let message: Message private let selectAll: Bool - init(chatNode: ChatControllerNode, message: Message, selectAll: Bool) { + var shouldBeDismissed: Signal { + let viewKey = PostboxViewKey.messages(Set([self.message.id])) + return self.postbox.combinedView(keys: [viewKey]) + |> map { views -> Bool in + guard let view = views.views[viewKey] as? MessagesView else { + return false + } + if view.messages.isEmpty { + return true + } else { + return false + } + } + |> distinctUntilChanged + } + + init(chatNode: ChatControllerNode, postbox: Postbox, message: Message, selectAll: Bool) { self.chatNode = chatNode + self.postbox = postbox self.message = message self.selectAll = selectAll } From 6417d8862c5837855024b4579375406f8df53977 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 18 Feb 2021 23:48:41 +0400 Subject: [PATCH 5/5] Display the saved messages icon in widgets --- Telegram/BUILD | 12 ++- Telegram/SiriIntents/IntentHandler.swift | 96 +++++++++++++++++- .../IntentsImages.xcassets/Contents.json | 6 ++ .../Intents/Contents.json | 9 ++ .../SavedMessages.imageset/Contents.json | 12 +++ .../ic_av_savedmessages (2).pdf | Bin 0 -> 4021 bytes Telegram/WidgetKitWidget/PeerNode.swift | 69 ++++++++++++- .../WidgetKitWidget/TodayViewController.swift | 11 +- .../SavedMessages.imageset/Contents.json | 12 +++ .../ic_av_savedmessages (2).pdf | Bin 0 -> 4021 bytes .../Sources/WidgetDataContext.swift | 3 +- .../WidgetItems/Sources/WidgetItems.swift | 9 +- 12 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 Telegram/SiriIntents/IntentsImages.xcassets/Contents.json create mode 100644 Telegram/SiriIntents/IntentsImages.xcassets/Intents/Contents.json create mode 100644 Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/Contents.json create mode 100644 Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/ic_av_savedmessages (2).pdf create mode 100644 Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/Contents.json create mode 100644 Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/ic_av_savedmessages (2).pdf diff --git a/Telegram/BUILD b/Telegram/BUILD index 9051cf015a..d9af42bf74 100644 --- a/Telegram/BUILD +++ b/Telegram/BUILD @@ -1339,13 +1339,23 @@ swift_intent_library( visibility = ["//visibility:public"], ) +filegroup( + name = "IntentsAssets", + srcs = glob(["SiriIntents/IntentsImages.xcassets/**"]), + visibility = ["//visibility:public"], +) + swift_library( name = "IntentsExtensionLib", module_name = "IntentsExtensionLib", srcs = glob([ "SiriIntents/**/*.swift", ]), - data = glob(["SiriIntents/*.lproj/Intents.intentdefinition"]), + data = glob([ + "SiriIntents/*.lproj/Intents.intentdefinition" + ]) + [ + ":IntentsAssets" + ], deps = [ "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/Postbox:Postbox", diff --git a/Telegram/SiriIntents/IntentHandler.swift b/Telegram/SiriIntents/IntentHandler.swift index d8b6dc86e5..2d833a6c71 100644 --- a/Telegram/SiriIntents/IntentHandler.swift +++ b/Telegram/SiriIntents/IntentHandler.swift @@ -1224,6 +1224,74 @@ private func avatarImage(path: String?, peerId: Int64, accountPeerId: Int64, let } } +private func generateTintedImage(image: UIImage?, color: UIColor, backgroundColor: UIColor? = nil) -> UIImage? { + guard let image = image else { + return nil + } + + let imageSize = image.size + + UIGraphicsBeginImageContextWithOptions(imageSize, backgroundColor != nil, image.scale) + if let context = UIGraphicsGetCurrentContext() { + if let backgroundColor = backgroundColor { + context.setFillColor(backgroundColor.cgColor) + context.fill(CGRect(origin: CGPoint(), size: imageSize)) + } + + let imageRect = CGRect(origin: CGPoint(), size: imageSize) + context.saveGState() + context.translateBy(x: imageRect.midX, y: imageRect.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageRect.midX, y: -imageRect.midY) + context.clip(to: imageRect, mask: image.cgImage!) + context.setFillColor(color.cgColor) + context.fill(imageRect) + context.restoreGState() + } + + let tintedImage = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + + return tintedImage +} + +private let savedMessagesColors: NSArray = [ + UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor +] + +private func savedMessagesImage(size: CGSize) -> UIImage? { + guard let icon = generateTintedImage(image: UIImage(named: "Intents/SavedMessages"), color: .white) else { + return nil + } + 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() + + let colorsArray = savedMessagesColors + 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 factor = size.width / 60.0 + context?.translateBy(x: size.width / 2.0, y: size.height / 2.0) + context?.scaleBy(x: factor, y: -factor) + context?.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + + if let context = context { + context.draw(icon.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - icon.size.width) / 2.0), y: floor((size.height - icon.size.height) / 2.0)), size: icon.size)) + } + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image +} + @available(iOSApplicationExtension 14.0, iOS 14.0, *) private func mapPeersToFriends(accountId: AccountRecordId, accountPeerId: PeerId, mediaBox: MediaBox, peers: [Peer]) -> [Friend] { var items: [Friend] = [] @@ -1231,7 +1299,26 @@ private func mapPeersToFriends(accountId: AccountRecordId, accountPeerId: PeerId autoreleasepool { var profileImage: INImage? - if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let path = mediaBox.completedResourcePath(resource) { + if peer.id == accountPeerId { + let cachedPath = mediaBox.cachedRepresentationPathForId("savedMessagesAvatar50x50", representationId: "intents.png", keepDuration: .shortLived) + if let _ = fileSize(cachedPath) { + do { + let data = try Data(contentsOf: URL(fileURLWithPath: cachedPath), options: .alwaysMapped) + profileImage = INImage(imageData: data) + } catch { + } + } else { + let image = savedMessagesImage(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) + profileImage = INImage(imageData: data) + } catch { + } + } + } else if let resource = smallestImageRepresentation(peer.profileImageRepresentations)?.resource, let path = mediaBox.completedResourcePath(resource) { let cachedPath = mediaBox.cachedRepresentationPathForId(resource.id.uniqueId, representationId: "intents.png", keepDuration: .shortLived) if let _ = fileSize(cachedPath) { do { @@ -1272,7 +1359,12 @@ private func mapPeersToFriends(accountId: AccountRecordId, accountPeerId: PeerId } } - items.append(Friend(identifier: "\(accountId.int64):\(peer.id.toInt64())", display: peer.debugDisplayTitle, subtitle: nil, image: profileImage)) + var displayTitle = peer.debugDisplayTitle + if peer.id == accountPeerId { + displayTitle = WidgetPresentationData.getForExtension().chatSavedMessages + } + + items.append(Friend(identifier: "\(accountId.int64):\(peer.id.toInt64())", display: displayTitle, subtitle: nil, image: profileImage)) } } return items diff --git a/Telegram/SiriIntents/IntentsImages.xcassets/Contents.json b/Telegram/SiriIntents/IntentsImages.xcassets/Contents.json new file mode 100644 index 0000000000..73c00596a7 --- /dev/null +++ b/Telegram/SiriIntents/IntentsImages.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Telegram/SiriIntents/IntentsImages.xcassets/Intents/Contents.json b/Telegram/SiriIntents/IntentsImages.xcassets/Intents/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/Telegram/SiriIntents/IntentsImages.xcassets/Intents/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/Contents.json b/Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/Contents.json new file mode 100644 index 0000000000..2265cbc0ac --- /dev/null +++ b/Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_av_savedmessages (2).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/ic_av_savedmessages (2).pdf b/Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/ic_av_savedmessages (2).pdf new file mode 100644 index 0000000000000000000000000000000000000000..6199cc1152aa1f74e9c271d089296d34e6c81f2b GIT binary patch literal 4021 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 Sb{YalK@ebZaZLj)@P7bWn$%7J literal 0 HcmV?d00001 diff --git a/Telegram/WidgetKitWidget/PeerNode.swift b/Telegram/WidgetKitWidget/PeerNode.swift index 97cd9ba502..7308b76090 100644 --- a/Telegram/WidgetKitWidget/PeerNode.swift +++ b/Telegram/WidgetKitWidget/PeerNode.swift @@ -92,10 +92,77 @@ private func avatarViewLettersImage(size: CGSize, peerId: Int64, accountPeerId: return image } +private func generateTintedImage(image: UIImage?, color: UIColor, backgroundColor: UIColor? = nil) -> UIImage? { + guard let image = image else { + return nil + } + + let imageSize = image.size + + UIGraphicsBeginImageContextWithOptions(imageSize, backgroundColor != nil, image.scale) + if let context = UIGraphicsGetCurrentContext() { + if let backgroundColor = backgroundColor { + context.setFillColor(backgroundColor.cgColor) + context.fill(CGRect(origin: CGPoint(), size: imageSize)) + } + + let imageRect = CGRect(origin: CGPoint(), size: imageSize) + context.saveGState() + context.translateBy(x: imageRect.midX, y: imageRect.midY) + context.scaleBy(x: 1.0, y: -1.0) + context.translateBy(x: -imageRect.midX, y: -imageRect.midY) + context.clip(to: imageRect, mask: image.cgImage!) + context.setFillColor(color.cgColor) + context.fill(imageRect) + context.restoreGState() + } + + let tintedImage = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + + return tintedImage +} + +private let savedMessagesColors: NSArray = [ + UIColor(rgb: 0x2a9ef1).cgColor, UIColor(rgb: 0x72d5fd).cgColor +] + +private func savedMessagesImage(size: CGSize) -> 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() + + let colorsArray = savedMessagesColors + 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 factor = size.width / 60.0 + context?.translateBy(x: size.width / 2.0, y: size.height / 2.0) + context?.scaleBy(x: factor, y: -factor) + context?.translateBy(x: -size.width / 2.0, y: -size.height / 2.0) + + if let context = context, let icon = generateTintedImage(image: UIImage(named: "Widget/SavedMessages"), color: .white) { + context.draw(icon.cgImage!, in: CGRect(origin: CGPoint(x: floor((size.width - icon.size.width) / 2.0), y: floor((size.height - icon.size.height) / 2.0)), size: icon.size)) + } + + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return image +} + private let avatarSize = CGSize(width: 50.0, height: 50.0) func avatarImage(accountPeerId: Int64?, peer: WidgetDataPeer?, size: CGSize) -> UIImage { - if let path = peer?.avatarPath, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) { + if let peer = peer, let accountPeerId = accountPeerId, peer.id == accountPeerId { + return savedMessagesImage(size: size)! + } else if let path = peer?.avatarPath, let image = UIImage(contentsOfFile: path), let roundImage = avatarRoundImage(size: size, source: image) { return roundImage } else { return avatarViewLettersImage(size: size, peerId: peer?.id ?? 1, accountPeerId: accountPeerId ?? 1, letters: peer?.letters ?? [" "])! diff --git a/Telegram/WidgetKitWidget/TodayViewController.swift b/Telegram/WidgetKitWidget/TodayViewController.swift index b459177fcf..efa8b4798f 100644 --- a/Telegram/WidgetKitWidget/TodayViewController.swift +++ b/Telegram/WidgetKitWidget/TodayViewController.swift @@ -316,7 +316,9 @@ struct WidgetView: View { dateText = "" } var formattedName = peer.peer.name - if let lastName = peer.peer.lastName { + if peer.accountPeerId == peer.peer.id { + formattedName = self.presentationData.chatSavedMessages + } else if let lastName = peer.peer.lastName { formattedName.append(" \(lastName)") } chatTitle = AnyView(Text(formattedName) @@ -575,10 +577,15 @@ struct WidgetView: View { content = .placeholder } + let avatarView: AvatarItemView + avatarView = AvatarItemView(peer: peers?.peers[index], itemSize: 54.0, placeholderColor: getPlaceholderColor()) + return AnyView( Link(destination: url, label: { HStack(alignment: .center, spacing: 0.0, content: { - AvatarItemView(peer: peers?.peers[index], itemSize: 54.0, placeholderColor: getPlaceholderColor()).frame(width: 54.0, height: 54.0, alignment: .leading).padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0)) + avatarView + .frame(width: 54.0, height: 54.0, alignment: .leading) + .padding(EdgeInsets(top: 0.0, leading: 10.0, bottom: 0.0, trailing: 10.0)) chatContent(content).frame(maxWidth: .infinity).padding(EdgeInsets(top: 0.0, leading: 0.0, bottom: 0.0, trailing: 10.0)) }) }) diff --git a/Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/Contents.json b/Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/Contents.json new file mode 100644 index 0000000000..2265cbc0ac --- /dev/null +++ b/Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_av_savedmessages (2).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/ic_av_savedmessages (2).pdf b/Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/ic_av_savedmessages (2).pdf new file mode 100644 index 0000000000000000000000000000000000000000..6199cc1152aa1f74e9c271d089296d34e6c81f2b GIT binary patch literal 4021 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 Sb{YalK@ebZaZLj)@P7bWn$%7J literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Sources/WidgetDataContext.swift b/submodules/TelegramUI/Sources/WidgetDataContext.swift index d4c99d73f7..c21891bade 100644 --- a/submodules/TelegramUI/Sources/WidgetDataContext.swift +++ b/submodules/TelegramUI/Sources/WidgetDataContext.swift @@ -277,7 +277,8 @@ final class WidgetDataContext { autodeleteTimerUpdated: presentationData.strings.Widget_MessageAutoremoveTimerUpdated, autodeleteTimerRemoved: presentationData.strings.Widget_MessageAutoremoveTimerRemoved, generalLockedTitle: presentationData.strings.Intents_ErrorLockedTitle, - generalLockedText: presentationData.strings.Intents_ErrorLockedText + generalLockedText: presentationData.strings.Intents_ErrorLockedText, + chatSavedMessages: presentationData.strings.DialogList_SavedMessages ) } |> distinctUntilChanged).start(next: { value in diff --git a/submodules/WidgetItems/Sources/WidgetItems.swift b/submodules/WidgetItems/Sources/WidgetItems.swift index 5466b2c6b3..4cf5ea1328 100644 --- a/submodules/WidgetItems/Sources/WidgetItems.swift +++ b/submodules/WidgetItems/Sources/WidgetItems.swift @@ -300,6 +300,8 @@ public struct WidgetPresentationData: Codable, Equatable { public var generalLockedTitle: String public var generalLockedText: String + public var chatSavedMessages: String + public init( widgetChatsGalleryTitle: String, widgetChatsGalleryDescription: String, @@ -321,7 +323,8 @@ public struct WidgetPresentationData: Codable, Equatable { autodeleteTimerUpdated: String, autodeleteTimerRemoved: String, generalLockedTitle: String, - generalLockedText: String + generalLockedText: String, + chatSavedMessages: String ) { self.widgetChatsGalleryTitle = widgetChatsGalleryTitle self.widgetChatsGalleryDescription = widgetChatsGalleryDescription @@ -344,6 +347,7 @@ public struct WidgetPresentationData: Codable, Equatable { self.autodeleteTimerRemoved = autodeleteTimerRemoved self.generalLockedTitle = generalLockedTitle self.generalLockedText = generalLockedText + self.chatSavedMessages = chatSavedMessages } public static func getForExtension() -> WidgetPresentationData { @@ -392,7 +396,8 @@ public extension WidgetPresentationData { autodeleteTimerUpdated: "Auto-delete timer updated", autodeleteTimerRemoved: "Auto-delete timer disabled", generalLockedTitle: "Locked", - generalLockedText: "Open Telegram and enter passcode to edit widget." + generalLockedText: "Open Telegram and enter passcode to edit widget.", + chatSavedMessages: "Saved Messages" ) }