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 0000000000..6199cc1152 Binary files /dev/null and b/Telegram/SiriIntents/IntentsImages.xcassets/Intents/SavedMessages.imageset/ic_av_savedmessages (2).pdf differ 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 0000000000..6199cc1152 Binary files /dev/null and b/Telegram/WidgetKitWidget/WidgetImages.xcassets/Widget/SavedMessages.imageset/ic_av_savedmessages (2).pdf differ 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" ) }