From ed7cf17233a9adab52213fd454774f023be21627 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 13:18:00 +0400 Subject: [PATCH 01/21] Various Improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 19 +- .../Sources/AccountContext.swift | 2 + submodules/AuthTransferUI/BUILD | 1 + .../AuthTransferConfirmationScreen.swift | 5 +- submodules/ContactListUI/BUILD | 1 + .../Sources/ContactsController.swift | 38 + .../Sources/ContactsControllerNode.swift | 11 + .../DeviceAccess/Sources/DeviceAccess.swift | 8 + submodules/Display/Source/TextNode.swift | 11 +- .../Sources/RecognizedTextSelectionNode.swift | 8 +- .../Sources/ImageContentAnalysis.swift | 30 + .../Sources/InvisibleInkDustNode.swift | 10 +- .../Sources/ItemListPeerItem.swift | 6 +- .../Sources/LegacyWallpaperPicker.swift | 4 +- submodules/QrCodeUI/BUILD | 9 + .../QrCodeUI/Sources/QrCodeScanScreen.swift | 725 ++++++++++++++++++ .../QrCodeUI/Sources/QrCodeScreen.swift | 18 +- submodules/SettingsUI/BUILD | 1 + .../RecentSessionsController.swift | 5 +- .../Sources/ShareController.swift | 39 +- .../Sources/PermissionController.swift | 2 + .../DefaultDarkPresentationTheme.swift | 4 +- .../Sources/PresentationData.swift | 1 - .../Images.xcassets/Share/Contents.json | 6 +- .../Share/Instagram.imageset/Contents.json | 12 + .../Share/Instagram.imageset/instagram_60.pdf | 151 ++++ .../Share/QrPlaneIcon.imageset/Contents.json | 12 + .../Share/QrPlaneIcon.imageset/ic_qrlogo.pdf | Bin 0 -> 4391 bytes .../Sources/ChatHistoryEntriesForView.swift | 2 +- .../TelegramUI/Sources/ChatQrCodeScreen.swift | 534 +++++++++---- .../TelegramUI/Sources/EmojiResources.swift | 10 +- .../PeerInfoScreenLabeledValueItem.swift | 29 +- .../ListItems/PeerInfoScreenMemberItem.swift | 2 +- .../PeerInfoGroupsInCommonPaneNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 32 +- .../Sources/SharedAccountContext.swift | 4 + submodules/Translate/Sources/Translate.swift | 5 +- .../Sources/WallpaperBackgroundNode.swift | 12 +- 38 files changed, 1506 insertions(+), 265 deletions(-) create mode 100644 submodules/QrCodeUI/Sources/QrCodeScanScreen.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Share/Instagram.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Share/Instagram.imageset/instagram_60.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Share/QrPlaneIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Share/QrPlaneIcon.imageset/ic_qrlogo.pdf diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 08524f9e6c..f6c11d3ca3 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7168,12 +7168,6 @@ Sorry for the inconvenience."; "Conversation.LargeEmojiEnable" = "Enable Large Emoji"; "Conversation.LargeEmojiEnabled" = "Large emoji enabled."; -"GroupInfo.QRCode.Info" = "Everyone on Telegram can scan this code to join this group."; -"ChannelInfo.QRCode.Info" = "Everyone on Telegram can scan this code to join this channel."; -"UserInfo.QRCode.InfoYou" = "Everyone on Telegram can scan this code to message you."; -"UserInfo.QRCode.InfoBot" = "Everyone on Telegram can scan this code to use this bot."; -"UserInfo.QRCode.InfoOther" = "Everyone on Telegram can scan this code to message %@."; - "PeerInfo.QRCode.Title" = "QR Code"; "ChatList.Archive" = "Archive"; @@ -7190,8 +7184,17 @@ Sorry for the inconvenience."; "Localization.ShowTranslate" = "Show Translate Button"; "Localization.ShowTranslateInfo" = "Show 'Translate' button in the message action menu."; "Localization.DoNotTranslate" = "Do Not Translate"; -"Localization.DoNotTranslateInfo" = "Do not show 'Translate' button in the message action menu for this language"; -"Localization.DoNotTranslateManyInfo" = "Do not show 'Translate' button in the message action menu for this languages"; +"Localization.DoNotTranslateInfo" = "Do not show 'Translate' button in the message action menu for this language."; +"Localization.DoNotTranslateManyInfo" = "Do not show 'Translate' button in the message action menu for this languages."; "Localization.InterfaceLanguage" = "Interface Language"; "DoNotTranslate.Title" = "Do Not Translate"; + +"Contacts.ScanQrCode" = "Scan QR Code"; +"Contacts.QrCode.MyCode" = "My QR Code"; +"Contacts.QrCode.NoCodeFound" = "No valid QR code found in the image. Please try again."; + +"AccessDenied.QrCode" = "Telegram needs access to your photo library to scan QR codes.\n\nPlease go to Settings > Privacy > Photos and set Telegram to ON."; +"AccessDenied.QrCamera" = "Telegram needs access to your camera to scan QR codes.\n\nPlease go to Settings > Privacy > Camera and set Telegram to ON."; + +"Share.ShareToInstagramStories" = "Share to Instagram Stories"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 7065a4f7e4..f014e50870 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -630,6 +630,8 @@ public protocol SharedAccountContext: AnyObject { func makeRecentSessionsController(context: AccountContext, activeSessionsContext: ActiveSessionsContext) -> ViewController & RecentSessionsController + func makeChatQrCodeScreen(context: AccountContext, peer: Peer) -> ViewController + func navigateToCurrentCall() var hasOngoingCall: ValuePromise { get } var immediateHasOngoingCall: Bool { get } diff --git a/submodules/AuthTransferUI/BUILD b/submodules/AuthTransferUI/BUILD index 59ad1f59bd..805946bb57 100644 --- a/submodules/AuthTransferUI/BUILD +++ b/submodules/AuthTransferUI/BUILD @@ -30,6 +30,7 @@ swift_library( "//submodules/UndoUI:UndoUI", "//submodules/TextFormat:TextFormat", "//submodules/Markdown:Markdown", + "//submodules/QrCodeUI:QrCodeUI", ], visibility = [ "//visibility:public", diff --git a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift index ccb27eaeeb..75660fb5a4 100644 --- a/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift +++ b/submodules/AuthTransferUI/Sources/AuthTransferConfirmationScreen.swift @@ -13,6 +13,7 @@ import PresentationDataUtils import TelegramCore import Markdown import DeviceAccess +import QrCodeUI private func transformedWithTheme(data: Data, theme: PresentationTheme) -> Data { return transformedWithColors(data: data, colors: [(UIColor(rgb: 0x333333), theme.list.itemPrimaryTextColor.mixedWith(.white, alpha: 0.2)), (UIColor(rgb: 0xFFFFFF), theme.list.plainBackgroundColor), (UIColor(rgb: 0x50A7EA), theme.list.itemAccentColor), (UIColor(rgb: 0x212121), theme.list.plainBackgroundColor)]) @@ -55,7 +56,7 @@ public final class AuthDataTransferSplashScreen: ViewController { return } - DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: strongSelf.presentationData, present: { c, a in + DeviceAccess.authorizeAccess(to: .camera(.qrCode), presentationData: strongSelf.presentationData, present: { c, a in guard let strongSelf = self else { return } @@ -70,7 +71,7 @@ public final class AuthDataTransferSplashScreen: ViewController { guard granted else { return } - (strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: AuthTransferScanScreen(context: strongSelf.context, activeSessionsContext: strongSelf.activeSessionsContext), animated: true) + (strongSelf.navigationController as? NavigationController)?.replaceController(strongSelf, with: QrCodeScanScreen(context: strongSelf.context, subject: .authTransfer(activeSessionsContext: strongSelf.activeSessionsContext)), animated: true) }) }) diff --git a/submodules/ContactListUI/BUILD b/submodules/ContactListUI/BUILD index 733cb8688e..7d77adb63e 100644 --- a/submodules/ContactListUI/BUILD +++ b/submodules/ContactListUI/BUILD @@ -35,6 +35,7 @@ swift_library( "//submodules/StickerResources:StickerResources", "//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", + "//submodules/QrCodeUI:QrCodeUI", ], visibility = [ "//visibility:public", diff --git a/submodules/ContactListUI/Sources/ContactsController.swift b/submodules/ContactListUI/Sources/ContactsController.swift index f1b92630d8..58793e4d82 100644 --- a/submodules/ContactListUI/Sources/ContactsController.swift +++ b/submodules/ContactListUI/Sources/ContactsController.swift @@ -18,6 +18,7 @@ import TelegramPermissionsUI import AppBundle import StickerResources import ContextUI +import QrCodeUI private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBarSearchContentNode) -> Bool { if searchNode.expansionProgress > 0.0 && searchNode.expansionProgress < 1.0 { @@ -375,6 +376,43 @@ public class ContactsController: ViewController { }) } + self.contactsNode.openQrScan = { [weak self] in + if let strongSelf = self { + let context = strongSelf.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + DeviceAccess.authorizeAccess(to: .camera(.qrCode), presentationData: presentationData, present: { c, a in + c.presentationArguments = a + context.sharedContext.mainWindow?.present(c, on: .root) + }, openSettings: { + context.sharedContext.applicationBindings.openSettings() + }, { [weak self] granted in + guard let strongSelf = self else { + return + } + guard granted else { + strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) + return + } + let controller = QrCodeScanScreen(context: strongSelf.context, subject: .peer) + controller.showMyCode = { [weak self, weak controller] in + if let strongSelf = self { + let _ = (strongSelf.context.account.postbox.loadedPeerWithId(strongSelf.context.account.peerId) + |> deliverOnMainQueue).start(next: { [weak self, weak controller] peer in + if let strongSelf = self, let controller = controller { + controller.present(strongSelf.context.sharedContext.makeChatQrCodeScreen(context: strongSelf.context, peer: peer), in: .window(.root)) + } + }) + } + } + (strongSelf.navigationController as? NavigationController)?.pushViewController(controller, completion: { + if let strongSelf = self { + strongSelf.contactsNode.contactListNode.listNode.clearHighlightAnimated(true) + } + }) + }) + } + } + self.contactsNode.contactListNode.openSortMenu = { [weak self] in self?.presentSortMenu() } diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index e0fa6d6573..0c68933b2d 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -56,6 +56,7 @@ final class ContactsControllerNode: ASDisplayNode { var requestAddContact: ((String) -> Void)? var openPeopleNearby: (() -> Void)? var openInvite: (() -> Void)? + var openQrScan: (() -> Void)? private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -70,8 +71,12 @@ final class ContactsControllerNode: ASDisplayNode { var addNearbyImpl: (() -> Void)? var inviteImpl: (() -> Void)? + var qrScanImpl: (() -> Void)? + let options = [ContactListAdditionalOption(title: presentationData.strings.Contacts_AddPeopleNearby, icon: .generic(UIImage(bundleImageName: "Contact List/PeopleNearbyIcon")!), action: { addNearbyImpl?() + }), ContactListAdditionalOption(title: presentationData.strings.Contacts_ScanQrCode, icon: .generic(UIImage(bundleImageName: "Settings/QrIcon")!), action: { + qrScanImpl?() }), ContactListAdditionalOption(title: presentationData.strings.Contacts_InviteFriends, icon: .generic(UIImage(bundleImageName: "Contact List/AddMemberIcon")!), action: { inviteImpl?() })] @@ -128,6 +133,12 @@ final class ContactsControllerNode: ASDisplayNode { } } + qrScanImpl = { [weak self] in + if let strongSelf = self { + strongSelf.openQrScan?() + } + } + contextAction = { [weak self] peer, node, gesture in self?.contextAction(peer: peer, node: node, gesture: gesture) } diff --git a/submodules/DeviceAccess/Sources/DeviceAccess.swift b/submodules/DeviceAccess/Sources/DeviceAccess.swift index c3aabcf48b..6f5642e2da 100644 --- a/submodules/DeviceAccess/Sources/DeviceAccess.swift +++ b/submodules/DeviceAccess/Sources/DeviceAccess.swift @@ -17,6 +17,7 @@ import AccountContext public enum DeviceAccessCameraSubject { case video case videoCall + case qrCode } @@ -30,6 +31,7 @@ public enum DeviceAccessMediaLibrarySubject { case send case save case wallpaper + case qrCode } public enum DeviceAccessLocationSubject { @@ -269,6 +271,8 @@ public final class DeviceAccess { text = presentationData.strings.AccessDenied_Camera case .videoCall: text = presentationData.strings.AccessDenied_VideoCallCamera + case .qrCode: + text = presentationData.strings.AccessDenied_QrCamera } present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { openSettings() @@ -289,6 +293,8 @@ public final class DeviceAccess { text = presentationData.strings.AccessDenied_Camera case .videoCall: text = presentationData.strings.AccessDenied_VideoCallCamera + case .qrCode: + text = presentationData.strings.AccessDenied_QrCamera } } completion(false) @@ -345,6 +351,8 @@ public final class DeviceAccess { text = presentationData.strings.AccessDenied_SaveMedia case .wallpaper: text = presentationData.strings.AccessDenied_Wallpapers + case .qrCode: + text = presentationData.strings.AccessDenied_QrCode } present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { openSettings() diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 5cff2c7bbd..72ec3d63b0 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -207,8 +207,11 @@ public final class TextNodeLayout: NSObject { if line.isRTL { hasRTL = true } - spoilers.append(contentsOf: line.spoilers.map { ( $0.range, $0.frame.offsetBy(dx: line.frame.minX, dy: line.frame.minY)) }) - spoilerWords.append(contentsOf: line.spoilerWords.map { ( $0.range, $0.frame.offsetBy(dx: line.frame.minX, dy: line.frame.minY)) }) + + let lineFrame = displayLineFrame(frame: line.frame, isRTL: line.isRTL, boundingRect: CGRect(origin: CGPoint(), size: size), cutout: cutout) + + spoilers.append(contentsOf: line.spoilers.map { ( $0.range, $0.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) }) + spoilerWords.append(contentsOf: line.spoilerWords.map { ( $0.range, $0.frame.offsetBy(dx: lineFrame.minX, dy: lineFrame.minY)) }) } self.hasRTL = hasRTL self.spoilers = spoilers @@ -1095,7 +1098,7 @@ public class TextNode: ASDisplayNode { addSpoilerWord(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex, rightInset: truncated ? 12.0 : 0.0) } - addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length - 1) + addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length) } else if let _ = attributes[NSAttributedString.Key.strikethroughStyle] { let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) @@ -1176,7 +1179,7 @@ public class TextNode: ASDisplayNode { addSpoilerWord(line: coreTextLine, ascent: ascent, descent: descent, startIndex: currentStartIndex, endIndex: endIndex) } - addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length - 1) + addSpoiler(line: coreTextLine, ascent: ascent, descent: descent, startIndex: range.location, endIndex: range.location + range.length) } else if let _ = attributes[NSAttributedString.Key.strikethroughStyle] { let lowerX = floor(CTLineGetOffsetForStringIndex(coreTextLine, range.location, nil)) let upperX = ceil(CTLineGetOffsetForStringIndex(coreTextLine, range.location + range.length, nil)) diff --git a/submodules/GalleryUI/Sources/RecognizedTextSelectionNode.swift b/submodules/GalleryUI/Sources/RecognizedTextSelectionNode.swift index 8f5713c25c..6aac0ba01f 100644 --- a/submodules/GalleryUI/Sources/RecognizedTextSelectionNode.swift +++ b/submodules/GalleryUI/Sources/RecognizedTextSelectionNode.swift @@ -540,7 +540,13 @@ public final class RecognizedTextSelectionNode: ASDisplayNode { return self.view } if self.bounds.contains(point) { - return self.view + for recognition in self.recognitions { + let mappedRect = recognition.rect.convertTo(size: self.bounds.size) + if mappedRect.boundingFrame.insetBy(dx: -20.0, dy: -20.0).contains(point) { + return self.view + } + } + return nil } return nil } diff --git a/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift b/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift index 8ebeb58f25..85654b1bcf 100644 --- a/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift +++ b/submodules/ImageContentAnalysis/Sources/ImageContentAnalysis.swift @@ -351,3 +351,33 @@ public func recognizedContent(postbox: Postbox, image: @escaping () -> UIImage?, } } } + +public func recognizeQRCode(in image: UIImage?) -> Signal { + if #available(iOS 11.0, *) { + guard let cgImage = image?.cgImage else { + return .complete() + } + return Signal { subscriber in + let barcodeRequest = VNDetectBarcodesRequest { request, error in + if let result = request.results?.first as? VNBarcodeObservation { + subscriber.putNext(result.payloadStringValue) + } else { + subscriber.putNext(nil) + } + subscriber.putCompletion() + } + barcodeRequest.preferBackgroundProcessing = true + + let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) + try? handler.perform([barcodeRequest]) + + return ActionDisposable { + if #available(iOS 13.0, *) { + barcodeRequest.cancel() + } + } + } + } else { + return .single(nil) + } +} diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index 655e9651b1..f237be32fd 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -201,8 +201,14 @@ public class InvisibleInkDustNode: ASDisplayNode { } - let textLength = CGFloat((textNode.cachedLayout?.attributedString?.string ?? "").count) - let timeToRead = min(45.0, ceil(max(4.0, textLength * 0.04))) + var spoilersLength: Int = 0 + if let spoilers = textNode.cachedLayout?.spoilers { + for spoiler in spoilers { + spoilersLength += spoiler.0.length + } + } + + let timeToRead = min(45.0, ceil(max(4.0, Double(spoilersLength) * 0.04))) Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) { self.isRevealed = false diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 12d2e57962..d5b76e5ce9 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -339,13 +339,14 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { let hasTopStripe: Bool let hasTopGroupInset: Bool let noInsets: Bool + let noCorners: Bool public let tag: ItemListItemTag? let header: ListViewItemHeader? let shimmering: ItemListPeerItemShimmering? let displayDecorations: Bool let disableInteractiveTransitionIfNecessary: Bool - public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: EnginePeer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: EnginePeer.Presence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) { + public init(presentationData: ItemListPresentationData, dateTimeFormat: PresentationDateTimeFormat, nameDisplayOrder: PresentationPersonNameOrder, context: AccountContext, peer: EnginePeer, height: ItemListPeerItemHeight = .peerList, aliasHandling: ItemListPeerItemAliasHandling = .standard, nameColor: ItemListPeerItemNameColor = .primary, nameStyle: ItemListPeerItemNameStyle = .distinctBold, presence: EnginePeer.Presence?, text: ItemListPeerItemText, label: ItemListPeerItemLabel, editing: ItemListPeerItemEditing, revealOptions: ItemListPeerItemRevealOptions? = nil, switchValue: ItemListPeerItemSwitch?, enabled: Bool, highlighted: Bool = false, selectable: Bool, sectionId: ItemListSectionId, action: (() -> Void)?, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, removePeer: @escaping (EnginePeer.Id) -> Void, toggleUpdated: ((Bool) -> Void)? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, hasTopStripe: Bool = true, hasTopGroupInset: Bool = true, noInsets: Bool = false, noCorners: Bool = false, tag: ItemListItemTag? = nil, header: ListViewItemHeader? = nil, shimmering: ItemListPeerItemShimmering? = nil, displayDecorations: Bool = true, disableInteractiveTransitionIfNecessary: Bool = false) { self.presentationData = presentationData self.dateTimeFormat = dateTimeFormat self.nameDisplayOrder = nameDisplayOrder @@ -373,6 +374,7 @@ public final class ItemListPeerItem: ListViewItem, ItemListItem { self.hasTopStripe = hasTopStripe self.hasTopGroupInset = hasTopGroupInset self.noInsets = noInsets + self.noCorners = noCorners self.tag = tag self.header = header self.shimmering = shimmering @@ -999,7 +1001,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo strongSelf.addSubnode(strongSelf.maskNode) } - let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noInsets + let hasCorners = itemListHasRoundedBlockLayout(params) && !item.noCorners var hasTopCorners = false var hasBottomCorners = false switch neighbors.top { diff --git a/submodules/LegacyMediaPickerUI/Sources/LegacyWallpaperPicker.swift b/submodules/LegacyMediaPickerUI/Sources/LegacyWallpaperPicker.swift index 05968635c7..a556175eea 100644 --- a/submodules/LegacyMediaPickerUI/Sources/LegacyWallpaperPicker.swift +++ b/submodules/LegacyMediaPickerUI/Sources/LegacyWallpaperPicker.swift @@ -7,11 +7,11 @@ import TelegramPresentationData import DeviceAccess import AccountContext -public func legacyWallpaperPicker(context: AccountContext, presentationData: PresentationData) -> Signal<(LegacyComponentsContext) -> TGMediaAssetsController, Void> { +public func legacyWallpaperPicker(context: AccountContext, presentationData: PresentationData, subject: DeviceAccessMediaLibrarySubject = .wallpaper) -> Signal<(LegacyComponentsContext) -> TGMediaAssetsController, Void> { return Signal { subscriber in let intent = TGMediaAssetsControllerSetCustomWallpaperIntent - DeviceAccess.authorizeAccess(to: .mediaLibrary(.wallpaper), presentationData: presentationData, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in + DeviceAccess.authorizeAccess(to: .mediaLibrary(subject), presentationData: presentationData, present: context.sharedContext.presentGlobalController, openSettings: context.sharedContext.applicationBindings.openSettings, { value in if !value { subscriber.putError(Void()) return diff --git a/submodules/QrCodeUI/BUILD b/submodules/QrCodeUI/BUILD index 159c83a8c5..0308b4224b 100644 --- a/submodules/QrCodeUI/BUILD +++ b/submodules/QrCodeUI/BUILD @@ -22,6 +22,15 @@ swift_library( "//submodules/AnimatedStickerNode:AnimatedStickerNode", "//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode", "//submodules/PresentationDataUtils:PresentationDataUtils", + "//submodules/GlassButtonNode:GlassButtonNode", + "//submodules/TextFormat:TextFormat", + "//submodules/Markdown:Markdown", + "//submodules/UndoUI:UndoUI", + "//submodules/Camera:Camera", + "//submodules/LegacyUI:LegacyUI", + "//submodules/LegacyComponents:LegacyComponents", + "//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI", + "//submodules/ImageContentAnalysis:ImageContentAnalysis", ], visibility = [ "//visibility:public", diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift new file mode 100644 index 0000000000..c96a5a07d0 --- /dev/null +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -0,0 +1,725 @@ +import Foundation +import UIKit +import AccountContext +import AsyncDisplayKit +import Display +import SwiftSignalKit +import Camera +import GlassButtonNode +import CoreImage +import AlertUI +import TelegramPresentationData +import TelegramCore +import UndoUI +import Markdown +import TextFormat +import LegacyUI +import LegacyComponents +import LegacyMediaPickerUI +import ImageContentAnalysis +import PresentationDataUtils + +private func parseAuthTransferUrl(_ url: URL) -> Data? { + var tokenString: String? + if let query = url.query, let components = URLComponents(string: "/?" + query), let queryItems = components.queryItems { + for queryItem in queryItems { + if let value = queryItem.value { + if queryItem.name == "token", !value.isEmpty { + tokenString = value + } + } + } + } + if var tokenString = tokenString { + tokenString = tokenString.replacingOccurrences(of: "-", with: "+") + tokenString = tokenString.replacingOccurrences(of: "_", with: "/") + while tokenString.count % 4 != 0 { + tokenString.append("=") + } + if let data = Data(base64Encoded: tokenString) { + return data + } + } + return nil +} + +private func generateFrameImage() -> UIImage? { + return generateImage(CGSize(width: 64.0, height: 64.0), contextGenerator: { size, context in + let bounds = CGRect(origin: CGPoint(), size: size) + context.clear(bounds) + context.setStrokeColor(UIColor.white.cgColor) + context.setLineWidth(4.0) + context.setLineCap(.round) + + let path = CGMutablePath() + path.move(to: CGPoint(x: 2.0, y: 2.0 + 26.0)) + path.addArc(tangent1End: CGPoint(x: 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0) + path.addLine(to: CGPoint(x: 2.0 + 26.0, y: 2.0)) + context.addPath(path) + context.strokePath() + + path.move(to: CGPoint(x: size.width - 2.0, y: 2.0 + 26.0)) + path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0) + path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: 2.0)) + context.addPath(path) + context.strokePath() + + path.move(to: CGPoint(x: 2.0, y: size.height - 2.0 - 26.0)) + path.addArc(tangent1End: CGPoint(x: 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0) + path.addLine(to: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0)) + context.addPath(path) + context.strokePath() + + path.move(to: CGPoint(x: size.width - 2.0, y: size.height - 2.0 - 26.0)) + path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0) + path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: size.height - 2.0)) + context.addPath(path) + context.strokePath() + })?.stretchableImage(withLeftCapWidth: 32, topCapHeight: 32) +} + +public final class QrCodeScanScreen: ViewController { + public enum Subject { + case authTransfer(activeSessionsContext: ActiveSessionsContext) + case peer + } + + private let context: AccountContext + private let subject: QrCodeScanScreen.Subject + private var presentationData: PresentationData + + private var codeDisposable: Disposable? + private var inForegroundDisposable: Disposable? + private let approveDisposable = MetaDisposable() + + private var controllerNode: QrCodeScanScreenNode { + return self.displayNode as! QrCodeScanScreenNode + } + + public var showMyCode: () -> Void = {} + + private var codeResolved = false + + public init(context: AccountContext, subject: QrCodeScanScreen.Subject) { + self.context = context + self.subject = subject + + self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let navigationBarTheme = NavigationBarTheme(buttonColor: .white, disabledButtonColor: .white, primaryTextColor: .white, backgroundColor: .clear, enableBackgroundBlur: false, separatorColor: .clear, badgeBackgroundColor: .clear, badgeStrokeColor: .clear, badgeTextColor: .clear) + + super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: NavigationBarStrings(back: self.presentationData.strings.Common_Back, close: self.presentationData.strings.Common_Close))) + + self.statusBar.statusBarStyle = .White + + self.navigationPresentation = .modalInLargeLayout + self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) + self.navigationBar?.intrinsicCanTransitionInline = false + + self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Back, style: .plain, target: nil, action: nil) + + self.inForegroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground + |> deliverOnMainQueue).start(next: { [weak self] inForeground in + guard let strongSelf = self else { + return + } + (strongSelf.displayNode as! QrCodeScanScreenNode).updateInForeground(inForeground) + }) + + if case .peer = subject { + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Contacts_QrCode_MyCode, style: .plain, target: self, action: #selector(self.myCodePressed)) + } else { + #if DEBUG + self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, target: self, action: #selector(self.testPressed)) + #endif + } + } + + required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.codeDisposable?.dispose() + self.inForegroundDisposable?.dispose() + self.approveDisposable.dispose() + } + + @objc private func myCodePressed() { + self.showMyCode() + } + + @objc private func testPressed() { + self.dismissWithSession(session: nil) + } + + private func dismissWithSession(session: RecentAccountSession?) { + guard case let .authTransfer(activeSessionsContext) = self.subject else { + return + } + if let navigationController = navigationController as? NavigationController { + self.present(UndoOverlayController(presentationData: self.presentationData, content: .actionSucceeded(title: self.presentationData.strings.AuthSessions_AddedDeviceTitle, text: session?.appName ?? "Telegram for macOS", cancel: self.presentationData.strings.AuthSessions_AddedDeviceTerminate), elevatedLayout: false, animateInAsReplacement: false, action: { value in + if value == .undo, let session = session { + let _ = activeSessionsContext.remove(hash: session.hash).start() + return true + } else { + return false + } + }), in: .window(.root)) + + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { controller in + if controller is RecentSessionsController { + return false + } + if controller === self { + return false + } + return true + } + viewControllers.append(self.context.sharedContext.makeRecentSessionsController(context: self.context, activeSessionsContext: activeSessionsContext)) + navigationController.setViewControllers(viewControllers, animated: true) + } else { + self.dismiss() + } + } + + override public func loadDisplayNode() { + self.displayNode = QrCodeScanScreenNode(context: self.context, presentationData: self.presentationData, controller: self, subject: self.subject) + + self.displayNodeDidLoad() + + self.codeDisposable = ((self.displayNode as! QrCodeScanScreenNode).focusedCode.get() + |> map { code -> String? in + return code?.message + } + |> distinctUntilChanged + |> mapToSignal { code -> Signal in + return .single(code) + |> delay(0.5, queue: Queue.mainQueue()) + }).start(next: { [weak self] code in + guard let strongSelf = self, !strongSelf.codeResolved else { + return + } + guard let code = code else { + return + } + switch strongSelf.subject { + case let .authTransfer(activeSessionsContext): + if let url = URL(string: code), let parsedToken = parseAuthTransferUrl(url) { + strongSelf.approveDisposable.set((approveAuthTransferToken(account: strongSelf.context.account, token: parsedToken, activeSessionsContext: activeSessionsContext) + |> deliverOnMainQueue).start(next: { session in + guard let strongSelf = self else { + return + } + strongSelf.controllerNode.codeWithError = nil + if case let .authTransfer(activeSessionsContext) = strongSelf.subject { + Queue.mainQueue().after(1.5, { + activeSessionsContext.loadMore() + }) + } + strongSelf.dismissWithSession(session: session) + }, error: { _ in + guard let strongSelf = self else { + return + } + strongSelf.controllerNode.codeWithError = code + strongSelf.controllerNode.updateFocusedRect(nil) + })) + } + case .peer: + if let _ = URL(string: code) { + strongSelf.controllerNode.resolveCode(code: code, completion: { [weak self] result in + if let strongSelf = self { + strongSelf.codeResolved = true + } + }) + } + } + }) + + self.controllerNode.present = { [weak self] c in + self?.present(c, in: .window(.root)) + } + } + + override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { + super.containerLayoutUpdated(layout, transition: transition) + + (self.displayNode as! QrCodeScanScreenNode).containerLayoutUpdated(layout: layout, navigationHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) + } +} + +private final class QrCodeScanScreenNode: ViewControllerTracingNode, UIScrollViewDelegate { + private let context: AccountContext + private var presentationData: PresentationData + private weak var controller: QrCodeScanScreen? + private let subject: QrCodeScanScreen.Subject + + private let previewNode: CameraPreviewNode + private let fadeNode: ASDisplayNode + private let topDimNode: ASDisplayNode + private let bottomDimNode: ASDisplayNode + private let leftDimNode: ASDisplayNode + private let rightDimNode: ASDisplayNode + private let centerDimNode: ASDisplayNode + private let frameNode: ASImageNode + private let galleryButtonNode: GlassButtonNode + private let torchButtonNode: GlassButtonNode + private let titleNode: ImmediateTextNode + private let textNode: ImmediateTextNode + private let errorTextNode: ImmediateTextNode + + private let camera: Camera + private let codeDisposable = MetaDisposable() + private var torchDisposable: Disposable? + private let resolveDisposable = MetaDisposable() + + fileprivate let focusedCode = ValuePromise(ignoreRepeated: true) + private var focusedRect: CGRect? + + var present: (ViewController) -> Void = { _ in } + + private var validLayout: (ContainerViewLayout, CGFloat)? + + var codeWithError: String? { + didSet { + if self.codeWithError != oldValue { + if self.codeWithError != nil { + self.errorTextNode.isHidden = false + } else { + self.errorTextNode.isHidden = true + } + } + } + } + + private var highlightViews: [UIVisualEffectView] = [] + + init(context: AccountContext, presentationData: PresentationData, controller: QrCodeScanScreen, subject: QrCodeScanScreen.Subject) { + self.context = context + self.presentationData = presentationData + self.controller = controller + self.subject = subject + + self.previewNode = CameraPreviewNode() + self.previewNode.backgroundColor = .black + + self.fadeNode = ASDisplayNode() + self.fadeNode.alpha = 0.0 + self.fadeNode.backgroundColor = .black + + self.topDimNode = ASDisplayNode() + self.topDimNode.alpha = 0.625 + self.topDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + + self.bottomDimNode = ASDisplayNode() + self.bottomDimNode.alpha = 0.625 + self.bottomDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + + self.leftDimNode = ASDisplayNode() + self.leftDimNode.alpha = 0.625 + self.leftDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + + self.rightDimNode = ASDisplayNode() + self.rightDimNode.alpha = 0.625 + self.rightDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + + self.centerDimNode = ASDisplayNode() + self.centerDimNode.alpha = 0.0 + self.centerDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.8) + + self.frameNode = ASImageNode() + self.frameNode.image = generateFrameImage() + + self.galleryButtonNode = GlassButtonNode(icon: UIImage(bundleImageName: "Wallet/CameraGalleryIcon")!, label: nil) + self.torchButtonNode = GlassButtonNode(icon: UIImage(bundleImageName: "Wallet/CameraFlashIcon")!, label: nil) + + let title: String + var text: String + switch subject { + case .authTransfer: + title = presentationData.strings.AuthSessions_AddDevice_ScanTitle + text = presentationData.strings.AuthSessions_AddDevice_ScanInstallInfo + case .peer: + title = "" + text = "" + } + + self.titleNode = ImmediateTextNode() + self.titleNode.displaysAsynchronously = false + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(32.0), textColor: .white) + self.titleNode.maximumNumberOfLines = 0 + self.titleNode.textAlignment = .center + + let textFont = Font.regular(17.0) + let boldFont = Font.bold(17.0) + + text = text.replacingOccurrences(of: " [", with: " [").replacingOccurrences(of: ") ", with: ") ") + + let attributedText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(text, attributes: MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: .white), bold: MarkdownAttributeSet(font: boldFont, textColor: .white), link: MarkdownAttributeSet(font: boldFont, textColor: .white), linkAttribute: { contents in + return (TelegramTextAttributes.URL, contents) + }))) + + self.textNode = ImmediateTextNode() + self.textNode.displaysAsynchronously = false + self.textNode.attributedText = attributedText + self.textNode.maximumNumberOfLines = 0 + self.textNode.textAlignment = .center + self.textNode.lineSpacing = 0.5 + + self.errorTextNode = ImmediateTextNode() + self.errorTextNode.displaysAsynchronously = false + self.errorTextNode.attributedText = NSAttributedString(string: presentationData.strings.AuthSessions_AddDevice_InvalidQRCode, font: Font.medium(16.0), textColor: .white) + self.errorTextNode.maximumNumberOfLines = 0 + self.errorTextNode.textAlignment = .center + self.errorTextNode.isHidden = true + + self.camera = Camera(configuration: .init(preset: .hd1920x1080, position: .back, audio: false)) + + super.init() + + self.backgroundColor = self.presentationData.theme.list.plainBackgroundColor + + self.torchDisposable = (self.camera.hasTorch + |> deliverOnMainQueue).start(next: { [weak self] hasTorch in + if let strongSelf = self { + strongSelf.torchButtonNode.isHidden = !hasTorch + } + }) + + self.addSubnode(self.previewNode) + self.addSubnode(self.fadeNode) + self.addSubnode(self.topDimNode) + self.addSubnode(self.bottomDimNode) + self.addSubnode(self.leftDimNode) + self.addSubnode(self.rightDimNode) + self.addSubnode(self.centerDimNode) + self.addSubnode(self.frameNode) + if case .peer = subject { + self.addSubnode(self.galleryButtonNode) + } + self.addSubnode(self.torchButtonNode) + self.addSubnode(self.titleNode) + self.addSubnode(self.textNode) + self.addSubnode(self.errorTextNode) + + self.galleryButtonNode.addTarget(self, action: #selector(self.galleryPressed), forControlEvents: .touchUpInside) + self.torchButtonNode.addTarget(self, action: #selector(self.torchPressed), forControlEvents: .touchUpInside) + } + + deinit { + self.codeDisposable.dispose() + self.torchDisposable?.dispose() + self.resolveDisposable.dispose() + self.camera.stopCapture(invalidate: true) + } + + fileprivate func updateInForeground(_ inForeground: Bool) { + if !inForeground { + self.camera.stopCapture(invalidate: false) + } else { + self.camera.startCapture() + } + } + + override func didLoad() { + super.didLoad() + + self.camera.attachPreviewNode(self.previewNode) + self.camera.startCapture() + + let throttledSignal = self.camera.detectedCodes + |> mapToThrottled { next -> Signal<[CameraCode], NoError> in + return .single(next) |> then(.complete() |> delay(0.3, queue: Queue.concurrentDefaultQueue())) + } + + self.codeDisposable.set((throttledSignal + |> deliverOnMainQueue).start(next: { [weak self] codes in + guard let strongSelf = self else { + return + } + let filteredCodes: [CameraCode] + switch strongSelf.subject { + case .authTransfer: + filteredCodes = codes.filter { $0.message.hasPrefix("tg://") } + case .peer: + filteredCodes = codes.filter { $0.message.hasPrefix("https://t.me/") || $0.message.hasPrefix("t.me/") } + } + if let code = filteredCodes.first, CGRect(x: 0.3, y: 0.3, width: 0.4, height: 0.4).contains(code.boundingBox.center) { + if strongSelf.codeWithError != code.message { + strongSelf.codeWithError = nil + } + if strongSelf.codeWithError == code.message { + strongSelf.focusedCode.set(nil) + strongSelf.updateFocusedRect(nil) + } else { + strongSelf.focusedCode.set(code) + strongSelf.updateFocusedRect(code.boundingBox) + } + } else { + strongSelf.codeWithError = nil + strongSelf.focusedCode.set(nil) + strongSelf.updateFocusedRect(nil) + } + })) + + let recognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapLongTapOrDoubleTapGesture(_:))) + recognizer.tapActionAtPoint = { _ in + return .waitForSingleTap + } + self.textNode.view.addGestureRecognizer(recognizer) + } + + @objc private func tapLongTapOrDoubleTapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) { + switch recognizer.state { + case .ended: + if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation { + switch gesture { + case .tap: + if let (_, attributes) = self.textNode.attributesAtPoint(location) { + if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { + switch url { + case "desktop": + self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: "https://getdesktop.telegram.org", forceExternal: true, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {}) + case "web": + self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: "https://web.telegram.org", forceExternal: true, presentationData: self.context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {}) + default: + break + } + } + } + default: + break + } + } + default: + break + } + } + + func updateFocusedRect(_ rect: CGRect?) { + self.focusedRect = rect + if let (layout, navigationHeight) = self.validLayout { + self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.4, curve: .spring)) + } + } + + func containerLayoutUpdated(layout: ContainerViewLayout, navigationHeight: CGFloat, transition: ContainedViewLayoutTransition) { + self.validLayout = (layout, navigationHeight) + + let sideInset: CGFloat = 66.0 + let titleSpacing: CGFloat = 48.0 + let bounds = CGRect(origin: CGPoint(), size: layout.size) + + if case .tablet = layout.deviceMetrics.type { + if UIDevice.current.orientation == .landscapeLeft { + self.previewNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + } else if UIDevice.current.orientation == .landscapeRight { + self.previewNode.transform = CATransform3DMakeRotation(CGFloat.pi / 2.0, 0.0, 0.0, 1.0) + } else { + self.previewNode.transform = CATransform3DIdentity + } + } + transition.updateFrame(node: self.previewNode, frame: bounds) + transition.updateFrame(node: self.fadeNode, frame: bounds) + + let frameSide = max(240.0, layout.size.width - sideInset * 2.0) + let dimHeight = ceil((layout.size.height - frameSide) / 2.0) + let dimInset = (layout.size.width - frameSide) / 2.0 + + let dimAlpha: CGFloat + let dimRect: CGRect + let controlsAlpha: CGFloat + let centerDimAlpha: CGFloat = 0.0 + let frameAlpha: CGFloat = 1.0 + if let focusedRect = self.focusedRect { + controlsAlpha = 0.0 + dimAlpha = 1.0 + let side = max(bounds.width * focusedRect.width, bounds.height * focusedRect.height) * 0.6 + let center = CGPoint(x: (1.0 - focusedRect.center.y) * bounds.width, y: focusedRect.center.x * bounds.height) + dimRect = CGRect(x: center.x - side / 2.0, y: center.y - side / 2.0, width: side, height: side) + } else { + controlsAlpha = 1.0 + dimAlpha = 0.625 + dimRect = CGRect(x: dimInset, y: dimHeight, width: layout.size.width - dimInset * 2.0, height: layout.size.height - dimHeight * 2.0) + } + + transition.updateAlpha(node: self.topDimNode, alpha: dimAlpha) + transition.updateAlpha(node: self.bottomDimNode, alpha: dimAlpha) + transition.updateAlpha(node: self.leftDimNode, alpha: dimAlpha) + transition.updateAlpha(node: self.rightDimNode, alpha: dimAlpha) + transition.updateAlpha(node: self.centerDimNode, alpha: centerDimAlpha) + transition.updateAlpha(node: self.frameNode, alpha: frameAlpha) + + transition.updateFrame(node: self.topDimNode, frame: CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: dimRect.minY)) + transition.updateFrame(node: self.bottomDimNode, frame: CGRect(x: 0.0, y: dimRect.maxY, width: layout.size.width, height: max(0.0, layout.size.height - dimRect.maxY))) + transition.updateFrame(node: self.leftDimNode, frame: CGRect(x: 0.0, y: dimRect.minY, width: max(0.0, dimRect.minX), height: dimRect.height)) + transition.updateFrame(node: self.rightDimNode, frame: CGRect(x: dimRect.maxX, y: dimRect.minY, width: max(0.0, layout.size.width - dimRect.maxX), height: dimRect.height)) + transition.updateFrame(node: self.frameNode, frame: dimRect.insetBy(dx: -2.0, dy: -2.0)) + transition.updateFrame(node: self.centerDimNode, frame: dimRect) + + let buttonSize = CGSize(width: 72.0, height: 72.0) + var torchFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - buttonSize.width) / 2.0), y: dimHeight + frameSide + 98.0), size: buttonSize) + let updatedTorchY = min(torchFrame.minY, layout.size.height - torchFrame.height - 10.0) + let additionalTorchOffset: CGFloat = updatedTorchY - torchFrame.minY + torchFrame.origin.y = updatedTorchY + + var galleryFrame = torchFrame + if case .peer = self.subject { + galleryFrame.origin.x -= buttonSize.width + torchFrame.origin.x += buttonSize.width + } + transition.updateFrame(node: self.galleryButtonNode, frame: galleryFrame) + transition.updateFrame(node: self.torchButtonNode, frame: torchFrame) + + transition.updateAlpha(node: self.textNode, alpha: controlsAlpha) + transition.updateAlpha(node: self.errorTextNode, alpha: controlsAlpha) + transition.updateAlpha(node: self.galleryButtonNode, alpha: controlsAlpha) + transition.updateAlpha(node: self.torchButtonNode, alpha: controlsAlpha) + for view in self.highlightViews { + transition.updateAlpha(layer: view.layer, alpha: controlsAlpha) + } + + let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) + let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) + let errorTextSize = self.errorTextNode.updateLayout(CGSize(width: layout.size.width - 16.0, height: layout.size.height)) + var textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: max(dimHeight - textSize.height - titleSpacing, navigationHeight + floorToScreenPixels((dimHeight - navigationHeight - textSize.height) / 2.0) + 5.0)), size: textSize) + let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: textFrame.minY - 18.0 - titleSize.height), size: titleSize) + if titleFrame.minY < navigationHeight { + transition.updateAlpha(node: self.titleNode, alpha: 0.0) + textFrame = textFrame.offsetBy(dx: 0.0, dy: -5.0) + } else { + transition.updateAlpha(node: self.titleNode, alpha: controlsAlpha) + } + var errorTextFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - errorTextSize.width) / 2.0), y: dimHeight + frameSide + 48.0), size: errorTextSize) + errorTextFrame.origin.y += floor(additionalTorchOffset / 2.0) + + transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame) + transition.updateFrameAdditive(node: self.textNode, frame: textFrame) + transition.updateFrameAdditive(node: self.errorTextNode, frame: errorTextFrame) + + if self.highlightViews.isEmpty { + let urlAttributesAndRects = self.textNode.cachedLayout?.allAttributeRects(name: "UrlAttributeT") ?? [] + + for (_, rect) in urlAttributesAndRects { + let view = UIVisualEffectView(effect: UIBlurEffect(style: .light)) + view.clipsToBounds = true + view.layer.cornerRadius = 5.0 + view.frame = rect.offsetBy(dx: self.textNode.frame.minX, dy: self.textNode.frame.minY).insetBy(dx: -4.0, dy: -2.0) + self.view.insertSubview(view, belowSubview: self.textNode.view) + self.highlightViews.append(view) + } + } + } + + @objc private func galleryPressed() { + let context = self.context + let presentationData = context.sharedContext.currentPresentationData.with { $0 } + + let presentError = { [weak self] in + let alertController = textAlertController(context: context, title: nil, text: presentationData.strings.Contacts_QrCode_NoCodeFound, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]) + self?.present(alertController) + } + + let _ = legacyWallpaperPicker(context: context, presentationData: presentationData, subject: .qrCode).start(next: { [weak self] generator in + let legacyController = LegacyController(presentation: .modal(animateIn: true), theme: presentationData.theme) + legacyController.statusBar.statusBarStyle = presentationData.theme.rootController.statusBarStyle.style + + let controller = generator(legacyController.context) + legacyController.bind(controller: controller) + legacyController.deferScreenEdgeGestures = [.top] + controller.selectionBlock = { [weak legacyController] asset, _ in + if let asset = asset { + TGMediaAssetImageSignals.image(for: asset, imageType: TGMediaAssetImageTypeScreen, size: CGSize(width: 1280.0, height: 1280.0)).start(next: { image in + if let image = image as? UIImage { + let _ = (recognizeQRCode(in: image) + |> deliverOnMainQueue).start(next: { [weak self] result in + if let result = result, let strongSelf = self { + strongSelf.resolveCode(code: result, completion: { result in + if result { + + } else { + presentError() + } + }) + } else { + presentError() + } + }) + } else { + presentError() + } + }, error: { _ in + presentError() + }, completed: { + }) + + legacyController?.dismiss() + } + } + controller.dismissalBlock = { [weak legacyController] in + if let legacyController = legacyController { + legacyController.dismiss() + } + } + self?.present(legacyController) + }) + } + + @objc private func torchPressed() { + self.torchButtonNode.isSelected = !self.torchButtonNode.isSelected + self.camera.setTorchActive(self.torchButtonNode.isSelected) + } + + fileprivate func resolveCode(code: String, completion: @escaping (Bool) -> Void) { + self.resolveDisposable.set((self.context.sharedContext.resolveUrl(context: self.context, peerId: nil, url: code, skipUrlAuth: false) + |> deliverOnMainQueue).start(next: { [weak self] result in + if let strongSelf = self { + completion(strongSelf.openResolved(result)) + } + })) + } + + private func openResolved(_ result: ResolvedUrl) -> Bool { + switch result { + case .peer, .stickerPack, .join, .wallpaper, .theme: + break + default: + return false + } + + guard let navigationController = self.controller?.navigationController as? NavigationController else { + return false + } + self.context.sharedContext.openResolvedUrl(result, context: self.context, urlContext: .generic, navigationController: navigationController, openPeer: { [weak self] peerId, navigation in + guard let strongSelf = self else { + return + } + strongSelf.context.sharedContext.navigateToChatController(NavigateToChatControllerParams(navigationController: navigationController, context: strongSelf.context, chatLocation: .peer(peerId), subject: nil, keepStack: .always, peekData: nil, completion: { [weak navigationController] _ in + if let navigationController = navigationController { + var viewControllers = navigationController.viewControllers + viewControllers = viewControllers.filter { controller in + if controller is QrCodeScanScreen { + return false + } + return true + } + navigationController.setViewControllers(viewControllers, animated: false) + } + })) + }, sendFile: nil, + sendSticker: { _, _, _ in + return false + }, requestMessageActionUrlAuth: nil, + joinVoiceChat: { peerId, invite, call in + }, present: { [weak self] c, a in + self?.controller?.present(c, in: .window(.root), with: a) + }, dismissInput: { [weak self] in + self?.view.endEditing(true) + }, contentContext: nil) + + return true + } +} + diff --git a/submodules/QrCodeUI/Sources/QrCodeScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScreen.swift index b6aadc72ae..707dc8d2f2 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScreen.swift @@ -230,24 +230,12 @@ public final class QrCodeScreen: ViewController { let title: String let text: String switch subject { - case let .peer(peer): - title = self.presentationData.strings.PeerInfo_QRCode_Title - if case let .user(user) = peer { - if user.id == context.account.peerId { - text = self.presentationData.strings.UserInfo_QRCode_InfoYou - } else if user.botInfo != nil { - text = self.presentationData.strings.UserInfo_QRCode_InfoBot - } else { - text = self.presentationData.strings.UserInfo_QRCode_InfoOther(peer.compactDisplayTitle).string - } - } else if case let .channel(channel) = peer, case .broadcast = channel.info { - text = self.presentationData.strings.GroupInfo_QRCode_Info - } else { - text = self.presentationData.strings.ChannelInfo_QRCode_Info - } case let .invite(_, isGroup): title = self.presentationData.strings.InviteLink_QRCode_Title text = isGroup ? self.presentationData.strings.InviteLink_QRCode_Info : self.presentationData.strings.InviteLink_QRCode_InfoChannel + default: + title = "" + text = "" } self.titleNode = ASTextNode() diff --git a/submodules/SettingsUI/BUILD b/submodules/SettingsUI/BUILD index aa68c921f7..7c0f25465a 100644 --- a/submodules/SettingsUI/BUILD +++ b/submodules/SettingsUI/BUILD @@ -93,6 +93,7 @@ swift_library( "//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode", "//submodules/WebPBinding:WebPBinding", "//submodules/Translate:Translate", + "//submodules/QrCodeUI:QrCodeUI", ], visibility = [ "//visibility:public", diff --git a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift index 340cfaed54..db65fcdc95 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/Recent Sessions/RecentSessionsController.swift @@ -12,6 +12,7 @@ import AccountContext import AuthTransferUI import ItemListPeerActionItem import DeviceAccess +import QrCodeUI private final class RecentSessionsControllerArguments { let context: AccountContext @@ -749,7 +750,7 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet)) }, addDevice: { let presentationData = context.sharedContext.currentPresentationData.with { $0 } - DeviceAccess.authorizeAccess(to: .camera(.video), presentationData: presentationData, present: { c, a in + DeviceAccess.authorizeAccess(to: .camera(.qrCode), presentationData: presentationData, present: { c, a in c.presentationArguments = a context.sharedContext.mainWindow?.present(c, on: .root) }, openSettings: { @@ -758,7 +759,7 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont guard granted else { return } - pushControllerImpl?(AuthTransferScanScreen(context: context, activeSessionsContext: activeSessionsContext)) + pushControllerImpl?(QrCodeScanScreen(context: context, subject: .authTransfer(activeSessionsContext: activeSessionsContext))) }) }, openOtherAppsUrl: { context.sharedContext.openExternalUrl(context: context, urlContext: .generic, url: "https://telegram.org/apps", forceExternal: true, presentationData: context.sharedContext.currentPresentationData.with { $0 }, navigationController: nil, dismissInput: {}) diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index ac4db6b397..ec6a220a46 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -1080,41 +1080,50 @@ final class MessageStoryRenderer { } } -private class ShareToInstagramActivity: UIActivity { +public class ShareToInstagramActivity: UIActivity { + private let context: AccountContext private var activityItems = [Any]() - private var action: ([Any]) -> Void - init(action: @escaping ([Any]) -> Void) { - self.action = action + public init(context: AccountContext) { + self.context = context + super.init() } - override var activityTitle: String? { - return "Share to Instagram Stories" + public override var activityTitle: String? { + return self.context.sharedContext.currentPresentationData.with { $0 }.strings.Share_ShareToInstagramStories } - override var activityImage: UIImage? { - return nil + public override var activityImage: UIImage? { + return UIImage(bundleImageName: "Share/Instagram") } - override var activityType: UIActivity.ActivityType? { + public override var activityType: UIActivity.ActivityType? { return UIActivity.ActivityType(rawValue: "org.telegram.Telegram.ShareToInstagram") } - override class var activityCategory: UIActivity.Category { + public override class var activityCategory: UIActivity.Category { return .action } - override func canPerform(withActivityItems activityItems: [Any]) -> Bool { - return true + public override func canPerform(withActivityItems activityItems: [Any]) -> Bool { + return self.context.sharedContext.applicationBindings.canOpenUrl("instagram-stories://") } - override func prepare(withActivityItems activityItems: [Any]) { + public override func prepare(withActivityItems activityItems: [Any]) { self.activityItems = activityItems } - override func perform() { - self.action(self.activityItems) + public override func perform() { + if let url = self.activityItems.first as? URL, let data = try? Data(contentsOf: url) { + let pasteboardItems: [[String: Any]] = [["com.instagram.sharedSticker.backgroundImage": data]] + if #available(iOS 10.0, *) { + UIPasteboard.general.setItems(pasteboardItems, options: [.expirationDate: Date().addingTimeInterval(5 * 60)]) + } else { + UIPasteboard.general.items = pasteboardItems + } + context.sharedContext.applicationBindings.openUrl("instagram-stories://share") + } activityDidFinish(true) } } diff --git a/submodules/TelegramPermissionsUI/Sources/PermissionController.swift b/submodules/TelegramPermissionsUI/Sources/PermissionController.swift index dc2040b2a5..9589dcc71d 100644 --- a/submodules/TelegramPermissionsUI/Sources/PermissionController.swift +++ b/submodules/TelegramPermissionsUI/Sources/PermissionController.swift @@ -228,6 +228,8 @@ public final class PermissionController: ViewController { self.displayNode = PermissionControllerNode(context: self.context, splitTest: self.splitTest) self.displayNodeDidLoad() + self.navigationBar?.updateBackgroundAlpha(0.0, transition: .immediate) + self.controllerNode.allow = { [weak self] in self?.allow?() } diff --git a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift index 781c2a4f4d..50213ed33a 100644 --- a/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift +++ b/submodules/TelegramPresentationData/Sources/DefaultDarkPresentationTheme.swift @@ -79,7 +79,7 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit } else { badgeFillColor = UIColor(rgb: 0xeb5545) badgeTextColor = UIColor(rgb: 0xffffff) - if initialAccentColor.lightness > 0.7 { + if initialAccentColor.lightness > 0.735 { secondaryBadgeTextColor = UIColor(rgb: 0x000000) } else { secondaryBadgeTextColor = UIColor(rgb: 0xffffff) @@ -153,7 +153,7 @@ public func customizeDefaultDarkPresentationTheme(theme: PresentationTheme, edit outgoingBubbleFillColors = bubbleColors.map(UIColor.init(rgb:)) let lightnessColor = topBubbleColor.mixedWith(bottomBubbleColor, alpha: 0.5) - if lightnessColor.lightness > 0.7 { + if lightnessColor.lightness > 0.735 { outgoingPrimaryTextColor = UIColor(rgb: 0x000000) outgoingSecondaryTextColor = UIColor(rgb: 0x000000, alpha: 0.5) outgoingLinkTextColor = UIColor(rgb: 0x000000) diff --git a/submodules/TelegramPresentationData/Sources/PresentationData.swift b/submodules/TelegramPresentationData/Sources/PresentationData.swift index 27550e256a..12f73d381a 100644 --- a/submodules/TelegramPresentationData/Sources/PresentationData.swift +++ b/submodules/TelegramPresentationData/Sources/PresentationData.swift @@ -309,7 +309,6 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 10.500000 10.500000 cm +0.000000 0.000000 0.000000 scn +17.432882 39.000000 m +17.500000 39.000000 l +21.500000 39.000000 l +21.567118 39.000000 l +24.310560 39.000008 26.461430 39.000015 28.190128 38.858776 c +29.950411 38.714954 31.404631 38.417244 32.720890 37.746574 c +34.884754 36.644032 36.644032 34.884754 37.746574 32.720890 c +38.417244 31.404629 38.714954 29.950411 38.858776 28.190128 c +39.000015 26.461479 39.000008 24.310677 39.000000 21.567352 c +39.000000 21.567255 l +39.000000 21.567158 l +39.000000 21.500000 l +39.000000 17.500000 l +39.000000 17.432842 l +39.000000 17.432745 l +39.000000 17.432648 l +39.000008 14.689320 39.000015 12.538519 38.858776 10.809872 c +38.714954 9.049589 38.417244 7.595369 37.746574 6.279110 c +36.644032 4.115246 34.884754 2.355968 32.720890 1.253426 c +31.404631 0.582756 29.950411 0.285046 28.190128 0.141224 c +26.461481 -0.000015 24.310678 -0.000008 21.567352 0.000000 c +21.567255 0.000000 l +21.567158 0.000000 l +21.500000 0.000000 l +17.500000 0.000000 l +17.432840 0.000000 l +17.432743 0.000000 l +17.432646 0.000000 l +14.689321 -0.000008 12.538522 -0.000015 10.809872 0.141224 c +9.049589 0.285046 7.595370 0.582756 6.279109 1.253426 c +4.115245 2.355968 2.355969 4.115246 1.253425 6.279110 c +0.582757 7.595369 0.285045 9.049589 0.141224 10.809872 c +-0.000016 12.538570 -0.000009 14.689442 0.000000 17.432882 c +0.000000 17.500000 l +0.000000 21.500000 l +0.000000 21.567120 l +-0.000009 24.310558 -0.000016 26.461430 0.141224 28.190128 c +0.285045 29.950411 0.582757 31.404629 1.253425 32.720890 c +2.355969 34.884754 4.115245 36.644032 6.279109 37.746574 c +7.595370 38.417244 9.049589 38.714954 10.809872 38.858776 c +12.538570 39.000015 14.689443 39.000008 17.432882 39.000000 c +h +11.054168 35.868740 m +9.479408 35.740078 8.463938 35.492821 7.641081 35.073555 c +6.041704 34.258633 4.741369 32.958298 3.926445 31.358919 c +3.507179 30.536062 3.259924 29.520592 3.131261 27.945833 c +3.001167 26.353561 3.000000 24.325014 3.000000 21.500000 c +3.000000 17.500000 l +3.000000 14.674986 3.001167 12.646439 3.131261 11.054167 c +3.259924 9.479408 3.507179 8.463938 3.926445 7.641081 c +4.741369 6.041702 6.041704 4.741367 7.641081 3.926445 c +8.463938 3.507179 9.479408 3.259922 11.054167 3.131260 c +12.646438 3.001167 14.674986 3.000000 17.500000 3.000000 c +21.500000 3.000000 l +24.325014 3.000000 26.353561 3.001167 27.945833 3.131260 c +29.520592 3.259922 30.536062 3.507179 31.358919 3.926445 c +32.958298 4.741367 34.258633 6.041702 35.073555 7.641081 c +35.492821 8.463938 35.740078 9.479408 35.868740 11.054167 c +35.998833 12.646437 36.000000 14.674986 36.000000 17.500000 c +36.000000 21.500000 l +36.000000 24.325014 35.998833 26.353561 35.868740 27.945831 c +35.740078 29.520592 35.492821 30.536062 35.073555 31.358919 c +34.258633 32.958298 32.958298 34.258633 31.358919 35.073555 c +30.536062 35.492821 29.520592 35.740078 27.945833 35.868740 c +26.353563 35.998833 24.325014 36.000000 21.500000 36.000000 c +17.500000 36.000000 l +14.674986 36.000000 12.646438 35.998833 11.054168 35.868740 c +h +32.100098 30.099976 m +32.100098 28.995407 31.204668 28.099976 30.100098 28.099976 c +28.995527 28.099976 28.100098 28.995407 28.100098 30.099976 c +28.100098 31.204544 28.995527 32.099976 30.100098 32.099976 c +31.204668 32.099976 32.100098 31.204544 32.100098 30.099976 c +h +12.000000 19.500000 m +12.000000 23.642136 15.357864 27.000000 19.500000 27.000000 c +23.642136 27.000000 27.000000 23.642136 27.000000 19.500000 c +27.000000 15.357864 23.642136 12.000000 19.500000 12.000000 c +15.357864 12.000000 12.000000 15.357864 12.000000 19.500000 c +h +19.500000 30.000000 m +13.701010 30.000000 9.000000 25.298990 9.000000 19.500000 c +9.000000 13.701010 13.701010 9.000000 19.500000 9.000000 c +25.298990 9.000000 30.000000 13.701010 30.000000 19.500000 c +30.000000 25.298990 25.298990 30.000000 19.500000 30.000000 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 3898 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 60.000000 60.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000003988 00000 n +0000004011 00000 n +0000004184 00000 n +0000004258 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +4317 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Share/QrPlaneIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Share/QrPlaneIcon.imageset/Contents.json new file mode 100644 index 0000000000..de3bb69a15 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Share/QrPlaneIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "ic_qrlogo.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Share/QrPlaneIcon.imageset/ic_qrlogo.pdf b/submodules/TelegramUI/Images.xcassets/Share/QrPlaneIcon.imageset/ic_qrlogo.pdf new file mode 100644 index 0000000000000000000000000000000000000000..8d4ada089c6515caf3c2286b5bd24c98dc8dee83 GIT binary patch literal 4391 zcmai&2{@Er`^V8_8A4^JJc&WXEVfDZWh@Da?3x*4U&>C&k|kSq6NLtmWGS+S?CZ}Y zTe2m_zGTTR@Axh6ulN1E|Lgx;=X#!V-RC~{d9M3B-*bKLM?g zHlBZMZUrL%DBy~50AIZdh^yN>dEng0PbahoP8ElBwZ;MB*KjVj9(I7Vv@{?u4|ex( z!=atQK9sm5oyUf<3?Z|&tegnipE}>x%qX)f0@-MDsOkKU&SVl#E1X#vti6@|`AtFR zPkH(1Rh8EAwRZ|T0n1F2+ABoj)X|!Ab87i||06jO)u_eW`4<)&l!sU)ap|4vJ#Sj{ ze=C{tHcxqsS1(^o?6_Di?Iw{an*|AZXzgeDW@cP)=q$=+h%zPq?Y@zm!XS&MKXXUf zX=LeEabW%D(l7;~wc)XWBg(QOw~*m+n^ z_R?uh$}B#1-)kI=@kJUoR&O6D(uxYLj&5>S5foieHB1Vp@RUn^)XM+^`b2oSvvL&U| zI=a4s-ObBUkLK-fPMOmm9tMA{EAuP}wy+<)hYmsdau}I1s4l@d4t^3gRV}U2+G8{G z)h#8o^xD0pG3_v~CeGJa_@}-@gDT%qu6FEtbJsC!P0O;q;NlF&P%?Dqq}N!Ysf&4e zR}CX|+VE>X>!riioaYX=sV(e$61Cjg8@H@bm~z{u!$!*h>~CYGR+dN2KSTY`XSRlC z-ftIF0^?k)|I`GzcPu2>FM_)1dxxwbJ+$rbcQ>4i2LLAz-vGpK;M`q3-LN=!K;jRB zimQtUdE6a17UYd%sr?!6c>FJc>bbdM4R9WS89At`I$!~atGGJ3x*6O-V{yQ-f>qpM zfaIS6eu*LVON{H}6~vW}h3-ODIUufz^Rma{ZlIL^Ep@mQ4F10lgI^DT^c3%#2E%OO z#9Havk7xJ*flV$yI^dJ61}jKELNid0Dl7!zrFv4G<+QMo_012IsBk{48VmiyV9XHR zIT*-wWCVae^4uzn)*_swRCGmEYP}UM6_NH;y!p35YH#A!cR>B zRvVDI`T)XCqDnF-ZQ+C;fjT#nh2dc^I2(a3>own0bFtG_-i%01#}hncn!sh!!}(q! zBf3U!JWXISI~qhVecJa%FJj=@_gEMx!^}`eA9~d0UwfpTGu0fWF8A{O^CKFCeuf=QG zD7N@bpPnnNOE#n^bEZ}#O}^G$1t``}Qk0E9+c+QQWSP+XjX{t;YH7*Ioifu=Ok_@a zKQq|1vpx*Do!YgsZzP+`t`KxllNEteBjx7hYO8j4Gce+b)`#27M^hh=_P=#Bi9XBx zz`hnbXQ)4yL>CfaR}*?Q$T&{S8XOWZFd4uhB}l{hAl;G0&eMv*KNP7~sY%oBDo3Gm z@dUq%f*+Otq8T#`++O$It}wpXJr?fwb9RrH-aOJ}@|qH#!ORvzthJj!mMCZTM2J%Z z)tNXNkLO^CATcPF=;-DJR4%*x#^bCP0MQ~VyHk5Ka$euR%J2PvR(yKVO9Hvy|L1u1KT3;0< zJsFl0Eh-Ul%n6FSA0aO}E6OR?>Ws!K!W;^(m&+%6RJM+sm~PU zfx{)TJhZ6BuCJ;-tj}oJKa5OjRkF!5Ry49ta(|}~4Yg`aq*ZLEJ<+RfOv}BdsvKv` zPkH)+V?19DRlZtU9Agf5Rg6ab-RTq5;RN&(4z^a#sc@6%NsJMC5TgYhc!GUmbd7yR z%7z&cv>W^aGw>$YSn#!i7oF7Ur!kkCjc&)@HY>ruIq@02B+J9t_JQ*o&z%RA9OiHr z$s$k%(@=Cx;9}FMNywVd`%B|2tHDeS?%y%@FVukpcmmE(#PYV*3&zozf*gaHE~Ehb zPW(5SxoBr;4?|8jO6Ui@)b-#CWA<$}#|Xc9{8{O}CJ*F#B8z4iKl=@S;ncFAo0<-M z77#f|?S;gSE7mAC4iiO7-6H->z)FBAK|wD`2*RSP#iOP?d>yG(rRb~ZtMcN;sh2MO z#;=}Kr<>~5s`_jBi=z?|_znD2M`2u2skC6#`I zVuL|Yc8L)|eX^`kE>u%3Eh`!G2$P11z&!jW*`hD-J|QTf;*4&-;(W;`ffpCJl>1)z zeyS|fDZBCo_XTFhg8XmaXG_e~pt(kLXMuxL5&a<6>OSbO}u#rmrRdHu}7%$#~uy;(r#hmXLQ+fhnS8(tD33H9gl0*|M!N*z&uPYw}ZOkdlaW%>>Pu4jqHf{o^-pR};&K430B)5QzfGnzpQx zQ4^Eb>VwabO;_9RXZt?g6`_}lGLA}T8)LH)T^2RCyeP_RB5ZQf*tu+>WUuK}OH+`CU6X_;B!ab*YvgsMvzq9vW=5Xu1J6Xx&7B6Nk7H#Eo zhtO|0l00R)xYw;S6f^79?A$!|S#J2Sa$kf=n<*X@)s~c(ILk$(w|s;zz7>C~xFTU2 zv+A&#Cx1mgLOw?RY@JtKfxq|;`jG0-Yj130==<#6(7rB62K1V44x9s02N}`G)5Gb` zob&`WHBdIx1oMi2vf?-IXQNU+Q1J@AN+-!+!Mf+pAtoyE8nWbDY3TF7#~XARqo%6T zT%!@&{;3@m6~m^@=B?g=YEc_N>8RaP!>eiIjYUmLr`4lPqk&vGHePPld;DH*`P-ky zw8N*CzJaG!#1Q5wrAuGj-jVXVh(&Q$4JLOlo#*B~H^F=qakep^bggL9s>+)L8cM7Q z-%prr6`74$v)I8M#Dnl5Z$o-j@y!+ySj{3$R?R6YU8Q$lJ>Ocb2TkWE#U*76Bl3Im zf3puM+bQc)7C=Xqh*%)x1Z%~cB(k9@)t&YOD~4yY6no>Z3Avlt)I53o^V$!MX#S@H z4^8Mx9o~-HqHvj=`rW5zHmB33UAoS7Y;^2DkhGEX_Oz|@`Wbf6Ai3hmSQt83WIj@L z-P@v}dh$ozVyrXZtTFt(OT8-w$35W^Io_}TT)(95wq5D!OsjI0vi@Z4CjPf}$GW#dmTO;Zz1a8u>P724IyPUuVVsIF zu^j$b^wpw})V-dyUQu&vsm=&(cK0@ueXMnC8T{qjTCHLS)Ii=s&O#7ZY}!oi!o7;+ z^e=5AL-iBR0ezG)Ct7Ia&$IBn4`|=kb1YPHZb6SuzHTBeW-V0L+Xh$#s4i=)#S>AX zs26RGv7Q66{X^0(rIVx@V-i~5OxXpO(DL+%Z3InP?pP4{`*ujb}Oju+G^Yy}+>_OY97@$*Z%JG#l4F$&)I z=?>O?AkH>36Wbvh`~PI+V^lq6TQbjTs;DTV-Er2yF{|DHEdIjZV_yCXfB(j5cR*YR zXKjyGcJ%?wpkx!^ktS#2_yS!1vLU3U$hrPc8x#sBC;Y!` zNXh>(7x51p3<^X3_gsXG1UcXT84n?Y{AFDaH?+MI&h7XA4+i#rH~>j5j$BzqaF`?xBL$Oz%1C38lJemH-{p@l X!rg UIImage? { return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in @@ -554,7 +555,6 @@ final class ChatQrCodeScreen: ViewController { private var animatedIn = false private let context: AccountContext - private let animatedEmojiStickers: [String: [StickerPackItem]] private let peer: Peer private var presentationData: PresentationData @@ -563,10 +563,9 @@ final class ChatQrCodeScreen: ViewController { var dismissed: (() -> Void)? - init(context: AccountContext, animatedEmojiStickers: [String: [StickerPackItem]], peer: Peer) { + init(context: AccountContext, peer: Peer) { self.context = context self.presentationData = context.sharedContext.currentPresentationData.with { $0 } - self.animatedEmojiStickers = animatedEmojiStickers self.peer = peer super.init(navigationBarPresentationData: nil) @@ -591,6 +590,8 @@ final class ChatQrCodeScreen: ViewController { }) self.statusBar.statusBarStyle = .Ignore + + self.ready.set(self.controllerNode.ready.get()) } required init(coder aDecoder: NSCoder) { @@ -602,7 +603,7 @@ final class ChatQrCodeScreen: ViewController { } override public func loadDisplayNode() { - self.displayNode = ChatQrCodeScreenNode(context: self.context, presentationData: self.presentationData, controller: self, animatedEmojiStickers: self.animatedEmojiStickers, peer: self.peer) + self.displayNode = ChatQrCodeScreenNode(context: self.context, presentationData: self.presentationData, controller: self, peer: self.peer) self.controllerNode.previewTheme = { [weak self] _, _, theme in self?.presentationThemePromise.set(.single(theme)) } @@ -659,10 +660,6 @@ final class ChatQrCodeScreen: ViewController { self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition) } - - func dimTapped() { - self.controllerNode.dimTapped() - } } private func iconColors(theme: PresentationTheme) -> [String: UIColor] { @@ -689,20 +686,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg private var presentationData: PresentationData private weak var controller: ChatQrCodeScreen? - private let dimNode: ASDisplayNode - private let containerNode: ASDisplayNode - private let wallpaperBackgroundNode: WallpaperBackgroundNode - private let codeBackgroundNode: ASDisplayNode - private let codeForegroundNode: ASDisplayNode - private var codeForegroundContentNode: ASDisplayNode? - private var codeForegroundDimNode: ASDisplayNode - private let codeMaskNode: ASDisplayNode - private let codeTextNode: ImmediateTextNode - private let codeImageNode: TransformImageNode - private let codeIconBackgroundNode: ASImageNode - private let codeIconNode: AnimatedStickerNode - private let avatarNode: ImageNode - private var qrCodeSize: Int? + private let contentNode: QrContentNode private let wrappingScrollNode: ASScrollNode private let contentContainerNode: ASDisplayNode @@ -723,14 +707,17 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg private var initialized = false private var themes: [TelegramTheme] = [] + let ready = Promise() + private let peer: Peer - private var selectedEmoticon: String? { + private var initiallySelectedEmoticon: String? + private var selectedEmoticon: String? = nil { didSet { self.selectedEmoticonPromise.set(self.selectedEmoticon) } } - private var selectedEmoticonPromise: ValuePromise + private var selectedEmoticonPromise = ValuePromise(nil) private var isDarkAppearancePromise: ValuePromise private var isDarkAppearance: Bool = false { @@ -749,26 +736,19 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg var dismiss: (() -> Void)? var cancel: (() -> Void)? - init(context: AccountContext, presentationData: PresentationData, controller: ChatQrCodeScreen, animatedEmojiStickers: [String: [StickerPackItem]], peer: Peer) { + init(context: AccountContext, presentationData: PresentationData, controller: ChatQrCodeScreen, peer: Peer) { self.context = context self.controller = controller self.peer = peer - self.selectedEmoticon = defaultEmoticon - self.selectedEmoticonPromise = ValuePromise(self.selectedEmoticon) self.presentationData = presentationData self.wrappingScrollNode = ASScrollNode() self.wrappingScrollNode.view.alwaysBounceVertical = true self.wrappingScrollNode.view.delaysContentTouches = false self.wrappingScrollNode.view.canCancelContentTouches = true - - self.dimNode = ASDisplayNode() - self.dimNode.backgroundColor = .clear - - self.containerNode = ASDisplayNode() - - self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false, useExperimentalImplementation: self.context.sharedContext.immediateExperimentalUISettings.experimentalBackground) + self.contentNode = QrContentNode(context: context, peer: peer, isStatic: false) + self.contentContainerNode = ASDisplayNode() self.contentContainerNode.isOpaque = false @@ -812,62 +792,14 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg self.listNode = ListView() self.listNode.transform = CATransform3DMakeRotation(-CGFloat.pi / 2.0, 0.0, 0.0, 1.0) - self.codeBackgroundNode = ASDisplayNode() - self.codeBackgroundNode.backgroundColor = .white - self.codeBackgroundNode.cornerRadius = 42.0 - if #available(iOS 13.0, *) { - self.codeBackgroundNode.layer.cornerCurve = .continuous - } - self.codeForegroundNode = ASDisplayNode() - self.codeForegroundNode.backgroundColor = .black - - self.codeForegroundDimNode = ASDisplayNode() - self.codeForegroundDimNode.alpha = 0.3 - self.codeForegroundDimNode.backgroundColor = .black - - self.codeMaskNode = ASDisplayNode() - - self.codeImageNode = TransformImageNode() - - self.codeIconBackgroundNode = ASImageNode() - - self.codeIconNode = AnimatedStickerNode() - self.codeIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogoPlain"), width: 240, height: 240, mode: .direct(cachePathPrefix: nil)) - self.codeIconNode.visibility = true - - self.codeTextNode = ImmediateTextNode() - self.codeTextNode.attributedText = NSAttributedString(string: "@\(peer.addressName ?? "")".uppercased(), font: Font.with(size: 24.0, design: .round, weight: .bold, traits: []), textColor: .black) - - self.avatarNode = ImageNode() - self.avatarNode.displaysAsynchronously = false - self.avatarNode.setSignal(peerAvatarCompleteImage(account: self.context.account, peer: EnginePeer(peer), size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true)) - super.init() self.backgroundColor = nil self.isOpaque = false - - self.addSubnode(self.dimNode) - - self.wrappingScrollNode.view.delegate = self + self.addSubnode(self.wrappingScrollNode) - self.wrappingScrollNode.addSubnode(self.containerNode) - - self.containerNode.addSubnode(self.wallpaperBackgroundNode) - - self.containerNode.addSubnode(self.codeBackgroundNode) - self.containerNode.addSubnode(self.codeForegroundNode) - - self.codeForegroundNode.addSubnode(self.codeForegroundDimNode) - - self.codeMaskNode.addSubnode(self.codeImageNode) - self.codeMaskNode.addSubnode(self.codeIconBackgroundNode) - self.codeMaskNode.addSubnode(self.codeTextNode) - - self.containerNode.addSubnode(self.avatarNode) - - self.wrappingScrollNode.addSubnode(self.codeIconNode) + self.wrappingScrollNode.addSubnode(self.contentNode) self.wrappingScrollNode.addSubnode(self.backgroundNode) self.wrappingScrollNode.addSubnode(self.contentContainerNode) @@ -889,20 +821,102 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg self.doneButton.pressed = { [weak self] in if let strongSelf = self { strongSelf.doneButton.isUserInteractionEnabled = false - strongSelf.completion?(strongSelf.selectedEmoticon) + + strongSelf.contentNode.generateImage { [weak self] image in + if let strongSelf = self, let image = image, let jpgData = image.jpegData(compressionQuality: 0.9) { + let tempFilePath = NSTemporaryDirectory() + "t_me-\(peer.addressName ?? "").jpg" + try? FileManager.default.removeItem(atPath: tempFilePath) + let tempFileUrl = URL(fileURLWithPath: tempFilePath) + try? jpgData.write(to: tempFileUrl) + + let activityController = UIActivityViewController(activityItems: [tempFileUrl], applicationActivities: [ShareToInstagramActivity(context: strongSelf.context)]) + activityController.completionWithItemsHandler = { [weak self] _, finished, _, _ in + if let strongSelf = self { + if finished { + strongSelf.completion?(strongSelf.selectedEmoticon) + } else { + strongSelf.doneButton.isUserInteractionEnabled = true + } + } + } + if let window = strongSelf.view.window { + activityController.popoverPresentationController?.sourceView = window + activityController.popoverPresentationController?.sourceRect = CGRect(origin: CGPoint(x: window.bounds.width / 2.0, y: window.bounds.size.height - 1.0), size: CGSize(width: 1.0, height: 1.0)) + } + context.sharedContext.applicationBindings.presentNativeController(activityController) + } + } } } - self.disposable.set(combineLatest(queue: Queue.mainQueue(), self.context.engine.themes.getChatThemes(accountManager: self.context.sharedContext.accountManager), self.selectedEmoticonPromise.get(), self.isDarkAppearancePromise.get()).start(next: { [weak self] themes, selectedEmoticon, isDarkAppearance in + let animatedEmojiStickers = context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false) + |> map { animatedEmoji -> [String: [StickerPackItem]] in + var animatedEmojiStickers: [String: [StickerPackItem]] = [:] + switch animatedEmoji { + case let .result(_, items, _): + for item in items { + if let emoji = item.getStringRepresentationsOfIndexKeys().first { + animatedEmojiStickers[emoji.basicEmoji.0] = [item] + let strippedEmoji = emoji.basicEmoji.0.strippedEmoji + if animatedEmojiStickers[strippedEmoji] == nil { + animatedEmojiStickers[strippedEmoji] = [item] + } + } + } + default: + break + } + return animatedEmojiStickers + } + + let initiallySelectedEmoticon: Signal + if self.peer.id == self.context.account.peerId { + initiallySelectedEmoticon = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) + |> take(1) + |> map { sharedData -> String in + let themeSettings: PresentationThemeSettings + if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) { + themeSettings = current + } else { + themeSettings = PresentationThemeSettings.defaultSettings + } + return themeSettings.theme.emoticon ?? defaultEmoticon + } + } else { + initiallySelectedEmoticon = self.context.account.postbox.transaction { transaction in + return transaction.getPeerCachedData(peerId: peer.id) + } + |> take(1) + |> map { cachedData -> String in + if let cachedData = cachedData as? CachedUserData { + return cachedData.themeEmoticon ?? defaultEmoticon + } else if let cachedData = cachedData as? CachedGroupData { + return cachedData.themeEmoticon ?? defaultEmoticon + } else if let cachedData = cachedData as? CachedChannelData { + return cachedData.themeEmoticon ?? defaultEmoticon + } else { + return defaultEmoticon + } + } + } + + self.disposable.set(combineLatest(queue: Queue.mainQueue(), animatedEmojiStickers, initiallySelectedEmoticon, self.context.engine.themes.getChatThemes(accountManager: self.context.sharedContext.accountManager), self.selectedEmoticonPromise.get(), self.isDarkAppearancePromise.get()).start(next: { [weak self] animatedEmojiStickers, initiallySelectedEmoticon, themes, selectedEmoticon, isDarkAppearance in guard let strongSelf = self else { return } + var selectedEmoticon = selectedEmoticon + if strongSelf.initiallySelectedEmoticon == nil { + strongSelf.initiallySelectedEmoticon = initiallySelectedEmoticon + strongSelf.selectedEmoticon = initiallySelectedEmoticon + selectedEmoticon = initiallySelectedEmoticon + } + let isFirstTime = strongSelf.entries == nil let presentationData = strongSelf.presentationData var entries: [ThemeSettingsThemeEntry] = [] - entries.append(ThemeSettingsThemeEntry(index: 0, emoticon: defaultEmoticon, emojiFile: animatedEmojiStickers[defaultEmoticon]?.first?.file, themeReference: .builtin(.dayClassic), nightMode: isDarkAppearance, selected: selectedEmoticon == defaultEmoticon, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil)) + entries.append(ThemeSettingsThemeEntry(index: 0, emoticon: defaultEmoticon, emojiFile: animatedEmojiStickers[defaultEmoticon]?.first?.file, themeReference: .builtin(isDarkAppearance ? .night : .dayClassic), nightMode: isDarkAppearance, selected: selectedEmoticon == defaultEmoticon, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil)) for theme in themes { guard let emoticon = theme.emoticon else { continue @@ -910,13 +924,14 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg entries.append(ThemeSettingsThemeEntry(index: entries.count, emoticon: emoticon, emojiFile: animatedEmojiStickers[emoticon]?.first?.file, themeReference: .cloud(PresentationCloudTheme(theme: theme, resolvedWallpaper: nil, creatorAccountId: nil)), nightMode: isDarkAppearance, selected: selectedEmoticon == theme.emoticon, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil)) } + let wallpaper: TelegramWallpaper if selectedEmoticon == defaultEmoticon { let presentationTheme = makeDefaultPresentationTheme(reference: isDarkAppearance ? .night : .dayClassic, serviceBackgroundColor: nil) - strongSelf.wallpaperBackgroundNode.update(wallpaper: presentationTheme.chat.defaultWallpaper) - } else if let theme = themes.first(where: { $0.emoticon == selectedEmoticon }) { - if let presentationTheme = makePresentationTheme(cloudTheme: theme, dark: isDarkAppearance) { - strongSelf.wallpaperBackgroundNode.update(wallpaper: presentationTheme.chat.defaultWallpaper) - } + wallpaper = presentationTheme.chat.defaultWallpaper + } else if let theme = themes.first(where: { $0.emoticon == selectedEmoticon }), let presentationTheme = makePresentationTheme(cloudTheme: theme, dark: isDarkAppearance) { + wallpaper = presentationTheme.chat.defaultWallpaper + } else { + wallpaper = .color(0x000000) } let action: (String?) -> Void = { [weak self] emoticon in @@ -946,16 +961,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg strongSelf.entries = entries strongSelf.themes = themes - if isDarkAppearance && selectedEmoticon == defaultEmoticon { - strongSelf.codeForegroundDimNode.alpha = 1.0 - } else { - strongSelf.codeForegroundDimNode.alpha = isDarkAppearance ? 0.4 : 0.3 - } - if strongSelf.codeForegroundContentNode == nil, let contentNode = strongSelf.wallpaperBackgroundNode.makeDimmedNode() { - contentNode.frame = CGRect(origin: CGPoint(x: -strongSelf.codeForegroundNode.frame.minX, y: -strongSelf.codeForegroundNode.frame.minY), size: strongSelf.wallpaperBackgroundNode.frame.size) - strongSelf.codeForegroundContentNode = contentNode - strongSelf.codeForegroundNode.insertSubnode(contentNode, at: 0) - } + strongSelf.contentNode.update(wallpaper: wallpaper, isDarkAppearance: isDarkAppearance, selectedEmoticon: selectedEmoticon) if isFirstTime { for theme in themes { @@ -1001,15 +1007,7 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } } - self.codeImageNode.setSignal(qrCode(string: "https://t.me/\(peer.addressName ?? "")", color: .black, backgroundColor: nil, icon: .cutout, ecl: "Q") |> beforeNext { [weak self] size, _ in - guard let strongSelf = self else { - return - } - strongSelf.qrCodeSize = size - if let (layout, navigationHeight) = strongSelf.containerLayout { - strongSelf.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .immediate) - } - } |> map { $0.1 }, attemptSynchronously: true) + self.ready.set(self.contentNode.isReady) } private func enqueueTransition(_ transition: ThemeSettingsThemeItemNodeTransition) { @@ -1034,7 +1032,12 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg var scrollToItem: ListViewScrollToItem? if !self.initialized { - scrollToItem = ListViewScrollToItem(index: 0, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down) + if let index = transition.entries.firstIndex(where: { entry in + return entry.emoticon == self.initiallySelectedEmoticon + }) { + scrollToItem = ListViewScrollToItem(index: index, position: .bottom(-57.0), animated: false, curve: .Default(duration: 0.0), directionHint: .Down) + self.initialized = true + } self.initialized = true } @@ -1080,23 +1083,18 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg override func didLoad() { super.didLoad() + self.wrappingScrollNode.view.delegate = self if #available(iOSApplicationExtension 11.0, iOS 11.0, *) { self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never } - self.codeForegroundNode.view.mask = self.codeMaskNode.view - self.listNode.view.disablesInteractiveTransitionGestureRecognizer = true } @objc func cancelButtonPressed() { self.cancel?() } - - func dimTapped() { - self.cancelButtonPressed() - } - + @objc func switchThemePressed() { self.switchThemeButton.isUserInteractionEnabled = false Queue.mainQueue().after(0.5) { @@ -1131,8 +1129,8 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } private func animateCrossfade(animateIcon: Bool) { - if let snapshotView = self.containerNode.view.snapshotView(afterScreenUpdates: false) { - self.wrappingScrollNode.view.insertSubview(snapshotView, aboveSubview: self.containerNode.view) + if let snapshotView = self.contentNode.containerNode.view.snapshotView(afterScreenUpdates: false) { + self.contentNode.view.insertSubview(snapshotView, aboveSubview: self.contentNode.containerNode.view) snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: ChatQrCodeScreen.themeCrossfadeDuration, delay: ChatQrCodeScreen.themeCrossfadeDelay, timingFunction: CAMediaTimingFunctionName.linear.rawValue, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView?.removeFromSuperview() @@ -1180,15 +1178,12 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg private var animatedOut = false func animateIn() { let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY - let dimPosition = self.dimNode.layer.position let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring) let targetBounds = self.bounds self.bounds = self.bounds.offsetBy(dx: 0.0, dy: -offset) - self.dimNode.position = CGPoint(x: dimPosition.x, y: dimPosition.y - offset) transition.animateView({ self.bounds = targetBounds - self.dimNode.position = dimPosition }) } @@ -1234,16 +1229,11 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg if backgroundFrame.minY < contentFrame.minY { backgroundFrame.origin.y = contentFrame.minY } - transition.updateFrame(node: self.wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - self.wallpaperBackgroundNode.updateLayout(size: layout.size, transition: transition) - - transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: layout.size)) transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame) transition.updateFrame(node: self.effectNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size)) transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size)) let titleSize = self.titleNode.measure(CGSize(width: width - 90.0, height: titleHeight)) let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 19.0 + UIScreenPixel), size: titleSize) @@ -1276,10 +1266,263 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg self.listNode.position = CGPoint(x: contentSize.width / 2.0, y: contentSize.height / 2.0 + titleHeight + 6.0) self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: nil, updateSizeAndInsets: ListViewUpdateSizeAndInsets(size: CGSize(width: contentSize.height, height: contentSize.width), insets: listInsets, duration: 0.0, curve: .Default(duration: nil)), stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in }) - let codeInset: CGFloat = 45.0 - let codeBackgroundWidth = layout.size.width - codeInset * 2.0 + self.contentNode.updateLayout(size: layout.size, topInset: 44.0, bottomInset: contentHeight, transition: transition) + transition.updateFrame(node: self.contentNode, frame: CGRect(origin: CGPoint(), size: layout.size)) + } +} + +private class QrContentNode: ASDisplayNode { + private let context: AccountContext + private let peer: Peer + private let isStatic: Bool + + fileprivate let containerNode: ASDisplayNode + fileprivate let wallpaperBackgroundNode: WallpaperBackgroundNode + private let codeBackgroundNode: ASDisplayNode + private let codeForegroundNode: ASDisplayNode + private var codeForegroundContentNode: ASDisplayNode? + private var codeForegroundDimNode: ASDisplayNode + private let codeMaskNode: ASDisplayNode + private let codeTextNode: ImmediateTextNode + private let codeImageNode: TransformImageNode + private let codeIconBackgroundNode: ASImageNode + private let codeStaticIconNode: ASImageNode? + private let codeAnimatedIconNode: AnimatedStickerNode? + private let avatarNode: ImageNode + private var qrCodeSize: Int? + + private var currentParams: (TelegramWallpaper, Bool, String?)? + private var validLayout: (CGSize, CGFloat, CGFloat)? + + private let _ready = Promise() + var isReady: Signal { + return self._ready.get() + } + + init(context: AccountContext, peer: Peer, isStatic: Bool = false) { + self.context = context + self.peer = peer + self.isStatic = isStatic + + self.containerNode = ASDisplayNode() + + self.wallpaperBackgroundNode = createWallpaperBackgroundNode(context: context, forChatDisplay: true, useSharedAnimationPhase: false, useExperimentalImplementation: context.sharedContext.immediateExperimentalUISettings.experimentalBackground) + + self.codeBackgroundNode = ASDisplayNode() + self.codeBackgroundNode.backgroundColor = .white + self.codeBackgroundNode.cornerRadius = 42.0 + if #available(iOS 13.0, *) { + self.codeBackgroundNode.layer.cornerCurve = .continuous + } + + self.codeForegroundNode = ASDisplayNode() + self.codeForegroundNode.backgroundColor = .black + + self.codeForegroundDimNode = ASDisplayNode() + self.codeForegroundDimNode.alpha = 0.3 + self.codeForegroundDimNode.backgroundColor = .black + + self.codeMaskNode = ASDisplayNode() + + self.codeImageNode = TransformImageNode() + self.codeIconBackgroundNode = ASImageNode() + + if isStatic { + let codeStaticIconNode = ASImageNode() + codeStaticIconNode.displaysAsynchronously = false + codeStaticIconNode.contentMode = .scaleToFill + codeStaticIconNode.image = UIImage(bundleImageName: "Share/QrPlaneIcon") + self.codeStaticIconNode = codeStaticIconNode + self.codeAnimatedIconNode = nil + } else { + let codeAnimatedIconNode = AnimatedStickerNode() + codeAnimatedIconNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "PlaneLogoPlain"), width: 120, height: 120, mode: .direct(cachePathPrefix: nil)) + codeAnimatedIconNode.visibility = true + self.codeAnimatedIconNode = codeAnimatedIconNode + self.codeStaticIconNode = nil + } + + self.codeTextNode = ImmediateTextNode() + self.codeTextNode.displaysAsynchronously = false + self.codeTextNode.attributedText = NSAttributedString(string: "@\(peer.addressName ?? "")".uppercased(), font: Font.with(size: 23.0, design: .round, weight: .bold, traits: []), textColor: .black) + self.codeTextNode.truncationMode = .byCharWrapping + self.codeTextNode.maximumNumberOfLines = 2 + self.codeTextNode.textAlignment = .center + if isStatic { + self.codeTextNode.setNeedsDisplayAtScale(3.0) + } + + self.avatarNode = ImageNode() + self.avatarNode.displaysAsynchronously = false + self.avatarNode.setSignal(peerAvatarCompleteImage(account: context.account, peer: EnginePeer(peer), size: CGSize(width: 180.0, height: 180.0), font: avatarPlaceholderFont(size: 78.0), fullSize: true)) + + super.init() + + self.addSubnode(self.containerNode) + + self.containerNode.addSubnode(self.wallpaperBackgroundNode) + + self.containerNode.addSubnode(self.codeBackgroundNode) + self.containerNode.addSubnode(self.codeForegroundNode) + + self.codeForegroundNode.addSubnode(self.codeForegroundDimNode) + + self.codeMaskNode.addSubnode(self.codeImageNode) + self.codeMaskNode.addSubnode(self.codeIconBackgroundNode) + self.codeMaskNode.addSubnode(self.codeTextNode) + + self.containerNode.addSubnode(self.avatarNode) + + if let codeStaticIconNode = self.codeStaticIconNode { + self.containerNode.addSubnode(codeStaticIconNode) + } else if let codeAnimatedIconNode = self.codeAnimatedIconNode { + self.addSubnode(codeAnimatedIconNode) + } + + let codeReadyPromise = ValuePromise() + self.codeImageNode.setSignal(qrCode(string: "https://t.me/\(peer.addressName ?? "")", color: .black, backgroundColor: nil, icon: .cutout, ecl: "Q") |> beforeNext { [weak self] size, _ in + guard let strongSelf = self else { + return + } + strongSelf.qrCodeSize = size + if let (size, topInset, bottomInset) = strongSelf.validLayout { + strongSelf.updateLayout(size: size, topInset: topInset, bottomInset: bottomInset, transition: .immediate) + } + codeReadyPromise.set(true) + } |> map { $0.1 }, attemptSynchronously: true) + + self._ready.set(combineLatest(codeReadyPromise.get(), self.wallpaperBackgroundNode.isReady) + |> map { codeReady, wallpaperReady in + return codeReady && wallpaperReady + }) + } + + override func didLoad() { + super.didLoad() + + self.codeForegroundNode.view.mask = self.codeMaskNode.view + } + + func generateImage(completion: @escaping (UIImage?) -> Void) { + guard let (wallpaper, isDarkAppearance, selectedEmoticon) = self.currentParams else { + return + } + + let size = CGSize(width: 390.0, height: 844.0) + let scale: CGFloat = 3.0 + + let copyNode = QrContentNode(context: self.context, peer: self.peer, isStatic: true) + + func prepare(view: UIView, scale: CGFloat) { + view.contentScaleFactor = scale + for subview in view.subviews { + prepare(view: subview, scale: scale) + } + } + prepare(view: copyNode.view, scale: scale) + + copyNode.updateLayout(size: size, topInset: 0.0, bottomInset: 0.0, transition: .immediate) + copyNode.update(wallpaper: wallpaper, isDarkAppearance: isDarkAppearance, selectedEmoticon: selectedEmoticon) + copyNode.frame = CGRect(x: -1000, y: -1000, width: size.width, height: size.height) + + self.addSubnode(copyNode) + + + let _ = (copyNode.isReady + |> take(1) + |> deliverOnMainQueue).start(next: { [weak copyNode] _ in + Queue.mainQueue().after(0.1) { + if #available(iOS 10.0, *) { + let format = UIGraphicsImageRendererFormat() + format.scale = scale + let renderer = UIGraphicsImageRenderer(size: size, format: format) + let image = renderer.image { rendererContext in + copyNode?.containerNode.layer.render(in: rendererContext.cgContext) + } + completion(image) + } else { + UIGraphicsBeginImageContextWithOptions(size, true, scale) + copyNode?.containerNode.view.drawHierarchy(in: CGRect(origin: CGPoint(), size: size), afterScreenUpdates: true) + let image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + completion(image) + } + copyNode?.removeFromSupernode() + } + }) + } + + func update(wallpaper: TelegramWallpaper, isDarkAppearance: Bool, selectedEmoticon: String?) { + self.currentParams = (wallpaper, isDarkAppearance, selectedEmoticon) + + self.wallpaperBackgroundNode.update(wallpaper: wallpaper) + + if isDarkAppearance && selectedEmoticon == defaultEmoticon { + self.codeForegroundDimNode.alpha = 1.0 + } else { + self.codeForegroundDimNode.alpha = isDarkAppearance ? 0.4 : 0.3 + } + if self.codeForegroundContentNode == nil, let contentNode = self.wallpaperBackgroundNode.makeDimmedNode() { + contentNode.frame = CGRect(origin: CGPoint(x: -self.codeForegroundNode.frame.minX, y: -self.codeForegroundNode.frame.minY), size: self.wallpaperBackgroundNode.frame.size) + self.codeForegroundContentNode = contentNode + self.codeForegroundNode.insertSubnode(contentNode, at: 0) + } + if isDarkAppearance && selectedEmoticon == defaultEmoticon, let codeForegroundContentNode = self.codeForegroundContentNode { + codeForegroundContentNode.removeFromSupernode() + self.codeForegroundContentNode = nil + } + } + + func updateLayout(size: CGSize, topInset: CGFloat, bottomInset: CGFloat, transition: ContainedViewLayoutTransition) { + self.validLayout = (size, topInset, bottomInset) + + transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: size)) + + transition.updateFrame(node: self.wallpaperBackgroundNode, frame: CGRect(origin: CGPoint(), size: size)) + self.wallpaperBackgroundNode.updateLayout(size: size, transition: transition) + + let textLength = self.codeTextNode.attributedText?.string.count ?? 0 + + var topInset = topInset + let avatarSize: CGSize + let codeInset: CGFloat + let imageSide: CGFloat + let fontSize: CGFloat + if size.width > 320.0 { + avatarSize = CGSize(width: 100.0, height: 100.0) + codeInset = 45.0 + imageSide = 220.0 + + if size.width > 375.0 { + if textLength > 12 { + fontSize = 22.0 + } else { + fontSize = 24.0 + } + } else { + if textLength > 12 { + fontSize = 21.0 + } else { + fontSize = 23.0 + } + } + } else { + avatarSize = CGSize(width: 70.0, height: 70.0) + codeInset = 55.0 + imageSide = 160.0 + topInset = floor(topInset * 0.6) + if textLength > 12 { + fontSize = 18.0 + } else { + fontSize = 20.0 + } + } + + self.codeTextNode.attributedText = NSAttributedString(string: self.codeTextNode.attributedText?.string ?? "", font: Font.with(size: fontSize, design: .round, weight: .bold, traits: []), textColor: .black) + + let codeBackgroundWidth = size.width - codeInset * 2.0 let codeBackgroundHeight = floor(codeBackgroundWidth * 1.1) - let codeBackgroundFrame = CGRect(x: codeInset, y: floor((layout.size.height - contentHeight - codeBackgroundHeight) / 2.0) + 44.0, width: codeBackgroundWidth, height: codeBackgroundHeight) + let codeBackgroundFrame = CGRect(x: codeInset, y: topInset + floor((size.height - bottomInset - codeBackgroundHeight) / 2.0), width: codeBackgroundWidth, height: codeBackgroundHeight) transition.updateFrame(node: self.codeBackgroundNode, frame: codeBackgroundFrame) transition.updateFrame(node: self.codeForegroundNode, frame: codeBackgroundFrame) transition.updateFrame(node: self.codeMaskNode, frame: CGRect(origin: CGPoint(), size: codeBackgroundFrame.size)) @@ -1290,33 +1533,38 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } let makeImageLayout = self.codeImageNode.asyncLayout() - let imageSide: CGFloat = 220.0 + let imageSize = CGSize(width: imageSide, height: imageSide) - let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil)) + let imageApply = makeImageLayout(TransformImageArguments(corners: ImageCorners(), imageSize: imageSize, boundingSize: imageSize, intrinsicInsets: UIEdgeInsets(), emptyColor: nil, scale: self.isStatic ? 3.0 : nil )) let _ = imageApply() let imageFrame = CGRect(origin: CGPoint(x: floor((codeBackgroundFrame.width - imageSize.width) / 2.0), y: floor((codeBackgroundFrame.width - imageSize.height) / 2.0)), size: imageSize) transition.updateFrame(node: self.codeImageNode, frame: imageFrame) - let codeTextSize = self.codeTextNode.updateLayout(codeBackgroundFrame.size) - transition.updateFrame(node: self.codeTextNode, frame: CGRect(origin: CGPoint(x: floor((codeBackgroundFrame.width - codeTextSize.width) / 2.0), y: imageFrame.maxY + floor((codeBackgroundHeight - imageFrame.maxY - codeTextSize.height) / 2.0) - 7.0), size: codeTextSize)) + let codeTextSize = self.codeTextNode.updateLayout(CGSize(width: codeBackgroundFrame.width - floor(imageFrame.minX * 1.5), height: codeBackgroundFrame.height)) + transition.updateFrame(node: self.codeTextNode, frame: CGRect(origin: CGPoint(x: floor((codeBackgroundFrame.width - codeTextSize.width) / 2.0), y: imageFrame.maxY + floor((codeBackgroundHeight - imageFrame.maxY - codeTextSize.height) / 2.0) - 5.0), size: codeTextSize)) - let avatarSize = CGSize(width: 100.0, height: 100.0) - transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - avatarSize.width) / 2.0), y: codeBackgroundFrame.minY - 70.0), size: avatarSize)) + transition.updateFrame(node: self.avatarNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - avatarSize.width) / 2.0), y: codeBackgroundFrame.minY - floor(avatarSize.height * 0.7)), size: avatarSize)) if let qrCodeSize = self.qrCodeSize { let (_, cutoutFrame, _) = qrCodeCutout(size: qrCodeSize, dimensions: imageSize, scale: nil) - self.codeIconNode.updateLayout(size: cutoutFrame.size) + let imageCenter = imageFrame.center.offsetBy(dx: codeBackgroundFrame.minX, dy: codeBackgroundFrame.minY) + + if let codeStaticIconNode = self.codeStaticIconNode { + transition.updateBounds(node: codeStaticIconNode, bounds: CGRect(origin: CGPoint(), size: cutoutFrame.size)) + transition.updatePosition(node: codeStaticIconNode, position: imageCenter.offsetBy(dx: 0.0, dy: -1.0)) + } else if let codeAnimatedIconNode = self.codeAnimatedIconNode { + codeAnimatedIconNode.updateLayout(size: cutoutFrame.size) + + transition.updateBounds(node: codeAnimatedIconNode, bounds: CGRect(origin: CGPoint(), size: cutoutFrame.size)) + transition.updatePosition(node: codeAnimatedIconNode, position: imageCenter.offsetBy(dx: 0.0, dy: -1.0)) + } let backgroundSize = CGSize(width: floorToScreenPixels(cutoutFrame.width - 8.0), height: floorToScreenPixels(cutoutFrame.height - 8.0)) transition.updateFrame(node: self.codeIconBackgroundNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(imageFrame.center.x - backgroundSize.width / 2.0), y: floorToScreenPixels(imageFrame.center.y - backgroundSize.height / 2.0)), size: backgroundSize)) if self.codeIconBackgroundNode.image == nil { self.codeIconBackgroundNode.image = generateFilledCircleImage(diameter: backgroundSize.width, color: .black) } - - let imageCenter = imageFrame.center.offsetBy(dx: codeBackgroundFrame.minX, dy: codeBackgroundFrame.minY) - transition.updateBounds(node: self.codeIconNode, bounds: CGRect(origin: CGPoint(), size: cutoutFrame.size)) - transition.updatePosition(node: self.codeIconNode, position: imageCenter.offsetBy(dx: 0.0, dy: -1.0)) } } } diff --git a/submodules/TelegramUI/Sources/EmojiResources.swift b/submodules/TelegramUI/Sources/EmojiResources.swift index 9c104fff3d..ac58b2ae1e 100644 --- a/submodules/TelegramUI/Sources/EmojiResources.swift +++ b/submodules/TelegramUI/Sources/EmojiResources.swift @@ -185,15 +185,7 @@ private func matchingEmojiEntry(_ emoji: String) -> (UInt8, UInt8, UInt8)? { func messageIsElligibleForLargeEmoji(_ message: Message) -> Bool { if !message.text.isEmpty && message.text.containsOnlyEmoji && message.text.emojis.count < 4 { - var messageEntities: [MessageTextEntity]? - for attribute in message.attributes { - if let attribute = attribute as? TextEntitiesMessageAttribute { - messageEntities = attribute.entities - break - } - } - - if !(messageEntities?.isEmpty ?? true) { + if !(message.textEntitiesAttribute?.entities.isEmpty ?? true) { return false } diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift index 67b0302480..8c17da1cdb 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenLabeledValueItem.swift @@ -309,20 +309,6 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { } } - let labelSize = self.labelNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude)) - let textLayout = self.textNode.updateLayoutInfo(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude)) - let textSize = textLayout.size - - if case .multiLine = item.textBehavior, textLayout.truncated, !self.isExpanded { - self.expandBackgroundNode.isHidden = false - self.expandNode.isHidden = false - self.expandButonNode.isHidden = false - } else { - self.expandBackgroundNode.isHidden = true - self.expandNode.isHidden = true - self.expandButonNode.isHidden = true - } - if let icon = item.icon { let iconImage: UIImage? switch icon { @@ -337,6 +323,21 @@ private final class PeerInfoScreenLabeledValueItemNode: PeerInfoScreenItemNode { self.iconButtonNode.isHidden = true } + let additionalSideInset: CGFloat = !self.iconNode.isHidden ? 32.0 : 0.0 + let labelSize = self.labelNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: .greatestFiniteMagnitude)) + let textLayout = self.textNode.updateLayoutInfo(CGSize(width: width - sideInset * 2.0 - additionalSideInset, height: .greatestFiniteMagnitude)) + let textSize = textLayout.size + + if case .multiLine = item.textBehavior, textLayout.truncated, !self.isExpanded { + self.expandBackgroundNode.isHidden = false + self.expandNode.isHidden = false + self.expandButonNode.isHidden = false + } else { + self.expandBackgroundNode.isHidden = true + self.expandNode.isHidden = true + self.expandButonNode.isHidden = true + } + let labelFrame = CGRect(origin: CGPoint(x: sideInset, y: 11.0), size: labelSize) let textFrame = CGRect(origin: CGPoint(x: sideInset, y: labelFrame.maxY + 3.0), size: textSize) diff --git a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift index b1e3bc666b..457e781c73 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/ListItems/PeerInfoScreenMemberItem.swift @@ -192,7 +192,7 @@ private final class PeerInfoScreenMemberItemNode: PeerInfoScreenItemNode { }, removePeer: { _ in - }, contextAction: item.contextAction, hasTopStripe: false, hasTopGroupInset: false, noInsets: true, displayDecorations: false) + }, contextAction: item.contextAction, hasTopStripe: false, hasTopGroupInset: false, noInsets: true, noCorners: true, displayDecorations: false) let params = ListViewItemLayoutParams(width: width, leftInset: safeInsets.left, rightInset: safeInsets.right, availableHeight: 1000.0) diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift index 786fd31eda..b8cf88124d 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -42,7 +42,7 @@ private struct GroupsInCommonListEntry: Comparable, Identifiable { }, removePeer: { _ in }, contextAction: { node, gesture in openPeerContextAction(peer, node, gesture) - }, hasTopStripe: false, noInsets: true) + }, hasTopStripe: false, noInsets: true, noCorners: true) } } diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index fc84b8c5f4..bcb80f9050 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3245,6 +3245,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self?.paneContainerNode.currentPane?.node.ensureMessageIsVisible(id: messageId) })) } + private func openResolved(_ result: ResolvedUrl) { guard let navigationController = self.controller?.navigationController as? NavigationController else { return @@ -5479,35 +5480,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate guard let data = self.data, let peer = data.peer, let controller = self.controller else { return } - - let animatedEmojiStickers = context.engine.stickers.loadedStickerPack(reference: .animatedEmoji, forceActualized: false) - |> map { animatedEmoji -> [String: [StickerPackItem]] in - var animatedEmojiStickers: [String: [StickerPackItem]] = [:] - switch animatedEmoji { - case let .result(_, items, _): - for item in items { - if let emoji = item.getStringRepresentationsOfIndexKeys().first { - animatedEmojiStickers[emoji.basicEmoji.0] = [item] - let strippedEmoji = emoji.basicEmoji.0.strippedEmoji - if animatedEmojiStickers[strippedEmoji] == nil { - animatedEmojiStickers[strippedEmoji] = [item] - } - } - } - default: - break - } - return animatedEmojiStickers - } - - let _ = (animatedEmojiStickers - |> deliverOnMainQueue).start(next: { [weak self, weak controller] animatedEmojiStickers in - if let strongSelf = self, let controller = controller { - controller.present(ChatQrCodeScreen(context: strongSelf.context, animatedEmojiStickers: animatedEmojiStickers, peer: peer), in: .window(.root)) - } - }) - -// controller.present(QrCodeScreen(context: self.context, updatedPresentationData: controller.updatedPresentationData, subject: .peer(peer: EnginePeer(peer))), in: .window(.root)) + + controller.present(ChatQrCodeScreen(context: self.context, peer: peer), in: .window(.root)) } fileprivate func openSettings(section: PeerInfoSettingsSection) { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 27ee669a26..036bd5ca2a 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1414,6 +1414,10 @@ public final class SharedAccountContextImpl: SharedAccountContext { return recentSessionsController(context: context, activeSessionsContext: activeSessionsContext, webSessionsContext: context.engine.privacy.webSessions(), websitesOnly: false) } + public func makeChatQrCodeScreen(context: AccountContext, peer: Peer) -> ViewController { + return ChatQrCodeScreen(context: context, peer: peer) + } + public func makePrivacyAndSecurityController(context: AccountContext) -> ViewController { return SettingsUI.makePrivacyAndSecurityController(context: context) } diff --git a/submodules/Translate/Sources/Translate.swift b/submodules/Translate/Sources/Translate.swift index 984f805382..836f825fd5 100644 --- a/submodules/Translate/Sources/Translate.swift +++ b/submodules/Translate/Sources/Translate.swift @@ -40,10 +40,11 @@ public func canTranslateText(context: AccountContext, text: String, showTranslat let text = String(text.prefix(64)) languageRecognizer.processString(text) - let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 2) + let hypotheses = languageRecognizer.languageHypotheses(withMaximum: 3) languageRecognizer.reset() - if let language = hypotheses.first(where: { supportedTranslationLanguages.contains($0.key.rawValue) }) { + let filteredLanguages = hypotheses.filter { supportedTranslationLanguages.contains($0.key.rawValue) }.sorted(by: { $0.value > $1.value }) + if let language = filteredLanguages.first(where: { supportedTranslationLanguages.contains($0.key.rawValue) }) { return !dontTranslateLanguages.contains(language.key.rawValue) } else { return false diff --git a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift index 8105368973..d133ee81ac 100644 --- a/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift +++ b/submodules/WallpaperBackgroundNode/Sources/WallpaperBackgroundNode.swift @@ -1740,18 +1740,18 @@ private class WallpaperNewYearNode: ASDisplayNode { let cell1 = CAEmitterCell() cell1.contents = UIImage(bundleImageName: "Components/Snowflake")?.cgImage cell1.name = "snow" - cell1.birthRate = 352.0 + cell1.birthRate = 252.0 cell1.lifetime = 20.0 - cell1.velocity = 39.0 - cell1.velocityRange = -15.0 - cell1.xAcceleration = 5.0 - cell1.yAcceleration = 25.0 + cell1.velocity = 19.0 + cell1.velocityRange = -5.0 + cell1.xAcceleration = 2.5 + cell1.yAcceleration = 10.0 cell1.emissionRange = .pi cell1.spin = -28.6 * (.pi / 180.0) cell1.spinRange = 57.2 * (.pi / 180.0) cell1.scale = 0.04 cell1.scaleRange = 0.15 - cell1.color = UIColor.white.withAlphaComponent(0.88).cgColor + cell1.color = UIColor.white.withAlphaComponent(0.58).cgColor // cell1.alphaRange = -0.2 particlesLayer.emitterCells = [cell1] From b1a998b6cd0636afb6ae0e5bf6e7c680ae7f3b6a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 15:53:07 +0400 Subject: [PATCH 02/21] Reaction improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 23 +++++++++++++++++ .../PeerAllowedReactionListController.swift | 17 +++++++++---- .../QuickReactionSetupController.swift | 10 +++----- .../Reactions/ReactionChatPreviewItem.swift | 8 +++--- .../InstalledStickerPacksController.swift | 3 +-- .../ApiUtils/ReactionsMessageAttribute.swift | 9 ++++--- .../SyncCore_ReactionsMessageAttribute.swift | 9 ++++++- .../ChatInterfaceStateContextMenus.swift | 25 +++---------------- .../ChatMessageAnimatedStickerItemNode.swift | 4 +-- .../Sources/ChatMessageBubbleItemNode.swift | 4 +-- .../ChatMessageInstantVideoItemNode.swift | 4 +-- ...hatMessageReactionsFooterContentNode.swift | 16 +++++++++++- .../Sources/ChatMessageStickerItemNode.swift | 4 +-- .../Sources/PeerInfo/PeerInfoScreen.swift | 20 ++++++--------- 14 files changed, 92 insertions(+), 64 deletions(-) diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 2da7e9dcee..813b263350 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7201,3 +7201,26 @@ Sorry for the inconvenience."; "AccessDenied.QrCamera" = "Telegram needs access to your camera to scan QR codes.\n\nPlease go to Settings > Privacy > Camera and set Telegram to ON."; "Share.ShareToInstagramStories" = "Share to Instagram Stories"; + +"PeerInfo.AllowedReactions.Title" = "Reactions"; +"PeerInfo.AllowedReactions.AllowAllText" = "Allow Reactions"; +"PeerInfo.AllowedReactions.AllowAllGroupInfo" = "Allow subscribers to react to group messages."; +"PeerInfo.AllowedReactions.AllowAllChannelInfo" = "Allow subscribers to react to channel posts."; +"PeerInfo.AllowedReactions.ReactionListHeader" = "AVAILABLE REACTIONS"; + +"PeerInfo.Reactions" = "Reactions"; +"PeerInfo.ReactionsDisabled" = "Disabled"; + +"Settings.QuickReactionSetup.NavigationTitle" = "Quick Reaction"; +"Settings.QuickReactionSetup.Title" = "Quick Reaction"; +"Settings.QuickReactionSetup.DemoHeader" = "DOUBLE TAP ON MESSAGE TO REACT"; +"Settings.QuickReactionSetup.DemoInfo" = "You can double tap on message for a quick reaction."; +"Settings.QuickReactionSetup.ReactionListHeader" = "QUICK REACTION"; +"Settings.QuickReactionSetup.DemoMessageAuthor" = "Dino"; +"Settings.QuickReactionSetup.DemoMessageText" = "I hope you're enjoying your day as much as I am."; + +"Chat.ContextReactionCount_1" = "1 reaction"; +"Chat.ContextReactionCount_any" = "%@ reactions"; +"Chat.OutgoingContextReactionCount_1" = "1 reacted"; +"Chat.OutgoingContextReactionCount_any" = "%@ reacted"; +"Chat.OutgoingContextMixedReactionCount" = "%1$@/%2$@ reacted"; diff --git a/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift b/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift index bdf88e6e35..dcd12aa8e6 100644 --- a/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift +++ b/submodules/PeerInfoUI/Sources/PeerAllowedReactionListController.swift @@ -150,16 +150,23 @@ private struct PeerAllowedReactionListControllerState: Equatable { private func peerAllowedReactionListControllerEntries( presentationData: PresentationData, availableReactions: AvailableReactions?, + peer: Peer?, cachedData: CachedPeerData?, state: PeerAllowedReactionListControllerState ) -> [PeerAllowedReactionListControllerEntry] { var entries: [PeerAllowedReactionListControllerEntry] = [] if let availableReactions = availableReactions, let allowedReactions = state.updatedAllowedReactions { - entries.append(.allowAll(text: "Allow Reactions", isEnabled: !allowedReactions.isEmpty)) - entries.append(.allowAllInfo("Allow subscribers to reacts to channel posts.")) + entries.append(.allowAll(text: presentationData.strings.PeerInfo_AllowedReactions_AllowAllText, isEnabled: !allowedReactions.isEmpty)) + let allInfoText: String + if let peer = peer as? TelegramChannel, case .broadcast = peer.info { + allInfoText = presentationData.strings.PeerInfo_AllowedReactions_AllowAllChannelInfo + } else { + allInfoText = presentationData.strings.PeerInfo_AllowedReactions_AllowAllGroupInfo + } + entries.append(.allowAllInfo(allInfoText)) - entries.append(.itemsHeader("AVAILABLE REACTIONS")) + entries.append(.itemsHeader(presentationData.strings.PeerInfo_AllowedReactions_ReactionListHeader)) var index = 0 for availableReaction in availableReactions.reactions { if !availableReaction.isEnabled { @@ -262,12 +269,12 @@ public func peerAllowedReactionListController( ) |> deliverOnMainQueue |> map { presentationData, state, availableReactions, peerView -> (ItemListControllerState, (ItemListNodeState, Any)) in - //TODO:localize - let title: String = "Reactions" + let title: String = presentationData.strings.PeerInfo_AllowedReactions_Title let entries = peerAllowedReactionListControllerEntries( presentationData: presentationData, availableReactions: availableReactions, + peer: peerView.peers[peerId], cachedData: peerView.cachedData, state: state ) diff --git a/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift b/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift index caa56a2271..4a1f7515a2 100644 --- a/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift +++ b/submodules/SettingsUI/Sources/Reactions/QuickReactionSetupController.swift @@ -177,8 +177,7 @@ private func quickReactionSetupControllerEntries( var entries: [QuickReactionSetupControllerEntry] = [] if let availableReactions = availableReactions { - //TODO:localize - entries.append(.demoHeader("DOUBLE TAP ON MESSAGE TO REACT")) + entries.append(.demoHeader(presentationData.strings.Settings_QuickReactionSetup_DemoHeader)) entries.append(.demoMessage( wallpaper: presentationData.chatWallpaper, fontSize: presentationData.chatFontSize, @@ -188,9 +187,9 @@ private func quickReactionSetupControllerEntries( availableReactions: availableReactions, reaction: reactionSettings.quickReaction )) - entries.append(.demoDescription("You can double tap on message for a quick reaction.")) + entries.append(.demoDescription(presentationData.strings.Settings_QuickReactionSetup_DemoInfo)) - entries.append(.itemsHeader("QUICK REACTION")) + entries.append(.itemsHeader(presentationData.strings.Settings_QuickReactionSetup_ReactionListHeader)) var index = 0 for availableReaction in availableReactions.reactions { if !availableReaction.isEnabled { @@ -298,8 +297,7 @@ public func quickReactionSetupController( ) |> deliverOnMainQueue |> map { presentationData, _, availableReactions, settings, images -> (ItemListControllerState, (ItemListNodeState, Any)) in - //TODO:localize - let title: String = "Quick Reaction" + let title: String = presentationData.strings.Settings_QuickReactionSetup_Title let entries = quickReactionSetupControllerEntries( presentationData: presentationData, diff --git a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift index 662acc661b..ffffe4cb19 100644 --- a/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Reactions/ReactionChatPreviewItem.swift @@ -197,15 +197,13 @@ class ReactionChatPreviewItemNode: ListViewItemNode { let messages = SimpleDictionary() peers[chatPeerId] = TelegramGroup(id: chatPeerId, title: "Chat", photo: [], participantCount: 1, role: .member, membership: .Member, flags: [], defaultBannedRights: nil, migrationReference: nil, creationDate: 1, version: 1) - //TODO:localize - peers[userPeerId] = TelegramUser(id: userPeerId, accessHash: nil, firstName: "Dino", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) + peers[userPeerId] = TelegramUser(id: userPeerId, accessHash: nil, firstName: item.strings.Settings_QuickReactionSetup_DemoMessageAuthor, lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: []) - //TODO:localize - let messageText = "I hope you're enjoying your day as much as I am." + let messageText = item.strings.Settings_QuickReactionSetup_DemoMessageText var attributes: [MessageAttribute] = [] if let reaction = item.reaction { - attributes.append(ReactionsMessageAttribute(reactions: [MessageReaction(value: reaction, count: 1, isSelected: true)], recentPeers: [])) + attributes.append(ReactionsMessageAttribute(canViewList: false, reactions: [MessageReaction(value: reaction, count: 1, isSelected: true)], recentPeers: [])) } let messageItem = item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: chatPeerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, author: peers[userPeerId], text: messageText, attributes: attributes, media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [])], theme: item.theme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: item.availableReactions) diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index ccfaa4c005..ed02df9081 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -543,8 +543,7 @@ private func installedStickerPacksControllerEntries(presentationData: Presentati } entries.append(.masks(presentationData.theme, presentationData.strings.MaskStickerSettings_Title)) - //TODO:localize - entries.append(.quickReaction("Quick Reaction", quickReactionImage)) + entries.append(.quickReaction(presentationData.strings.Settings_QuickReactionSetup_NavigationTitle, quickReactionImage)) entries.append(.animatedStickers(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickers, stickerSettings.loopAnimatedStickers)) entries.append(.animatedStickersInfo(presentationData.theme, presentationData.strings.StickerPacksSettings_AnimatedStickersInfo)) diff --git a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift index cbf81ee67a..359fc61bc3 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/ReactionsMessageAttribute.swift @@ -7,6 +7,7 @@ extension ReactionsMessageAttribute { switch reactions { case let .messageReactions(flags, results, recentReactions): let min = (flags & (1 << 0)) != 0 + let canViewList = (flags & (1 << 2)) != 0 var reactions = results.map { result -> MessageReaction in switch result { case let .reactionCount(flags, reaction, count): @@ -41,7 +42,7 @@ extension ReactionsMessageAttribute { } } } - return ReactionsMessageAttribute(reactions: reactions, recentPeers: parsedRecentReactions) + return ReactionsMessageAttribute(canViewList: canViewList, reactions: reactions, recentPeers: parsedRecentReactions) } } } @@ -116,7 +117,7 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM } } if !reactions.isEmpty { - return ReactionsMessageAttribute(reactions: reactions, recentPeers: recentPeers) + return ReactionsMessageAttribute(canViewList: current?.canViewList ?? false, reactions: reactions, recentPeers: recentPeers) } else { return nil } @@ -130,7 +131,8 @@ public func mergedMessageReactions(attributes: [MessageAttribute]) -> ReactionsM extension ReactionsMessageAttribute { convenience init(apiReactions: Api.MessageReactions) { switch apiReactions { - case let .messageReactions(_, results, recentReactions): + case let .messageReactions(flags, results, recentReactions): + let canViewList = (flags & (1 << 2)) != 0 let parsedRecentReactions: [ReactionsMessageAttribute.RecentPeer] if let recentReactions = recentReactions { parsedRecentReactions = recentReactions.map { recentReaction -> ReactionsMessageAttribute.RecentPeer in @@ -144,6 +146,7 @@ extension ReactionsMessageAttribute { } self.init( + canViewList: canViewList, reactions: results.map { result in switch result { case let .reactionCount(flags, reaction, count): diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift index c416b3257b..4315937127 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReactionsMessageAttribute.swift @@ -51,6 +51,7 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { } } + public let canViewList: Bool public let reactions: [MessageReaction] public let recentPeers: [RecentPeer] @@ -58,22 +59,28 @@ public final class ReactionsMessageAttribute: Equatable, MessageAttribute { return self.recentPeers.map(\.peerId) } - public init(reactions: [MessageReaction], recentPeers: [RecentPeer]) { + public init(canViewList: Bool, reactions: [MessageReaction], recentPeers: [RecentPeer]) { + self.canViewList = canViewList self.reactions = reactions self.recentPeers = recentPeers } required public init(decoder: PostboxDecoder) { + self.canViewList = decoder.decodeBoolForKey("vl", orElse: true) self.reactions = decoder.decodeObjectArrayWithDecoderForKey("r") self.recentPeers = decoder.decodeObjectArrayWithDecoderForKey("rp") } public func encode(_ encoder: PostboxEncoder) { + encoder.encodeBool(self.canViewList, forKey: "vl") encoder.encodeObjectArray(self.reactions, forKey: "r") encoder.encodeObjectArray(self.recentPeers, forKey: "rp") } public static func ==(lhs: ReactionsMessageAttribute, rhs: ReactionsMessageAttribute) -> Bool { + if lhs.canViewList != rhs.canViewList { + return false + } if lhs.reactions != rhs.reactions { return false } diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 5d7e24d06b..d3ed7eccc4 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1978,13 +1978,7 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus if let currentStats = self.currentStats { if currentStats.peers.isEmpty { if reactionCount != 0 { - //TODO:localize - let text: String - if reactionCount == 1 { - text = "1 reaction" - } else { - text = "\(reactionCount) reactions" - } + let text: String = self.presentationData.strings.Chat_ContextReactionCount(Int32(reactionCount)) self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) } else { var text = self.presentationData.strings.Conversation_ContextMenuNoViews @@ -2002,29 +1996,18 @@ private final class ChatReadReportContextItemNode: ASDisplayNode, ContextMenuCus } } else if currentStats.peers.count == 1 { if reactionCount != 0 { - //TODO:localize - let text: String - if reactionCount == 1 { - text = "1 reacted" - } else { - text = "\(reactionCount) reacted" - } + let text: String = self.presentationData.strings.Chat_OutgoingContextReactionCount(Int32(reactionCount)) self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) } else { self.textNode.attributedText = NSAttributedString(string: currentStats.peers[0].displayTitle(strings: self.presentationData.strings, displayOrder: self.presentationData.nameDisplayOrder), font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) } } else { if reactionCount != 0 { - //TODO:localize let text: String if reactionCount >= currentStats.peers.count { - if reactionCount == 1 { - text = "1 reacted" - } else { - text = "\(reactionCount) reacted" - } + text = self.presentationData.strings.Chat_OutgoingContextReactionCount(Int32(reactionCount)) } else { - text = "\(reactionCount)/\(currentStats.peers.count) reacted" + text = self.presentationData.strings.Chat_OutgoingContextMixedReactionCount("\(reactionCount)", "\(currentStats.peers.count)").string } self.textNode.attributedText = NSAttributedString(string: text, font: textFont, textColor: self.presentationData.theme.contextMenu.primaryColor) } else { diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index ad2c8bfc1a..975916a24f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1111,9 +1111,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { let reactions: ReactionsMessageAttribute if shouldDisplayInlineDateReactions(message: item.message) { - reactions = ReactionsMessageAttribute(reactions: [], recentPeers: []) + reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? if !reactions.reactions.isEmpty { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index b108bb6b31..1a91c45b44 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -1435,9 +1435,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode let bubbleReactions: ReactionsMessageAttribute if needReactions { - bubbleReactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(reactions: [], recentPeers: []) + bubbleReactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } else { - bubbleReactions = ReactionsMessageAttribute(reactions: [], recentPeers: []) + bubbleReactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } if !bubbleReactions.reactions.isEmpty { bottomNodeMergeStatus = .Both diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index 0857dcdb14..b112da8c36 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -549,9 +549,9 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let reactions: ReactionsMessageAttribute if shouldDisplayInlineDateReactions(message: item.message) { - reactions = ReactionsMessageAttribute(reactions: [], recentPeers: []) + reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? diff --git a/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift index db8d90b2d0..9337d725cc 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReactionsFooterContentNode.swift @@ -14,6 +14,20 @@ import AccountContext import WallpaperBackgroundNode func canViewMessageReactionList(message: Message) -> Bool { + var found = false + for attribute in message.attributes { + if let attribute = attribute as? ReactionsMessageAttribute { + if !attribute.canViewList { + return false + } + found = true + } + } + + if !found { + return false + } + if let peer = message.peers[message.id.peerId] { if let channel = peer as? TelegramChannel { if case .broadcast = channel.info { @@ -487,7 +501,7 @@ final class ChatMessageReactionsFooterContentNode: ChatMessageBubbleContentNode } return (contentProperties, nil, CGFloat.greatestFiniteMagnitude, { constrainedSize, position in - let reactionsAttribute = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(reactions: [], recentPeers: []) + let reactionsAttribute = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) let buttonsUpdate = buttonsNode.prepareUpdate( context: item.context, presentationData: item.presentationData, diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index b34d37ca52..083827b70a 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -655,9 +655,9 @@ class ChatMessageStickerItemNode: ChatMessageItemView { let reactions: ReactionsMessageAttribute if shouldDisplayInlineDateReactions(message: item.message) { - reactions = ReactionsMessageAttribute(reactions: [], recentPeers: []) + reactions = ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } else { - reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(reactions: [], recentPeers: []) + reactions = mergedMessageReactions(attributes: item.message.attributes) ?? ReactionsMessageAttribute(canViewList: false, reactions: [], recentPeers: []) } var reactionButtonsFinalize: ((CGFloat) -> (CGSize, (_ animation: ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode))? diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index f882236f2b..460f2cca3e 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -1181,18 +1181,17 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr } if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - //TODO:localize let label: String if let cachedData = data.cachedData as? CachedChannelData, let allowedReactions = cachedData.allowedReactions { if allowedReactions.isEmpty { - label = "Disabled" + label = presentationData.strings.PeerInfo_ReactionsDisabled } else { label = "\(allowedReactions.count)" } } else { label = "" } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: "Reactions", icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { interaction.editingOpenReactionsSetup() })) } @@ -1306,18 +1305,17 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr } if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - //TODO:localize let label: String if let cachedData = data.cachedData as? CachedChannelData, let allowedReactions = cachedData.allowedReactions { if allowedReactions.isEmpty { - label = "Disabled" + label = presentationData.strings.PeerInfo_ReactionsDisabled } else { label = "\(allowedReactions.count)" } } else { label = "" } - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: "Reactions", icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { interaction.editingOpenReactionsSetup() })) } @@ -1329,18 +1327,17 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr } } else { if isCreator || (channel.adminRights?.rights.contains(.canChangeInfo) == true) { - //TODO:localize let label: String if let cachedData = data.cachedData as? CachedChannelData, let allowedReactions = cachedData.allowedReactions { if allowedReactions.isEmpty { - label = "Disabled" + label = presentationData.strings.PeerInfo_ReactionsDisabled } else { label = "\(allowedReactions.count)" } } else { label = "" } - items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: "Reactions", icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + items[.peerPublicSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { interaction.editingOpenReactionsSetup() })) } @@ -1440,18 +1437,17 @@ private func editingItems(data: PeerInfoScreenData?, context: AccountContext, pr })) do { - //TODO:localize let label: String if let cachedData = data.cachedData as? CachedGroupData, let allowedReactions = cachedData.allowedReactions { if allowedReactions.isEmpty { - label = "Disabled" + label = presentationData.strings.PeerInfo_ReactionsDisabled } else { label = "\(allowedReactions.count)" } } else { label = "" } - items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: "Reactions", icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { + items[.peerSettings]!.append(PeerInfoScreenDisclosureItem(id: ItemReactions, label: .text(label), text: presentationData.strings.PeerInfo_Reactions, icon: UIImage(bundleImageName: "Settings/Menu/Reactions"), action: { interaction.editingOpenReactionsSetup() })) } From 009c99ac9695cf1895a8e70e2f189e1fafcecb2f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 16:43:48 +0400 Subject: [PATCH 03/21] Adjust timing --- .../ReactionContextBackgroundNode.swift | 26 ++++++++++--------- .../Sources/ReactionContextNode.swift | 2 +- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift index 68d3baee67..5a87fe2493 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift @@ -163,25 +163,27 @@ final class ReactionContextBackgroundNode: ASDisplayNode { } func animateIn() { - let smallCircleDuration: Double = 0.5 - let largeCircleDuration: Double = 0.5 - let largeCircleDelay: Double = 0.08 - let mainCircleDuration: Double = 0.5 - let mainCircleDelay: Double = 0.1 + let smallCircleDuration: Double = 0.35 + let largeCircleDuration: Double = 0.35 + let largeCircleDelay: Double = 0.13 + let mainCircleDuration: Double = 0.25 + let mainCircleDelay: Double = 0.16 - self.smallCircleLayer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: smallCircleDuration) + //self.smallCircleLayer.animate(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: smallCircleDuration) + self.smallCircleLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: smallCircleDuration, delay: 0.0) - self.largeCircleLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: largeCircleDelay) - self.largeCircleLayer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay) + self.largeCircleLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.01, delay: largeCircleDelay) + self.largeCircleLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay) + //self.largeCircleLayer.animate(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: largeCircleDuration) - self.backgroundLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15, delay: mainCircleDelay) - self.backgroundLayer.animateSpring(from: 0.1 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay) + self.backgroundLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.01, delay: mainCircleDelay) + self.backgroundLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay) } func animateInFromAnchorRect(size: CGSize, sourceBackgroundFrame: CGRect) { - let springDuration: Double = 0.42 + let springDuration: Double = 0.2 let springDamping: CGFloat = 104.0 - let springDelay: Double = 0.22 + let springDelay: Double = 0.25 self.backgroundLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) self.backgroundLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 66caa4ca0e..46cb0bc9db 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -337,7 +337,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { guard let itemNode = self.visibleItemNodes[i] else { continue } - let itemDelay = mainCircleDelay + 0.1 + Double(i) * 0.03 + let itemDelay = mainCircleDelay + 0.1 + Double(i) * 0.035 DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + itemDelay, execute: { [weak itemNode] in itemNode?.animateIn() }) From 7d218f4812e5fd3b97bea9fc652500c5e69eb11a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 16:53:22 +0400 Subject: [PATCH 04/21] Various Improvements --- .../Sources/Node/ChatListItem.swift | 2 +- .../Sources/ContactsControllerNode.swift | 9 - submodules/InvisibleInkDustNode/BUILD | 1 + .../Sources/InvisibleInkDustNode.swift | 136 +- .../Sources/InviteLinkInviteController.swift | 2 +- .../Sources/InviteLinkListController.swift | 4 +- .../Sources/InviteLinkViewController.swift | 2 +- .../Sources/ChannelVisibilityController.swift | 2 +- .../QrButtonIcon.imageset/Contents.json | 2 +- .../Settings/QrButtonIcon.imageset/qr_24.pdf | 323 +++ .../QrButtonIcon.imageset/qrbutton_24.pdf | 2497 ----------------- .../Settings/QrIcon.imageset/Contents.json | 2 +- .../Settings/QrIcon.imageset/Icon-33.pdf | 359 --- .../Settings/QrIcon.imageset/qr_30.pdf | 357 +++ .../Sources/ChatMessageNotificationItem.swift | 4 + .../Sources/ChatMessageReplyInfoNode.swift | 6 +- .../ChatPinnedMessageTitlePanelNode.swift | 6 +- .../Sources/ReplyAccessoryPanelNode.swift | 6 +- 18 files changed, 804 insertions(+), 2916 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qr_24.pdf delete mode 100644 submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qrbutton_24.pdf delete mode 100644 submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Icon-33.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/qr_30.pdf diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 72502f1f39..6e1ab4d3f7 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1061,7 +1061,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } } let messageString: NSAttributedString - if !message.text.isEmpty { + if !message.text.isEmpty && entities.count > 0 { messageString = stringWithAppliedEntities(message.text, entities: entities, baseColor: theme.messageTextColor, linkColor: theme.messageTextColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false) } else { messageString = NSAttributedString(string: messageText, font: textFont, textColor: theme.messageTextColor) diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift index 0c68933b2d..5556e6b62f 100644 --- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift +++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift @@ -71,12 +71,9 @@ final class ContactsControllerNode: ASDisplayNode { var addNearbyImpl: (() -> Void)? var inviteImpl: (() -> Void)? - var qrScanImpl: (() -> Void)? let options = [ContactListAdditionalOption(title: presentationData.strings.Contacts_AddPeopleNearby, icon: .generic(UIImage(bundleImageName: "Contact List/PeopleNearbyIcon")!), action: { addNearbyImpl?() - }), ContactListAdditionalOption(title: presentationData.strings.Contacts_ScanQrCode, icon: .generic(UIImage(bundleImageName: "Settings/QrIcon")!), action: { - qrScanImpl?() }), ContactListAdditionalOption(title: presentationData.strings.Contacts_InviteFriends, icon: .generic(UIImage(bundleImageName: "Contact List/AddMemberIcon")!), action: { inviteImpl?() })] @@ -133,12 +130,6 @@ final class ContactsControllerNode: ASDisplayNode { } } - qrScanImpl = { [weak self] in - if let strongSelf = self { - strongSelf.openQrScan?() - } - } - contextAction = { [weak self] peer, node, gesture in self?.contextAction(peer: peer, node: node, gesture: gesture) } diff --git a/submodules/InvisibleInkDustNode/BUILD b/submodules/InvisibleInkDustNode/BUILD index 684ce8c618..596e698613 100644 --- a/submodules/InvisibleInkDustNode/BUILD +++ b/submodules/InvisibleInkDustNode/BUILD @@ -14,6 +14,7 @@ swift_library( "//submodules/Display:Display", "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit", "//submodules/AppBundle:AppBundle", + "//submodules/LegacyComponents:LegacyComponents", ], visibility = [ "//visibility:public", diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index f237be32fd..5fd6ab395a 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -5,6 +5,7 @@ import SwiftSignalKit import AsyncDisplayKit import Display import AppBundle +import LegacyComponents private func createEmitterBehavior(type: String) -> NSObject { let selector = ["behaviorWith", "Type:"].joined(separator: "") @@ -14,8 +15,8 @@ private func createEmitterBehavior(type: String) -> NSObject { return castedBehaviorWithType(behaviorClass, NSSelectorFromString(selector), type) } -private let textMaskImage: UIImage = { - return generateImage(CGSize(width: 60.0, height: 60.0), rotatedContext: { size, context in +private func generateTextMaskImage(size: CGSize, position: CGPoint) -> UIImage? { + return generateImage(size, rotatedContext: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) @@ -24,13 +25,13 @@ private let textMaskImage: UIImage = { let colorSpace = CGColorSpaceCreateDeviceRGB() let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! - let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0) - context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: size.width / 2.0, options: .drawsAfterEndLocation) + let center = position + context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: min(10.0, min(size.width, size.height) * 0.4), options: .drawsAfterEndLocation) })! -}() +} -private let emitterMaskImage: UIImage = { - return generateImage(CGSize(width: 120.0, height: 120.0), rotatedContext: { size, context in +private func generateEmitterMaskImage(size: CGSize, position: CGPoint) -> UIImage? { + return generateImage(size, rotatedContext: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) @@ -39,13 +40,14 @@ private let emitterMaskImage: UIImage = { let colorSpace = CGColorSpaceCreateDeviceRGB() let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! - let center = CGPoint(x: size.width / 2.0, y: size.height / 2.0) - context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: size.width / 10.0, options: .drawsAfterEndLocation) - })! -}() + let center = position + context.drawRadialGradient(gradient, startCenter: center, startRadius: 0.0, endCenter: center, endRadius: min(10.0, min(size.width, size.height) * 0.4), options: .drawsAfterEndLocation) + }) +} public class InvisibleInkDustNode: ASDisplayNode { private var currentParams: (size: CGSize, color: UIColor, rects: [CGRect], wordRects: [CGRect])? + private var animColor: CGColor? private weak var textNode: TextNode? private let textMaskNode: ASDisplayNode @@ -68,13 +70,10 @@ public class InvisibleInkDustNode: ASDisplayNode { self.textMaskNode = ASDisplayNode() self.textSpotNode = ASImageNode() - let img = textMaskImage - self.textSpotNode.image = img self.emitterMaskNode = ASDisplayNode() self.emitterSpotNode = ASImageNode() - let simg = emitterMaskImage - self.emitterSpotNode.image = simg + self.emitterSpotNode.contentMode = .scaleToFill self.emitterMaskFillNode = ASDisplayNode() self.emitterMaskFillNode.backgroundColor = .white @@ -93,24 +92,22 @@ public class InvisibleInkDustNode: ASDisplayNode { let emitter = CAEmitterCell() emitter.contents = UIImage(bundleImageName: "Components/TextSpeckle")?.cgImage - emitter.setValue(1.8, forKey: "contentsScale") + emitter.contentsScale = 1.8 emitter.emissionRange = .pi * 2.0 - emitter.setValue(3.0, forKey: "mass") - emitter.setValue(2.0, forKey: "massRange") emitter.lifetime = 1.0 emitter.scale = 0.5 emitter.velocityRange = 20.0 emitter.name = "dustCell" - emitter.setValue("point", forKey: "particleType") - emitter.color = UIColor.white.withAlphaComponent(0.0).cgColor emitter.alphaRange = 1.0 +// emitter.setValue("point", forKey: "particleType") +// emitter.setValue(3.0, forKey: "mass") +// emitter.setValue(2.0, forKey: "massRange") self.emitter = emitter let fingerAttractor = createEmitterBehavior(type: "simpleAttractor") fingerAttractor.setValue("fingerAttractor", forKey: "name") let alphaBehavior = createEmitterBehavior(type: "valueOverLife") - alphaBehavior.setValue("alphaBehavior", forKey: "name") alphaBehavior.setValue("color.alpha", forKey: "keyPath") alphaBehavior.setValue([0.0, 0.0, 1.0, 0.0, -1.0], forKey: "values") alphaBehavior.setValue(true, forKey: "additive") @@ -122,12 +119,12 @@ public class InvisibleInkDustNode: ASDisplayNode { emitterLayer.allowsGroupOpacity = true emitterLayer.lifetime = 1 emitterLayer.emitterCells = [emitter] - emitterLayer.setValue(behaviors, forKey: "emitterBehaviors") emitterLayer.emitterPosition = CGPoint(x: 0, y: 0) emitterLayer.seed = arc4random() - emitterLayer.setValue("rectangles", forKey: "emitterShape") emitterLayer.emitterSize = CGSize(width: 1, height: 1) - emitterLayer.setValue(0.0322, forKey: "updateInterval") + emitterLayer.emitterShape = CAEmitterLayerEmitterShape(rawValue: "rectangles") + emitterLayer.setValue(behaviors, forKey: "emitterBehaviors") +// emitterLayer.setValue(0.0322, forKey: "updateInterval") emitterLayer.setValue(4.0, forKeyPath: "emitterBehaviors.fingerAttractor.stiffness") emitterLayer.setValue(false, forKeyPath: "emitterBehaviors.fingerAttractor.enabled") @@ -160,7 +157,7 @@ public class InvisibleInkDustNode: ASDisplayNode { } @objc private func tap(_ gestureRecognizer: UITapGestureRecognizer) { - guard let (size, _, _, _) = self.currentParams, let textNode = self.textNode, !self.isRevealed else { + guard let (_, _, _, _) = self.currentParams, let textNode = self.textNode, !self.isRevealed else { return } @@ -171,23 +168,43 @@ public class InvisibleInkDustNode: ASDisplayNode { self.emitterLayer?.setValue(position, forKeyPath: "emitterBehaviors.fingerAttractor.position") Queue.mainQueue().after(0.1 * UIView.animationDurationFactor()) { - textNode.view.mask = self.textMaskNode.view textNode.alpha = 1.0 - - let radius = max(size.width, size.height) - self.textSpotNode.frame = CGRect(x: position.x - radius / 2.0, y: position.y - radius / 2.0, width: radius, height: radius) - - self.textSpotNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) - self.textSpotNode.layer.animateScale(from: 0.1, to: 3.5, duration: 0.71, removeOnCompletion: false, completion: { _ in + + textNode.view.mask = self.textMaskNode.view + self.textSpotNode.frame = CGRect(x: 0.0, y: 0.0, width: self.emitterMaskNode.frame.width * 3.0, height: self.emitterMaskNode.frame.height * 3.0) + let txtImg = generateTextMaskImage(size: self.emitterNode.frame.size, position: position) + self.textSpotNode.image = txtImg + + let xFactor = (position.x / self.emitterNode.frame.width - 0.5) * 2.0 + let yFactor = (position.y / self.emitterNode.frame.height - 0.5) * 2.0 + let maxFactor = max(abs(xFactor), abs(yFactor)) + + var scaleAddition = maxFactor * 4.0 + var durationAddition = -maxFactor * 0.2 + if self.emitterNode.frame.height > 0.0, self.emitterNode.frame.width / self.emitterNode.frame.height < 0.3 { + scaleAddition *= 4.0 + durationAddition *= 2.0 + } + + self.textSpotNode.layer.anchorPoint = CGPoint(x: position.x / self.emitterMaskNode.frame.width, y: position.y / self.emitterMaskNode.frame.height) + self.textSpotNode.position = position + self.textSpotNode.layer.animateScale(from: 0.3333, to: 10.5 + scaleAddition, duration: 0.55 + durationAddition, removeOnCompletion: false, completion: { _ in textNode.view.mask = nil }) + self.textSpotNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) self.emitterNode.view.mask = self.emitterMaskNode.view - let emitterSide = radius * 5.0 - self.emitterSpotNode.frame = CGRect(x: position.x - emitterSide / 2.0, y: position.y - emitterSide / 2.0, width: emitterSide, height: emitterSide) - self.emitterSpotNode.layer.animateScale(from: 0.1, to: 3.0, duration: 0.71, removeOnCompletion: false, completion: { [weak self] _ in + self.emitterSpotNode.frame = CGRect(x: 0.0, y: 0.0, width: self.emitterMaskNode.frame.width * 3.0, height: self.emitterMaskNode.frame.height * 3.0) + let img = generateEmitterMaskImage(size: self.emitterNode.frame.size, position: position) + self.emitterSpotNode.image = img + + self.emitterSpotNode.layer.anchorPoint = CGPoint(x: position.x / self.emitterMaskNode.frame.width, y: position.y / self.emitterMaskNode.frame.height) + self.emitterSpotNode.position = position + self.emitterSpotNode.layer.animateScale(from: 0.3333, to: 10.5 + scaleAddition, duration: 0.55 + durationAddition, removeOnCompletion: false, completion: { [weak self] _ in self?.alpha = 0.0 self?.emitterNode.view.mask = nil + + self?.emitter?.color = UIColor(rgb: 0x000000).cgColor }) self.emitterMaskFillNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false) } @@ -211,10 +228,49 @@ public class InvisibleInkDustNode: ASDisplayNode { let timeToRead = min(45.0, ceil(max(4.0, Double(spoilersLength) * 0.04))) Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) { self.isRevealed = false + + if let (_, color, _, _) = self.currentParams { + let colorSpace = CGColorSpaceCreateDeviceRGB() + let animation = POPBasicAnimation() + animation.property = (POPAnimatableProperty.property(withName: "color", initializer: { property in + property?.readBlock = { node, values in + if let color = (node as! InvisibleInkDustNode).emitter?.color { + if let a = color.components { + values?[0] = a[0] + values?[1] = a[1] + values?[2] = a[2] + values?[3] = a[3] + } + } + } + property?.writeBlock = { node, values in + if let values = values, let color = CGColor(colorSpace: colorSpace, components: values) { + let uicolor = UIColor(cgColor: color) + print(uicolor) + (node as! InvisibleInkDustNode).animColor = color + (node as! InvisibleInkDustNode).updateEmitter() + } + } + property?.threshold = 0.4 + }) as! POPAnimatableProperty) + animation.fromValue = self.emitter?.color + animation.toValue = color + animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + animation.duration = 0.1 + animation.completionBlock = { [weak self] _, _ in + if let strongSelf = self { + strongSelf.animColor = nil + strongSelf.updateEmitter() + } + } + self.pop_add(animation, forKey: "color") + } - let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear) - transition.updateAlpha(node: self, alpha: 1.0) - transition.updateAlpha(node: textNode, alpha: 0.0) + Queue.mainQueue().after(0.15) { + let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear) + transition.updateAlpha(node: self, alpha: 1.0) + transition.updateAlpha(node: textNode, alpha: 0.0) + } } } @@ -223,7 +279,7 @@ public class InvisibleInkDustNode: ASDisplayNode { return } - self.emitter?.color = color.cgColor + self.emitter?.color = self.animColor ?? color.cgColor self.emitterLayer?.setValue(wordRects, forKey: "emitterRects") self.emitterLayer?.frame = CGRect(origin: CGPoint(), size: size) @@ -236,7 +292,7 @@ public class InvisibleInkDustNode: ASDisplayNode { square += Float(rect.width * rect.height) } - self.emitter?.birthRate = min(100000, square * 0.33) + self.emitter?.birthRate = min(120000, square * 0.35) } public func update(size: CGSize, color: UIColor, rects: [CGRect], wordRects: [CGRect]) { diff --git a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift index 56932c07f2..5b11e4bfd1 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkInviteController.swift @@ -355,7 +355,7 @@ public final class InviteLinkInviteController: ViewController { }))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor) + return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.dismissWithoutContent) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift index 3ef8645c8a..e41cb6efc0 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkListController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkListController.swift @@ -500,7 +500,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio }))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor) + return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.dismissWithoutContent) @@ -672,7 +672,7 @@ public func inviteLinkListController(context: AccountContext, updatedPresentatio }))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor) + return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.default) diff --git a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift index e51a053dde..067b5725e4 100644 --- a/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift +++ b/submodules/InviteLinksUI/Sources/InviteLinkViewController.swift @@ -582,7 +582,7 @@ public final class InviteLinkViewController: ViewController { } else { if !invitationAvailability(invite).isZero { items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor) + return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.dismissWithoutContent) diff --git a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift index 3031968d2a..ee9c41ae01 100644 --- a/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift +++ b/submodules/PeerInfoUI/Sources/ChannelVisibilityController.swift @@ -1049,7 +1049,7 @@ public func channelVisibilityController(context: AccountContext, updatedPresenta }))) items.append(.action(ContextMenuActionItem(text: presentationData.strings.InviteLink_ContextGetQRCode, icon: { theme in - return generateTintedImage(image: UIImage(bundleImageName: "Wallet/QrIcon"), color: theme.contextMenu.primaryColor) + return generateTintedImage(image: UIImage(bundleImageName: "Settings/QrIcon"), color: theme.contextMenu.primaryColor) }, action: { _, f in f(.dismissWithoutContent) diff --git a/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/Contents.json index b6043c8f31..59b23854a3 100644 --- a/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "qrbutton_24.pdf", + "filename" : "qr_24.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qr_24.pdf b/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qr_24.pdf new file mode 100644 index 0000000000..4196196552 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qr_24.pdf @@ -0,0 +1,323 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 3.334961 3.334961 cm +0.000000 0.000000 0.000000 scn +3.665008 17.330078 m +3.642213 17.330078 l +3.642173 17.330078 l +3.195620 17.330084 2.827102 17.330088 2.525429 17.309504 c +2.212656 17.288164 1.923462 17.242495 1.645156 17.127216 c +0.992156 16.856735 0.473350 16.337929 0.202869 15.684929 c +0.087591 15.406623 0.041921 15.117428 0.020580 14.804656 c +-0.000003 14.502975 0.000002 14.134445 0.000007 13.687876 c +0.000007 13.687872 l +0.000008 13.665077 l +0.000007 13.642282 l +0.000007 13.642279 l +0.000002 13.195708 -0.000003 12.827179 0.020580 12.525498 c +0.041921 12.212727 0.087591 11.923532 0.202869 11.645226 c +0.473350 10.992226 0.992156 10.473419 1.645156 10.202938 c +1.923462 10.087660 2.212656 10.041990 2.525429 10.020650 c +2.827112 10.000067 3.195646 10.000071 3.642220 10.000077 c +3.665008 10.000077 l +3.687795 10.000077 l +4.134369 10.000071 4.502903 10.000067 4.804586 10.020650 c +5.117359 10.041990 5.406553 10.087660 5.684859 10.202938 c +6.337859 10.473419 6.856665 10.992226 7.127147 11.645226 c +7.242424 11.923532 7.288095 12.212727 7.309435 12.525498 c +7.330019 12.827182 7.330014 13.195715 7.330008 13.642290 c +7.330008 13.665077 l +7.330008 13.687864 l +7.330014 14.134439 7.330019 14.502973 7.309435 14.804656 c +7.288095 15.117428 7.242424 15.406623 7.127147 15.684929 c +6.856665 16.337929 6.337859 16.856735 5.684859 17.127216 c +5.406553 17.242495 5.117359 17.288164 4.804586 17.309504 c +4.502913 17.330088 4.134395 17.330084 3.687842 17.330078 c +3.687802 17.330078 l +3.665008 17.330078 l +h +2.154125 15.898457 m +2.243362 15.935419 2.370909 15.965870 2.615963 15.982590 c +2.866992 15.999717 3.189967 16.000076 3.665008 16.000076 c +4.140048 16.000076 4.463024 15.999717 4.714052 15.982590 c +4.959106 15.965870 5.086654 15.935419 5.175890 15.898457 c +5.503003 15.762961 5.762892 15.503072 5.898387 15.175960 c +5.935350 15.086723 5.965800 14.959176 5.982520 14.714121 c +5.999648 14.463093 6.000008 14.140118 6.000008 13.665077 c +6.000008 13.190037 5.999648 12.867062 5.982520 12.616034 c +5.965800 12.370978 5.935350 12.243431 5.898387 12.154195 c +5.762892 11.827082 5.503003 11.567192 5.175890 11.431698 c +5.086654 11.394735 4.959106 11.364285 4.714052 11.347565 c +4.463024 11.330437 4.140048 11.330077 3.665008 11.330077 c +3.189967 11.330077 2.866992 11.330437 2.615963 11.347565 c +2.370909 11.364285 2.243362 11.394735 2.154125 11.431698 c +1.827013 11.567192 1.567123 11.827082 1.431628 12.154195 c +1.394665 12.243431 1.364215 12.370978 1.347495 12.616034 c +1.330368 12.867062 1.330008 13.190037 1.330008 13.665077 c +1.330008 14.140118 1.330368 14.463093 1.347495 14.714121 c +1.364215 14.959176 1.394665 15.086723 1.431628 15.175960 c +1.567123 15.503072 1.827013 15.762961 2.154125 15.898457 c +h +13.665008 17.330078 m +13.642213 17.330078 l +13.642173 17.330078 l +13.195621 17.330084 12.827102 17.330088 12.525429 17.309504 c +12.212657 17.288164 11.923462 17.242495 11.645156 17.127216 c +10.992156 16.856735 10.473350 16.337929 10.202868 15.684929 c +10.087590 15.406623 10.041921 15.117428 10.020580 14.804656 c +9.999997 14.502989 10.000002 14.134479 10.000008 13.687937 c +10.000008 13.687872 l +10.000008 13.665077 l +10.000008 13.642282 l +10.000008 13.642218 l +10.000002 13.195675 9.999997 12.827166 10.020580 12.525498 c +10.041921 12.212727 10.087590 11.923532 10.202868 11.645226 c +10.473350 10.992226 10.992156 10.473419 11.645156 10.202938 c +11.923462 10.087660 12.212657 10.041990 12.525429 10.020650 c +12.827112 10.000067 13.195646 10.000071 13.642220 10.000077 c +13.665008 10.000077 l +13.687795 10.000077 l +14.134369 10.000071 14.502903 10.000067 14.804586 10.020650 c +15.117359 10.041990 15.406553 10.087660 15.684858 10.202938 c +16.337858 10.473419 16.856665 10.992226 17.127148 11.645226 c +17.242424 11.923532 17.288094 12.212727 17.309435 12.525498 c +17.330017 12.827168 17.330013 13.195679 17.330008 13.642224 c +17.330008 13.642290 l +17.330008 13.665077 l +17.330008 13.687864 l +17.330008 13.687929 l +17.330013 14.134475 17.330017 14.502987 17.309435 14.804656 c +17.288094 15.117428 17.242424 15.406623 17.127148 15.684929 c +16.856665 16.337929 16.337858 16.856735 15.684858 17.127216 c +15.406553 17.242495 15.117359 17.288164 14.804586 17.309504 c +14.502913 17.330088 14.134395 17.330084 13.687842 17.330078 c +13.687802 17.330078 l +13.665008 17.330078 l +h +12.154125 15.898457 m +12.243361 15.935419 12.370909 15.965870 12.615964 15.982590 c +12.866991 15.999717 13.189967 16.000076 13.665008 16.000076 c +14.140048 16.000076 14.463024 15.999717 14.714052 15.982590 c +14.959106 15.965870 15.086654 15.935419 15.175890 15.898457 c +15.503002 15.762961 15.762892 15.503072 15.898388 15.175960 c +15.935350 15.086723 15.965799 14.959176 15.982521 14.714121 c +15.999647 14.463093 16.000008 14.140118 16.000008 13.665077 c +16.000008 13.190037 15.999647 12.867062 15.982521 12.616034 c +15.965799 12.370978 15.935350 12.243431 15.898388 12.154195 c +15.762892 11.827082 15.503002 11.567192 15.175890 11.431698 c +15.086654 11.394735 14.959106 11.364285 14.714052 11.347565 c +14.463024 11.330437 14.140048 11.330077 13.665008 11.330077 c +13.189967 11.330077 12.866991 11.330437 12.615964 11.347565 c +12.370909 11.364285 12.243361 11.394735 12.154125 11.431698 c +11.827013 11.567192 11.567122 11.827082 11.431628 12.154195 c +11.394666 12.243431 11.364215 12.370978 11.347495 12.616034 c +11.330368 12.867062 11.330008 13.190037 11.330008 13.665077 c +11.330008 14.140118 11.330368 14.463093 11.347495 14.714121 c +11.364215 14.959176 11.394666 15.086723 11.431628 15.175960 c +11.567122 15.503072 11.827013 15.762961 12.154125 15.898457 c +h +3.642206 7.330039 m +3.665000 7.330039 l +3.687795 7.330039 l +3.687860 7.330039 l +4.134403 7.330045 4.502913 7.330050 4.804579 7.309466 c +5.117352 7.288126 5.406546 7.242456 5.684852 7.127178 c +6.337852 6.856697 6.856658 6.337891 7.127140 5.684891 c +7.242417 5.406585 7.288087 5.117390 7.309428 4.804618 c +7.330011 4.502934 7.330007 4.134401 7.330000 3.687826 c +7.330000 3.665039 l +7.330000 3.642252 l +7.330007 3.195677 7.330011 2.827144 7.309428 2.525460 c +7.288087 2.212688 7.242417 1.923493 7.127140 1.645187 c +6.856658 0.992188 6.337852 0.473381 5.684852 0.202900 c +5.406546 0.087622 5.117352 0.041952 4.804579 0.020611 c +4.502896 0.000029 4.134362 0.000032 3.687788 0.000038 c +3.665000 0.000038 l +3.642213 0.000038 l +3.195639 0.000032 2.827105 0.000029 2.525422 0.020611 c +2.212649 0.041952 1.923455 0.087622 1.645149 0.202900 c +0.992149 0.473381 0.473343 0.992188 0.202861 1.645187 c +0.087583 1.923493 0.041913 2.212688 0.020573 2.525460 c +-0.000011 2.827142 -0.000006 3.195673 0.000000 3.642244 c +0.000000 3.665039 l +0.000000 3.687834 l +-0.000006 4.134405 -0.000011 4.502936 0.020573 4.804618 c +0.041913 5.117390 0.087583 5.406585 0.202861 5.684891 c +0.473343 6.337891 0.992149 6.856697 1.645149 7.127178 c +1.923455 7.242456 2.212649 7.288126 2.525422 7.309466 c +2.827089 7.330050 3.195599 7.330045 3.642140 7.330039 c +3.642206 7.330039 l +h +2.615956 5.982552 m +2.370902 5.965832 2.243355 5.935381 2.154118 5.898418 c +1.827006 5.762924 1.567116 5.503034 1.431621 5.175921 c +1.394658 5.086685 1.364208 4.959138 1.347488 4.714083 c +1.330361 4.463055 1.330000 4.140079 1.330000 3.665039 c +1.330000 3.189999 1.330361 2.867023 1.347488 2.615995 c +1.364208 2.370940 1.394658 2.243393 1.431621 2.154157 c +1.567116 1.827044 1.827006 1.567154 2.154118 1.431660 c +2.243355 1.394697 2.370902 1.364246 2.615956 1.347527 c +2.866984 1.330399 3.189960 1.330040 3.665000 1.330040 c +4.140041 1.330040 4.463017 1.330399 4.714045 1.347527 c +4.959099 1.364246 5.086647 1.394697 5.175883 1.431660 c +5.502995 1.567154 5.762885 1.827044 5.898380 2.154157 c +5.935343 2.243393 5.965793 2.370940 5.982513 2.615995 c +5.999640 2.867023 6.000000 3.189999 6.000000 3.665039 c +6.000000 4.140079 5.999640 4.463055 5.982513 4.714083 c +5.965793 4.959138 5.935343 5.086685 5.898380 5.175921 c +5.762885 5.503034 5.502995 5.762924 5.175883 5.898418 c +5.086647 5.935381 4.959099 5.965832 4.714045 5.982552 c +4.463017 5.999679 4.140041 6.000039 3.665000 6.000039 c +3.189960 6.000039 2.866984 5.999679 2.615956 5.982552 c +h +10.164962 6.365039 m +10.164962 6.645065 10.164962 6.785078 10.219459 6.892035 c +10.267395 6.986115 10.343885 7.062606 10.437966 7.110542 c +10.544923 7.165039 10.684936 7.165039 10.964962 7.165039 c +11.364962 7.165039 l +11.644988 7.165039 11.785001 7.165039 11.891957 7.110542 c +11.986038 7.062606 12.062529 6.986115 12.110465 6.892035 c +12.164962 6.785078 12.164962 6.645065 12.164962 6.365039 c +12.164962 5.965039 l +12.164962 5.685013 12.164962 5.545000 12.110465 5.438044 c +12.062529 5.343963 11.986038 5.267472 11.891957 5.219536 c +11.785001 5.165039 11.644988 5.165039 11.364962 5.165039 c +10.964962 5.165039 l +10.684936 5.165039 10.544923 5.165039 10.437966 5.219536 c +10.343885 5.267472 10.267395 5.343963 10.219459 5.438044 c +10.164962 5.545000 10.164962 5.685013 10.164962 5.965039 c +10.164962 6.365039 l +h +12.719459 4.392035 m +12.664962 4.285078 12.664962 4.145065 12.664962 3.865039 c +12.664962 3.465039 l +12.664962 3.185013 12.664962 3.045000 12.719459 2.938044 c +12.767395 2.843963 12.843885 2.767472 12.937966 2.719536 c +13.044922 2.665039 13.184936 2.665039 13.464962 2.665039 c +13.864962 2.665039 l +14.144988 2.665039 14.285002 2.665039 14.391957 2.719536 c +14.486038 2.767472 14.562529 2.843963 14.610465 2.938044 c +14.664962 3.045000 14.664962 3.185013 14.664962 3.465039 c +14.664962 3.865039 l +14.664962 4.145065 14.664962 4.285078 14.610465 4.392035 c +14.562529 4.486115 14.486038 4.562606 14.391957 4.610542 c +14.285002 4.665039 14.144988 4.665039 13.864962 4.665039 c +13.464962 4.665039 l +13.184936 4.665039 13.044922 4.665039 12.937966 4.610542 c +12.843885 4.562606 12.767395 4.486115 12.719459 4.392035 c +h +10.219459 1.892035 m +10.164962 1.785078 10.164962 1.645065 10.164962 1.365039 c +10.164962 0.965038 l +10.164962 0.685013 10.164962 0.545000 10.219459 0.438044 c +10.267395 0.343964 10.343885 0.267471 10.437966 0.219536 c +10.544923 0.165039 10.684936 0.165039 10.964962 0.165039 c +11.364962 0.165039 l +11.644988 0.165039 11.785001 0.165039 11.891957 0.219536 c +11.986038 0.267471 12.062529 0.343964 12.110465 0.438044 c +12.164962 0.545000 12.164962 0.685013 12.164962 0.965038 c +12.164962 1.365039 l +12.164962 1.645065 12.164962 1.785078 12.110465 1.892035 c +12.062529 1.986115 11.986038 2.062606 11.891957 2.110542 c +11.785001 2.165039 11.644988 2.165039 11.364962 2.165039 c +10.964962 2.165039 l +10.684936 2.165039 10.544923 2.165039 10.437966 2.110542 c +10.343885 2.062606 10.267395 1.986115 10.219459 1.892035 c +h +15.219459 6.892035 m +15.164962 6.785078 15.164962 6.645065 15.164962 6.365039 c +15.164962 5.965039 l +15.164962 5.685013 15.164962 5.545000 15.219459 5.438044 c +15.267395 5.343963 15.343885 5.267472 15.437966 5.219536 c +15.544922 5.165039 15.684937 5.165039 15.964962 5.165039 c +16.364962 5.165039 l +16.644989 5.165039 16.785002 5.165039 16.891956 5.219536 c +16.986038 5.267472 17.062529 5.343963 17.110466 5.438044 c +17.164963 5.545000 17.164963 5.685013 17.164963 5.965039 c +17.164963 6.365039 l +17.164963 6.645065 17.164963 6.785078 17.110466 6.892035 c +17.062529 6.986115 16.986038 7.062606 16.891956 7.110542 c +16.785002 7.165039 16.644989 7.165039 16.364962 7.165039 c +15.964962 7.165039 l +15.684937 7.165039 15.544922 7.165039 15.437966 7.110542 c +15.343885 7.062606 15.267395 6.986115 15.219459 6.892035 c +h +15.164962 1.365039 m +15.164962 1.645065 15.164962 1.785078 15.219459 1.892035 c +15.267395 1.986115 15.343885 2.062606 15.437966 2.110542 c +15.544922 2.165039 15.684937 2.165039 15.964962 2.165039 c +16.364962 2.165039 l +16.644989 2.165039 16.785002 2.165039 16.891956 2.110542 c +16.986038 2.062606 17.062529 1.986115 17.110466 1.892035 c +17.164963 1.785078 17.164963 1.645065 17.164963 1.365039 c +17.164963 0.965038 l +17.164963 0.685013 17.164963 0.545000 17.110466 0.438044 c +17.062529 0.343964 16.986038 0.267471 16.891956 0.219536 c +16.785002 0.165039 16.644989 0.165039 16.364962 0.165039 c +15.964962 0.165039 l +15.684937 0.165039 15.544922 0.165039 15.437966 0.219536 c +15.343885 0.267471 15.267395 0.343964 15.219459 0.438044 c +15.164962 0.545000 15.164962 0.685013 15.164962 0.965038 c +15.164962 1.365039 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 12183 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000012273 00000 n +0000012297 00000 n +0000012470 00000 n +0000012544 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +12603 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qrbutton_24.pdf b/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qrbutton_24.pdf deleted file mode 100644 index 21f3ebb92c..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Settings/QrButtonIcon.imageset/qrbutton_24.pdf +++ /dev/null @@ -1,2497 +0,0 @@ -%PDF-1.7 - -1 0 obj - << >> -endobj - -2 0 obj - << /Length 3 0 R >> -stream -/DeviceRGB CS -/DeviceRGB cs -q -1.000000 0.000000 -0.000000 1.000000 3.334961 13.334961 cm -0.000000 0.000000 0.000000 scn -3.642206 7.330017 m -3.195634 7.330023 2.827103 7.330028 2.525421 7.309444 c -2.212649 7.288104 1.923455 7.242434 1.645149 7.127156 c -0.992149 6.856675 0.473343 6.337868 0.202861 5.684868 c -0.087583 5.406563 0.041913 5.117368 0.020573 4.804596 c --0.000011 4.502914 -0.000006 4.134383 0.000000 3.687811 c -0.000000 3.642222 l --0.000006 3.195651 -0.000011 2.827120 0.020573 2.525438 c -0.041913 2.212666 0.087583 1.923471 0.202861 1.645166 c -0.473343 0.992166 0.992149 0.473360 1.645149 0.202878 c -1.923455 0.087600 2.212649 0.041930 2.525421 0.020590 c -2.827105 0.000006 3.195638 0.000011 3.642213 0.000017 c -3.687788 0.000017 l -4.134362 0.000011 4.502895 0.000006 4.804579 0.020590 c -5.117351 0.041930 5.406546 0.087600 5.684851 0.202878 c -6.337851 0.473360 6.856658 0.992166 7.127139 1.645166 c -7.242417 1.923471 7.288087 2.212666 7.309427 2.525438 c -7.330011 2.827122 7.330006 3.195655 7.330000 3.642230 c -7.330000 3.687804 l -7.330006 4.134378 7.330011 4.502913 7.309427 4.804596 c -7.288087 5.117368 7.242417 5.406563 7.127139 5.684868 c -6.856658 6.337868 6.337851 6.856675 5.684851 7.127156 c -5.406546 7.242434 5.117351 7.288104 4.804579 7.309444 c -4.502897 7.330028 4.134366 7.330023 3.687795 7.330017 c -3.642206 7.330017 l -h -2.154118 5.898396 m -2.243354 5.935359 2.370901 5.965809 2.615956 5.982529 c -2.866984 5.999657 3.189960 6.000017 3.665000 6.000017 c -4.140040 6.000017 4.463016 5.999657 4.714044 5.982529 c -4.959099 5.965809 5.086646 5.935359 5.175882 5.898396 c -5.502995 5.762901 5.762885 5.503012 5.898379 5.175900 c -5.935342 5.086663 5.965792 4.959116 5.982512 4.714061 c -5.999640 4.463033 6.000000 4.140057 6.000000 3.665017 c -6.000000 3.189977 5.999640 2.867001 5.982512 2.615973 c -5.965792 2.370918 5.935342 2.243371 5.898379 2.154135 c -5.762885 1.827022 5.502995 1.567132 5.175882 1.431638 c -5.086646 1.394675 4.959099 1.364225 4.714044 1.347505 c -4.463016 1.330377 4.140040 1.330017 3.665000 1.330017 c -3.189960 1.330017 2.866984 1.330377 2.615956 1.347505 c -2.370901 1.364225 2.243354 1.394675 2.154118 1.431638 c -1.827005 1.567132 1.567116 1.827022 1.431621 2.154135 c -1.394658 2.243371 1.364208 2.370918 1.347488 2.615973 c -1.330360 2.867001 1.330000 3.189977 1.330000 3.665017 c -1.330000 4.140057 1.330360 4.463033 1.347488 4.714061 c -1.364208 4.959116 1.394658 5.086663 1.431621 5.175900 c -1.567116 5.503012 1.827005 5.762901 2.154118 5.898396 c -h -f* -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 13.334961 cm -0.000000 0.000000 0.000000 scn -13.642205 7.330017 m -13.195634 7.330023 12.827103 7.330028 12.525421 7.309444 c -12.212649 7.288104 11.923454 7.242434 11.645148 7.127156 c -10.992148 6.856675 10.473342 6.337868 10.202861 5.684868 c -10.087583 5.406563 10.041913 5.117368 10.020573 4.804596 c -9.999990 4.502929 9.999994 4.134418 10.000000 3.687876 c -10.000000 3.642222 l -9.999994 3.195680 9.999990 2.827105 10.020573 2.525438 c -10.041913 2.212666 10.087583 1.923471 10.202861 1.645166 c -10.473342 0.992166 10.992148 0.473360 11.645148 0.202878 c -11.923454 0.087600 12.212649 0.041930 12.525421 0.020590 c -12.827105 0.000006 13.195639 0.000011 13.642213 0.000017 c -13.687787 0.000017 l -14.134361 0.000011 14.502895 0.000006 14.804579 0.020590 c -15.117352 0.041930 15.406546 0.087600 15.684851 0.202878 c -16.337851 0.473360 16.856657 0.992166 17.127140 1.645166 c -17.242416 1.923471 17.288086 2.212666 17.309427 2.525438 c -17.330009 2.827107 17.330006 3.195619 17.330000 3.642164 c -17.330000 3.687804 l -17.330006 4.134349 17.330009 4.502927 17.309427 4.804596 c -17.288086 5.117368 17.242416 5.406563 17.127140 5.684868 c -16.856657 6.337868 16.337851 6.856675 15.684851 7.127156 c -15.406546 7.242434 15.117352 7.288104 14.804579 7.309444 c -14.502897 7.330028 14.134366 7.330023 13.687795 7.330017 c -13.642205 7.330017 l -h -12.154118 5.898396 m -12.243354 5.935359 12.370901 5.965809 12.615956 5.982529 c -12.866983 5.999657 13.189960 6.000017 13.665000 6.000017 c -14.140040 6.000017 14.463017 5.999657 14.714045 5.982529 c -14.959099 5.965809 15.086646 5.935359 15.175882 5.898396 c -15.502995 5.762901 15.762884 5.503012 15.898379 5.175900 c -15.935343 5.086663 15.965792 4.959116 15.982512 4.714061 c -15.999640 4.463033 16.000000 4.140057 16.000000 3.665017 c -16.000000 3.189977 15.999640 2.867001 15.982512 2.615973 c -15.965792 2.370918 15.935343 2.243371 15.898379 2.154135 c -15.762884 1.827022 15.502995 1.567132 15.175882 1.431638 c -15.086646 1.394675 14.959099 1.364225 14.714045 1.347505 c -14.463017 1.330377 14.140040 1.330017 13.665000 1.330017 c -13.189960 1.330017 12.866983 1.330377 12.615956 1.347505 c -12.370901 1.364225 12.243354 1.394675 12.154118 1.431638 c -11.827005 1.567132 11.567115 1.827022 11.431621 2.154135 c -11.394658 2.243371 11.364207 2.370918 11.347487 2.615973 c -11.330360 2.867001 11.330000 3.189977 11.330000 3.665017 c -11.330000 4.140057 11.330360 4.463033 11.347487 4.714061 c -11.364207 4.959116 11.394658 5.086663 11.431621 5.175900 c -11.567115 5.503012 11.827005 5.762901 12.154118 5.898396 c -h -f* -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 13.334961 cm -0.000000 0.000000 0.000000 scn -13.642205 -2.669983 m -13.687795 -2.669983 l -14.134336 -2.669977 14.502912 -2.669972 14.804579 -2.690556 c -15.117352 -2.711896 15.406546 -2.757565 15.684851 -2.872844 c -16.337851 -3.143325 16.856657 -3.662131 17.127140 -4.315131 c -17.242416 -4.593437 17.288086 -4.882632 17.309427 -5.195404 c -17.330009 -5.497073 17.330006 -5.865585 17.330000 -6.312130 c -17.330000 -6.357770 l -17.330006 -6.804315 17.330009 -7.172893 17.309427 -7.474562 c -17.288086 -7.787334 17.242416 -8.076529 17.127140 -8.354834 c -16.856657 -9.007833 16.337851 -9.526640 15.684851 -9.797123 c -15.406546 -9.912399 15.117352 -9.958069 14.804579 -9.979410 c -14.502910 -9.999992 14.134398 -9.999989 13.687853 -9.999983 c -13.642213 -9.999983 l -13.195668 -9.999989 12.827090 -9.999992 12.525421 -9.979410 c -12.212649 -9.958069 11.923454 -9.912399 11.645148 -9.797123 c -10.992148 -9.526640 10.473342 -9.007833 10.202861 -8.354834 c -10.087583 -8.076529 10.041913 -7.787334 10.020573 -7.474562 c -9.999990 -7.172894 9.999994 -6.804385 10.000000 -6.357843 c -10.000000 -6.312188 l -9.999994 -5.865646 9.999990 -5.497071 10.020573 -5.195404 c -10.041913 -4.882632 10.087583 -4.593437 10.202861 -4.315131 c -10.473342 -3.662131 10.992148 -3.143325 11.645148 -2.872844 c -11.923454 -2.757565 12.212649 -2.711896 12.525421 -2.690556 c -12.827088 -2.669972 13.195663 -2.669977 13.642205 -2.669983 c -h -12.615956 -4.017470 m -12.370901 -4.034190 12.243354 -4.064641 12.154118 -4.101604 c -11.827005 -4.237098 11.567115 -4.496988 11.431621 -4.824100 c -11.394658 -4.913337 11.364207 -5.040884 11.347487 -5.285939 c -11.330360 -5.536966 11.330000 -5.859942 11.330000 -6.334983 c -11.330000 -6.810023 11.330360 -7.132999 11.347487 -7.384027 c -11.364207 -7.629082 11.394658 -7.756629 11.431621 -7.845865 c -11.567115 -8.172977 11.827005 -8.432867 12.154118 -8.568362 c -12.243354 -8.605326 12.370901 -8.635775 12.615956 -8.652495 c -12.866983 -8.669622 13.189960 -8.669983 13.665000 -8.669983 c -14.140040 -8.669983 14.463017 -8.669622 14.714045 -8.652495 c -14.959099 -8.635775 15.086646 -8.605326 15.175882 -8.568362 c -15.502995 -8.432867 15.762884 -8.172977 15.898379 -7.845865 c -15.935343 -7.756629 15.965792 -7.629082 15.982512 -7.384027 c -15.999640 -7.132999 16.000000 -6.810023 16.000000 -6.334983 c -16.000000 -5.859942 15.999640 -5.536966 15.982512 -5.285939 c -15.965792 -5.040884 15.935343 -4.913337 15.898379 -4.824100 c -15.762884 -4.496988 15.502995 -4.237098 15.175882 -4.101604 c -15.086646 -4.064641 14.959099 -4.034190 14.714045 -4.017470 c -14.463017 -4.000343 14.140040 -3.999983 13.665000 -3.999983 c -13.189960 -3.999983 12.866983 -4.000343 12.615956 -4.017470 c -h -f* -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 18.664978 cm -0.000000 0.000000 0.000000 scn -2.719497 -10.938004 m -2.665000 -11.044961 2.665000 -11.184974 2.665000 -11.465000 c -2.665000 -11.865000 l -2.665000 -12.145026 2.665000 -12.285039 2.719497 -12.391995 c -2.767434 -12.486076 2.843924 -12.562567 2.938005 -12.610503 c -3.044961 -12.665000 3.184974 -12.665000 3.465000 -12.665000 c -3.865000 -12.665000 l -4.145026 -12.665000 4.285039 -12.665000 4.391995 -12.610503 c -4.486076 -12.562567 4.562567 -12.486076 4.610503 -12.391995 c -4.665000 -12.285039 4.665000 -12.145026 4.665000 -11.865000 c -4.665000 -11.465000 l -4.665000 -11.184974 4.665000 -11.044961 4.610503 -10.938004 c -4.562567 -10.843924 4.486076 -10.767433 4.391995 -10.719497 c -4.285039 -10.665000 4.145026 -10.665000 3.865000 -10.665000 c -3.465000 -10.665000 l -3.184974 -10.665000 3.044961 -10.665000 2.938005 -10.719497 c -2.843924 -10.767433 2.767434 -10.843924 2.719497 -10.938004 c -h -f -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 18.664978 cm -0.000000 0.000000 0.000000 scn -0.165000 -13.965000 m -0.165000 -13.684974 0.165000 -13.544961 0.219497 -13.438004 c -0.267434 -13.343924 0.343924 -13.267433 0.438005 -13.219497 c -0.544961 -13.165000 0.684974 -13.165000 0.965000 -13.165000 c -1.365000 -13.165000 l -1.645026 -13.165000 1.785040 -13.165000 1.891995 -13.219497 c -1.986076 -13.267433 2.062567 -13.343924 2.110503 -13.438004 c -2.165000 -13.544961 2.165000 -13.684974 2.165000 -13.965000 c -2.165000 -14.365000 l -2.165000 -14.645027 2.165000 -14.785040 2.110503 -14.891994 c -2.062567 -14.986076 1.986076 -15.062567 1.891995 -15.110504 c -1.785040 -15.165001 1.645026 -15.165001 1.365000 -15.165001 c -0.965000 -15.165001 l -0.684974 -15.165001 0.544961 -15.165001 0.438005 -15.110504 c -0.343924 -15.062567 0.267434 -14.986076 0.219497 -14.891994 c -0.165000 -14.785040 0.165000 -14.645027 0.165000 -14.365000 c -0.165000 -13.965000 l -h -f -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 18.664978 cm -0.000000 0.000000 0.000000 scn -0.219497 -8.438004 m -0.165000 -8.544961 0.165000 -8.684974 0.165000 -8.965000 c -0.165000 -9.365000 l -0.165000 -9.645026 0.165000 -9.785039 0.219497 -9.891995 c -0.267434 -9.986076 0.343924 -10.062567 0.438005 -10.110503 c -0.544961 -10.165000 0.684974 -10.165000 0.965000 -10.165000 c -1.365000 -10.165000 l -1.645026 -10.165000 1.785040 -10.165000 1.891995 -10.110503 c -1.986076 -10.062567 2.062567 -9.986076 2.110503 -9.891995 c -2.165000 -9.785039 2.165000 -9.645026 2.165000 -9.365000 c -2.165000 -8.965000 l -2.165000 -8.684974 2.165000 -8.544961 2.110503 -8.438004 c -2.062567 -8.343924 1.986076 -8.267433 1.891995 -8.219497 c -1.785040 -8.165000 1.645026 -8.165000 1.365000 -8.165000 c -0.965000 -8.165000 l -0.684974 -8.165000 0.544961 -8.165000 0.438005 -8.219497 c -0.343924 -8.267433 0.267434 -8.343924 0.219497 -8.438004 c -h -f -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 18.664978 cm -0.000000 0.000000 0.000000 scn -5.165000 -8.965000 m -5.165000 -8.684974 5.165000 -8.544961 5.219497 -8.438004 c -5.267433 -8.343924 5.343924 -8.267433 5.438004 -8.219497 c -5.544960 -8.165000 5.684974 -8.165000 5.965000 -8.165000 c -6.365000 -8.165000 l -6.645026 -8.165000 6.785039 -8.165000 6.891995 -8.219497 c -6.986076 -8.267433 7.062567 -8.343924 7.110503 -8.438004 c -7.165000 -8.544961 7.165000 -8.684974 7.165000 -8.965000 c -7.165000 -9.365000 l -7.165000 -9.645026 7.165000 -9.785039 7.110503 -9.891995 c -7.062567 -9.986076 6.986076 -10.062567 6.891995 -10.110503 c -6.785039 -10.165000 6.645026 -10.165000 6.365000 -10.165000 c -5.965000 -10.165000 l -5.684974 -10.165000 5.544960 -10.165000 5.438004 -10.110503 c -5.343924 -10.062567 5.267433 -9.986076 5.219497 -9.891995 c -5.165000 -9.785039 5.165000 -9.645026 5.165000 -9.365000 c -5.165000 -8.965000 l -h -f -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 18.664978 cm -0.000000 0.000000 0.000000 scn -5.219497 -13.438004 m -5.165000 -13.544961 5.165000 -13.684974 5.165000 -13.965000 c -5.165000 -14.365000 l -5.165000 -14.645027 5.165000 -14.785040 5.219497 -14.891994 c -5.267433 -14.986076 5.343924 -15.062567 5.438004 -15.110504 c -5.544960 -15.165001 5.684974 -15.165001 5.965000 -15.165001 c -6.365000 -15.165001 l -6.645026 -15.165001 6.785039 -15.165001 6.891995 -15.110504 c -6.986076 -15.062567 7.062567 -14.986076 7.110503 -14.891994 c -7.165000 -14.785040 7.165000 -14.645027 7.165000 -14.365000 c -7.165000 -13.965000 l -7.165000 -13.684974 7.165000 -13.544961 7.110503 -13.438004 c -7.062567 -13.343924 6.986076 -13.267433 6.891995 -13.219497 c -6.785039 -13.165000 6.645026 -13.165000 6.365000 -13.165000 c -5.965000 -13.165000 l -5.684974 -13.165000 5.544960 -13.165000 5.438004 -13.219497 c -5.343924 -13.267433 5.267433 -13.343924 5.219497 -13.438004 c -h -f -n -Q -q -1.000000 0.000000 -0.000000 1.000000 3.334961 3.004761 cm -0.000000 0.000000 0.000000 scn -3.642206 17.660217 m -3.642203 17.495216 l -3.642206 17.495216 l -3.642206 17.660217 l -h -2.525421 17.639645 m -2.514190 17.804262 l -2.514190 17.804262 l -2.525421 17.639645 l -h -1.645149 17.457355 m -1.708292 17.304916 l -1.708292 17.304916 l -1.645149 17.457355 l -h -0.202861 16.015068 m -0.355301 15.951925 l -0.355301 15.951925 l -0.202861 16.015068 l -h -0.020573 15.134796 m --0.144045 15.146028 l --0.144045 15.146028 l -0.020573 15.134796 l -h -0.000000 14.018011 m -0.165000 14.018011 l -0.165000 14.018014 l -0.000000 14.018011 l -h -0.000000 13.972423 m -0.165000 13.972420 l -0.165000 13.972423 l -0.000000 13.972423 l -h -0.020573 12.855639 m --0.144045 12.844406 l --0.144045 12.844406 l -0.020573 12.855639 l -h -0.202861 11.975367 m -0.355301 12.038509 l -0.355301 12.038509 l -0.202861 11.975367 l -h -1.645149 10.533078 m -1.582006 10.380638 l -1.582006 10.380638 l -1.645149 10.533078 l -h -2.525421 10.350790 m -2.514189 10.186172 l -2.514190 10.186172 l -2.525421 10.350790 l -h -3.642213 10.330217 m -3.642213 10.495217 l -3.642210 10.495217 l -3.642213 10.330217 l -h -3.687788 10.330217 m -3.687790 10.495217 l -3.687788 10.495217 l -3.687788 10.330217 l -h -4.804579 10.350790 m -4.815811 10.186172 l -4.815811 10.186172 l -4.804579 10.350790 l -h -5.684851 10.533078 m -5.747994 10.380638 l -5.747994 10.380638 l -5.684851 10.533078 l -h -7.127139 11.975367 m -7.279579 11.912224 l -7.279579 11.912224 l -7.127139 11.975367 l -h -7.309427 12.855639 m -7.474044 12.844406 l -7.474044 12.844406 l -7.309427 12.855639 l -h -7.330000 13.972429 m -7.165000 13.972429 l -7.165000 13.972427 l -7.330000 13.972429 l -h -7.330000 14.018004 m -7.165000 14.018007 l -7.165000 14.018004 l -7.330000 14.018004 l -h -7.309427 15.134796 m -7.474044 15.146028 l -7.474044 15.146028 l -7.309427 15.134796 l -h -7.127139 16.015068 m -7.279579 16.078211 l -7.279579 16.078211 l -7.127139 16.015068 l -h -5.684851 17.457355 m -5.621708 17.304916 l -5.621708 17.304916 l -5.684851 17.457355 l -h -4.804579 17.639645 m -4.815811 17.804262 l -4.815811 17.804262 l -4.804579 17.639645 l -h -3.687795 17.660217 m -3.687795 17.495216 l -3.687797 17.495216 l -3.687795 17.660217 l -h -2.615956 16.312729 m -2.627188 16.148111 l -2.627188 16.148111 l -2.615956 16.312729 l -h -2.154118 16.228596 m -2.217261 16.076157 l -2.154118 16.228596 l -h -4.714044 16.312729 m -4.702812 16.148111 l -4.702813 16.148111 l -4.714044 16.312729 l -h -5.175882 16.228596 m -5.112740 16.076157 l -5.112740 16.076157 l -5.175882 16.228596 l -h -5.898379 15.506100 m -6.050819 15.569242 l -6.050819 15.569242 l -5.898379 15.506100 l -h -5.982512 15.044261 m -6.147130 15.055492 l -6.147130 15.055493 l -5.982512 15.044261 l -h -5.982512 12.946173 m -6.147130 12.934941 l -6.147130 12.934941 l -5.982512 12.946173 l -h -5.898379 12.484335 m -5.745939 12.547478 l -5.745939 12.547478 l -5.898379 12.484335 l -h -5.175882 11.761838 m -5.112740 11.914278 l -5.112740 11.914278 l -5.175882 11.761838 l -h -4.714044 11.677705 m -4.725276 11.513088 l -4.725276 11.513088 l -4.714044 11.677705 l -h -2.615956 11.677705 m -2.604724 11.513088 l -2.604724 11.513088 l -2.615956 11.677705 l -h -2.154118 11.761838 m -2.090975 11.609398 l -2.090975 11.609398 l -2.154118 11.761838 l -h -1.431621 12.484335 m -1.584061 12.547478 l -1.584061 12.547478 l -1.431621 12.484335 l -h -1.347488 12.946173 m -1.512105 12.957405 l -1.512105 12.957405 l -1.347488 12.946173 l -h -1.347488 15.044261 m -1.512105 15.033030 l -1.512105 15.033030 l -1.347488 15.044261 l -h -1.431621 15.506100 m -1.584061 15.442957 l -1.431621 15.506100 l -h -13.642205 17.660217 m -13.642203 17.495216 l -13.642205 17.495216 l -13.642205 17.660217 l -h -12.525421 17.639645 m -12.514190 17.804262 l -12.514190 17.804262 l -12.525421 17.639645 l -h -11.645148 17.457355 m -11.582006 17.609797 l -11.582006 17.609797 l -11.645148 17.457355 l -h -10.202861 16.015068 m -10.355301 15.951925 l -10.355301 15.951925 l -10.202861 16.015068 l -h -10.020573 15.134796 m -10.185190 15.123564 l -10.185190 15.123564 l -10.020573 15.134796 l -h -10.000000 14.018077 m -10.165000 14.018077 l -10.165000 14.018079 l -10.000000 14.018077 l -h -10.000000 13.972423 m -10.165000 13.972421 l -10.165000 13.972423 l -10.000000 13.972423 l -h -10.020573 12.855639 m -10.185190 12.866871 l -10.185190 12.866871 l -10.020573 12.855639 l -h -10.202861 11.975367 m -10.355301 12.038509 l -10.355301 12.038509 l -10.202861 11.975367 l -h -11.645148 10.533078 m -11.582006 10.380638 l -11.582006 10.380638 l -11.645148 10.533078 l -h -12.525421 10.350790 m -12.514190 10.186172 l -12.514190 10.186172 l -12.525421 10.350790 l -h -13.642213 10.330217 m -13.642213 10.495217 l -13.642211 10.495217 l -13.642213 10.330217 l -h -13.687787 10.330217 m -13.687789 10.495217 l -13.687787 10.495217 l -13.687787 10.330217 l -h -14.804579 10.350790 m -14.815810 10.186172 l -14.815810 10.186172 l -14.804579 10.350790 l -h -15.684851 10.533078 m -15.747993 10.380638 l -15.747993 10.380638 l -15.684851 10.533078 l -h -17.127140 11.975367 m -17.279579 11.912223 l -17.279581 11.912224 l -17.127140 11.975367 l -h -17.309427 12.855639 m -17.474045 12.844406 l -17.474045 12.844407 l -17.309427 12.855639 l -h -17.330000 13.972364 m -17.164999 13.972364 l -17.164999 13.972363 l -17.330000 13.972364 l -h -17.330000 14.018004 m -17.164999 14.018006 l -17.164999 14.018004 l -17.330000 14.018004 l -h -17.309427 15.134796 m -17.474045 15.146028 l -17.474045 15.146029 l -17.309427 15.134796 l -h -17.127140 16.015068 m -17.279581 16.078211 l -17.279579 16.078211 l -17.127140 16.015068 l -h -15.684851 17.457355 m -15.621708 17.304916 l -15.621708 17.304916 l -15.684851 17.457355 l -h -14.804579 17.639645 m -14.793347 17.475027 l -14.793347 17.475027 l -14.804579 17.639645 l -h -13.687795 17.660217 m -13.687795 17.495216 l -13.687797 17.495216 l -13.687795 17.660217 l -h -12.615956 16.312729 m -12.627188 16.148111 l -12.627188 16.148111 l -12.615956 16.312729 l -h -12.154118 16.228596 m -12.217260 16.076157 l -12.217260 16.076157 l -12.154118 16.228596 l -h -14.714045 16.312729 m -14.702813 16.148111 l -14.702813 16.148111 l -14.714045 16.312729 l -h -15.175882 16.228596 m -15.112740 16.076157 l -15.112740 16.076157 l -15.175882 16.228596 l -h -15.898379 15.506100 m -15.745939 15.442957 l -15.745939 15.442956 l -15.898379 15.506100 l -h -15.982512 15.044261 m -16.147129 15.055492 l -16.147129 15.055493 l -15.982512 15.044261 l -h -15.982512 12.946173 m -16.147129 12.934940 l -16.147129 12.934942 l -15.982512 12.946173 l -h -15.898379 12.484335 m -15.745939 12.547479 l -15.745939 12.547478 l -15.898379 12.484335 l -h -15.175882 11.761838 m -15.112740 11.914278 l -15.112740 11.914278 l -15.175882 11.761838 l -h -14.714045 11.677705 m -14.725276 11.513088 l -14.725277 11.513088 l -14.714045 11.677705 l -h -12.615956 11.677705 m -12.604724 11.513088 l -12.604725 11.513088 l -12.615956 11.677705 l -h -12.154118 11.761838 m -12.217260 11.914278 l -12.217260 11.914278 l -12.154118 11.761838 l -h -11.431621 12.484335 m -11.279181 12.421192 l -11.279181 12.421192 l -11.431621 12.484335 l -h -11.347487 12.946173 m -11.182870 12.934942 l -11.182870 12.934942 l -11.347487 12.946173 l -h -11.347487 15.044261 m -11.182870 15.055492 l -11.182870 15.055492 l -11.347487 15.044261 l -h -11.431621 15.506100 m -11.279181 15.569242 l -11.279181 15.569242 l -11.431621 15.506100 l -h -13.642205 7.660217 m -13.642203 7.495217 l -13.642205 7.495217 l -13.642205 7.660217 l -h -13.687795 7.660217 m -13.687795 7.495217 l -13.687797 7.495217 l -13.687795 7.660217 l -h -14.804579 7.639645 m -14.793346 7.475027 l -14.793347 7.475027 l -14.804579 7.639645 l -h -15.684851 7.457356 m -15.621708 7.304916 l -15.621708 7.304916 l -15.684851 7.457356 l -h -17.127140 6.015069 m -17.279581 6.078211 l -17.279579 6.078212 l -17.127140 6.015069 l -h -17.309427 5.134796 m -17.474045 5.146028 l -17.474045 5.146029 l -17.309427 5.134796 l -h -17.330000 4.018070 m -17.164999 4.018072 l -17.164999 4.018070 l -17.330000 4.018070 l -h -17.330000 3.972430 m -17.164999 3.972430 l -17.164999 3.972428 l -17.330000 3.972430 l -h -17.309427 2.855639 m -17.474045 2.844406 l -17.474045 2.844407 l -17.309427 2.855639 l -h -17.127140 1.975367 m -17.279579 1.912224 l -17.279581 1.912225 l -17.127140 1.975367 l -h -15.684851 0.533077 m -15.747993 0.380636 l -15.747993 0.380638 l -15.684851 0.533077 l -h -14.804579 0.350790 m -14.815810 0.186172 l -14.815811 0.186172 l -14.804579 0.350790 l -h -13.687853 0.330217 m -13.687855 0.495218 l -13.687853 0.495218 l -13.687853 0.330217 l -h -13.642213 0.330217 m -13.642213 0.495218 l -13.642211 0.495218 l -13.642213 0.330217 l -h -12.525421 0.350790 m -12.514189 0.186172 l -12.514190 0.186172 l -12.525421 0.350790 l -h -11.645148 0.533077 m -11.582006 0.380638 l -11.582006 0.380636 l -11.645148 0.533077 l -h -10.202861 1.975367 m -10.355301 2.038509 l -10.355301 2.038509 l -10.202861 1.975367 l -h -10.020573 2.855639 m -10.185190 2.866870 l -10.185190 2.866871 l -10.020573 2.855639 l -h -10.000000 3.972357 m -10.165000 3.972355 l -10.165000 3.972357 l -10.000000 3.972357 l -h -10.000000 4.018012 m -10.165000 4.018012 l -10.165000 4.018014 l -10.000000 4.018012 l -h -10.020573 5.134796 m -10.185190 5.123564 l -10.185190 5.123565 l -10.020573 5.134796 l -h -10.202861 6.015069 m -10.355301 5.951926 l -10.355301 5.951926 l -10.202861 6.015069 l -h -11.645148 7.457356 m -11.708291 7.304916 l -11.708291 7.304916 l -11.645148 7.457356 l -h -12.525421 7.639645 m -12.536653 7.475027 l -12.536654 7.475027 l -12.525421 7.639645 l -h -12.154118 6.228597 m -12.090976 6.381037 l -12.090975 6.381037 l -12.154118 6.228597 l -h -12.615956 6.312730 m -12.604725 6.477347 l -12.604725 6.477347 l -12.615956 6.312730 l -h -11.431621 5.506100 m -11.279181 5.569242 l -11.279181 5.569242 l -11.431621 5.506100 l -h -11.347487 5.044261 m -11.182870 5.055492 l -11.182870 5.055492 l -11.347487 5.044261 l -h -11.347487 2.946173 m -11.182870 2.934941 l -11.182870 2.934941 l -11.347487 2.946173 l -h -11.431621 2.484335 m -11.279181 2.421193 l -11.279181 2.421192 l -11.431621 2.484335 l -h -12.154118 1.761838 m -12.217261 1.914278 l -12.217260 1.914278 l -12.154118 1.761838 l -h -12.615956 1.677705 m -12.604724 1.513088 l -12.604725 1.513088 l -12.615956 1.677705 l -h -14.714045 1.677705 m -14.725276 1.513088 l -14.725277 1.513088 l -14.714045 1.677705 l -h -15.175882 1.761838 m -15.112740 1.914278 l -15.112739 1.914278 l -15.175882 1.761838 l -h -15.898379 2.484335 m -15.745939 2.547479 l -15.745939 2.547478 l -15.898379 2.484335 l -h -15.982512 2.946173 m -16.147129 2.934940 l -16.147129 2.934941 l -15.982512 2.946173 l -h -15.982512 5.044261 m -16.147129 5.055492 l -16.147129 5.055493 l -15.982512 5.044261 l -h -15.898379 5.506100 m -15.745939 5.442957 l -15.745939 5.442956 l -15.898379 5.506100 l -h -15.175882 6.228597 m -15.239025 6.381037 l -15.239024 6.381037 l -15.175882 6.228597 l -h -14.714045 6.312730 m -14.725276 6.477347 l -14.725276 6.477347 l -14.714045 6.312730 l -h -2.719497 4.722213 m -2.572481 4.797121 l -2.572481 4.797121 l -2.719497 4.722213 l -h -2.719497 3.268222 m -2.572481 3.193314 l -2.572481 3.193314 l -2.719497 3.268222 l -h -2.938005 3.049714 m -3.012913 3.196730 l -3.012913 3.196731 l -2.938005 3.049714 l -h -4.391995 3.049714 m -4.317087 3.196731 l -4.317087 3.196731 l -4.391995 3.049714 l -h -4.610503 3.268222 m -4.757519 3.193314 l -4.757519 3.193314 l -4.610503 3.268222 l -h -4.610503 4.722213 m -4.757519 4.797121 l -4.757519 4.797121 l -4.610503 4.722213 l -h -4.391995 4.940721 m -4.317087 4.793704 l -4.317087 4.793704 l -4.391995 4.940721 l -h -2.938005 4.940721 m -3.012913 4.793704 l -3.012913 4.793705 l -2.938005 4.940721 l -h -0.219497 2.222213 m -0.072481 2.297121 l -0.072481 2.297121 l -0.219497 2.222213 l -h -0.438005 2.440721 m -0.512913 2.293704 l -0.512913 2.293704 l -0.438005 2.440721 l -h -1.891995 2.440721 m -1.817087 2.293704 l -1.817087 2.293704 l -1.891995 2.440721 l -h -2.110503 2.222213 m -2.257520 2.297121 l -2.257519 2.297121 l -2.110503 2.222213 l -h -2.110503 0.768223 m -1.963488 0.843132 l -1.963487 0.843130 l -2.110503 0.768223 l -h -1.891995 0.549713 m -1.966904 0.402697 l -1.966905 0.402697 l -1.891995 0.549713 l -h -0.438005 0.549713 m -0.363096 0.402697 l -0.363096 0.402697 l -0.438005 0.549713 l -h -0.219497 0.768223 m -0.366513 0.843130 l -0.366513 0.843132 l -0.219497 0.768223 l -h -0.219497 7.222213 m -0.072481 7.297121 l -0.072481 7.297121 l -0.219497 7.222213 l -h -0.219497 5.768222 m -0.072481 5.693314 l -0.072481 5.693314 l -0.219497 5.768222 l -h -0.438005 5.549714 m -0.512913 5.696731 l -0.512913 5.696731 l -0.438005 5.549714 l -h -1.891995 5.549714 m -1.817087 5.696731 l -1.817087 5.696731 l -1.891995 5.549714 l -h -2.110503 5.768222 m -2.257519 5.693314 l -2.257520 5.693314 l -2.110503 5.768222 l -h -2.110503 7.222213 m -2.257520 7.297121 l -2.257519 7.297121 l -2.110503 7.222213 l -h -1.891995 7.440721 m -1.817087 7.293704 l -1.817087 7.293704 l -1.891995 7.440721 l -h -0.438005 7.440721 m -0.512913 7.293704 l -0.512913 7.293704 l -0.438005 7.440721 l -h -5.219497 7.222213 m -5.072481 7.297121 l -5.072481 7.297121 l -5.219497 7.222213 l -h -5.438004 7.440721 m -5.512913 7.293704 l -5.512913 7.293704 l -5.438004 7.440721 l -h -6.891995 7.440721 m -6.817087 7.293704 l -6.817087 7.293704 l -6.891995 7.440721 l -h -7.110503 7.222213 m -7.257519 7.297121 l -7.257519 7.297121 l -7.110503 7.222213 l -h -7.110503 5.768222 m -7.257519 5.693314 l -7.257519 5.693314 l -7.110503 5.768222 l -h -6.891995 5.549714 m -6.817087 5.696731 l -6.817087 5.696731 l -6.891995 5.549714 l -h -5.438004 5.549714 m -5.512913 5.696731 l -5.512913 5.696731 l -5.438004 5.549714 l -h -5.219497 5.768222 m -5.072481 5.693314 l -5.072481 5.693314 l -5.219497 5.768222 l -h -5.219497 2.222213 m -5.072481 2.297121 l -5.072481 2.297121 l -5.219497 2.222213 l -h -5.219497 0.768223 m -5.366513 0.843130 l -5.366512 0.843132 l -5.219497 0.768223 l -h -5.438004 0.549713 m -5.363095 0.402697 l -5.363096 0.402697 l -5.438004 0.549713 l -h -6.891995 0.549713 m -6.966904 0.402697 l -6.966905 0.402697 l -6.891995 0.549713 l -h -7.110503 0.768223 m -6.963488 0.843132 l -6.963487 0.843130 l -7.110503 0.768223 l -h -7.110503 2.222213 m -7.257519 2.297121 l -7.257519 2.297121 l -7.110503 2.222213 l -h -6.891995 2.440721 m -6.817087 2.293704 l -6.817087 2.293704 l -6.891995 2.440721 l -h -5.438004 2.440721 m -5.512913 2.293704 l -5.512913 2.293704 l -5.438004 2.440721 l -h -2.536653 17.475027 m -2.831474 17.495142 3.193613 17.495224 3.642203 17.495216 c -3.642208 17.825216 l -3.197656 17.825224 2.822733 17.825314 2.514190 17.804262 c -2.536653 17.475027 l -h -1.708292 17.304916 m -1.963168 17.410488 2.232327 17.454264 2.536653 17.475027 c -2.514190 17.804262 l -2.192971 17.782345 1.883741 17.734779 1.582006 17.609797 c -1.708292 17.304916 l -h -0.355301 15.951925 m -0.609036 16.564495 1.095721 17.051182 1.708292 17.304916 c -1.582006 17.609797 l -0.888577 17.322569 0.337649 16.771641 0.050421 16.078211 c -0.355301 15.951925 l -h -0.185190 15.123564 m -0.205954 15.427890 0.249728 15.697050 0.355301 15.951925 c -0.050421 16.078211 l --0.074562 15.776476 -0.122128 15.467246 -0.144045 15.146028 c -0.185190 15.123564 l -h -0.165000 14.018014 m -0.164994 14.466604 0.165075 14.828744 0.185190 15.123564 c --0.144045 15.146028 l --0.165096 14.837484 -0.165006 14.462562 -0.165000 14.018009 c -0.165000 14.018014 l -h -0.185190 12.866871 m -0.165075 13.161690 0.164994 13.523830 0.165000 13.972420 c --0.165000 13.972425 l --0.165006 13.527872 -0.165096 13.152950 -0.144045 12.844406 c -0.185190 12.866871 l -h -0.355301 12.038509 m -0.249728 12.293385 0.205954 12.562544 0.185190 12.866871 c --0.144045 12.844406 l --0.122128 12.523188 -0.074562 12.213959 0.050421 11.912224 c -0.355301 12.038509 l -h -1.708292 10.685518 m -1.095721 10.939253 0.609036 11.425939 0.355301 12.038509 c -0.050421 11.912224 l -0.337649 11.218794 0.888577 10.667866 1.582006 10.380638 c -1.708292 10.685518 l -h -2.536653 10.515408 m -2.232327 10.536171 1.963167 10.579946 1.708292 10.685518 c -1.582006 10.380638 l -1.883742 10.255655 2.192971 10.208090 2.514189 10.186172 c -2.536653 10.515408 l -h -3.642210 10.495217 m -3.193617 10.495211 2.831475 10.495292 2.536653 10.515408 c -2.514190 10.186172 l -2.822734 10.165121 3.197660 10.165211 3.642215 10.165217 c -3.642210 10.495217 l -h -4.793347 10.515408 m -4.498525 10.495292 4.136383 10.495211 3.687790 10.495217 c -3.687785 10.165217 l -4.132341 10.165211 4.507266 10.165121 4.815811 10.186172 c -4.793347 10.515408 l -h -5.621708 10.685518 m -5.366833 10.579946 5.097673 10.536171 4.793347 10.515408 c -4.815811 10.186172 l -5.137029 10.208090 5.446259 10.255655 5.747994 10.380638 c -5.621708 10.685518 l -h -6.974699 12.038509 m -6.720964 11.425939 6.234279 10.939253 5.621708 10.685518 c -5.747994 10.380638 l -6.441423 10.667866 6.992351 11.218794 7.279579 11.912224 c -6.974699 12.038509 l -h -7.144810 12.866871 m -7.124046 12.562544 7.080272 12.293385 6.974699 12.038509 c -7.279579 11.912224 l -7.404562 12.213959 7.452128 12.523188 7.474044 12.844406 c -7.144810 12.866871 l -h -7.165000 13.972427 m -7.165006 13.523834 7.164926 13.161692 7.144810 12.866871 c -7.474044 12.844406 l -7.495096 13.152952 7.495006 13.527876 7.495000 13.972432 c -7.165000 13.972427 l -h -7.144810 15.123564 m -7.164926 14.828742 7.165006 14.466600 7.165000 14.018007 c -7.495000 14.018002 l -7.495006 14.462558 7.495096 14.837483 7.474044 15.146028 c -7.144810 15.123564 l -h -6.974699 15.951925 m -7.080272 15.697050 7.124046 15.427890 7.144810 15.123564 c -7.474044 15.146028 l -7.452128 15.467246 7.404562 15.776476 7.279579 16.078211 c -6.974699 15.951925 l -h -5.621708 17.304916 m -6.234279 17.051182 6.720964 16.564495 6.974699 15.951925 c -7.279579 16.078211 l -6.992351 16.771641 6.441423 17.322569 5.747994 17.609797 c -5.621708 17.304916 l -h -4.793347 17.475027 m -5.097673 17.454264 5.366833 17.410488 5.621708 17.304916 c -5.747994 17.609797 l -5.446259 17.734779 5.137029 17.782345 4.815811 17.804262 c -4.793347 17.475027 l -h -3.687797 17.495216 m -4.136387 17.495224 4.498527 17.495142 4.793347 17.475027 c -4.815811 17.804262 l -4.507267 17.825314 4.132345 17.825224 3.687792 17.825216 c -3.687797 17.495216 l -h -2.604724 16.477346 m -2.351359 16.460060 2.203719 16.427736 2.090975 16.381037 c -2.217261 16.076157 l -2.282989 16.103382 2.390444 16.131960 2.627188 16.148111 c -2.604724 16.477346 l -h -3.665000 16.495216 m -3.192279 16.495216 2.862686 16.494947 2.604724 16.477346 c -2.627188 16.148111 l -2.871282 16.164766 3.187641 16.165216 3.665000 16.165216 c -3.665000 16.495216 l -h -4.725276 16.477346 m -4.467314 16.494947 4.137722 16.495216 3.665000 16.495216 c -3.665000 16.165216 l -4.142359 16.165216 4.458718 16.164766 4.702812 16.148111 c -4.725276 16.477346 l -h -5.239025 16.381037 m -5.126281 16.427736 4.978642 16.460060 4.725276 16.477346 c -4.702813 16.148111 l -4.939556 16.131960 5.047011 16.103382 5.112740 16.076157 c -5.239025 16.381037 l -h -6.050819 15.569242 m -5.898578 15.936785 5.606567 16.228796 5.239025 16.381037 c -5.112740 16.076157 l -5.399423 15.957408 5.627191 15.729639 5.745939 15.442957 c -6.050819 15.569242 l -h -6.147130 15.055493 m -6.129842 15.308859 6.097520 15.456497 6.050819 15.569242 c -5.745939 15.442957 l -5.773165 15.377228 5.801742 15.269773 5.817895 15.033030 c -6.147130 15.055493 l -h -6.165000 13.995217 m -6.165000 14.467938 6.164731 14.797531 6.147130 15.055492 c -5.817895 15.033030 l -5.834549 14.788935 5.835000 14.472576 5.835000 13.995217 c -6.165000 13.995217 l -h -6.147130 12.934941 m -6.164731 13.192904 6.165000 13.522495 6.165000 13.995217 c -5.835000 13.995217 l -5.835000 13.517858 5.834549 13.201500 5.817895 12.957405 c -6.147130 12.934941 l -h -6.050819 12.421192 m -6.097520 12.533937 6.129842 12.681576 6.147130 12.934941 c -5.817895 12.957405 l -5.801742 12.720661 5.773165 12.613206 5.745939 12.547478 c -6.050819 12.421192 l -h -5.239025 11.609398 m -5.606567 11.761639 5.898578 12.053650 6.050819 12.421192 c -5.745939 12.547478 l -5.627191 12.260795 5.399423 12.033026 5.112740 11.914278 c -5.239025 11.609398 l -h -4.725276 11.513088 m -4.978641 11.530375 5.126281 11.562697 5.239025 11.609398 c -5.112740 11.914278 l -5.047011 11.887053 4.939557 11.858475 4.702812 11.842321 c -4.725276 11.513088 l -h -3.665000 11.495217 m -4.137722 11.495217 4.467314 11.495487 4.725276 11.513088 c -4.702812 11.842321 l -4.458718 11.825668 4.142360 11.825217 3.665000 11.825217 c -3.665000 11.495217 l -h -2.604724 11.513088 m -2.862686 11.495487 3.192279 11.495217 3.665000 11.495217 c -3.665000 11.825217 l -3.187641 11.825217 2.871282 11.825668 2.627188 11.842321 c -2.604724 11.513088 l -h -2.090975 11.609398 m -2.203720 11.562697 2.351359 11.530375 2.604724 11.513088 c -2.627188 11.842321 l -2.390444 11.858475 2.282989 11.887053 2.217261 11.914278 c -2.090975 11.609398 l -h -1.279181 12.421192 m -1.431422 12.053650 1.723433 11.761639 2.090975 11.609398 c -2.217261 11.914278 l -1.930578 12.033026 1.702809 12.260795 1.584061 12.547478 c -1.279181 12.421192 l -h -1.182871 12.934942 m -1.200158 12.681576 1.232481 12.533936 1.279181 12.421192 c -1.584061 12.547478 l -1.556836 12.613207 1.528258 12.720661 1.512105 12.957405 c -1.182871 12.934942 l -h -1.165000 13.995217 m -1.165000 13.522495 1.165270 13.192904 1.182871 12.934941 c -1.512105 12.957405 l -1.495451 13.201500 1.495000 13.517859 1.495000 13.995217 c -1.165000 13.995217 l -h -1.182871 15.055492 m -1.165270 14.797531 1.165000 14.467938 1.165000 13.995217 c -1.495000 13.995217 l -1.495000 14.472576 1.495451 14.788935 1.512105 15.033030 c -1.182871 15.055492 l -h -1.279181 15.569242 m -1.232481 15.456498 1.200158 15.308859 1.182871 15.055492 c -1.512105 15.033030 l -1.528258 15.269773 1.556835 15.377228 1.584061 15.442957 c -1.279181 15.569242 l -h -2.090975 16.381037 m -1.723433 16.228796 1.431422 15.936785 1.279181 15.569242 c -1.584061 15.442957 l -1.702809 15.729639 1.930578 15.957408 2.217261 16.076157 c -2.090975 16.381037 l -h -12.536653 17.475027 m -12.831473 17.495142 13.193613 17.495224 13.642203 17.495216 c -13.642207 17.825216 l -13.197655 17.825224 12.822732 17.825314 12.514190 17.804262 c -12.536653 17.475027 l -h -11.708291 17.304916 m -11.963167 17.410488 12.232327 17.454264 12.536653 17.475027 c -12.514190 17.804262 l -12.192971 17.782345 11.883741 17.734779 11.582006 17.609797 c -11.708291 17.304916 l -h -10.355301 15.951925 m -10.609035 16.564495 11.095721 17.051182 11.708291 17.304916 c -11.582006 17.609797 l -10.888576 17.322569 10.337648 16.771641 10.050421 16.078211 c -10.355301 15.951925 l -h -10.185190 15.123564 m -10.205954 15.427891 10.249727 15.697050 10.355301 15.951925 c -10.050421 16.078211 l -9.925438 15.776476 9.877872 15.467246 9.855955 15.146028 c -10.185190 15.123564 l -h -10.165000 14.018079 m -10.164994 14.466640 10.165075 14.828758 10.185190 15.123564 c -9.855955 15.146029 l -9.834904 14.837500 9.834994 14.462597 9.835000 14.018074 c -10.165000 14.018079 l -h -10.185190 12.866871 m -10.165075 13.161676 10.164994 13.523858 10.165000 13.972421 c -9.835000 13.972425 l -9.834994 13.527902 9.834904 13.152935 9.855955 12.844406 c -10.185190 12.866871 l -h -10.355301 12.038509 m -10.249727 12.293385 10.205954 12.562544 10.185190 12.866871 c -9.855955 12.844406 l -9.877872 12.523188 9.925438 12.213959 10.050421 11.912224 c -10.355301 12.038509 l -h -11.708291 10.685518 m -11.095721 10.939253 10.609035 11.425939 10.355301 12.038509 c -10.050421 11.912224 l -10.337648 11.218794 10.888576 10.667866 11.582006 10.380638 c -11.708291 10.685518 l -h -12.536653 10.515408 m -12.232327 10.536171 11.963167 10.579946 11.708291 10.685518 c -11.582006 10.380638 l -11.883741 10.255655 12.192971 10.208090 12.514190 10.186172 c -12.536653 10.515408 l -h -13.642211 10.495217 m -13.193618 10.495211 12.831475 10.495292 12.536653 10.515408 c -12.514190 10.186172 l -12.822734 10.165121 13.197659 10.165211 13.642215 10.165217 c -13.642211 10.495217 l -h -14.793347 10.515408 m -14.498525 10.495292 14.136382 10.495211 13.687789 10.495217 c -13.687785 10.165217 l -14.132340 10.165211 14.507266 10.165121 14.815810 10.186172 c -14.793347 10.515408 l -h -15.621708 10.685518 m -15.366833 10.579946 15.097673 10.536171 14.793347 10.515408 c -14.815810 10.186172 l -15.137030 10.208090 15.446259 10.255655 15.747993 10.380638 c -15.621708 10.685518 l -h -16.974701 12.038509 m -16.720964 11.425939 16.234278 10.939253 15.621708 10.685518 c -15.747993 10.380638 l -16.441423 10.667866 16.992350 11.218794 17.279579 11.912223 c -16.974701 12.038509 l -h -17.144810 12.866871 m -17.124044 12.562544 17.080271 12.293385 16.974699 12.038507 c -17.279581 11.912224 l -17.404562 12.213959 17.452127 12.523188 17.474045 12.844406 c -17.144810 12.866871 l -h -17.164999 13.972363 m -17.165005 13.523798 17.164925 13.161678 17.144810 12.866870 c -17.474045 12.844407 l -17.495094 13.152937 17.495007 13.527840 17.495001 13.972366 c -17.164999 13.972363 l -h -17.144810 15.123565 m -17.164925 14.828756 17.165005 14.466571 17.164999 14.018006 c -17.495001 14.018003 l -17.495007 14.462528 17.495094 14.837498 17.474045 15.146028 c -17.144810 15.123565 l -h -16.974699 15.951926 m -17.080271 15.697050 17.124044 15.427890 17.144810 15.123564 c -17.474045 15.146029 l -17.452127 15.467246 17.404562 15.776475 17.279581 16.078211 c -16.974699 15.951926 l -h -15.621708 17.304916 m -16.234278 17.051182 16.720964 16.564495 16.974701 15.951925 c -17.279579 16.078211 l -16.992350 16.771641 16.441423 17.322569 15.747993 17.609797 c -15.621708 17.304916 l -h -14.793347 17.475027 m -15.097673 17.454264 15.366833 17.410488 15.621708 17.304916 c -15.747993 17.609797 l -15.446259 17.734779 15.137030 17.782345 14.815810 17.804262 c -14.793347 17.475027 l -h -13.687797 17.495216 m -14.136387 17.495224 14.498527 17.495142 14.793347 17.475027 c -14.815810 17.804262 l -14.507268 17.825314 14.132345 17.825224 13.687793 17.825216 c -13.687797 17.495216 l -h -12.604725 16.477346 m -12.351358 16.460060 12.203719 16.427736 12.090975 16.381037 c -12.217260 16.076157 l -12.282989 16.103382 12.390444 16.131960 12.627188 16.148111 c -12.604725 16.477346 l -h -13.665000 16.495216 m -13.192278 16.495216 12.862685 16.494947 12.604725 16.477346 c -12.627188 16.148111 l -12.871282 16.164766 13.187641 16.165216 13.665000 16.165216 c -13.665000 16.495216 l -h -14.725276 16.477346 m -14.467315 16.494947 14.137722 16.495216 13.665000 16.495216 c -13.665000 16.165216 l -14.142359 16.165216 14.458718 16.164766 14.702813 16.148111 c -14.725276 16.477346 l -h -15.239025 16.381037 m -15.126281 16.427736 14.978642 16.460060 14.725276 16.477346 c -14.702813 16.148111 l -14.939556 16.131960 15.047011 16.103382 15.112740 16.076157 c -15.239025 16.381037 l -h -16.050819 15.569242 m -15.898578 15.936785 15.606567 16.228796 15.239025 16.381037 c -15.112740 16.076157 l -15.399422 15.957408 15.627191 15.729639 15.745939 15.442957 c -16.050819 15.569242 l -h -16.147129 15.055493 m -16.129843 15.308857 16.097521 15.456497 16.050819 15.569242 c -15.745939 15.442956 l -15.773165 15.377228 15.801742 15.269774 15.817895 15.033029 c -16.147129 15.055493 l -h -16.165001 13.995217 m -16.165001 14.467938 16.164730 14.797531 16.147129 15.055492 c -15.817895 15.033030 l -15.834549 14.788935 15.835000 14.472576 15.835000 13.995217 c -16.165001 13.995217 l -h -16.147129 12.934942 m -16.164730 13.192904 16.165001 13.522495 16.165001 13.995217 c -15.835000 13.995217 l -15.835000 13.517859 15.834549 13.201500 15.817895 12.957405 c -16.147129 12.934942 l -h -16.050819 12.421191 m -16.097521 12.533937 16.129843 12.681578 16.147129 12.934940 c -15.817895 12.957405 l -15.801742 12.720659 15.773165 12.613206 15.745939 12.547479 c -16.050819 12.421191 l -h -15.239025 11.609398 m -15.606567 11.761639 15.898578 12.053650 16.050819 12.421192 c -15.745939 12.547478 l -15.627191 12.260795 15.399422 12.033026 15.112740 11.914278 c -15.239025 11.609398 l -h -14.725277 11.513088 m -14.978642 11.530375 15.126281 11.562697 15.239025 11.609398 c -15.112740 11.914278 l -15.047011 11.887053 14.939556 11.858475 14.702812 11.842321 c -14.725277 11.513088 l -h -13.665000 11.495217 m -14.137721 11.495217 14.467315 11.495487 14.725276 11.513088 c -14.702813 11.842321 l -14.458718 11.825668 14.142360 11.825217 13.665000 11.825217 c -13.665000 11.495217 l -h -12.604725 11.513088 m -12.862685 11.495487 13.192279 11.495217 13.665000 11.495217 c -13.665000 11.825217 l -13.187640 11.825217 12.871282 11.825668 12.627188 11.842321 c -12.604725 11.513088 l -h -12.090975 11.609398 m -12.203719 11.562697 12.351358 11.530375 12.604724 11.513088 c -12.627189 11.842321 l -12.390444 11.858475 12.282989 11.887053 12.217260 11.914278 c -12.090975 11.609398 l -h -11.279181 12.421192 m -11.431421 12.053650 11.723433 11.761639 12.090975 11.609398 c -12.217260 11.914278 l -11.930577 12.033026 11.702808 12.260795 11.584061 12.547478 c -11.279181 12.421192 l -h -11.182870 12.934942 m -11.200157 12.681576 11.232481 12.533936 11.279181 12.421192 c -11.584061 12.547478 l -11.556835 12.613207 11.528257 12.720661 11.512105 12.957405 c -11.182870 12.934942 l -h -11.165000 13.995217 m -11.165000 13.522495 11.165270 13.192904 11.182870 12.934942 c -11.512105 12.957405 l -11.495451 13.201500 11.495000 13.517859 11.495000 13.995217 c -11.165000 13.995217 l -h -11.182870 15.055492 m -11.165270 14.797531 11.165000 14.467938 11.165000 13.995217 c -11.495000 13.995217 l -11.495000 14.472576 11.495451 14.788935 11.512105 15.033030 c -11.182870 15.055492 l -h -11.279181 15.569242 m -11.232481 15.456498 11.200157 15.308859 11.182870 15.055492 c -11.512105 15.033030 l -11.528257 15.269773 11.556835 15.377228 11.584061 15.442957 c -11.279181 15.569242 l -h -12.090975 16.381037 m -11.723433 16.228796 11.431421 15.936785 11.279181 15.569242 c -11.584061 15.442957 l -11.702808 15.729639 11.930577 15.957408 12.217260 16.076157 c -12.090975 16.381037 l -h -14.815811 7.804262 m -14.507282 7.825314 14.132315 7.825223 13.687793 7.825217 c -13.687797 7.495217 l -14.136358 7.495223 14.498541 7.495142 14.793346 7.475027 c -14.815811 7.804262 l -h -15.747993 7.609797 m -15.446259 7.734779 15.137030 7.782345 14.815810 7.804262 c -14.793347 7.475027 l -15.097673 7.454264 15.366833 7.410490 15.621708 7.304916 c -15.747993 7.609797 l -h -17.279579 6.078212 m -16.992350 6.771642 16.441423 7.322569 15.747993 7.609797 c -15.621708 7.304916 l -16.234278 7.051182 16.720964 6.564496 16.974701 5.951926 c -17.279579 6.078212 l -h -17.474045 5.146029 m -17.452127 5.467246 17.404562 5.776476 17.279581 6.078211 c -16.974699 5.951927 l -17.080271 5.697050 17.124044 5.427890 17.144810 5.123564 c -17.474045 5.146029 l -h -17.495001 4.018068 m -17.495007 4.462594 17.495094 4.837498 17.474045 5.146028 c -17.144810 5.123565 l -17.164925 4.828756 17.165005 4.466636 17.164999 4.018072 c -17.495001 4.018068 l -h -17.474045 2.844407 m -17.495094 3.152937 17.495007 3.527906 17.495001 3.972432 c -17.164999 3.972428 l -17.165005 3.523865 17.164925 3.161678 17.144810 2.866870 c -17.474045 2.844407 l -h -17.279581 1.912225 m -17.404562 2.213959 17.452127 2.523188 17.474045 2.844406 c -17.144810 2.866871 l -17.124044 2.562544 17.080271 2.293385 16.974699 2.038508 c -17.279581 1.912225 l -h -15.747993 0.380638 m -16.441423 0.667868 16.992350 1.218794 17.279579 1.912224 c -16.974701 2.038509 l -16.720964 1.425940 16.234278 0.939253 15.621708 0.685516 c -15.747993 0.380638 l -h -14.815811 0.186172 m -15.137030 0.208090 15.446259 0.255655 15.747993 0.380636 c -15.621709 0.685518 l -15.366833 0.579947 15.097673 0.536173 14.793346 0.515408 c -14.815811 0.186172 l -h -13.687851 0.165216 m -14.132377 0.165211 14.507280 0.165123 14.815810 0.186172 c -14.793347 0.515408 l -14.498539 0.495293 14.136418 0.495213 13.687855 0.495218 c -13.687851 0.165216 l -h -12.514190 0.186172 m -12.822720 0.165123 13.197689 0.165211 13.642215 0.165216 c -13.642211 0.495218 l -13.193647 0.495213 12.831461 0.495293 12.536653 0.515408 c -12.514190 0.186172 l -h -11.582006 0.380636 m -11.883741 0.255655 12.192971 0.208090 12.514189 0.186172 c -12.536654 0.515408 l -12.232327 0.536173 11.963167 0.579947 11.708290 0.685518 c -11.582006 0.380636 l -h -10.050421 1.912224 m -10.337648 1.218794 10.888576 0.667868 11.582006 0.380638 c -11.708291 0.685516 l -11.095721 0.939253 10.609035 1.425940 10.355301 2.038509 c -10.050421 1.912224 l -h -9.855955 2.844407 m -9.877872 2.523188 9.925438 2.213959 10.050421 1.912224 c -10.355301 2.038509 l -10.249727 2.293385 10.205954 2.562544 10.185190 2.866870 c -9.855955 2.844407 l -h -9.835000 3.972359 m -9.834994 3.527836 9.834904 3.152935 9.855955 2.844406 c -10.185190 2.866871 l -10.165075 3.161676 10.164994 3.523794 10.165000 3.972355 c -9.835000 3.972359 l -h -9.855955 5.146029 m -9.834904 4.837500 9.834994 4.462532 9.835000 4.018010 c -10.165000 4.018014 l -10.164994 4.466576 10.165075 4.828758 10.185190 5.123564 c -9.855955 5.146029 l -h -10.050421 6.078212 m -9.925438 5.776476 9.877872 5.467246 9.855955 5.146028 c -10.185190 5.123565 l -10.205954 5.427890 10.249727 5.697050 10.355301 5.951926 c -10.050421 6.078212 l -h -11.582006 7.609797 m -10.888576 7.322569 10.337648 6.771642 10.050421 6.078212 c -10.355301 5.951926 l -10.609035 6.564496 11.095721 7.051182 11.708291 7.304916 c -11.582006 7.609797 l -h -12.514190 7.804262 m -12.192971 7.782345 11.883741 7.734779 11.582006 7.609797 c -11.708291 7.304916 l -11.963167 7.410490 12.232327 7.454264 12.536653 7.475027 c -12.514190 7.804262 l -h -13.642207 7.825217 m -13.197685 7.825223 12.822718 7.825314 12.514189 7.804262 c -12.536654 7.475027 l -12.831459 7.495142 13.193642 7.495223 13.642203 7.495217 c -13.642207 7.825217 l -h -12.217259 6.076157 m -12.282989 6.103382 12.390444 6.131960 12.627188 6.148112 c -12.604725 6.477347 l -12.351358 6.460060 12.203719 6.427736 12.090976 6.381037 c -12.217259 6.076157 l -h -11.584061 5.442957 m -11.702808 5.729639 11.930578 5.957409 12.217260 6.076157 c -12.090975 6.381037 l -11.723433 6.228796 11.431421 5.936785 11.279181 5.569242 c -11.584061 5.442957 l -h -11.512105 5.033030 m -11.528257 5.269773 11.556835 5.377229 11.584061 5.442958 c -11.279181 5.569242 l -11.232481 5.456498 11.200157 5.308859 11.182870 5.055492 c -11.512105 5.033030 l -h -11.495000 3.995217 m -11.495000 4.472576 11.495451 4.788936 11.512105 5.033030 c -11.182870 5.055492 l -11.165270 4.797532 11.165000 4.467939 11.165000 3.995217 c -11.495000 3.995217 l -h -11.512105 2.957404 m -11.495451 3.201499 11.495000 3.517859 11.495000 3.995217 c -11.165000 3.995217 l -11.165000 3.522495 11.165270 3.192903 11.182870 2.934941 c -11.512105 2.957404 l -h -11.584061 2.547477 m -11.556835 2.613206 11.528257 2.720661 11.512105 2.957404 c -11.182870 2.934941 l -11.200157 2.681576 11.232481 2.533937 11.279181 2.421193 c -11.584061 2.547477 l -h -12.217260 1.914278 m -11.930577 2.033027 11.702808 2.260796 11.584061 2.547478 c -11.279181 2.421192 l -11.431421 2.053650 11.723433 1.761640 12.090975 1.609398 c -12.217260 1.914278 l -h -12.627189 1.842322 m -12.390443 1.858476 12.282989 1.887053 12.217261 1.914278 c -12.090974 1.609398 l -12.203719 1.562696 12.351359 1.530375 12.604724 1.513088 c -12.627189 1.842322 l -h -13.665000 1.825217 m -13.187641 1.825217 12.871282 1.825668 12.627188 1.842322 c -12.604725 1.513088 l -12.862685 1.495487 13.192278 1.495216 13.665000 1.495216 c -13.665000 1.825217 l -h -14.702813 1.842322 m -14.458718 1.825668 14.142359 1.825217 13.665000 1.825217 c -13.665000 1.495216 l -14.137722 1.495216 14.467315 1.495487 14.725276 1.513088 c -14.702813 1.842322 l -h -15.112739 1.914278 m -15.047011 1.887053 14.939557 1.858476 14.702812 1.842322 c -14.725277 1.513088 l -14.978641 1.530375 15.126281 1.562696 15.239026 1.609398 c -15.112739 1.914278 l -h -15.745939 2.547478 m -15.627191 2.260796 15.399422 2.033027 15.112740 1.914278 c -15.239025 1.609398 l -15.606567 1.761640 15.898578 2.053650 16.050819 2.421192 c -15.745939 2.547478 l -h -15.817895 2.957405 m -15.801742 2.720660 15.773165 2.613206 15.745939 2.547479 c -16.050819 2.421191 l -16.097521 2.533937 16.129843 2.681577 16.147129 2.934940 c -15.817895 2.957405 l -h -15.835000 3.995217 m -15.835000 3.517859 15.834549 3.201499 15.817895 2.957404 c -16.147129 2.934941 l -16.164730 3.192903 16.165001 3.522495 16.165001 3.995217 c -15.835000 3.995217 l -h -15.817895 5.033030 m -15.834549 4.788936 15.835000 4.472576 15.835000 3.995217 c -16.165001 3.995217 l -16.165001 4.467939 16.164730 4.797532 16.147129 5.055492 c -15.817895 5.033030 l -h -15.745939 5.442956 m -15.773165 5.377229 15.801742 5.269774 15.817895 5.033029 c -16.147129 5.055493 l -16.129843 5.308858 16.097521 5.456498 16.050819 5.569243 c -15.745939 5.442956 l -h -15.112740 6.076157 m -15.399422 5.957409 15.627191 5.729640 15.745939 5.442957 c -16.050819 5.569242 l -15.898578 5.936784 15.606567 6.228796 15.239025 6.381037 c -15.112740 6.076157 l -h -14.702813 6.148112 m -14.939556 6.131960 15.047011 6.103382 15.112741 6.076157 c -15.239024 6.381037 l -15.126281 6.427736 14.978642 6.460060 14.725276 6.477347 c -14.702813 6.148112 l -h -13.665000 6.165217 m -14.142359 6.165217 14.458718 6.164766 14.702813 6.148112 c -14.725276 6.477347 l -14.467315 6.494947 14.137722 6.495217 13.665000 6.495217 c -13.665000 6.165217 l -h -12.627188 6.148112 m -12.871282 6.164766 13.187641 6.165217 13.665000 6.165217 c -13.665000 6.495217 l -13.192278 6.495217 12.862685 6.494947 12.604725 6.477347 c -12.627188 6.148112 l -h -2.830000 4.195217 m -2.830000 4.337953 2.830128 4.435081 2.836264 4.510180 c -2.842243 4.583352 2.853092 4.620963 2.866513 4.647305 c -2.572481 4.797121 l -2.531405 4.716506 2.515006 4.630632 2.507360 4.537052 c -2.499872 4.445399 2.500000 4.332508 2.500000 4.195217 c -2.830000 4.195217 l -h -2.830000 3.795218 m -2.830000 4.195217 l -2.500000 4.195217 l -2.500000 3.795218 l -2.830000 3.795218 l -h -2.866513 3.343130 m -2.853092 3.369472 2.842243 3.407083 2.836264 3.480255 c -2.830128 3.555353 2.830000 3.652482 2.830000 3.795218 c -2.500000 3.795218 l -2.500000 3.657927 2.499872 3.545035 2.507360 3.453382 c -2.515006 3.359802 2.531405 3.273929 2.572481 3.193314 c -2.866513 3.343130 l -h -3.012913 3.196731 m -2.949879 3.228848 2.898631 3.280096 2.866513 3.343130 c -2.572481 3.193314 l -2.636237 3.068186 2.737968 2.966454 2.863097 2.902698 c -3.012913 3.196731 l -h -3.465000 3.160217 m -3.322264 3.160217 3.225136 3.160346 3.150038 3.166481 c -3.076865 3.172460 3.039254 3.183309 3.012913 3.196730 c -2.863096 2.902699 l -2.943712 2.861623 3.029585 2.845223 3.123165 2.837578 c -3.214818 2.830089 3.327710 2.830217 3.465000 2.830217 c -3.465000 3.160217 l -h -3.865000 3.160217 m -3.465000 3.160217 l -3.465000 2.830217 l -3.865000 2.830217 l -3.865000 3.160217 l -h -4.317087 3.196731 m -4.290746 3.183309 4.253135 3.172460 4.179963 3.166481 c -4.104865 3.160346 4.007736 3.160217 3.865000 3.160217 c -3.865000 2.830217 l -4.002291 2.830217 4.115182 2.830089 4.206835 2.837578 c -4.300415 2.845223 4.386289 2.861623 4.466904 2.902698 c -4.317087 3.196731 l -h -4.463487 3.343130 m -4.431370 3.280096 4.380121 3.228848 4.317087 3.196731 c -4.466904 2.902698 l -4.592031 2.966454 4.693764 3.068186 4.757519 3.193314 c -4.463487 3.343130 l -h -4.500000 3.795218 m -4.500000 3.652482 4.499872 3.555353 4.493736 3.480255 c -4.487757 3.407083 4.476908 3.369472 4.463487 3.343130 c -4.757519 3.193314 l -4.798595 3.273929 4.814994 3.359802 4.822640 3.453382 c -4.830128 3.545035 4.830000 3.657927 4.830000 3.795218 c -4.500000 3.795218 l -h -4.500000 4.195217 m -4.500000 3.795218 l -4.830000 3.795218 l -4.830000 4.195217 l -4.500000 4.195217 l -h -4.463487 4.647305 m -4.476908 4.620963 4.487757 4.583352 4.493736 4.510180 c -4.499872 4.435081 4.500000 4.337953 4.500000 4.195217 c -4.830000 4.195217 l -4.830000 4.332508 4.830128 4.445399 4.822640 4.537052 c -4.814994 4.630632 4.798595 4.716506 4.757519 4.797121 c -4.463487 4.647305 l -h -4.317087 4.793704 m -4.380121 4.761587 4.431370 4.710339 4.463487 4.647305 c -4.757519 4.797121 l -4.693764 4.922249 4.592031 5.023981 4.466904 5.087737 c -4.317087 4.793704 l -h -3.865000 4.830217 m -4.007736 4.830217 4.104865 4.830089 4.179963 4.823954 c -4.253135 4.817975 4.290746 4.807126 4.317087 4.793704 c -4.466904 5.087737 l -4.386289 5.128812 4.300415 5.145211 4.206835 5.152857 c -4.115182 5.160346 4.002291 5.160217 3.865000 5.160217 c -3.865000 4.830217 l -h -3.465000 4.830217 m -3.865000 4.830217 l -3.865000 5.160217 l -3.465000 5.160217 l -3.465000 4.830217 l -h -3.012913 4.793705 m -3.039254 4.807126 3.076865 4.817975 3.150038 4.823954 c -3.225136 4.830089 3.322264 4.830217 3.465000 4.830217 c -3.465000 5.160217 l -3.327710 5.160217 3.214818 5.160346 3.123165 5.152857 c -3.029585 5.145211 2.943712 5.128812 2.863096 5.087736 c -3.012913 4.793705 l -h -2.866513 4.647305 m -2.898631 4.710339 2.949879 4.761587 3.012913 4.793704 c -2.863097 5.087737 l -2.737968 5.023981 2.636237 4.922249 2.572481 4.797121 c -2.866513 4.647305 l -h -0.072481 2.297121 m -0.031405 2.216506 0.015006 2.130632 0.007360 2.037052 c --0.000128 1.945399 0.000000 1.832508 0.000000 1.695217 c -0.330000 1.695217 l -0.330000 1.837953 0.330128 1.935081 0.336264 2.010180 c -0.342243 2.083352 0.353092 2.120963 0.366513 2.147305 c -0.072481 2.297121 l -h -0.363097 2.587737 m -0.237969 2.523981 0.136237 2.422249 0.072481 2.297121 c -0.366513 2.147305 l -0.398631 2.210339 0.449879 2.261587 0.512913 2.293704 c -0.363097 2.587737 l -h -0.965000 2.660217 m -0.827710 2.660217 0.714818 2.660346 0.623165 2.652857 c -0.529585 2.645211 0.443712 2.628812 0.363096 2.587737 c -0.512913 2.293704 l -0.539254 2.307126 0.576865 2.317975 0.650037 2.323954 c -0.725136 2.330089 0.822264 2.330217 0.965000 2.330217 c -0.965000 2.660217 l -h -1.365000 2.660217 m -0.965000 2.660217 l -0.965000 2.330217 l -1.365000 2.330217 l -1.365000 2.660217 l -h -1.966904 2.587737 m -1.886289 2.628812 1.800415 2.645211 1.706836 2.652857 c -1.615182 2.660346 1.502291 2.660217 1.365000 2.660217 c -1.365000 2.330217 l -1.507736 2.330217 1.604864 2.330089 1.679963 2.323954 c -1.753135 2.317975 1.790746 2.307126 1.817087 2.293704 c -1.966904 2.587737 l -h -2.257519 2.297121 m -2.193764 2.422249 2.092032 2.523981 1.966904 2.587737 c -1.817087 2.293704 l -1.880121 2.261587 1.931370 2.210339 1.963487 2.147305 c -2.257519 2.297121 l -h -2.330000 1.695217 m -2.330000 1.832508 2.330128 1.945399 2.322640 2.037052 c -2.314994 2.130632 2.298595 2.216506 2.257520 2.297121 c -1.963487 2.147305 l -1.976909 2.120963 1.987758 2.083352 1.993736 2.010180 c -1.999872 1.935081 2.000000 1.837953 2.000000 1.695217 c -2.330000 1.695217 l -h -2.330000 1.295218 m -2.330000 1.695217 l -2.000000 1.695217 l -2.000000 1.295218 l -2.330000 1.295218 l -h -2.257519 0.693314 m -2.298595 0.773928 2.314994 0.859802 2.322640 0.953382 c -2.330128 1.045034 2.330000 1.157927 2.330000 1.295218 c -2.000000 1.295218 l -2.000000 1.152481 1.999872 1.055353 1.993736 0.980253 c -1.987758 0.907082 1.976909 0.869473 1.963488 0.843132 c -2.257519 0.693314 l -h -1.966905 0.402697 m -2.092032 0.466454 2.193764 0.568186 2.257520 0.693316 c -1.963487 0.843130 l -1.931370 0.780096 1.880121 0.728848 1.817086 0.696730 c -1.966905 0.402697 l -h -1.365000 0.330215 m -1.502291 0.330215 1.615182 0.330088 1.706836 0.337576 c -1.800415 0.345222 1.886289 0.361622 1.966904 0.402697 c -1.817087 0.696730 l -1.790746 0.683308 1.753135 0.672459 1.679963 0.666481 c -1.604864 0.660345 1.507736 0.660217 1.365000 0.660217 c -1.365000 0.330215 l -h -0.965000 0.330215 m -1.365000 0.330215 l -1.365000 0.660217 l -0.965000 0.660217 l -0.965000 0.330215 l -h -0.363096 0.402697 m -0.443712 0.361622 0.529585 0.345222 0.623165 0.337576 c -0.714818 0.330088 0.827710 0.330215 0.965000 0.330215 c -0.965000 0.660217 l -0.822264 0.660217 0.725136 0.660345 0.650037 0.666481 c -0.576865 0.672459 0.539254 0.683308 0.512913 0.696730 c -0.363096 0.402697 l -h -0.072481 0.693316 m -0.136236 0.568186 0.237968 0.466454 0.363096 0.402697 c -0.512914 0.696730 l -0.449879 0.728848 0.398631 0.780096 0.366513 0.843130 c -0.072481 0.693316 l -h -0.000000 1.295218 m -0.000000 1.157927 -0.000128 1.045034 0.007360 0.953382 c -0.015006 0.859802 0.031405 0.773928 0.072481 0.693314 c -0.366513 0.843132 l -0.353092 0.869473 0.342243 0.907082 0.336264 0.980253 c -0.330128 1.055353 0.330000 1.152481 0.330000 1.295218 c -0.000000 1.295218 l -h -0.000000 1.695217 m -0.000000 1.295218 l -0.330000 1.295218 l -0.330000 1.695217 l -0.000000 1.695217 l -h -0.330000 6.695217 m -0.330000 6.837953 0.330128 6.935081 0.336264 7.010180 c -0.342243 7.083352 0.353092 7.120963 0.366513 7.147305 c -0.072481 7.297121 l -0.031405 7.216506 0.015006 7.130632 0.007360 7.037052 c --0.000128 6.945399 0.000000 6.832508 0.000000 6.695217 c -0.330000 6.695217 l -h -0.330000 6.295218 m -0.330000 6.695217 l -0.000000 6.695217 l -0.000000 6.295218 l -0.330000 6.295218 l -h -0.366513 5.843130 m -0.353092 5.869472 0.342243 5.907083 0.336264 5.980255 c -0.330128 6.055353 0.330000 6.152482 0.330000 6.295218 c -0.000000 6.295218 l -0.000000 6.157927 -0.000128 6.045035 0.007360 5.953382 c -0.015006 5.859802 0.031405 5.773929 0.072481 5.693314 c -0.366513 5.843130 l -h -0.512913 5.696731 m -0.449879 5.728848 0.398631 5.780096 0.366513 5.843130 c -0.072481 5.693314 l -0.136237 5.568186 0.237969 5.466454 0.363097 5.402698 c -0.512913 5.696731 l -h -0.965000 5.660217 m -0.822264 5.660217 0.725136 5.660346 0.650037 5.666481 c -0.576865 5.672460 0.539254 5.683309 0.512913 5.696731 c -0.363096 5.402698 l -0.443712 5.361623 0.529585 5.345223 0.623165 5.337578 c -0.714818 5.330089 0.827710 5.330217 0.965000 5.330217 c -0.965000 5.660217 l -h -1.365000 5.660217 m -0.965000 5.660217 l -0.965000 5.330217 l -1.365000 5.330217 l -1.365000 5.660217 l -h -1.817087 5.696731 m -1.790746 5.683309 1.753135 5.672460 1.679963 5.666481 c -1.604864 5.660346 1.507736 5.660217 1.365000 5.660217 c -1.365000 5.330217 l -1.502291 5.330217 1.615182 5.330089 1.706836 5.337578 c -1.800415 5.345223 1.886289 5.361623 1.966904 5.402698 c -1.817087 5.696731 l -h -1.963487 5.843130 m -1.931370 5.780096 1.880121 5.728848 1.817087 5.696731 c -1.966904 5.402698 l -2.092032 5.466454 2.193764 5.568186 2.257519 5.693314 c -1.963487 5.843130 l -h -2.000000 6.295218 m -2.000000 6.152482 1.999872 6.055353 1.993736 5.980255 c -1.987758 5.907083 1.976909 5.869472 1.963487 5.843130 c -2.257520 5.693314 l -2.298595 5.773929 2.314994 5.859802 2.322640 5.953382 c -2.330128 6.045035 2.330000 6.157927 2.330000 6.295218 c -2.000000 6.295218 l -h -2.000000 6.695217 m -2.000000 6.295218 l -2.330000 6.295218 l -2.330000 6.695217 l -2.000000 6.695217 l -h -1.963487 7.147305 m -1.976909 7.120963 1.987758 7.083352 1.993736 7.010180 c -1.999872 6.935081 2.000000 6.837953 2.000000 6.695217 c -2.330000 6.695217 l -2.330000 6.832508 2.330128 6.945399 2.322640 7.037052 c -2.314994 7.130632 2.298595 7.216506 2.257520 7.297121 c -1.963487 7.147305 l -h -1.817087 7.293704 m -1.880121 7.261587 1.931370 7.210339 1.963487 7.147305 c -2.257519 7.297121 l -2.193764 7.422249 2.092032 7.523981 1.966904 7.587737 c -1.817087 7.293704 l -h -1.365000 7.330217 m -1.507736 7.330217 1.604864 7.330089 1.679963 7.323954 c -1.753135 7.317975 1.790746 7.307126 1.817087 7.293704 c -1.966904 7.587737 l -1.886289 7.628812 1.800415 7.645211 1.706836 7.652857 c -1.615182 7.660346 1.502291 7.660217 1.365000 7.660217 c -1.365000 7.330217 l -h -0.965000 7.330217 m -1.365000 7.330217 l -1.365000 7.660217 l -0.965000 7.660217 l -0.965000 7.330217 l -h -0.512913 7.293704 m -0.539254 7.307126 0.576865 7.317975 0.650037 7.323954 c -0.725136 7.330089 0.822264 7.330217 0.965000 7.330217 c -0.965000 7.660217 l -0.827710 7.660217 0.714818 7.660346 0.623165 7.652857 c -0.529585 7.645211 0.443712 7.628812 0.363096 7.587737 c -0.512913 7.293704 l -h -0.366513 7.147305 m -0.398631 7.210339 0.449879 7.261587 0.512913 7.293704 c -0.363097 7.587737 l -0.237969 7.523981 0.136237 7.422249 0.072481 7.297121 c -0.366513 7.147305 l -h -5.072481 7.297121 m -5.031405 7.216506 5.015006 7.130632 5.007360 7.037052 c -4.999872 6.945399 5.000000 6.832508 5.000000 6.695217 c -5.330000 6.695217 l -5.330000 6.837953 5.330128 6.935081 5.336264 7.010180 c -5.342243 7.083352 5.353092 7.120963 5.366513 7.147305 c -5.072481 7.297121 l -h -5.363096 7.587737 m -5.237968 7.523981 5.136236 7.422249 5.072481 7.297121 c -5.366513 7.147305 l -5.398630 7.210339 5.449879 7.261587 5.512913 7.293704 c -5.363096 7.587737 l -h -5.965000 7.660217 m -5.827710 7.660217 5.714818 7.660346 5.623165 7.652857 c -5.529585 7.645211 5.443711 7.628812 5.363096 7.587737 c -5.512913 7.293704 l -5.539254 7.307126 5.576865 7.317975 5.650037 7.323954 c -5.725136 7.330089 5.822264 7.330217 5.965000 7.330217 c -5.965000 7.660217 l -h -6.365000 7.660217 m -5.965000 7.660217 l -5.965000 7.330217 l -6.365000 7.330217 l -6.365000 7.660217 l -h -6.966904 7.587737 m -6.886289 7.628812 6.800415 7.645211 6.706835 7.652857 c -6.615181 7.660346 6.502290 7.660217 6.365000 7.660217 c -6.365000 7.330217 l -6.507736 7.330217 6.604864 7.330089 6.679963 7.323954 c -6.753135 7.317975 6.790746 7.307126 6.817087 7.293704 c -6.966904 7.587737 l -h -7.257519 7.297121 m -7.193764 7.422249 7.092031 7.523981 6.966904 7.587737 c -6.817087 7.293704 l -6.880121 7.261587 6.931370 7.210339 6.963487 7.147305 c -7.257519 7.297121 l -h -7.330000 6.695217 m -7.330000 6.832508 7.330128 6.945399 7.322640 7.037052 c -7.314994 7.130632 7.298595 7.216506 7.257519 7.297121 c -6.963487 7.147305 l -6.976908 7.120963 6.987757 7.083352 6.993736 7.010180 c -6.999872 6.935081 7.000000 6.837953 7.000000 6.695217 c -7.330000 6.695217 l -h -7.330000 6.295218 m -7.330000 6.695217 l -7.000000 6.695217 l -7.000000 6.295218 l -7.330000 6.295218 l -h -7.257519 5.693314 m -7.298595 5.773929 7.314994 5.859802 7.322640 5.953382 c -7.330128 6.045035 7.330000 6.157927 7.330000 6.295218 c -7.000000 6.295218 l -7.000000 6.152482 6.999872 6.055353 6.993736 5.980255 c -6.987757 5.907083 6.976908 5.869472 6.963487 5.843130 c -7.257519 5.693314 l -h -6.966904 5.402698 m -7.092031 5.466454 7.193764 5.568186 7.257519 5.693314 c -6.963487 5.843130 l -6.931370 5.780096 6.880121 5.728848 6.817087 5.696731 c -6.966904 5.402698 l -h -6.365000 5.330217 m -6.502290 5.330217 6.615181 5.330089 6.706835 5.337578 c -6.800415 5.345223 6.886289 5.361623 6.966904 5.402698 c -6.817087 5.696731 l -6.790746 5.683309 6.753135 5.672460 6.679963 5.666481 c -6.604864 5.660346 6.507736 5.660217 6.365000 5.660217 c -6.365000 5.330217 l -h -5.965000 5.330217 m -6.365000 5.330217 l -6.365000 5.660217 l -5.965000 5.660217 l -5.965000 5.330217 l -h -5.363096 5.402698 m -5.443711 5.361623 5.529585 5.345223 5.623165 5.337578 c -5.714818 5.330089 5.827710 5.330217 5.965000 5.330217 c -5.965000 5.660217 l -5.822264 5.660217 5.725136 5.660346 5.650037 5.666481 c -5.576865 5.672460 5.539254 5.683309 5.512913 5.696731 c -5.363096 5.402698 l -h -5.072481 5.693314 m -5.136236 5.568186 5.237968 5.466454 5.363096 5.402698 c -5.512913 5.696731 l -5.449879 5.728848 5.398630 5.780096 5.366513 5.843130 c -5.072481 5.693314 l -h -5.000000 6.295218 m -5.000000 6.157927 4.999872 6.045035 5.007360 5.953382 c -5.015006 5.859802 5.031405 5.773929 5.072481 5.693314 c -5.366513 5.843130 l -5.353092 5.869472 5.342243 5.907083 5.336264 5.980255 c -5.330128 6.055353 5.330000 6.152482 5.330000 6.295218 c -5.000000 6.295218 l -h -5.000000 6.695217 m -5.000000 6.295218 l -5.330000 6.295218 l -5.330000 6.695217 l -5.000000 6.695217 l -h -5.330000 1.695217 m -5.330000 1.837953 5.330128 1.935081 5.336264 2.010180 c -5.342243 2.083352 5.353092 2.120963 5.366513 2.147305 c -5.072481 2.297121 l -5.031405 2.216506 5.015006 2.130632 5.007360 2.037052 c -4.999872 1.945399 5.000000 1.832508 5.000000 1.695217 c -5.330000 1.695217 l -h -5.330000 1.295218 m -5.330000 1.695217 l -5.000000 1.695217 l -5.000000 1.295218 l -5.330000 1.295218 l -h -5.366512 0.843132 m -5.353092 0.869473 5.342243 0.907082 5.336264 0.980253 c -5.330128 1.055353 5.330000 1.152481 5.330000 1.295218 c -5.000000 1.295218 l -5.000000 1.157927 4.999872 1.045034 5.007360 0.953382 c -5.015006 0.859802 5.031405 0.773928 5.072481 0.693314 c -5.366512 0.843132 l -h -5.512914 0.696730 m -5.449879 0.728848 5.398631 0.780096 5.366513 0.843130 c -5.072480 0.693316 l -5.136236 0.568186 5.237968 0.466454 5.363095 0.402697 c -5.512914 0.696730 l -h -5.965000 0.660217 m -5.822264 0.660217 5.725136 0.660345 5.650037 0.666481 c -5.576865 0.672459 5.539254 0.683308 5.512913 0.696730 c -5.363096 0.402697 l -5.443711 0.361622 5.529585 0.345222 5.623165 0.337576 c -5.714818 0.330088 5.827710 0.330215 5.965000 0.330215 c -5.965000 0.660217 l -h -6.365000 0.660217 m -5.965000 0.660217 l -5.965000 0.330215 l -6.365000 0.330215 l -6.365000 0.660217 l -h -6.817087 0.696730 m -6.790746 0.683308 6.753135 0.672459 6.679963 0.666481 c -6.604864 0.660345 6.507736 0.660217 6.365000 0.660217 c -6.365000 0.330215 l -6.502290 0.330215 6.615181 0.330088 6.706835 0.337576 c -6.800415 0.345222 6.886289 0.361622 6.966904 0.402697 c -6.817087 0.696730 l -h -6.963487 0.843130 m -6.931369 0.780096 6.880121 0.728848 6.817086 0.696730 c -6.966905 0.402697 l -7.092032 0.466454 7.193764 0.568186 7.257520 0.693316 c -6.963487 0.843130 l -h -7.000000 1.295218 m -7.000000 1.152481 6.999872 1.055353 6.993736 0.980253 c -6.987757 0.907082 6.976908 0.869473 6.963488 0.843132 c -7.257519 0.693314 l -7.298595 0.773928 7.314994 0.859802 7.322640 0.953382 c -7.330128 1.045034 7.330000 1.157927 7.330000 1.295218 c -7.000000 1.295218 l -h -7.000000 1.695217 m -7.000000 1.295218 l -7.330000 1.295218 l -7.330000 1.695217 l -7.000000 1.695217 l -h -6.963487 2.147305 m -6.976908 2.120963 6.987757 2.083352 6.993736 2.010180 c -6.999872 1.935081 7.000000 1.837953 7.000000 1.695217 c -7.330000 1.695217 l -7.330000 1.832508 7.330128 1.945399 7.322640 2.037052 c -7.314994 2.130632 7.298595 2.216506 7.257519 2.297121 c -6.963487 2.147305 l -h -6.817087 2.293704 m -6.880121 2.261587 6.931370 2.210339 6.963487 2.147305 c -7.257519 2.297121 l -7.193764 2.422249 7.092031 2.523981 6.966904 2.587737 c -6.817087 2.293704 l -h -6.365000 2.330217 m -6.507736 2.330217 6.604864 2.330089 6.679963 2.323954 c -6.753135 2.317975 6.790746 2.307126 6.817087 2.293704 c -6.966904 2.587737 l -6.886289 2.628812 6.800415 2.645211 6.706835 2.652857 c -6.615181 2.660346 6.502290 2.660217 6.365000 2.660217 c -6.365000 2.330217 l -h -5.965000 2.330217 m -6.365000 2.330217 l -6.365000 2.660217 l -5.965000 2.660217 l -5.965000 2.330217 l -h -5.512913 2.293704 m -5.539254 2.307126 5.576865 2.317975 5.650037 2.323954 c -5.725136 2.330089 5.822264 2.330217 5.965000 2.330217 c -5.965000 2.660217 l -5.827710 2.660217 5.714818 2.660346 5.623165 2.652857 c -5.529585 2.645211 5.443711 2.628812 5.363096 2.587737 c -5.512913 2.293704 l -h -5.366513 2.147305 m -5.398630 2.210339 5.449879 2.261587 5.512913 2.293704 c -5.363096 2.587737 l -5.237968 2.523981 5.136236 2.422249 5.072481 2.297121 c -5.366513 2.147305 l -h -3.642206 17.495216 m -3.687795 17.495216 l -3.687795 17.825216 l -3.642206 17.825216 l -3.642206 17.495216 l -h -0.165000 13.972423 m -0.165000 14.018011 l --0.165000 14.018011 l --0.165000 13.972423 l -0.165000 13.972423 l -h -3.687788 10.495217 m -3.642213 10.495217 l -3.642213 10.165217 l -3.687788 10.165217 l -3.687788 10.495217 l -h -7.165000 14.018004 m -7.165000 13.972429 l -7.495000 13.972429 l -7.495000 14.018004 l -7.165000 14.018004 l -h -13.642205 17.495216 m -13.687795 17.495216 l -13.687795 17.825216 l -13.642205 17.825216 l -13.642205 17.495216 l -h -10.165000 13.972423 m -10.165000 14.018077 l -9.835000 14.018077 l -9.835000 13.972423 l -10.165000 13.972423 l -h -13.687787 10.495217 m -13.642213 10.495217 l -13.642213 10.165217 l -13.687787 10.165217 l -13.687787 10.495217 l -h -17.164999 14.018004 m -17.164999 13.972364 l -17.495001 13.972364 l -17.495001 14.018004 l -17.164999 14.018004 l -h -13.687795 7.825217 m -13.642205 7.825217 l -13.642205 7.495217 l -13.687795 7.495217 l -13.687795 7.825217 l -h -17.495001 3.972430 m -17.495001 4.018070 l -17.164999 4.018070 l -17.164999 3.972430 l -17.495001 3.972430 l -h -13.642213 0.165216 m -13.687853 0.165216 l -13.687853 0.495218 l -13.642213 0.495218 l -13.642213 0.165216 l -h -9.835000 4.018012 m -9.835000 3.972357 l -10.165000 3.972357 l -10.165000 4.018012 l -9.835000 4.018012 l -h -f -n -Q - -endstream -endobj - -3 0 obj - 66886 -endobj - -4 0 obj - << /Annots [] - /Type /Page - /MediaBox [ 0.000000 0.000000 24.000000 24.000000 ] - /Resources 1 0 R - /Contents 2 0 R - /Parent 5 0 R - >> -endobj - -5 0 obj - << /Kids [ 4 0 R ] - /Count 1 - /Type /Pages - >> -endobj - -6 0 obj - << /Pages 5 0 R - /Type /Catalog - >> -endobj - -xref -0 7 -0000000000 65535 f -0000000010 00000 n -0000000034 00000 n -0000066976 00000 n -0000067000 00000 n -0000067173 00000 n -0000067247 00000 n -trailer -<< /ID [ (some) (id) ] - /Root 6 0 R - /Size 7 ->> -startxref -67306 -%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Contents.json index 25896a7b9c..82b8057094 100644 --- a/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Contents.json @@ -1,7 +1,7 @@ { "images" : [ { - "filename" : "Icon-33.pdf", + "filename" : "qr_30.pdf", "idiom" : "universal" } ], diff --git a/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Icon-33.pdf b/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Icon-33.pdf deleted file mode 100644 index cccf0afccc..0000000000 --- a/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/Icon-33.pdf +++ /dev/null @@ -1,359 +0,0 @@ -%PDF-1.7 - -1 0 obj - << >> -endobj - -2 0 obj - << /Length 3 0 R >> -stream -/DeviceRGB CS -/DeviceRGB cs -q -1.000000 0.000000 -0.000000 1.000000 5.334961 5.334961 cm -0.000000 0.478431 1.000000 scn -3.865000 19.330078 m -3.837526 19.330078 l -3.837508 19.330078 l -3.300819 19.330086 2.857976 19.330093 2.497252 19.300621 c -2.122624 19.270012 1.778399 19.204330 1.455116 19.039610 c -0.953664 18.784107 0.545971 18.376413 0.290469 17.874962 c -0.125748 17.551680 0.060066 17.207455 0.029458 16.832825 c --0.000015 16.472101 -0.000008 16.029259 0.000000 15.492570 c -0.000000 15.492552 l -0.000000 15.465077 l -0.000000 14.365076 l -0.000000 14.337603 l -0.000000 14.337584 l --0.000008 13.800896 -0.000015 13.358052 0.029458 12.997328 c -0.060066 12.622700 0.125748 12.278475 0.290469 11.955193 c -0.545971 11.453741 0.953664 11.046047 1.455116 10.790545 c -1.778399 10.625824 2.122624 10.560143 2.497253 10.529534 c -2.857977 10.500062 3.300822 10.500069 3.837512 10.500077 c -3.837542 10.500077 l -3.865001 10.500077 l -4.965002 10.500077 l -4.992460 10.500077 l -4.992490 10.500077 l -5.529181 10.500069 5.972024 10.500062 6.332750 10.529534 c -6.707378 10.560143 7.051603 10.625824 7.374886 10.790545 c -7.876338 11.046047 8.284031 11.453741 8.539533 11.955193 c -8.704254 12.278475 8.769936 12.622700 8.800544 12.997328 c -8.830016 13.358053 8.830009 13.800898 8.830001 14.337588 c -8.830001 14.337618 l -8.830001 14.365078 l -8.830001 15.465077 l -8.830001 15.492537 l -8.830001 15.492566 l -8.830009 16.029257 8.830016 16.472101 8.800544 16.832825 c -8.769936 17.207455 8.704254 17.551680 8.539533 17.874962 c -8.284031 18.376413 7.876338 18.784107 7.374886 19.039610 c -7.051603 19.204330 6.707378 19.270012 6.332750 19.300621 c -5.972026 19.330093 5.529183 19.330086 4.992494 19.330078 c -4.992475 19.330078 l -4.965001 19.330078 l -3.865000 19.330078 l -h -2.058923 17.854570 m -2.163464 17.907837 2.313177 17.951149 2.605557 17.975037 c -2.905700 17.999559 3.293975 18.000076 3.865000 18.000076 c -4.965001 18.000076 l -5.536026 18.000076 5.924302 17.999559 6.224445 17.975037 c -6.516825 17.951149 6.666538 17.907837 6.771079 17.854570 c -7.022274 17.726580 7.226504 17.522350 7.354495 17.271154 c -7.407761 17.166615 7.451073 17.016901 7.474962 16.724522 c -7.499484 16.424377 7.500001 16.036102 7.500001 15.465077 c -7.500001 14.365078 l -7.500001 13.794052 7.499484 13.405777 7.474962 13.105633 c -7.451073 12.813253 7.407761 12.663540 7.354495 12.559000 c -7.226504 12.307804 7.022274 12.103575 6.771079 11.975584 c -6.666538 11.922318 6.516825 11.879005 6.224445 11.855116 c -5.924302 11.830595 5.536027 11.830076 4.965002 11.830076 c -3.865001 11.830076 l -3.293975 11.830076 2.905700 11.830595 2.605557 11.855116 c -2.313177 11.879005 2.163464 11.922318 2.058923 11.975584 c -1.807727 12.103575 1.603498 12.307804 1.475507 12.559000 c -1.422241 12.663540 1.378929 12.813253 1.355041 13.105633 c -1.330518 13.405777 1.330001 13.794050 1.330001 14.365076 c -1.330001 15.465077 l -1.330001 16.036102 1.330518 16.424377 1.355041 16.724522 c -1.378929 17.016901 1.422241 17.166615 1.475507 17.271154 c -1.603498 17.522350 1.807727 17.726580 2.058923 17.854570 c -h -14.365001 19.330078 m -14.337526 19.330078 l -14.337506 19.330078 l -13.800819 19.330086 13.357977 19.330093 12.997252 19.300621 c -12.622624 19.270012 12.278399 19.204330 11.955116 19.039610 c -11.453664 18.784107 11.045971 18.376413 10.790468 17.874962 c -10.625748 17.551680 10.560066 17.207455 10.529458 16.832825 c -10.499986 16.472103 10.499992 16.029264 10.500001 15.492581 c -10.500001 15.492552 l -10.500001 15.465077 l -10.500001 14.365076 l -10.500001 14.337603 l -10.500001 14.337572 l -10.499992 13.800890 10.499986 13.358049 10.529458 12.997328 c -10.560066 12.622700 10.625748 12.278475 10.790468 11.955193 c -11.045971 11.453741 11.453664 11.046047 11.955116 10.790545 c -12.278399 10.625824 12.622624 10.560143 12.997252 10.529534 c -13.357978 10.500062 13.800820 10.500069 14.337513 10.500077 c -14.337542 10.500077 l -14.365001 10.500077 l -15.465001 10.500077 l -15.492460 10.500077 l -15.492489 10.500077 l -16.029179 10.500069 16.472023 10.500062 16.832748 10.529534 c -17.207378 10.560143 17.551603 10.625824 17.874886 10.790545 c -18.376337 11.046047 18.784031 11.453741 19.039534 11.955193 c -19.204254 12.278475 19.269936 12.622700 19.300545 12.997328 c -19.330015 13.358044 19.330009 13.800873 19.330002 14.337545 c -19.330002 14.337618 l -19.330002 14.365078 l -19.330002 15.465077 l -19.330002 15.492537 l -19.330002 15.492609 l -19.330009 16.029282 19.330015 16.472111 19.300545 16.832825 c -19.269936 17.207455 19.204254 17.551680 19.039534 17.874962 c -18.784031 18.376413 18.376337 18.784107 17.874886 19.039610 c -17.551603 19.204330 17.207378 19.270012 16.832748 19.300621 c -16.472025 19.330093 16.029184 19.330086 15.492496 19.330078 c -15.492476 19.330078 l -15.465000 19.330078 l -14.365001 19.330078 l -h -12.558924 17.854570 m -12.663465 17.907837 12.813177 17.951149 13.105556 17.975037 c -13.405701 17.999559 13.793975 18.000076 14.365001 18.000076 c -15.465000 18.000076 l -16.036026 18.000076 16.424301 17.999559 16.724445 17.975037 c -17.016825 17.951149 17.166538 17.907837 17.271078 17.854570 c -17.522274 17.726580 17.726503 17.522350 17.854494 17.271154 c -17.907761 17.166615 17.951073 17.016901 17.974960 16.724522 c -17.999483 16.424377 18.000000 16.036102 18.000000 15.465077 c -18.000000 14.365078 l -18.000000 13.794052 17.999483 13.405777 17.974960 13.105633 c -17.951073 12.813253 17.907761 12.663540 17.854494 12.559000 c -17.726503 12.307804 17.522274 12.103575 17.271078 11.975584 c -17.166538 11.922318 17.016825 11.879005 16.724445 11.855116 c -16.424301 11.830595 16.036026 11.830076 15.465001 11.830076 c -14.365001 11.830076 l -13.793976 11.830076 13.405701 11.830595 13.105557 11.855116 c -12.813177 11.879005 12.663465 11.922318 12.558924 11.975584 c -12.307727 12.103575 12.103498 12.307804 11.975508 12.559000 c -11.922241 12.663540 11.878929 12.813253 11.855041 13.105633 c -11.830518 13.405777 11.830001 13.794050 11.830001 14.365076 c -11.830001 15.465077 l -11.830001 16.036102 11.830518 16.424377 11.855041 16.724522 c -11.878929 17.016901 11.922241 17.166615 11.975508 17.271154 c -12.103498 17.522350 12.307727 17.726580 12.558924 17.854570 c -h -14.337526 8.830077 m -14.365001 8.830077 l -15.465000 8.830077 l -15.492476 8.830077 l -15.492504 8.830077 l -16.029188 8.830086 16.472029 8.830092 16.832748 8.800620 c -17.207378 8.770012 17.551603 8.704330 17.874886 8.539610 c -18.376337 8.284107 18.784031 7.876414 19.039534 7.374962 c -19.204254 7.051679 19.269936 6.707454 19.300545 6.332826 c -19.330015 5.972111 19.330009 5.529281 19.330002 4.992608 c -19.330002 4.992537 l -19.330002 4.965077 l -19.330002 3.865078 l -19.330002 3.837619 l -19.330002 3.837547 l -19.330009 3.300873 19.330015 2.858044 19.300545 2.497330 c -19.269936 2.122700 19.204254 1.778475 19.039534 1.455193 c -18.784031 0.953741 18.376337 0.546047 17.874886 0.290545 c -17.551603 0.125824 17.207378 0.060143 16.832748 0.029533 c -16.472034 0.000063 16.029202 0.000069 15.492532 0.000076 c -15.492460 0.000076 l -15.465001 0.000076 l -14.365001 0.000076 l -14.337542 0.000076 l -14.337470 0.000076 l -13.800797 0.000069 13.357968 0.000063 12.997252 0.029533 c -12.622624 0.060143 12.278399 0.125824 11.955116 0.290545 c -11.453664 0.546047 11.045971 0.953741 10.790468 1.455193 c -10.625748 1.778475 10.560066 2.122700 10.529458 2.497330 c -10.499986 2.858051 10.499992 3.300890 10.500001 3.837574 c -10.500001 3.837603 l -10.500001 3.865077 l -10.500001 4.965077 l -10.500001 4.992552 l -10.500001 4.992580 l -10.499992 5.529266 10.499986 5.972104 10.529458 6.332826 c -10.560066 6.707454 10.625748 7.051679 10.790468 7.374962 c -11.045971 7.876414 11.453664 8.284107 11.955116 8.539610 c -12.278399 8.704330 12.622624 8.770012 12.997252 8.800620 c -13.357974 8.830092 13.800812 8.830086 14.337498 8.830077 c -14.337526 8.830077 l -h -13.105556 7.475038 m -12.813177 7.451149 12.663465 7.407837 12.558924 7.354570 c -12.307727 7.226580 12.103498 7.022351 11.975508 6.771154 c -11.922241 6.666614 11.878929 6.516901 11.855041 6.224521 c -11.830518 5.924377 11.830001 5.536102 11.830001 4.965077 c -11.830001 3.865077 l -11.830001 3.294052 11.830518 2.905777 11.855041 2.605633 c -11.878929 2.313253 11.922241 2.163540 11.975508 2.059000 c -12.103498 1.807804 12.307727 1.603575 12.558924 1.475584 c -12.663465 1.422318 12.813177 1.379005 13.105557 1.355118 c -13.405701 1.330595 13.793976 1.330078 14.365001 1.330078 c -15.465001 1.330078 l -16.036026 1.330078 16.424301 1.330595 16.724445 1.355118 c -17.016825 1.379005 17.166538 1.422318 17.271078 1.475584 c -17.522274 1.603575 17.726503 1.807804 17.854494 2.059000 c -17.907761 2.163540 17.951073 2.313253 17.974960 2.605633 c -17.999483 2.905777 18.000000 3.294052 18.000000 3.865078 c -18.000000 4.965077 l -18.000000 5.536103 17.999483 5.924377 17.974960 6.224522 c -17.951073 6.516901 17.907761 6.666614 17.854494 6.771154 c -17.726503 7.022351 17.522274 7.226580 17.271078 7.354570 c -17.166538 7.407837 17.016825 7.451149 16.724445 7.475038 c -16.424301 7.499560 16.036026 7.500077 15.465000 7.500077 c -14.365001 7.500077 l -13.793975 7.500077 13.405701 7.499560 13.105556 7.475038 c -h -0.665001 7.365077 m -0.665001 7.645103 0.665001 7.785116 0.719498 7.892073 c -0.767434 7.986154 0.843925 8.062644 0.938006 8.110580 c -1.044962 8.165077 1.184975 8.165077 1.465001 8.165077 c -1.865001 8.165077 l -2.145027 8.165077 2.285040 8.165077 2.391996 8.110580 c -2.486077 8.062644 2.562567 7.986154 2.610504 7.892073 c -2.665001 7.785116 2.665001 7.645103 2.665001 7.365077 c -2.665001 6.965077 l -2.665001 6.685051 2.665001 6.545038 2.610504 6.438082 c -2.562567 6.344001 2.486077 6.267510 2.391996 6.219574 c -2.285040 6.165077 2.145027 6.165077 1.865001 6.165077 c -1.465001 6.165077 l -1.184975 6.165077 1.044962 6.165077 0.938006 6.219574 c -0.843925 6.267510 0.767434 6.344001 0.719498 6.438082 c -0.665001 6.545038 0.665001 6.685051 0.665001 6.965077 c -0.665001 7.365077 l -h -6.219498 7.892073 m -6.165001 7.785116 6.165001 7.645103 6.165001 7.365077 c -6.165001 6.965077 l -6.165001 6.685051 6.165001 6.545038 6.219498 6.438082 c -6.267435 6.344001 6.343925 6.267510 6.438006 6.219574 c -6.544961 6.165077 6.684975 6.165077 6.965002 6.165077 c -7.365001 6.165077 l -7.645028 6.165077 7.785040 6.165077 7.891997 6.219574 c -7.986078 6.267510 8.062568 6.344001 8.110504 6.438082 c -8.165001 6.545038 8.165001 6.685051 8.165001 6.965077 c -8.165001 7.365077 l -8.165001 7.645103 8.165001 7.785116 8.110504 7.892073 c -8.062568 7.986154 7.986078 8.062644 7.891997 8.110580 c -7.785040 8.165077 7.645028 8.165077 7.365001 8.165077 c -6.965002 8.165077 l -6.684975 8.165077 6.544961 8.165077 6.438006 8.110580 c -6.343925 8.062644 6.267435 7.986154 6.219498 7.892073 c -h -3.469498 5.142073 m -3.415001 5.035116 3.415001 4.895103 3.415001 4.615077 c -3.415001 4.215077 l -3.415001 3.935051 3.415001 3.795038 3.469498 3.688082 c -3.517434 3.594001 3.593925 3.517510 3.688006 3.469574 c -3.794961 3.415077 3.934975 3.415077 4.215001 3.415077 c -4.615001 3.415077 l -4.895028 3.415077 5.035040 3.415077 5.141997 3.469574 c -5.236078 3.517510 5.312568 3.594001 5.360505 3.688082 c -5.415001 3.795038 5.415001 3.935051 5.415001 4.215077 c -5.415001 4.615077 l -5.415001 4.895103 5.415001 5.035116 5.360505 5.142073 c -5.312568 5.236154 5.236078 5.312644 5.141997 5.360580 c -5.035040 5.415077 4.895028 5.415077 4.615001 5.415077 c -4.215001 5.415077 l -3.934975 5.415077 3.794961 5.415077 3.688006 5.360580 c -3.593925 5.312644 3.517434 5.236154 3.469498 5.142073 c -h -0.719498 2.392073 m -0.665001 2.285116 0.665001 2.145103 0.665001 1.865078 c -0.665001 1.465076 l -0.665001 1.185051 0.665001 1.045038 0.719498 0.938082 c -0.767434 0.844002 0.843925 0.767509 0.938006 0.719574 c -1.044962 0.665077 1.184975 0.665077 1.465001 0.665077 c -1.865001 0.665077 l -2.145027 0.665077 2.285040 0.665077 2.391996 0.719574 c -2.486077 0.767509 2.562567 0.844002 2.610504 0.938082 c -2.665001 1.045038 2.665001 1.185051 2.665001 1.465076 c -2.665001 1.865078 l -2.665001 2.145103 2.665001 2.285116 2.610504 2.392073 c -2.562567 2.486153 2.486077 2.562645 2.391996 2.610580 c -2.285040 2.665077 2.145027 2.665077 1.865001 2.665077 c -1.465001 2.665077 l -1.184975 2.665077 1.044962 2.665077 0.938006 2.610580 c -0.843925 2.562645 0.767434 2.486153 0.719498 2.392073 c -h -6.165001 1.865078 m -6.165001 2.145103 6.165001 2.285116 6.219498 2.392073 c -6.267435 2.486153 6.343925 2.562645 6.438006 2.610580 c -6.544961 2.665077 6.684975 2.665077 6.965002 2.665077 c -7.365001 2.665077 l -7.645028 2.665077 7.785040 2.665077 7.891997 2.610580 c -7.986078 2.562645 8.062568 2.486153 8.110504 2.392073 c -8.165001 2.285116 8.165001 2.145103 8.165001 1.865078 c -8.165001 1.465076 l -8.165001 1.185051 8.165001 1.045038 8.110504 0.938082 c -8.062568 0.844002 7.986078 0.767509 7.891997 0.719574 c -7.785040 0.665077 7.645028 0.665077 7.365001 0.665077 c -6.965002 0.665077 l -6.684975 0.665077 6.544961 0.665077 6.438006 0.719574 c -6.343925 0.767509 6.267435 0.844002 6.219498 0.938082 c -6.165001 1.045038 6.165001 1.185051 6.165001 1.465076 c -6.165001 1.865078 l -h -f* -n -Q - -endstream -endobj - -3 0 obj - 12876 -endobj - -4 0 obj - << /Annots [] - /Type /Page - /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] - /Resources 1 0 R - /Contents 2 0 R - /Parent 5 0 R - >> -endobj - -5 0 obj - << /Kids [ 4 0 R ] - /Count 1 - /Type /Pages - >> -endobj - -6 0 obj - << /Type /Catalog - /Pages 5 0 R - >> -endobj - -xref -0 7 -0000000000 65535 f -0000000010 00000 n -0000000034 00000 n -0000012966 00000 n -0000012990 00000 n -0000013163 00000 n -0000013237 00000 n -trailer -<< /ID [ (some) (id) ] - /Root 6 0 R - /Size 7 ->> -startxref -13296 -%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/qr_30.pdf b/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/qr_30.pdf new file mode 100644 index 0000000000..e8ea5ae44b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Settings/QrIcon.imageset/qr_30.pdf @@ -0,0 +1,357 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 5.334961 5.334961 cm +0.000000 0.000000 0.000000 scn +3.865000 19.330078 m +3.837526 19.330078 l +3.837508 19.330078 l +3.300819 19.330086 2.857976 19.330093 2.497252 19.300621 c +2.122624 19.270012 1.778399 19.204330 1.455116 19.039610 c +0.953664 18.784107 0.545971 18.376413 0.290469 17.874962 c +0.125748 17.551680 0.060066 17.207455 0.029458 16.832825 c +-0.000015 16.472101 -0.000008 16.029259 0.000000 15.492570 c +0.000000 15.492552 l +0.000000 15.465077 l +0.000000 14.365076 l +0.000000 14.337603 l +0.000000 14.337584 l +-0.000008 13.800896 -0.000015 13.358052 0.029458 12.997328 c +0.060066 12.622700 0.125748 12.278475 0.290469 11.955193 c +0.545971 11.453741 0.953664 11.046047 1.455116 10.790545 c +1.778399 10.625824 2.122624 10.560143 2.497253 10.529534 c +2.857977 10.500062 3.300822 10.500069 3.837512 10.500077 c +3.837542 10.500077 l +3.865001 10.500077 l +4.965002 10.500077 l +4.992460 10.500077 l +4.992490 10.500077 l +5.529181 10.500069 5.972024 10.500062 6.332750 10.529534 c +6.707378 10.560143 7.051603 10.625824 7.374886 10.790545 c +7.876338 11.046047 8.284031 11.453741 8.539533 11.955193 c +8.704254 12.278475 8.769936 12.622700 8.800544 12.997328 c +8.830016 13.358053 8.830009 13.800898 8.830001 14.337588 c +8.830001 14.337618 l +8.830001 14.365078 l +8.830001 15.465077 l +8.830001 15.492537 l +8.830001 15.492566 l +8.830009 16.029257 8.830016 16.472101 8.800544 16.832825 c +8.769936 17.207455 8.704254 17.551680 8.539533 17.874962 c +8.284031 18.376413 7.876338 18.784107 7.374886 19.039610 c +7.051603 19.204330 6.707378 19.270012 6.332750 19.300621 c +5.972026 19.330093 5.529183 19.330086 4.992494 19.330078 c +4.992475 19.330078 l +4.965001 19.330078 l +3.865000 19.330078 l +h +2.058923 17.854570 m +2.163464 17.907837 2.313177 17.951149 2.605557 17.975037 c +2.905700 17.999559 3.293975 18.000076 3.865000 18.000076 c +4.965001 18.000076 l +5.536026 18.000076 5.924302 17.999559 6.224445 17.975037 c +6.516825 17.951149 6.666538 17.907837 6.771079 17.854570 c +7.022274 17.726580 7.226504 17.522350 7.354495 17.271154 c +7.407761 17.166615 7.451073 17.016901 7.474962 16.724522 c +7.499484 16.424377 7.500001 16.036102 7.500001 15.465077 c +7.500001 14.365078 l +7.500001 13.794052 7.499484 13.405777 7.474962 13.105633 c +7.451073 12.813253 7.407761 12.663540 7.354495 12.559000 c +7.226504 12.307804 7.022274 12.103575 6.771079 11.975584 c +6.666538 11.922318 6.516825 11.879005 6.224445 11.855116 c +5.924302 11.830595 5.536027 11.830076 4.965002 11.830076 c +3.865001 11.830076 l +3.293975 11.830076 2.905700 11.830595 2.605557 11.855116 c +2.313177 11.879005 2.163464 11.922318 2.058923 11.975584 c +1.807727 12.103575 1.603498 12.307804 1.475507 12.559000 c +1.422241 12.663540 1.378929 12.813253 1.355041 13.105633 c +1.330518 13.405777 1.330001 13.794050 1.330001 14.365076 c +1.330001 15.465077 l +1.330001 16.036102 1.330518 16.424377 1.355041 16.724522 c +1.378929 17.016901 1.422241 17.166615 1.475507 17.271154 c +1.603498 17.522350 1.807727 17.726580 2.058923 17.854570 c +h +14.365001 19.330078 m +14.337526 19.330078 l +14.337506 19.330078 l +13.800819 19.330086 13.357977 19.330093 12.997252 19.300621 c +12.622624 19.270012 12.278399 19.204330 11.955116 19.039610 c +11.453664 18.784107 11.045971 18.376413 10.790468 17.874962 c +10.625748 17.551680 10.560066 17.207455 10.529458 16.832825 c +10.499986 16.472103 10.499992 16.029264 10.500001 15.492581 c +10.500001 15.492552 l +10.500001 15.465077 l +10.500001 14.365076 l +10.500001 14.337603 l +10.500001 14.337572 l +10.499992 13.800890 10.499986 13.358049 10.529458 12.997328 c +10.560066 12.622700 10.625748 12.278475 10.790468 11.955193 c +11.045971 11.453741 11.453664 11.046047 11.955116 10.790545 c +12.278399 10.625824 12.622624 10.560143 12.997252 10.529534 c +13.357978 10.500062 13.800820 10.500069 14.337513 10.500077 c +14.337542 10.500077 l +14.365001 10.500077 l +15.465001 10.500077 l +15.492460 10.500077 l +15.492489 10.500077 l +16.029179 10.500069 16.472023 10.500062 16.832748 10.529534 c +17.207378 10.560143 17.551603 10.625824 17.874886 10.790545 c +18.376337 11.046047 18.784031 11.453741 19.039534 11.955193 c +19.204254 12.278475 19.269936 12.622700 19.300545 12.997328 c +19.330015 13.358044 19.330009 13.800873 19.330002 14.337545 c +19.330002 14.337618 l +19.330002 14.365078 l +19.330002 15.465077 l +19.330002 15.492537 l +19.330002 15.492609 l +19.330009 16.029282 19.330015 16.472111 19.300545 16.832825 c +19.269936 17.207455 19.204254 17.551680 19.039534 17.874962 c +18.784031 18.376413 18.376337 18.784107 17.874886 19.039610 c +17.551603 19.204330 17.207378 19.270012 16.832748 19.300621 c +16.472025 19.330093 16.029184 19.330086 15.492496 19.330078 c +15.492476 19.330078 l +15.465000 19.330078 l +14.365001 19.330078 l +h +12.558924 17.854570 m +12.663465 17.907837 12.813177 17.951149 13.105556 17.975037 c +13.405701 17.999559 13.793975 18.000076 14.365001 18.000076 c +15.465000 18.000076 l +16.036026 18.000076 16.424301 17.999559 16.724445 17.975037 c +17.016825 17.951149 17.166538 17.907837 17.271078 17.854570 c +17.522274 17.726580 17.726503 17.522350 17.854494 17.271154 c +17.907761 17.166615 17.951073 17.016901 17.974960 16.724522 c +17.999483 16.424377 18.000000 16.036102 18.000000 15.465077 c +18.000000 14.365078 l +18.000000 13.794052 17.999483 13.405777 17.974960 13.105633 c +17.951073 12.813253 17.907761 12.663540 17.854494 12.559000 c +17.726503 12.307804 17.522274 12.103575 17.271078 11.975584 c +17.166538 11.922318 17.016825 11.879005 16.724445 11.855116 c +16.424301 11.830595 16.036026 11.830076 15.465001 11.830076 c +14.365001 11.830076 l +13.793976 11.830076 13.405701 11.830595 13.105557 11.855116 c +12.813177 11.879005 12.663465 11.922318 12.558924 11.975584 c +12.307727 12.103575 12.103498 12.307804 11.975508 12.559000 c +11.922241 12.663540 11.878929 12.813253 11.855041 13.105633 c +11.830518 13.405777 11.830001 13.794050 11.830001 14.365076 c +11.830001 15.465077 l +11.830001 16.036102 11.830518 16.424377 11.855041 16.724522 c +11.878929 17.016901 11.922241 17.166615 11.975508 17.271154 c +12.103498 17.522350 12.307727 17.726580 12.558924 17.854570 c +h +3.837565 8.830039 m +3.865039 8.830039 l +4.965039 8.830039 l +4.992514 8.830039 l +4.992544 8.830039 l +5.529227 8.830048 5.972066 8.830054 6.332788 8.800582 c +6.707416 8.769974 7.051641 8.704292 7.374924 8.539572 c +7.876376 8.284069 8.284069 7.876376 8.539572 7.374924 c +8.704292 7.051641 8.769974 6.707416 8.800582 6.332788 c +8.830054 5.972063 8.830048 5.529220 8.830039 4.992527 c +8.830039 4.992498 l +8.830039 4.965039 l +8.830039 3.865040 l +8.830039 3.837581 l +8.830039 3.837552 l +8.830048 3.300861 8.830054 2.858015 8.800582 2.497292 c +8.769974 2.122662 8.704292 1.778437 8.539572 1.455154 c +8.284069 0.953703 7.876376 0.546009 7.374924 0.290506 c +7.051641 0.125786 6.707417 0.060104 6.332788 0.029495 c +5.972073 0.000025 5.529243 0.000031 4.992571 0.000038 c +4.992498 0.000038 l +4.965040 0.000038 l +3.865039 0.000038 l +3.837580 0.000038 l +3.837507 0.000038 l +3.300836 0.000031 2.858006 0.000025 2.497291 0.029495 c +2.122663 0.060104 1.778437 0.125786 1.455155 0.290506 c +0.953703 0.546009 0.546009 0.953703 0.290507 1.455154 c +0.125786 1.778437 0.060105 2.122662 0.029496 2.497292 c +0.000024 2.858019 0.000031 3.300867 0.000039 3.837564 c +0.000039 3.865039 l +0.000039 4.965039 l +0.000039 4.992514 l +0.000031 5.529211 0.000024 5.972059 0.029496 6.332788 c +0.060105 6.707416 0.125786 7.051641 0.290507 7.374924 c +0.546009 7.876376 0.953703 8.284069 1.455155 8.539572 c +1.778437 8.704292 2.122663 8.769974 2.497291 8.800582 c +2.858012 8.830054 3.300851 8.830048 3.837535 8.830039 c +3.837565 8.830039 l +h +2.605595 7.474999 m +2.313215 7.451111 2.163502 7.407799 2.058962 7.354532 c +1.807766 7.226542 1.603537 7.022313 1.475546 6.771116 c +1.422280 6.666575 1.378968 6.516863 1.355079 6.224483 c +1.330557 5.924339 1.330039 5.536064 1.330039 4.965039 c +1.330039 3.865039 l +1.330039 3.294014 1.330557 2.905739 1.355079 2.605595 c +1.378968 2.313215 1.422280 2.163502 1.475546 2.058962 c +1.603537 1.807766 1.807766 1.603537 2.058962 1.475546 c +2.163502 1.422279 2.313215 1.378967 2.605595 1.355080 c +2.905739 1.330557 3.294014 1.330040 3.865039 1.330040 c +4.965040 1.330040 l +5.536066 1.330040 5.924340 1.330557 6.224483 1.355080 c +6.516863 1.378967 6.666576 1.422279 6.771117 1.475546 c +7.022313 1.603537 7.226542 1.807766 7.354533 2.058962 c +7.407799 2.163502 7.451111 2.313215 7.475000 2.605595 c +7.499522 2.905739 7.500040 3.294014 7.500040 3.865040 c +7.500040 4.965039 l +7.500040 5.536065 7.499522 5.924339 7.475000 6.224483 c +7.451111 6.516863 7.407799 6.666575 7.354533 6.771116 c +7.226542 7.022313 7.022313 7.226542 6.771117 7.354532 c +6.666576 7.407799 6.516863 7.451111 6.224483 7.474999 c +5.924340 7.499522 5.536065 7.500039 4.965039 7.500039 c +3.865039 7.500039 l +3.294014 7.500039 2.905739 7.499522 2.605595 7.474999 c +h +11.165039 7.365039 m +11.165039 7.645065 11.165039 7.785078 11.219536 7.892035 c +11.267472 7.986115 11.343963 8.062606 11.438044 8.110542 c +11.545000 8.165039 11.685013 8.165039 11.965039 8.165039 c +12.365039 8.165039 l +12.645065 8.165039 12.785078 8.165039 12.892035 8.110542 c +12.986115 8.062606 13.062606 7.986115 13.110542 7.892035 c +13.165039 7.785078 13.165039 7.645065 13.165039 7.365039 c +13.165039 6.965039 l +13.165039 6.685013 13.165039 6.545000 13.110542 6.438044 c +13.062606 6.343963 12.986115 6.267472 12.892035 6.219536 c +12.785078 6.165039 12.645065 6.165039 12.365039 6.165039 c +11.965039 6.165039 l +11.685013 6.165039 11.545000 6.165039 11.438044 6.219536 c +11.343963 6.267472 11.267472 6.343963 11.219536 6.438044 c +11.165039 6.545000 11.165039 6.685013 11.165039 6.965039 c +11.165039 7.365039 l +h +16.719536 7.892035 m +16.665039 7.785078 16.665039 7.645065 16.665039 7.365039 c +16.665039 6.965039 l +16.665039 6.685013 16.665039 6.545000 16.719536 6.438044 c +16.767471 6.343963 16.843964 6.267472 16.938044 6.219536 c +17.045000 6.165039 17.185013 6.165039 17.465038 6.165039 c +17.865040 6.165039 l +18.145065 6.165039 18.285078 6.165039 18.392035 6.219536 c +18.486115 6.267472 18.562607 6.343963 18.610542 6.438044 c +18.665039 6.545000 18.665039 6.685013 18.665039 6.965039 c +18.665039 7.365039 l +18.665039 7.645065 18.665039 7.785078 18.610542 7.892035 c +18.562607 7.986115 18.486115 8.062606 18.392035 8.110542 c +18.285078 8.165039 18.145065 8.165039 17.865040 8.165039 c +17.465038 8.165039 l +17.185013 8.165039 17.045000 8.165039 16.938044 8.110542 c +16.843964 8.062606 16.767471 7.986115 16.719536 7.892035 c +h +13.969536 5.142035 m +13.915039 5.035078 13.915039 4.895065 13.915039 4.615039 c +13.915039 4.215039 l +13.915039 3.935013 13.915039 3.795000 13.969536 3.688044 c +14.017472 3.593963 14.093963 3.517472 14.188044 3.469536 c +14.295000 3.415039 14.435013 3.415039 14.715039 3.415039 c +15.115039 3.415039 l +15.395065 3.415039 15.535078 3.415039 15.642035 3.469536 c +15.736115 3.517472 15.812606 3.593963 15.860542 3.688044 c +15.915039 3.795000 15.915039 3.935013 15.915039 4.215039 c +15.915039 4.615039 l +15.915039 4.895065 15.915039 5.035078 15.860542 5.142035 c +15.812606 5.236115 15.736115 5.312606 15.642035 5.360542 c +15.535078 5.415039 15.395065 5.415039 15.115039 5.415039 c +14.715039 5.415039 l +14.435013 5.415039 14.295000 5.415039 14.188044 5.360542 c +14.093963 5.312606 14.017472 5.236115 13.969536 5.142035 c +h +11.219536 2.392035 m +11.165039 2.285078 11.165039 2.145065 11.165039 1.865040 c +11.165039 1.465038 l +11.165039 1.185013 11.165039 1.045000 11.219536 0.938044 c +11.267472 0.843964 11.343963 0.767471 11.438044 0.719536 c +11.545000 0.665039 11.685013 0.665039 11.965039 0.665039 c +12.365039 0.665039 l +12.645065 0.665039 12.785078 0.665039 12.892035 0.719536 c +12.986115 0.767471 13.062606 0.843964 13.110542 0.938044 c +13.165039 1.045000 13.165039 1.185013 13.165039 1.465038 c +13.165039 1.865040 l +13.165039 2.145065 13.165039 2.285078 13.110542 2.392035 c +13.062606 2.486115 12.986115 2.562607 12.892035 2.610542 c +12.785078 2.665039 12.645065 2.665039 12.365039 2.665039 c +11.965039 2.665039 l +11.685013 2.665039 11.545000 2.665039 11.438044 2.610542 c +11.343963 2.562607 11.267472 2.486115 11.219536 2.392035 c +h +16.665039 1.865040 m +16.665039 2.145065 16.665039 2.285078 16.719536 2.392035 c +16.767471 2.486115 16.843964 2.562607 16.938044 2.610542 c +17.045000 2.665039 17.185013 2.665039 17.465038 2.665039 c +17.865040 2.665039 l +18.145065 2.665039 18.285078 2.665039 18.392035 2.610542 c +18.486115 2.562607 18.562607 2.486115 18.610542 2.392035 c +18.665039 2.285078 18.665039 2.145065 18.665039 1.865040 c +18.665039 1.465038 l +18.665039 1.185013 18.665039 1.045000 18.610542 0.938044 c +18.562607 0.843964 18.486115 0.767471 18.392035 0.719536 c +18.285078 0.665039 18.145065 0.665039 17.865040 0.665039 c +17.465038 0.665039 l +17.185013 0.665039 17.045000 0.665039 16.938044 0.719536 c +16.843964 0.767471 16.767471 0.843964 16.719536 0.938044 c +16.665039 1.045000 16.665039 1.185013 16.665039 1.465038 c +16.665039 1.865040 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 12895 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 30.000000 30.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000012985 00000 n +0000013009 00000 n +0000013182 00000 n +0000013256 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +13315 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift index 589d06b240..0d2e0a76ec 100644 --- a/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift +++ b/submodules/TelegramUI/Sources/ChatMessageNotificationItem.swift @@ -194,6 +194,10 @@ final class ChatMessageNotificationItemNode: NotificationItemNode { return false } } + if messageEntities?.count == 0 { + messageEntities = nil + messageText = textString + } } else { messageText = textString } diff --git a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index 504c9a139f..bf7d4ea438 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -105,7 +105,11 @@ class ChatMessageReplyInfoNode: ASDisplayNode { return false } } - messageText = stringWithAppliedEntities(message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false) + if entities.count > 0 { + messageText = stringWithAppliedEntities(message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false) + } else { + messageText = NSAttributedString(string: textString, font: textFont, textColor: textColor) + } } else { messageText = NSAttributedString(string: textString, font: textFont, textColor: textColor) } diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index e84f1a03aa..008af418c4 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -467,7 +467,11 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { } } let textColor = theme.chat.inputPanel.primaryTextColor - messageText = stringWithAppliedEntities(message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false) + if entities.count > 0 { + messageText = stringWithAppliedEntities(message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false) + } else { + messageText = NSAttributedString(string: foldLineBreaks(textString), font: textFont, textColor: textColor) + } } else { messageText = NSAttributedString(string: foldLineBreaks(textString), font: textFont, textColor: message.media.isEmpty || message.media.first is TelegramMediaWebpage ? theme.chat.inputPanel.primaryTextColor : theme.chat.inputPanel.secondaryTextColor) } diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index 89553036a2..5c4a7ef4e9 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -134,7 +134,11 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { } } let textColor = strongSelf.theme.chat.inputPanel.primaryTextColor - messageText = stringWithAppliedEntities(message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false) + if entities.count > 0 { + messageText = stringWithAppliedEntities(message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false) + } else { + messageText = NSAttributedString(string: text, font: textFont, textColor: isMedia ? strongSelf.theme.chat.inputPanel.secondaryTextColor : strongSelf.theme.chat.inputPanel.primaryTextColor) + } } else { messageText = NSAttributedString(string: text, font: textFont, textColor: isMedia ? strongSelf.theme.chat.inputPanel.secondaryTextColor : strongSelf.theme.chat.inputPanel.primaryTextColor) } From cdd69f718812fa82bc16924414aeda75b8bc9805 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 17:42:32 +0400 Subject: [PATCH 05/21] Various Fixes --- .../InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift | 6 +++--- .../Sources/ChatMessageInstantVideoItemNode.swift | 2 +- .../PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index 5fd6ab395a..4eea9d8869 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -227,8 +227,6 @@ public class InvisibleInkDustNode: ASDisplayNode { let timeToRead = min(45.0, ceil(max(4.0, Double(spoilersLength) * 0.04))) Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) { - self.isRevealed = false - if let (_, color, _, _) = self.currentParams { let colorSpace = CGColorSpaceCreateDeviceRGB() let animation = POPBasicAnimation() @@ -269,7 +267,9 @@ public class InvisibleInkDustNode: ASDisplayNode { Queue.mainQueue().after(0.15) { let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .linear) transition.updateAlpha(node: self, alpha: 1.0) - transition.updateAlpha(node: textNode, alpha: 0.0) + transition.updateAlpha(node: textNode, alpha: 0.0, completion: { [weak self] _ in + self?.isRevealed = false + }) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index b112da8c36..c7f1201d13 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -1254,7 +1254,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD let animationProgress: CGFloat = (currentValue - initialHeight) / (targetHeight - initialHeight) let scaleProgress: CGFloat var effectiveAvatarInset = avatarInset - if abs(targetHeight - initialHeight) > 100.0 { + if abs(targetHeight - initialHeight) > 80.0 { if currentValue < targetHeight { initialSize = displaySize targetSize = maximumDisplaySize diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift index bfe7f53718..04f2464ecb 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift @@ -1205,7 +1205,7 @@ private final class SparseItemGridBindingImpl: SparseItemGridBinding, ListShimme self.listItemInteraction = listItemInteraction self.chatControllerInteraction = chatControllerInteraction self.directMediaImageCache = directMediaImageCache - self.captureProtected = captureProtected + self.captureProtected = false //captureProtected let presentationData = self.context.sharedContext.currentPresentationData.with { $0 } self.strings = presentationData.strings From 10147b56ca8cfaa9b375609f5456d5f8913b921a Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 18:21:13 +0400 Subject: [PATCH 06/21] Fix shadow layout --- .../ReactionListContextMenuContent.swift | 9 +++- .../ReactionContextBackgroundNode.swift | 22 ++++++--- .../Account/AccountIntermediateState.swift | 16 ++++--- .../State/AccountStateManagementUtils.swift | 46 +++++++++++++++++-- .../Sources/State/AccountStateManager.swift | 18 ++++++++ .../Sources/State/MessageReactions.swift | 22 ++++++--- 6 files changed, 109 insertions(+), 24 deletions(-) diff --git a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift index 0ae1d4ae53..a486a03aa4 100644 --- a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift +++ b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift @@ -398,9 +398,14 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent if !self.listState.canLoadMore { return self.mergedItems.count } else { - var value = self.listState.totalCount + let reactionCount = self.listState.totalCount + var value = reactionCount if let readStats = self.readStats { - value = max(value, readStats.peers.count) + if reactionCount < readStats.peers.count && self.listState.hasOutgoingReaction { + value = readStats.peers.count + 1 + } else { + value = max(reactionCount, readStats.peers.count) + } } return value } diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift index 5a87fe2493..515cff7fe1 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextBackgroundNode.swift @@ -174,29 +174,37 @@ final class ReactionContextBackgroundNode: ASDisplayNode { self.largeCircleLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.01, delay: largeCircleDelay) self.largeCircleLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay) + self.largeCircleShadowLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: largeCircleDuration, delay: largeCircleDelay) //self.largeCircleLayer.animate(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: largeCircleDuration) self.backgroundLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.01, delay: mainCircleDelay) self.backgroundLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay) + self.backgroundShadowLayer.animateSpring(from: 0.01 as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: mainCircleDuration, delay: mainCircleDelay) } func animateInFromAnchorRect(size: CGSize, sourceBackgroundFrame: CGRect) { let springDuration: Double = 0.2 let springDamping: CGFloat = 104.0 let springDelay: Double = 0.25 + let shadowInset: CGFloat = 15.0 - self.backgroundLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) - self.backgroundLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) - self.backgroundShadowLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) - self.backgroundShadowLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceBackgroundFrame.size)), to: NSValue(cgRect: CGRect(origin: CGPoint(), size: size)), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) + let contentBounds = self.backgroundNode.frame + + let visualSourceBackgroundFrame = sourceBackgroundFrame.offsetBy(dx: -contentBounds.minX, dy: -contentBounds.minY) + let sourceShadowFrame = visualSourceBackgroundFrame.insetBy(dx: -shadowInset, dy: -shadowInset) + + self.backgroundLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: visualSourceBackgroundFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) + self.backgroundLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: visualSourceBackgroundFrame.size)), to: NSValue(cgRect: self.backgroundLayer.bounds), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) + self.backgroundShadowLayer.animateSpring(from: NSValue(cgPoint: CGPoint(x: sourceShadowFrame.midX - size.width / 2.0, y: 0.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping, additive: true) + self.backgroundShadowLayer.animateSpring(from: NSValue(cgRect: CGRect(origin: CGPoint(), size: sourceShadowFrame.size)), to: NSValue(cgRect: self.backgroundShadowLayer.bounds), keyPath: "bounds", duration: springDuration, delay: springDelay, initialVelocity: 0.0, damping: springDamping) } func animateOut() { self.backgroundLayer.animateAlpha(from: CGFloat(self.backgroundLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) - self.backgroundShadowLayer.animateAlpha(from: CGFloat(self.backgroundShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) + self.backgroundShadowLayer.animateAlpha(from: CGFloat(self.backgroundShadowLayer.opacity), to: 0.0, duration: 0.1, removeOnCompletion: false) self.largeCircleLayer.animateAlpha(from: CGFloat(self.largeCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) - self.largeCircleShadowLayer.animateAlpha(from: CGFloat(self.largeCircleShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) + self.largeCircleShadowLayer.animateAlpha(from: CGFloat(self.largeCircleShadowLayer.opacity), to: 0.0, duration: 0.1, removeOnCompletion: false) self.smallCircleLayer.animateAlpha(from: CGFloat(self.smallCircleLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) - self.smallCircleShadowLayer.animateAlpha(from: CGFloat(self.smallCircleShadowLayer.opacity), to: 0.0, duration: 0.2, removeOnCompletion: false) + self.smallCircleShadowLayer.animateAlpha(from: CGFloat(self.smallCircleShadowLayer.opacity), to: 0.0, duration: 0.1, removeOnCompletion: false) } } diff --git a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift index 478dd24bf5..c27c3e73e0 100644 --- a/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift +++ b/submodules/TelegramCore/Sources/Account/AccountIntermediateState.swift @@ -65,7 +65,7 @@ enum AccountStateMutationOperation { case DeleteMessages([MessageId]) case EditMessage(MessageId, StoreMessage) case UpdateMessagePoll(MediaId, Api.Poll?, Api.PollResults) - case UpdateMessageReactions(MessageId, Api.MessageReactions) + case UpdateMessageReactions(MessageId, Api.MessageReactions, Int32?) case UpdateMedia(MediaId, Media?) case ReadInbox(MessageId) case ReadOutbox(MessageId, Int32?) @@ -258,8 +258,8 @@ struct AccountMutableState { self.addOperation(.UpdateMessagePoll(id, poll, results)) } - mutating func updateMessageReactions(_ messageId: MessageId, reactions: Api.MessageReactions) { - self.addOperation(.UpdateMessageReactions(messageId, reactions)) + mutating func updateMessageReactions(_ messageId: MessageId, reactions: Api.MessageReactions, eventTimestamp: Int32?) { + self.addOperation(.UpdateMessageReactions(messageId, reactions, eventTimestamp)) } mutating func updateMedia(_ id: MediaId, media: Media?) { @@ -610,6 +610,7 @@ struct AccountFinalState { struct AccountReplayedFinalState { let state: AccountFinalState let addedIncomingMessageIds: [MessageId] + let addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] let wasScheduledMessageIds: [MessageId] let addedSecretMessageIds: [MessageId] let deletedMessageIds: [DeletedMessageId] @@ -627,6 +628,7 @@ struct AccountReplayedFinalState { struct AccountFinalStateEvents { let addedIncomingMessageIds: [MessageId] + let addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] let wasScheduledMessageIds:[MessageId] let deletedMessageIds: [DeletedMessageId] let updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] @@ -646,11 +648,12 @@ struct AccountFinalStateEvents { let updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] var isEmpty: Bool { - return self.addedIncomingMessageIds.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty + return self.addedIncomingMessageIds.isEmpty && self.addedReactionEvents.isEmpty && self.wasScheduledMessageIds.isEmpty && self.deletedMessageIds.isEmpty && self.updatedTypingActivities.isEmpty && self.updatedWebpages.isEmpty && self.updatedCalls.isEmpty && self.addedCallSignalingData.isEmpty && self.updatedGroupCallParticipants.isEmpty && self.updatedPeersNearby?.isEmpty ?? true && self.isContactUpdates.isEmpty && self.displayAlerts.isEmpty && self.delayNotificatonsUntil == nil && self.updatedMaxMessageId == nil && self.updatedQts == nil && self.externallyUpdatedPeerId.isEmpty && !authorizationListUpdated && self.updatedIncomingThreadReadStates.isEmpty && self.updatedOutgoingThreadReadStates.isEmpty } - init(addedIncomingMessageIds: [MessageId] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { + init(addedIncomingMessageIds: [MessageId] = [], addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] = [], wasScheduledMessageIds: [MessageId] = [], deletedMessageIds: [DeletedMessageId] = [], updatedTypingActivities: [PeerActivitySpace: [PeerId: PeerInputActivity?]] = [:], updatedWebpages: [MediaId: TelegramMediaWebpage] = [:], updatedCalls: [Api.PhoneCall] = [], addedCallSignalingData: [(Int64, Data)] = [], updatedGroupCallParticipants: [(Int64, GroupCallParticipantsContext.Update)] = [], updatedPeersNearby: [PeerNearby]? = nil, isContactUpdates: [(PeerId, Bool)] = [], displayAlerts: [(text: String, isDropAuth: Bool)] = [], delayNotificatonsUntil: Int32? = nil, updatedMaxMessageId: Int32? = nil, updatedQts: Int32? = nil, externallyUpdatedPeerId: Set = Set(), authorizationListUpdated: Bool = false, updatedIncomingThreadReadStates: [MessageId: MessageId.Id] = [:], updatedOutgoingThreadReadStates: [MessageId: MessageId.Id] = [:]) { self.addedIncomingMessageIds = addedIncomingMessageIds + self.addedReactionEvents = addedReactionEvents self.wasScheduledMessageIds = wasScheduledMessageIds self.deletedMessageIds = deletedMessageIds self.updatedTypingActivities = updatedTypingActivities @@ -672,6 +675,7 @@ struct AccountFinalStateEvents { init(state: AccountReplayedFinalState) { self.addedIncomingMessageIds = state.addedIncomingMessageIds + self.addedReactionEvents = state.addedReactionEvents self.wasScheduledMessageIds = state.wasScheduledMessageIds self.deletedMessageIds = state.deletedMessageIds self.updatedTypingActivities = state.updatedTypingActivities @@ -714,6 +718,6 @@ struct AccountFinalStateEvents { let externallyUpdatedPeerId = self.externallyUpdatedPeerId.union(other.externallyUpdatedPeerId) let authorizationListUpdated = self.authorizationListUpdated || other.authorizationListUpdated - return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs })) + return AccountFinalStateEvents(addedIncomingMessageIds: self.addedIncomingMessageIds + other.addedIncomingMessageIds, addedReactionEvents: self.addedReactionEvents + other.addedReactionEvents, wasScheduledMessageIds: self.wasScheduledMessageIds + other.wasScheduledMessageIds, deletedMessageIds: self.deletedMessageIds + other.deletedMessageIds, updatedTypingActivities: self.updatedTypingActivities, updatedWebpages: self.updatedWebpages, updatedCalls: self.updatedCalls + other.updatedCalls, addedCallSignalingData: self.addedCallSignalingData + other.addedCallSignalingData, updatedGroupCallParticipants: self.updatedGroupCallParticipants + other.updatedGroupCallParticipants, isContactUpdates: self.isContactUpdates + other.isContactUpdates, displayAlerts: self.displayAlerts + other.displayAlerts, delayNotificatonsUntil: delayNotificatonsUntil, updatedMaxMessageId: updatedMaxMessageId, updatedQts: updatedQts, externallyUpdatedPeerId: externallyUpdatedPeerId, authorizationListUpdated: authorizationListUpdated, updatedIncomingThreadReadStates: self.updatedIncomingThreadReadStates.merging(other.updatedIncomingThreadReadStates, uniquingKeysWith: { lhs, _ in lhs })) } } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 67d2ccfa5a..4efc5b7060 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1474,7 +1474,7 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo } }) case let .updateMessageReactions(peer, msgId, reactions): - updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions) + updatedState.updateMessageReactions(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), reactions: reactions, eventTimestamp: updatesDate) default: break } @@ -2430,6 +2430,7 @@ func replayFinalState( } var wasScheduledMessageIds:[MessageId] = [] var addedIncomingMessageIds: [MessageId] = [] + var addedReactionEvents: [(reactionAuthor: Peer, message: Message, timestamp: Int32)] = [] if !wasOperationScheduledMessageIds.isEmpty { let existingIds = transaction.filterStoredMessageIds(Set(wasOperationScheduledMessageIds)) @@ -3228,16 +3229,19 @@ func replayFinalState( return state }) } - case let .UpdateMessageReactions(messageId, reactions): + case let .UpdateMessageReactions(messageId, reactions, eventTimestamp): + var generatedEvent: (reactionAuthor: Peer, message: Message, timestamp: Int32)? transaction.updateMessage(messageId, update: { currentMessage in var updatedReactions = ReactionsMessageAttribute(apiReactions: reactions) let storeForwardInfo = currentMessage.forwardInfo.flatMap(StoreMessageForwardInfo.init) var attributes = currentMessage.attributes + var previousReactions: ReactionsMessageAttribute? var added = false loop: for j in 0 ..< attributes.count { if let attribute = attributes[j] as? ReactionsMessageAttribute { added = true + previousReactions = attribute updatedReactions = attribute.withUpdatedResults(reactions) if updatedReactions == attribute { @@ -3251,8 +3255,44 @@ func replayFinalState( attributes.append(updatedReactions) } + if let eventTimestamp = eventTimestamp, !currentMessage.flags.contains(.Incoming), let chatPeer = currentMessage.peers[currentMessage.id.peerId] { + let _ = chatPeer + + var previousCount = 0 + if let previousReactions = previousReactions { + for reaction in previousReactions.reactions { + previousCount += Int(reaction.count) + } + } + + var updatedCount = 0 + for reaction in updatedReactions.reactions { + updatedCount += Int(reaction.count) + } + + if updatedCount > previousCount { + if let topPeer = updatedReactions.recentPeers.last { + var wasPresentBefore = false + if let previousReactions = previousReactions { + for recentPeer in previousReactions.recentPeers { + if recentPeer.peerId == topPeer.peerId { + wasPresentBefore = true + break + } + } + } + if !wasPresentBefore, let reactionAuthor = transaction.getPeer(topPeer.peerId), transaction.isPeerContact(peerId: topPeer.peerId) { + generatedEvent = (reactionAuthor: reactionAuthor, message: currentMessage.withUpdatedAttributes(attributes), timestamp: eventTimestamp) + } + } + } + } + return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: attributes, media: currentMessage.media)) }) + if let generatedEvent = generatedEvent { + addedReactionEvents.append(generatedEvent) + } } } @@ -3624,5 +3664,5 @@ func replayFinalState( requestChatListFiltersSync(transaction: transaction) } - return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates) + return AccountReplayedFinalState(state: finalState, addedIncomingMessageIds: addedIncomingMessageIds, addedReactionEvents: addedReactionEvents, wasScheduledMessageIds: wasScheduledMessageIds, addedSecretMessageIds: addedSecretMessageIds, deletedMessageIds: deletedMessageIds, updatedTypingActivities: updatedTypingActivities, updatedWebpages: updatedWebpages, updatedCalls: updatedCalls, addedCallSignalingData: addedCallSignalingData, updatedGroupCallParticipants: updatedGroupCallParticipants, updatedPeersNearby: updatedPeersNearby, isContactUpdates: isContactUpdates, delayNotificatonsUntil: delayNotificatonsUntil, updatedIncomingThreadReadStates: updatedIncomingThreadReadStates, updatedOutgoingThreadReadStates: updatedOutgoingThreadReadStates) } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManager.swift b/submodules/TelegramCore/Sources/State/AccountStateManager.swift index 1f27657478..a832e6a134 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManager.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManager.swift @@ -105,6 +105,11 @@ public final class AccountStateManager { return self.notificationMessagesPipe.signal() } + private let reactionNotificationsPipe = ValuePipe<[(reactionAuthor: Peer, message: Message)]>() + public var reactionNotifications: Signal<[(reactionAuthor: Peer, message: Message)], NoError> { + return self.reactionNotificationsPipe.signal() + } + private let displayAlertsPipe = ValuePipe<[(text: String, isDropAuth: Bool)]>() public var displayAlerts: Signal<[(text: String, isDropAuth: Bool)], NoError> { return self.displayAlertsPipe.signal() @@ -739,6 +744,19 @@ public final class AccountStateManager { completed() }) + let timestamp = Int32(Date().timeIntervalSince1970) + let minReactionTimestamp = timestamp - 20 + let reactionEvents = events.addedReactionEvents.compactMap { event -> (reactionAuthor: Peer, message: Message)? in + if event.timestamp >= minReactionTimestamp { + return (event.reactionAuthor, event.message) + } else { + return nil + } + } + if !reactionEvents.isEmpty { + self.reactionNotificationsPipe.putNext(reactionEvents) + } + if !events.displayAlerts.isEmpty { self.displayAlertsPipe.putNext(events.displayAlerts) } diff --git a/submodules/TelegramCore/Sources/State/MessageReactions.swift b/submodules/TelegramCore/Sources/State/MessageReactions.swift index ef7cc54a6f..066cdf0c7b 100644 --- a/submodules/TelegramCore/Sources/State/MessageReactions.swift +++ b/submodules/TelegramCore/Sources/State/MessageReactions.swift @@ -231,15 +231,20 @@ private func synchronizeMessageReactions(transaction: Transaction, postbox: Post public extension EngineMessageReactionListContext.State { init(message: EngineMessage, reaction: String?) { - var totalCount: Int = 0 + var totalCount = 0 + var hasOutgoingReaction = false if let reactionsAttribute = message._asMessage().reactionsAttribute { for messageReaction in reactionsAttribute.reactions { if reaction == nil || messageReaction.value == reaction { + if messageReaction.isSelected { + hasOutgoingReaction = true + } totalCount += Int(messageReaction.count) } } } self.init( + hasOutgoingReaction: hasOutgoingReaction, totalCount: totalCount, items: [], canLoadMore: totalCount != 0 @@ -272,15 +277,18 @@ public final class EngineMessageReactionListContext { } public struct State: Equatable { + public var hasOutgoingReaction: Bool public var totalCount: Int public var items: [Item] public var canLoadMore: Bool public init( + hasOutgoingReaction: Bool, totalCount: Int, items: [Item], canLoadMore: Bool ) { + self.hasOutgoingReaction = hasOutgoingReaction self.totalCount = totalCount self.items = items self.canLoadMore = canLoadMore @@ -289,6 +297,7 @@ public final class EngineMessageReactionListContext { private final class Impl { struct InternalState: Equatable { + var hasOutgoingReaction: Bool var totalCount: Int var items: [Item] var canLoadMore: Bool @@ -315,7 +324,7 @@ public final class EngineMessageReactionListContext { self.reaction = reaction let initialState = EngineMessageReactionListContext.State(message: message, reaction: reaction) - self.state = InternalState(totalCount: initialState.totalCount, items: initialState.items, canLoadMore: true, nextOffset: nil) + self.state = InternalState(hasOutgoingReaction: initialState.hasOutgoingReaction, totalCount: initialState.totalCount, items: initialState.items, canLoadMore: true, nextOffset: nil) if initialState.canLoadMore { self.loadMore() @@ -344,10 +353,10 @@ public final class EngineMessageReactionListContext { } |> mapToSignal { inputPeer -> Signal in if message.id.namespace != Namespaces.Message.Cloud { - return .single(InternalState(totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) + return .single(InternalState(hasOutgoingReaction: false, totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) } guard let inputPeer = inputPeer else { - return .single(InternalState(totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) + return .single(InternalState(hasOutgoingReaction: false, totalCount: 0, items: [], canLoadMore: false, nextOffset: nil)) } var flags: Int32 = 0 if reaction != nil { @@ -391,9 +400,9 @@ public final class EngineMessageReactionListContext { } } - return InternalState(totalCount: Int(count), items: items, canLoadMore: nextOffset != nil, nextOffset: nextOffset) + return InternalState(hasOutgoingReaction: false, totalCount: Int(count), items: items, canLoadMore: nextOffset != nil, nextOffset: nextOffset) case .none: - return InternalState(totalCount: 0, items: [], canLoadMore: false, nextOffset: nil) + return InternalState(hasOutgoingReaction: false, totalCount: 0, items: [], canLoadMore: false, nextOffset: nil) } } } @@ -438,6 +447,7 @@ public final class EngineMessageReactionListContext { self.impl.with { impl in disposable.set(impl.statePromise.get().start(next: { state in subscriber.putNext(State( + hasOutgoingReaction: state.hasOutgoingReaction, totalCount: state.totalCount, items: state.items, canLoadMore: state.canLoadMore From 581024c5f025b5e23a80b9cdb3a4285eae926638 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 18:35:46 +0400 Subject: [PATCH 07/21] Various Improvements --- .../Sources/InvisibleInkDustNode.swift | 12 +- .../TelegramUI/Sources/ChatController.swift | 1 + .../PeerSelectionTextInputPanelNode.swift | 188 +++++++++++++++++- 3 files changed, 186 insertions(+), 15 deletions(-) diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index 4eea9d8869..10c2a34041 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -99,9 +99,9 @@ public class InvisibleInkDustNode: ASDisplayNode { emitter.velocityRange = 20.0 emitter.name = "dustCell" emitter.alphaRange = 1.0 -// emitter.setValue("point", forKey: "particleType") -// emitter.setValue(3.0, forKey: "mass") -// emitter.setValue(2.0, forKey: "massRange") + emitter.setValue("point", forKey: "particleType") + emitter.setValue(3.0, forKey: "mass") + emitter.setValue(2.0, forKey: "massRange") self.emitter = emitter let fingerAttractor = createEmitterBehavior(type: "simpleAttractor") @@ -182,7 +182,7 @@ public class InvisibleInkDustNode: ASDisplayNode { var scaleAddition = maxFactor * 4.0 var durationAddition = -maxFactor * 0.2 if self.emitterNode.frame.height > 0.0, self.emitterNode.frame.width / self.emitterNode.frame.height < 0.3 { - scaleAddition *= 4.0 + scaleAddition *= 5.0 durationAddition *= 2.0 } @@ -292,7 +292,9 @@ public class InvisibleInkDustNode: ASDisplayNode { square += Float(rect.width * rect.height) } - self.emitter?.birthRate = min(120000, square * 0.35) + Queue.mainQueue().async { + self.emitter?.birthRate = min(100000, square * 0.35) + } } public func update(size: CGSize, color: UIColor, rects: [CGRect], wordRects: [CGRect]) { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 2405ede222..48ee69e457 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -9868,6 +9868,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) let inputPanelNode = PeerSelectionTextInputPanelNode(presentationInterfaceState: presentationInterfaceState, isCaption: true, presentController: { _ in }) + inputPanelNode.context = self.context inputPanelNode.interfaceInteraction = interfaceInteraction inputPanelNode.effectivePresentationInterfaceState = { return presentationInterfaceState diff --git a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift index 775e22681c..6e2e23917d 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift @@ -14,6 +14,7 @@ import ActivityIndicator import Speak import ObjCRuntimeUtils import LegacyComponents +import InvisibleInkDustNode private let counterFont = Font.with(size: 14.0, design: .regular, traits: [.monospacedNumbers]) private let minInputFontSize = chatTextInputMinFontSize @@ -120,6 +121,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A let textInputContainerBackgroundNode: ASImageNode let textInputContainer: ASDisplayNode var textInputNode: CaptionEditableTextNode? + var dustNode: InvisibleInkDustNode? private var oneLineNode: ImmediateTextNode let textInputBackgroundNode: ASDisplayNode @@ -207,10 +209,11 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) } - textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: false) + textInputNode.attributedText = textAttributedStringForStateText(state.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed) textInputNode.selectedRange = NSMakeRange(state.selectionRange.lowerBound, state.selectionRange.count) self.updatingInputState = false self.updateTextNodeText(animated: animated) + self.updateSpoiler() } } @@ -237,6 +240,8 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A private let textInputViewInternalInsets = UIEdgeInsets(top: 1.0, left: 13.0, bottom: 1.0, right: 13.0) + private var spoilersRevealed = false + init(presentationInterfaceState: ChatPresentationInterfaceState, isCaption: Bool = false, presentController: @escaping (ViewController) -> Void) { self.presentationInterfaceState = presentationInterfaceState self.isCaption = isCaption @@ -387,6 +392,8 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A let textInputFrame = self.textInputContainer.frame textInputNode.frame = CGRect(origin: CGPoint(x: self.textInputViewInternalInsets.left, y: self.textInputViewInternalInsets.top), size: CGSize(width: textInputFrame.size.width - (self.textInputViewInternalInsets.left + self.textInputViewInternalInsets.right), height: textInputFrame.size.height - self.textInputViewInternalInsets.top - self.textInputViewInternalInsets.bottom)) + textInputNode.view.layoutIfNeeded() + self.updateSpoiler() } self.textInputBackgroundNode.isUserInteractionEnabled = false @@ -501,6 +508,8 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A textInputNode.selectedRange = selectedRange } textInputNode.typingAttributes = [NSAttributedString.Key.font.rawValue: Font.regular(baseFontSize), NSAttributedString.Key.foregroundColor.rawValue: textColor] + + self.updateSpoiler() } } @@ -703,9 +712,11 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A @objc func editableTextNodeDidUpdateText(_ editableTextNode: ASEditableTextNode) { if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState { let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) - refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: false) + refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed) refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) + self.updateSpoiler() + let inputTextState = self.inputTextState self.skipUpdate = true @@ -726,6 +737,148 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A } } + private func updateSpoiler() { + guard let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState else { + return + } + + let textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor + + var rects: [CGRect] = [] + + if let attributedText = textInputNode.attributedText { + let beginning = textInputNode.textView.beginningOfDocument + attributedText.enumerateAttributes(in: NSMakeRange(0, attributedText.length), options: [], using: { attributes, range, _ in + if let _ = attributes[ChatTextInputAttributes.spoiler] { + func addSpoiler(startIndex: Int, endIndex: Int) { + if let start = textInputNode.textView.position(from: beginning, offset: startIndex), let end = textInputNode.textView.position(from: start, offset: endIndex - startIndex), let textRange = textInputNode.textView.textRange(from: start, to: end) { + let textRects = textInputNode.textView.selectionRects(for: textRange) + for textRect in textRects { + rects.append(textRect.rect.insetBy(dx: 1.0, dy: 1.0).offsetBy(dx: 0.0, dy: 1.0)) + } + } + } + + var startIndex: Int? + var currentIndex: Int? + + let nsString = (attributedText.string as NSString) + nsString.enumerateSubstrings(in: range, options: .byComposedCharacterSequences) { substring, range, _, _ in + if let substring = substring, substring.rangeOfCharacter(from: .whitespacesAndNewlines) != nil { + if let currentStartIndex = startIndex { + startIndex = nil + let endIndex = range.location + addSpoiler(startIndex: currentStartIndex, endIndex: endIndex) + } + } else if startIndex == nil { + startIndex = range.location + } + currentIndex = range.location + range.length + } + + if let currentStartIndex = startIndex, let currentIndex = currentIndex { + startIndex = nil + let endIndex = currentIndex + addSpoiler(startIndex: currentStartIndex, endIndex: endIndex) + } + } + }) + } + + if !rects.isEmpty { + let dustNode: InvisibleInkDustNode + if let current = self.dustNode { + dustNode = current + } else { + dustNode = InvisibleInkDustNode(textNode: nil) + dustNode.alpha = self.spoilersRevealed ? 0.0 : 1.0 + dustNode.isUserInteractionEnabled = false + textInputNode.textView.addSubview(dustNode.view) + self.dustNode = dustNode + } + dustNode.frame = CGRect(origin: CGPoint(), size: textInputNode.textView.contentSize) + dustNode.update(size: textInputNode.textView.contentSize, color: textColor, rects: rects, wordRects: rects) + } else if let dustNode = self.dustNode { + dustNode.removeFromSupernode() + self.dustNode = nil + } + } + + private func updateSpoilersRevealed(animated: Bool = true) { + guard let textInputNode = self.textInputNode else { + return + } + + let selectionRange = textInputNode.textView.selectedRange + + var revealed = false + if let attributedText = textInputNode.attributedText { + attributedText.enumerateAttributes(in: NSMakeRange(0, attributedText.length), options: [], using: { attributes, range, _ in + if let _ = attributes[ChatTextInputAttributes.spoiler] { + if let _ = selectionRange.intersection(range) { + revealed = true + } + } + }) + } + + guard self.spoilersRevealed != revealed else { + return + } + self.spoilersRevealed = revealed + + if revealed { + self.updateInternalSpoilersRevealed(true, animated: animated) + } else { + Queue.mainQueue().after(1.5, { + self.updateInternalSpoilersRevealed(false, animated: true) + }) + } + } + + private func updateInternalSpoilersRevealed(_ revealed: Bool, animated: Bool) { + guard self.spoilersRevealed == revealed, let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState else { + return + } + + let textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor + let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor + let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + + refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed) + + textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed) + + if textInputNode.textView.subviews.count > 1, animated { + let containerView = textInputNode.textView.subviews[1] + if let canvasView = containerView.subviews.first { + if let snapshotView = canvasView.snapshotView(afterScreenUpdates: false) { + textInputNode.view.insertSubview(snapshotView, at: 0) + canvasView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + } + } + + if animated { + if revealed { + let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) + if let dustNode = self.dustNode { + transition.updateAlpha(node: dustNode, alpha: 0.0) + } + } else { + let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) + if let dustNode = self.dustNode { + transition.updateAlpha(node: dustNode, alpha: 1.0) + } + } + } else if let dustNode = self.dustNode { + dustNode.alpha = revealed ? 0.0 : 1.0 + } + } + private func updateCounterTextNode(transition: ContainedViewLayoutTransition) { let inputTextMaxLength: Int32? if self.isCaption { @@ -830,7 +983,9 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A @objc func editableTextNodeDidChangeSelection(_ editableTextNode: ASEditableTextNode, fromSelectedRange: NSRange, toSelectedRange: NSRange, dueToEditing: Bool) { if !dueToEditing && !self.updatingInputState { let inputTextState = self.inputTextState + self.skipUpdate = true self.interfaceInteraction?.updateTextInputStateAndMode({ _, inputMode in return (inputTextState, inputMode) }) + self.skipUpdate = false } if let textInputNode = self.textInputNode, let presentationInterfaceState = self.presentationInterfaceState { @@ -840,6 +995,8 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) refreshChatTextInputTypingAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize) + + self.updateSpoilersRevealed() } } @@ -977,9 +1134,21 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A @objc func formatAttributesSpoiler(_ sender: Any) { self.inputMenu.back() + + var animated = false + if let attributedText = self.textInputNode?.attributedText { + attributedText.enumerateAttributes(in: NSMakeRange(0, attributedText.length), options: [], using: { attributes, _, _ in + if let _ = attributes[ChatTextInputAttributes.spoiler] { + animated = true + } + }) + } + self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in return (chatTextInputAddFormattingAttribute(current, attribute: ChatTextInputAttributes.spoiler), inputMode) } + + self.updateSpoilersRevealed(animated: animated) } @objc func editableTextNode(_ editableTextNode: ASEditableTextNode, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool { @@ -1005,7 +1174,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) } - let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: false) + let cleanReplacementString = textAttributedStringForStateText(NSAttributedString(string: cleanText), fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed) string.replaceCharacters(in: range, with: cleanReplacementString) self.textInputNode?.attributedText = string self.textInputNode?.selectedRange = NSMakeRange(range.lowerBound + cleanReplacementString.length, 0) @@ -1049,12 +1218,6 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A } @objc func sendButtonPressed() { - if let sendPressed = self.sendPressed, let presentationInterfaceState = self.effectivePresentationInterfaceState?() { - self.dismissInput() - let effectiveInputText = presentationInterfaceState.interfaceState.composeInputState.inputText - sendPressed(effectiveInputText) - return - } let inputTextMaxLength: Int32? if self.isCaption { inputTextMaxLength = self.context?.currentLimitsConfiguration.with { $0 }.maxMediaCaptionLength @@ -1071,7 +1234,12 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A return } } - + if let sendPressed = self.sendPressed, let presentationInterfaceState = self.effectivePresentationInterfaceState?() { + self.dismissInput() + let effectiveInputText = presentationInterfaceState.interfaceState.composeInputState.inputText + sendPressed(effectiveInputText) + return + } self.sendMessage(.generic) } From 5d1755c83e60add30b2736334b6f70780382206e Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 19:38:08 +0400 Subject: [PATCH 08/21] Don't allow editing games and invoices --- .../TelegramUI/Sources/ChatInterfaceStateContextMenus.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index d3ed7eccc4..6e3ca8bac7 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -138,6 +138,12 @@ private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: LimitsCo } else if let _ = media as? TelegramMediaDice { hasUneditableAttributes = true break + } else if let _ = media as? TelegramMediaGame { + hasUneditableAttributes = true + break + } else if let _ = media as? TelegramMediaInvoice { + hasUneditableAttributes = true + break } } From 380a608ee899465cad7d0eccd48eb362f32daaa8 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 20:17:18 +0400 Subject: [PATCH 09/21] Various Fixes --- .../ChatItemGalleryFooterContentNode.swift | 5 +-- .../Sources/InvisibleInkDustNode.swift | 2 ++ .../TelegramUI/Sources/ChatQrCodeScreen.swift | 33 ++++++++++++------- .../Sources/GenerateTextEntities.swift | 8 +++-- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index df4179027e..047f9c984a 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -99,8 +99,8 @@ class CaptionScrollWrapperNode: ASDisplayNode { if result == self.view, let subnode = self.subnodes?.first { let convertedPoint = self.view.convert(point, to: subnode.view) if let subnodes = subnode.subnodes { - for node in subnodes { - if node.frame.contains(convertedPoint) { + for node in subnodes.reversed() { + if node.frame.contains(convertedPoint) && node.isUserInteractionEnabled { return node.view } } @@ -722,6 +722,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll if self.spoilerTextNode == nil { let spoilerTextNode = ImmediateTextNode() spoilerTextNode.attributedText = textNode.attributedText + spoilerTextNode.maximumNumberOfLines = 0 spoilerTextNode.linkHighlightColor = UIColor(rgb: 0x5ac8fa, alpha: 0.2) spoilerTextNode.displaySpoilers = true spoilerTextNode.isHidden = false diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index 10c2a34041..0ad165ab2e 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -227,6 +227,8 @@ public class InvisibleInkDustNode: ASDisplayNode { let timeToRead = min(45.0, ceil(max(4.0, Double(spoilersLength) * 0.04))) Queue.mainQueue().after(timeToRead * UIView.animationDurationFactor()) { +// self.emitterLayer?.setValue(false, forKeyPath: "emitterBehaviors.fingerAttractor.enabled") + if let (_, color, _, _) = self.currentParams { let colorSpace = CGColorSpaceCreateDeviceRGB() let animation = POPBasicAnimation() diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index ba593996a9..47521515b8 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -870,9 +870,10 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg } let initiallySelectedEmoticon: Signal + let sharedData = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) + |> take(1) if self.peer.id == self.context.account.peerId { - initiallySelectedEmoticon = self.context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings]) - |> take(1) + initiallySelectedEmoticon = sharedData |> map { sharedData -> String in let themeSettings: PresentationThemeSettings if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) { @@ -883,19 +884,27 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg return themeSettings.theme.emoticon ?? defaultEmoticon } } else { - initiallySelectedEmoticon = self.context.account.postbox.transaction { transaction in + let cachedData = self.context.account.postbox.transaction { transaction in return transaction.getPeerCachedData(peerId: peer.id) } - |> take(1) - |> map { cachedData -> String in - if let cachedData = cachedData as? CachedUserData { - return cachedData.themeEmoticon ?? defaultEmoticon - } else if let cachedData = cachedData as? CachedGroupData { - return cachedData.themeEmoticon ?? defaultEmoticon - } else if let cachedData = cachedData as? CachedChannelData { - return cachedData.themeEmoticon ?? defaultEmoticon + initiallySelectedEmoticon = combineLatest(cachedData, sharedData) + |> map { cachedData, sharedData -> String in + let themeSettings: PresentationThemeSettings + if let current = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) { + themeSettings = current } else { - return defaultEmoticon + themeSettings = PresentationThemeSettings.defaultSettings + } + let currentDefaultEmoticon = themeSettings.theme.emoticon ?? defaultEmoticon + + if let cachedData = cachedData as? CachedUserData { + return cachedData.themeEmoticon ?? currentDefaultEmoticon + } else if let cachedData = cachedData as? CachedGroupData { + return cachedData.themeEmoticon ?? currentDefaultEmoticon + } else if let cachedData = cachedData as? CachedChannelData { + return cachedData.themeEmoticon ?? currentDefaultEmoticon + } else { + return currentDefaultEmoticon } } } diff --git a/submodules/TextFormat/Sources/GenerateTextEntities.swift b/submodules/TextFormat/Sources/GenerateTextEntities.swift index 8f0114185c..b65d3c72bd 100644 --- a/submodules/TextFormat/Sources/GenerateTextEntities.swift +++ b/submodules/TextFormat/Sources/GenerateTextEntities.swift @@ -101,8 +101,12 @@ private func commitEntity(_ utf16: String.UTF16View, _ type: CurrentEntityType, var overlaps = false for entity in entities { if entity.range.overlaps(indexRange) { - overlaps = true - break + if case .Spoiler = entity.type { + + } else { + overlaps = true + break + } } } if !overlaps { From c7464c2f1debe49e9f7dec816341a39c8eab228a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 20:21:55 +0400 Subject: [PATCH 10/21] Various Fixes --- .../InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index 0ad165ab2e..4b33f1ba21 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -313,7 +313,7 @@ public class InvisibleInkDustNode: ASDisplayNode { } public override func point(inside point: CGPoint, with event: UIEvent?) -> Bool { - if let (_, _, rects, _) = self.currentParams { + if let (_, _, rects, _) = self.currentParams, !self.isRevealed { for rect in rects { if rect.contains(point) { return true From cae1dbcd3b4675f7c7d8036042cea7b8cfc205e2 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 20:53:31 +0400 Subject: [PATCH 11/21] Fix translation crashing because of emoji in input text --- submodules/Translate/Sources/Translate.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submodules/Translate/Sources/Translate.swift b/submodules/Translate/Sources/Translate.swift index 836f825fd5..57b0b2fc15 100644 --- a/submodules/Translate/Sources/Translate.swift +++ b/submodules/Translate/Sources/Translate.swift @@ -59,6 +59,8 @@ public func translateText(context: AccountContext, text: String) { return } if #available(iOS 15.0, *) { + let text = text.unicodeScalars.filter { !$0.properties.isEmojiPresentation}.reduce("") { $0 + String($1) } + let textView = UITextView() textView.text = text textView.isEditable = false From 31ee543790a6314913a99ed85ca8207772cd28a0 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 21:56:46 +0400 Subject: [PATCH 12/21] Various Fixes --- .../Source/ASEditableTextNode.mm | 23 ++++++++++++++++++- .../Sources/Node/ChatListItem.swift | 3 +++ .../Sources/ChatTextInputPanelNode.swift | 14 +++++++++-- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm b/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm index 984e293e31..4e7f6c3935 100644 --- a/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm +++ b/submodules/AsyncDisplayKit/Source/ASEditableTextNode.mm @@ -114,7 +114,28 @@ } - (void)setContentSize:(CGSize)contentSize { - [super setContentSize:contentSize]; + if (_shouldBlockPanGesture) { + return; + } + [super setContentSize:contentSize]; +} + +- (void)setContentOffset:(CGPoint)contentOffset { + if (_shouldBlockPanGesture) { + return; + } + [super setContentOffset:contentOffset]; +} + +- (void)setContentOffset:(CGPoint)contentOffset animated:(BOOL)animated { + if (_shouldBlockPanGesture) { + return; + } + [super setContentOffset:contentOffset animated:animated]; +} + +- (void)setBounds:(CGRect)bounds { + [super setBounds:bounds]; } - (BOOL)canPerformAction:(SEL)action withSender:(id)sender diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 6e1ab4d3f7..790288bb53 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1867,6 +1867,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { transition.animatePosition(node: strongSelf.titleNode, from: CGPoint(x: titlePosition.x - contentDelta.x, y: titlePosition.y - contentDelta.y)) transition.animatePositionAdditive(node: strongSelf.textNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y)) + if let dustNode = strongSelf.dustNode { + transition.animatePositionAdditive(node: dustNode, offset: CGPoint(x: -contentDelta.x, y: -contentDelta.y)) + } let authorPosition = strongSelf.authorNode.position transition.animatePosition(node: strongSelf.authorNode, from: CGPoint(x: authorPosition.x - contentDelta.x, y: authorPosition.y - contentDelta.y)) diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 2c629d71cd..3bef46b634 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -1908,6 +1908,8 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + textInputNode.textView.isScrollEnabled = false + refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed) textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed) @@ -1916,15 +1918,23 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { let containerView = textInputNode.textView.subviews[1] if let canvasView = containerView.subviews.first { if let snapshotView = canvasView.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = canvasView.frame.offsetBy(dx: 0.0, dy: -textInputNode.textView.contentOffset.y) textInputNode.view.insertSubview(snapshotView, at: 0) canvasView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView, weak textInputNode] _ in + textInputNode?.textView.isScrollEnabled = false snapshotView?.removeFromSuperview() + Queue.mainQueue().after(0.1) { + textInputNode?.textView.isScrollEnabled = true + } }) } } } - + Queue.mainQueue().after(0.1) { + textInputNode.textView.isScrollEnabled = true + } + if animated { if revealed { let transition = ContainedViewLayoutTransition.animated(duration: 0.3, curve: .linear) From 19f9cf1d437a89c47958579f8c3034b85079b33d Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 22:05:38 +0400 Subject: [PATCH 13/21] Fix reactions in contacts --- .../Sources/ChatMessageContactBubbleContentNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift index e89c1e0dca..30682a759e 100644 --- a/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageContactBubbleContentNode.swift @@ -219,7 +219,7 @@ class ChatMessageContactBubbleContentNode: ChatMessageBubbleContentNode { impressionCount: viewCount, dateText: dateText, type: statusType, - layoutInput: .trailingContent(contentWidth: 1000.0, reactionSettings: nil), + layoutInput: .trailingContent(contentWidth: 1000.0, reactionSettings: shouldDisplayInlineDateReactions(message: item.message) ? ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: true, preferAdditionalInset: false) : nil), constrainedSize: CGSize(width: constrainedSize.width - sideInsets, height: .greatestFiniteMagnitude), availableReactions: item.associatedData.availableReactions, reactions: dateReactionsAndPeers.reactions, From 62cebdbc2d4930ca78c0c416b4337fe9a990d2d8 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 22:30:30 +0400 Subject: [PATCH 14/21] Reaction improvements --- .../ReactionListContextMenuContent.swift | 23 +++++++++++++++++++ .../ContextUI/Sources/ContextController.swift | 7 +++++- .../ContextControllerActionsStackNode.swift | 7 ++++++ ...tControllerExtractedPresentationNode.swift | 21 +++++++++++++++++ .../TelegramUI/Sources/ChatController.swift | 2 ++ .../Sources/ChatMessageTransitionNode.swift | 3 ++- 6 files changed, 61 insertions(+), 2 deletions(-) diff --git a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift index a486a03aa4..9fee3b4695 100644 --- a/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift +++ b/submodules/Components/ReactionListContextMenuContent/Sources/ReactionListContextMenuContent.swift @@ -29,15 +29,19 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent init() { self.highlightBackgroundNode = ASDisplayNode() + self.highlightBackgroundNode.isAccessibilityElement = false self.highlightBackgroundNode.alpha = 0.0 self.titleLabelNode = ImmediateTextNode() + self.titleLabelNode.isAccessibilityElement = false self.titleLabelNode.maximumNumberOfLines = 1 self.titleLabelNode.isUserInteractionEnabled = false self.iconNode = ASImageNode() + self.iconNode.isAccessibilityElement = false self.separatorNode = ASDisplayNode() + self.separatorNode.isAccessibilityElement = false super.init() @@ -46,6 +50,8 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent self.addSubnode(self.titleLabelNode) self.addSubnode(self.iconNode) + self.isAccessibilityElement = true + self.highligthedChanged = { [weak self] highlighted in guard let strongSelf = self else { return @@ -74,6 +80,8 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent if self.theme !== presentationData.theme { self.theme = presentationData.theme self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Back"), color: presentationData.theme.contextMenu.primaryColor) + + self.accessibilityLabel = presentationData.strings.Common_Back } self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor @@ -286,23 +294,32 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent var reactionIconNode: ReactionImageNode? let action: () -> Void + private var item: EngineMessageReactionListContext.Item? + init(context: AccountContext, availableReactions: AvailableReactions?, action: @escaping () -> Void) { self.action = action self.context = context self.availableReactions = availableReactions + self.avatarNode = AvatarNode(font: avatarFont) + self.avatarNode.isAccessibilityElement = false self.highlightBackgroundNode = ASDisplayNode() + self.highlightBackgroundNode.isAccessibilityElement = false self.highlightBackgroundNode.alpha = 0.0 self.titleLabelNode = ImmediateTextNode() + self.titleLabelNode.isAccessibilityElement = false self.titleLabelNode.maximumNumberOfLines = 1 self.titleLabelNode.isUserInteractionEnabled = false self.separatorNode = ASDisplayNode() + self.separatorNode.isAccessibilityElement = false super.init() + self.isAccessibilityElement = true + self.addSubnode(self.separatorNode) self.addSubnode(self.highlightBackgroundNode) self.addSubnode(self.avatarNode) @@ -345,6 +362,12 @@ public final class ReactionListContextMenuContent: ContextControllerItemsContent reactionIconNode.removeFromSupernode() } + if self.item != item { + self.item = item + + self.accessibilityLabel = "\(item.peer.debugDisplayTitle) \(item.reaction ?? "")" + } + self.highlightBackgroundNode.backgroundColor = presentationData.theme.contextMenu.itemHighlightedBackgroundColor self.separatorNode.backgroundColor = presentationData.theme.contextMenu.itemSeparatorColor diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index 0ac7f7abd5..6b3864e74c 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -1911,7 +1911,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi } transition.updateFrame(node: self.dismissNode, frame: CGRect(origin: CGPoint(), size: self.scrollNode.view.contentSize)) - self.dismissAccessibilityArea.frame = CGRect(origin: CGPoint(), size: self.scrollNode.view.contentSize) + self.dismissAccessibilityArea.frame = CGRect(origin: CGPoint(), size: self.scrollNode.view.contentSize) } func scrollViewDidScroll(_ scrollView: UIScrollView) { @@ -2370,6 +2370,11 @@ public final class ContextController: ViewController, StandalonePresentableContr self.dismiss(result: .default, completion: completion) } + public func dismissNow() { + self.presentingViewController?.dismiss(animated: false, completion: nil) + self.dismissed?() + } + public func dismissWithReaction(value: String, targetView: UIView, hideNode: Bool, completion: (() -> Void)?) { if !self.wasDismissed { self.wasDismissed = true diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index c3ae419655..ee4cbd847c 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -65,22 +65,29 @@ private final class ContextControllerActionsListActionItemNode: HighlightTrackin self.item = item self.highlightBackgroundNode = ASDisplayNode() + self.highlightBackgroundNode.isAccessibilityElement = false self.highlightBackgroundNode.isUserInteractionEnabled = false self.highlightBackgroundNode.alpha = 0.0 self.titleLabelNode = ImmediateTextNode() + self.titleLabelNode.isAccessibilityElement = false self.titleLabelNode.displaysAsynchronously = false self.titleLabelNode.isUserInteractionEnabled = false self.subtitleNode = ImmediateTextNode() + self.subtitleNode.isAccessibilityElement = false self.subtitleNode.displaysAsynchronously = false self.subtitleNode.isUserInteractionEnabled = false self.iconNode = ASImageNode() + self.iconNode.isAccessibilityElement = false self.iconNode.isUserInteractionEnabled = false super.init() + self.isAccessibilityElement = true + self.accessibilityLabel = item.text + self.addSubnode(self.highlightBackgroundNode) self.addSubnode(self.titleLabelNode) self.addSubnode(self.subtitleNode) diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index 7a8dbdbf98..bac0f86382 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -63,6 +63,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo private let backgroundNode: NavigationBackgroundNode private let dismissTapNode: ASDisplayNode + private let dismissAccessibilityArea: AccessibilityAreaNode private let clippingNode: ASDisplayNode private let scrollNode: ASScrollNode @@ -75,6 +76,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo private var animatingOutState: AnimatingOutState? + private var strings: PresentationStrings? + init( getController: @escaping () -> ContextControllerProtocol?, requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, @@ -91,6 +94,10 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo self.backgroundNode = NavigationBackgroundNode(color: .clear, enableBlur: false) self.dismissTapNode = ASDisplayNode() + + self.dismissAccessibilityArea = AccessibilityAreaNode() + self.dismissAccessibilityArea.accessibilityTraits = .button + self.clippingNode = ASDisplayNode() self.clippingNode.clipsToBounds = true @@ -120,6 +127,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo self.addSubnode(self.clippingNode) self.clippingNode.addSubnode(self.scrollNode) self.scrollNode.addSubnode(self.dismissTapNode) + self.scrollNode.addSubnode(self.dismissAccessibilityArea) self.scrollNode.addSubnode(self.actionsStackNode) /*#if DEBUG @@ -129,6 +137,12 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo self.scrollNode.view.delegate = self self.dismissTapNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dismissTapGesture(_:)))) + + self.dismissAccessibilityArea.activate = { [weak self] in + self?.requestDismiss(.default) + + return true + } } @objc func dismissTapGesture(_ recognizer: UITapGestureRecognizer) { @@ -224,6 +238,12 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo let contentNode: ContentNode var contentTransition = transition + if self.strings !== presentationData.strings { + self.strings = presentationData.strings + + self.dismissAccessibilityArea.accessibilityLabel = presentationData.strings.VoiceOver_DismissContextMenu + } + self.backgroundNode.updateColor( color: presentationData.theme.contextMenu.dimColor, enableBlur: true, @@ -413,6 +433,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo } self.dismissTapNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: max(contentSize.height, layout.size.height))) + self.dismissAccessibilityArea.frame = CGRect(origin: CGPoint(), size: CGSize(width: contentSize.width, height: max(contentSize.height, layout.size.height))) } switch stateTransition { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 48ee69e457..0a0d2100fa 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -8648,6 +8648,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G self.sendMessageActionsController?.dismiss() self.themeSceen?.dismiss() + self.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts() + if let _ = self.peekData { self.peekTimerDisposable.set(nil) } diff --git a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift index 62e47ed088..e07b8d4399 100644 --- a/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageTransitionNode.swift @@ -635,7 +635,8 @@ public final class ChatMessageTransitionNode: ASDisplayNode { func dismiss() { if let contextController = self.contextController { contextController.cancelReactionAnimation() - contextController.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { _ in + contextController.view.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak contextController] _ in + contextController?.dismissNow() }) } if let standaloneReactionAnimation = self.standaloneReactionAnimation { From 4cb8d4320c9271b7926283cc6c5bd3d349615fa8 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 22:37:51 +0400 Subject: [PATCH 15/21] Various Fixes --- submodules/Display/Source/TextNode.swift | 4 +- .../ChatItemGalleryFooterContentNode.swift | 3 ++ .../Sources/InvisibleInkDustNode.swift | 2 - .../PeerSelectionTextInputPanelNode.swift | 41 +++++++++++++++++-- 4 files changed, 43 insertions(+), 7 deletions(-) diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index 72ec3d63b0..34b564e1c9 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -1070,7 +1070,7 @@ public class TextNode: ASDisplayNode { var headIndent: CGFloat = 0.0 attributedString.enumerateAttributes(in: NSMakeRange(brokenLineRange.location, brokenLineRange.length), options: []) { attributes, range, _ in - if let _ = attributes[NSAttributedString.Key.init(rawValue: "TelegramSpoiler")] { + if attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil { var ascent: CGFloat = 0.0 var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) @@ -1151,7 +1151,7 @@ public class TextNode: ASDisplayNode { var headIndent: CGFloat = 0.0 attributedString.enumerateAttributes(in: NSMakeRange(lineRange.location, lineRange.length), options: []) { attributes, range, _ in - if let _ = attributes[NSAttributedString.Key.init(rawValue: "TelegramSpoiler")] { + if attributes[NSAttributedString.Key(rawValue: "TelegramSpoiler")] != nil || attributes[NSAttributedString.Key(rawValue: "Attribute__Spoiler")] != nil { var ascent: CGFloat = 0.0 var descent: CGFloat = 0.0 CTLineGetTypographicBounds(coreTextLine, &ascent, &descent, nil) diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 047f9c984a..099a53da2a 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -101,6 +101,9 @@ class CaptionScrollWrapperNode: ASDisplayNode { if let subnodes = subnode.subnodes { for node in subnodes.reversed() { if node.frame.contains(convertedPoint) && node.isUserInteractionEnabled { + if let dustNode = node as? InvisibleInkDustNode, dustNode.isRevealed { + continue + } return node.view } } diff --git a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift index 4b33f1ba21..a92ce30c6a 100644 --- a/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift +++ b/submodules/InvisibleInkDustNode/Sources/InvisibleInkDustNode.swift @@ -245,8 +245,6 @@ public class InvisibleInkDustNode: ASDisplayNode { } property?.writeBlock = { node, values in if let values = values, let color = CGColor(colorSpace: colorSpace, components: values) { - let uicolor = UIColor(cgColor: color) - print(uicolor) (node as! InvisibleInkDustNode).animColor = color (node as! InvisibleInkDustNode).updateEmitter() } diff --git a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift index 6e2e23917d..968da7a33e 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift @@ -123,6 +123,7 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A var textInputNode: CaptionEditableTextNode? var dustNode: InvisibleInkDustNode? private var oneLineNode: ImmediateTextNode + private var oneLineDustNode: InvisibleInkDustNode? let textInputBackgroundNode: ASDisplayNode let textInputBackgroundImageNode: ASImageNode @@ -633,17 +634,23 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A panelHeight = minimalHeight transition.updateAlpha(node: self.oneLineNode, alpha: inputHasText ? 1.0 : 0.0) + if let oneLineDustNode = self.oneLineDustNode { + transition.updateAlpha(node: oneLineDustNode, alpha: inputHasText ? 1.0 : 0.0) + } if let textInputNode = self.textInputNode { transition.updateAlpha(node: textInputNode, alpha: inputHasText ? 0.0 : 1.0) } } else { self.oneLineNode.alpha = 0.0 + self.oneLineDustNode?.alpha = 0.0 self.textInputNode?.alpha = 1.0 } let oneLineSize = self.oneLineNode.updateLayout(CGSize(width: baseWidth - textFieldInsets.left - textFieldInsets.right, height: CGFloat.greatestFiniteMagnitude)) let oneLineFrame = CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: oneLineSize) self.oneLineNode.frame = oneLineFrame + + self.updateOneLineSpoiler() } self.textPlaceholderNode.isHidden = inputHasText @@ -925,13 +932,19 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A self.textPlaceholderNode.isHidden = inputHasText } - if let attributedText = self.textInputNode?.attributedText, let presentationInterfaceState = self.presentationInterfaceState { + if let presentationInterfaceState = self.presentationInterfaceState { + let textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor + let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + let textFont = Font.regular(baseFontSize) + let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor + + let attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: false) + let range = (attributedText.string as NSString).range(of: "\n") if range.location != NSNotFound { - let textColor = presentationInterfaceState.theme.chat.inputPanel.inputTextColor - let textFont = Font.regular(max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize)) let trimmedText = NSMutableAttributedString(attributedString: attributedText.attributedSubstring(from: NSMakeRange(0, range.location))) trimmedText.append(NSAttributedString(string: "\u{2026}", font: textFont, textColor: textColor)) + self.oneLineNode.attributedText = trimmedText } else { self.oneLineNode.attributedText = attributedText @@ -943,6 +956,28 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A self.updateTextHeight(animated: animated) } + private func updateOneLineSpoiler() { + if let textLayout = self.oneLineNode.cachedLayout, !textLayout.spoilers.isEmpty { + if self.oneLineDustNode == nil { + let oneLineDustNode = InvisibleInkDustNode(textNode: nil) + self.oneLineDustNode = oneLineDustNode + self.oneLineNode.supernode?.insertSubnode(oneLineDustNode, aboveSubnode: self.oneLineNode) + + } + if let oneLineDustNode = self.oneLineDustNode { + let textFrame = self.oneLineNode.frame.insetBy(dx: 0.0, dy: -3.0) + + oneLineDustNode.update(size: textFrame.size, color: .white, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 0.0, dy: 3.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 0.0, dy: 3.0) }) + oneLineDustNode.frame = textFrame + } + } else { + if let oneLineDustNode = self.oneLineDustNode { + self.oneLineDustNode = nil + oneLineDustNode.removeFromSupernode() + } + } + } + private func updateTextHeight(animated: Bool) { if let (width, leftInset, rightInset, additionalSideInsets, maxHeight, metrics, _) = self.validLayout { let (_, textFieldHeight) = self.calculateTextFieldMetrics(width: width - leftInset - rightInset - additionalSideInsets.right, maxHeight: maxHeight, metrics: metrics) From 506c2c175f6da06f445cd4d01f6ffee2b70acf77 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 22:57:55 +0400 Subject: [PATCH 16/21] Various Fixes --- .../Sources/PeerSelectionTextInputPanelNode.swift | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift index 968da7a33e..e65b1d50dd 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionTextInputPanelNode.swift @@ -852,6 +852,8 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A let accentTextColor = presentationInterfaceState.theme.chat.inputPanel.panelControlAccentColor let baseFontSize = max(minInputFontSize, presentationInterfaceState.fontSize.baseDisplaySize) + textInputNode.textView.isScrollEnabled = false + refreshChatTextInputAttributes(textInputNode, theme: presentationInterfaceState.theme, baseFontSize: baseFontSize, spoilersRevealed: self.spoilersRevealed) textInputNode.attributedText = textAttributedStringForStateText(self.inputTextState.inputText, fontSize: baseFontSize, textColor: textColor, accentTextColor: accentTextColor, writingDirection: nil, spoilersRevealed: self.spoilersRevealed) @@ -860,14 +862,22 @@ class PeerSelectionTextInputPanelNode: ChatInputPanelNode, TGCaptionPanelView, A let containerView = textInputNode.textView.subviews[1] if let canvasView = containerView.subviews.first { if let snapshotView = canvasView.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = canvasView.frame.offsetBy(dx: 0.0, dy: -textInputNode.textView.contentOffset.y) textInputNode.view.insertSubview(snapshotView, at: 0) canvasView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView, weak textInputNode] _ in + textInputNode?.textView.isScrollEnabled = false snapshotView?.removeFromSuperview() + Queue.mainQueue().after(0.1) { + textInputNode?.textView.isScrollEnabled = true + } }) } } } + Queue.mainQueue().after(0.1) { + textInputNode.textView.isScrollEnabled = true + } if animated { if revealed { From 02d46a8751d2783c58964015fddcdd2ac91d7f6d Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 23:36:22 +0400 Subject: [PATCH 17/21] Completely hide media caption in landscape --- .../GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift index 099a53da2a..4644f4eb47 100644 --- a/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift +++ b/submodules/GalleryUI/Sources/ChatItemGalleryFooterContentNode.swift @@ -888,7 +888,7 @@ final class ChatItemGalleryFooterContentNode: GalleryFooterContentNode, UIScroll transition.updateBounds(layer: scrubberView.layer, bounds: CGRect(origin: CGPoint(), size: scrubberFrame.size)) transition.updatePosition(layer: scrubberView.layer, position: CGPoint(x: scrubberFrame.midX, y: scrubberFrame.midY)) } - transition.updateAlpha(node: self.textNode, alpha: displayCaption ? 1.0 : 0.0) + transition.updateAlpha(node: self.scrollWrapperNode, alpha: displayCaption ? 1.0 : 0.0) self.actionButton.frame = CGRect(origin: CGPoint(x: leftInset, y: panelHeight - bottomInset - 44.0), size: CGSize(width: 44.0, height: 44.0)) From f6e83ca5b1f4c0d05392bbada1108b189d8d493a Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 23:40:39 +0400 Subject: [PATCH 18/21] Fix video chapter title --- .../Sources/ChatVideoGalleryItemScrubberView.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/submodules/GalleryUI/Sources/ChatVideoGalleryItemScrubberView.swift b/submodules/GalleryUI/Sources/ChatVideoGalleryItemScrubberView.swift index be6ff20f02..22fc7e6b4b 100644 --- a/submodules/GalleryUI/Sources/ChatVideoGalleryItemScrubberView.swift +++ b/submodules/GalleryUI/Sources/ChatVideoGalleryItemScrubberView.swift @@ -171,7 +171,7 @@ final class ChatVideoGalleryItemScrubberView: UIView { if let mappedStatus = mappedStatus { self.chapterDisposable.set((mappedStatus |> deliverOnMainQueue).start(next: { [weak self] status in - if let strongSelf = self, status.duration > 1.0 { + if let strongSelf = self, status.duration > 1.0, strongSelf.chapters.count > 0 { var text: String = "" for chapter in strongSelf.chapters { @@ -230,7 +230,7 @@ final class ChatVideoGalleryItemScrubberView: UIView { if let fetchStatus = fetchStatus { self.fetchStatusDisposable.set((fetchStatus |> deliverOnMainQueue).start(next: { [weak self] status in - if let strongSelf = self { + if let strongSelf = self, strongSelf.chapters.isEmpty { var text: String switch status { case .Remote: @@ -247,10 +247,10 @@ final class ChatVideoGalleryItemScrubberView: UIView { } } })) - } else { + } else if self.chapters.isEmpty { self.infoNode.attributedText = NSAttributedString(string: dataSizeString(fileSize, forceDecimal: true, formatting: formatting), font: textFont, textColor: .white) } - } else { + } else if self.chapters.isEmpty { self.infoNode.attributedText = nil } } From 16d31832144e9c716fd1e4b9010b7e6da0ed7da3 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Thu, 23 Dec 2021 23:52:47 +0400 Subject: [PATCH 19/21] Fix RTL in link previews --- .../Sources/ChatMessageAttachedContentNode.swift | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift index 41fc48ffca..3cf96b13bc 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAttachedContentNode.swift @@ -647,6 +647,12 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { var statusSuggestedWidthAndContinue: (CGFloat, (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> Void))? if statusInText, let textStatusType = textStatusType { + let trailingContentWidth: CGFloat + if textLayout.hasRTL { + trailingContentWidth = 10000.0 + } else { + trailingContentWidth = textLayout.trailingLineWidth + } statusSuggestedWidthAndContinue = statusLayout(ChatMessageDateAndStatusNode.Arguments( context: context, presentationData: presentationData, @@ -654,7 +660,7 @@ final class ChatMessageAttachedContentNode: ASDisplayNode { impressionCount: viewCount, dateText: dateText, type: textStatusType, - layoutInput: .trailingContent(contentWidth: textLayout.trailingLineWidth, reactionSettings: shouldDisplayInlineDateReactions(message: message) ? ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: true, preferAdditionalInset: false) : nil), + layoutInput: .trailingContent(contentWidth: trailingContentWidth, reactionSettings: shouldDisplayInlineDateReactions(message: message) ? ChatMessageDateAndStatusNode.TrailingReactionSettings(displayInline: true, preferAdditionalInset: false) : nil), constrainedSize: textConstrainedSize, availableReactions: associatedData.availableReactions, reactions: dateReactionsAndPeers.reactions, From 1d58a81bcbd913a47c5872ace3cf0d6bf9c71236 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Thu, 23 Dec 2021 23:53:48 +0400 Subject: [PATCH 20/21] Fix default dark QR code theme wallpaper --- .../TelegramUI/Sources/ChatQrCodeScreen.swift | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift index 47521515b8..a5d326cf7a 100644 --- a/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift +++ b/submodules/TelegramUI/Sources/ChatQrCodeScreen.swift @@ -925,7 +925,15 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg let presentationData = strongSelf.presentationData var entries: [ThemeSettingsThemeEntry] = [] - entries.append(ThemeSettingsThemeEntry(index: 0, emoticon: defaultEmoticon, emojiFile: animatedEmojiStickers[defaultEmoticon]?.first?.file, themeReference: .builtin(isDarkAppearance ? .night : .dayClassic), nightMode: isDarkAppearance, selected: selectedEmoticon == defaultEmoticon, theme: presentationData.theme, strings: presentationData.strings, wallpaper: nil)) + + let defaultWallpaper: TelegramWallpaper? + if isDarkAppearance { + let dayTheme = makeDefaultPresentationTheme(reference: .dayClassic, serviceBackgroundColor: nil) + defaultWallpaper = dayTheme.chat.defaultWallpaper.withUpdatedSettings(WallpaperSettings(blur: false, motion: false, colors: [0x00b3dd, 0x3b59f2, 0x358be2, 0xa434cf], intensity: -55, rotation: nil)) + } else { + defaultWallpaper = nil + } + entries.append(ThemeSettingsThemeEntry(index: 0, emoticon: defaultEmoticon, emojiFile: animatedEmojiStickers[defaultEmoticon]?.first?.file, themeReference: .builtin(isDarkAppearance ? .night : .dayClassic), nightMode: isDarkAppearance, selected: selectedEmoticon == defaultEmoticon, theme: presentationData.theme, strings: presentationData.strings, wallpaper: defaultWallpaper)) for theme in themes { guard let emoticon = theme.emoticon else { continue @@ -936,7 +944,11 @@ private class ChatQrCodeScreenNode: ViewControllerTracingNode, UIScrollViewDeleg let wallpaper: TelegramWallpaper if selectedEmoticon == defaultEmoticon { let presentationTheme = makeDefaultPresentationTheme(reference: isDarkAppearance ? .night : .dayClassic, serviceBackgroundColor: nil) - wallpaper = presentationTheme.chat.defaultWallpaper + if isDarkAppearance { + wallpaper = entries.first?.wallpaper ?? .color(0x000000) + } else { + wallpaper = presentationTheme.chat.defaultWallpaper + } } else if let theme = themes.first(where: { $0.emoticon == selectedEmoticon }), let presentationTheme = makePresentationTheme(cloudTheme: theme, dark: isDarkAppearance) { wallpaper = presentationTheme.chat.defaultWallpaper } else { From 81810c94a992f998835300bbeaf79ff52d4410fc Mon Sep 17 00:00:00 2001 From: Ali <> Date: Fri, 24 Dec 2021 01:05:58 +0400 Subject: [PATCH 21/21] Implement tooltips --- .../Sources/ContextActionsContainerNode.swift | 2 +- .../ContextControllerActionsStackNode.swift | 74 ++++++++++++++++++- ...tControllerExtractedPresentationNode.swift | 2 + 3 files changed, 73 insertions(+), 5 deletions(-) diff --git a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift index 7dcabb6bb4..b4b0647024 100644 --- a/submodules/ContextUI/Sources/ContextActionsContainerNode.swift +++ b/submodules/ContextUI/Sources/ContextActionsContainerNode.swift @@ -337,7 +337,7 @@ private final class InnerActionsContainerNode: ASDisplayNode { } } -private final class InnerTextSelectionTipContainerNode: ASDisplayNode { +final class InnerTextSelectionTipContainerNode: ASDisplayNode { private let presentationData: PresentationData private var effectView: UIVisualEffectView? private let textNode: TextNode diff --git a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift index ee4cbd847c..41ee37707a 100644 --- a/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerActionsStackNode.swift @@ -29,6 +29,7 @@ public protocol ContextControllerActionsStackItem: AnyObject { requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void ) -> ContextControllerActionsStackItemNode + var tip: ContextController.Tip? { get } var reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? { get } } @@ -558,13 +559,16 @@ final class ContextControllerActionsListStackItem: ContextControllerActionsStack private let items: [ContextMenuItem] let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? + let tip: ContextController.Tip? init( items: [ContextMenuItem], - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? + reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?, + tip: ContextController.Tip? ) { self.items = items self.reactionItems = reactionItems + self.tip = tip } func node( @@ -632,13 +636,16 @@ final class ContextControllerActionsCustomStackItem: ContextControllerActionsSta private let content: ContextControllerItemsContent let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? + let tip: ContextController.Tip? init( content: ContextControllerItemsContent, - reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? + reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?, + tip: ContextController.Tip? ) { self.content = content self.reactionItems = reactionItems + self.tip = tip } func node( @@ -663,9 +670,9 @@ func makeContextControllerActionsStackItem(items: ContextController.Items) -> Co } switch items.content { case let .list(listItems): - return ContextControllerActionsListStackItem(items: listItems, reactionItems: reactionItems) + return ContextControllerActionsListStackItem(items: listItems, reactionItems: reactionItems, tip: items.tip) case let .custom(customContent): - return ContextControllerActionsCustomStackItem(content: customContent, reactionItems: reactionItems) + return ContextControllerActionsCustomStackItem(content: customContent, reactionItems: reactionItems, tip: items.tip) } } @@ -728,6 +735,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode { let requestUpdate: (ContainedViewLayoutTransition) -> Void let node: ContextControllerActionsStackItemNode let dimNode: ASDisplayNode + let tip: ContextController.Tip? + var tipNode: InnerTextSelectionTipContainerNode? let reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])? var storedScrollingState: CGFloat? let positionLock: CGFloat? @@ -738,6 +747,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, requestUpdateApparentHeight: @escaping (ContainedViewLayoutTransition) -> Void, item: ContextControllerActionsStackItem, + tip: ContextController.Tip?, reactionItems: (context: AccountContext, reactionItems: [ReactionContextItem])?, positionLock: CGFloat? ) { @@ -756,6 +766,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode { self.reactionItems = reactionItems self.positionLock = positionLock + self.tip = tip + super.init() self.clipsToBounds = true @@ -790,6 +802,27 @@ final class ContextControllerActionsStackNode: ASDisplayNode { return (size, apparentHeight) } + func updateTip(presentationData: PresentationData, width: CGFloat, transition: ContainedViewLayoutTransition) -> (node: ASDisplayNode, height: CGFloat)? { + if let tip = self.tip { + var updatedTransition = transition + if self.tipNode == nil { + updatedTransition = .immediate + let tipNode = InnerTextSelectionTipContainerNode(presentationData: presentationData, tip: tip) + tipNode.isUserInteractionEnabled = false + self.tipNode = tipNode + } + + if let tipNode = self.tipNode { + let size = tipNode.updateLayout(widthClass: .compact, width: width, transition: updatedTransition) + return (tipNode, size.height) + } else { + return nil + } + } else { + return nil + } + } + func updateDimNode(presentationData: PresentationData, size: CGSize, transitionFraction: CGFloat, transition: ContainedViewLayoutTransition) { self.dimNode.backgroundColor = presentationData.theme.contextMenu.sectionSeparatorColor @@ -906,6 +939,7 @@ final class ContextControllerActionsStackNode: ASDisplayNode { strongSelf.requestUpdate(transition) }, item: item, + tip: item.tip, reactionItems: item.reactionItems, positionLock: positionLock ) @@ -946,6 +980,8 @@ final class ContextControllerActionsStackNode: ASDisplayNode { constrainedSize: CGSize, transition: ContainedViewLayoutTransition ) -> CGSize { + let tipSpacing: CGFloat = 10.0 + self.navigationContainer.backgroundColor = presentationData.theme.contextMenu.backgroundColor let animateAppearingContainers = transition.isAnimated && !self.dismissingItemContainers.isEmpty @@ -1047,6 +1083,23 @@ final class ContextControllerActionsStackNode: ASDisplayNode { } self.itemContainers[i].updateDimNode(presentationData: presentationData, size: CGSize(width: itemLayouts[i].size.width, height: navigationContainerFrame.size.height), transitionFraction: itemLayouts[i].alphaTransitionFraction, transition: transition) + + if let (tipNode, tipHeight) = self.itemContainers[i].updateTip(presentationData: presentationData, width: itemLayouts[i].size.width, transition: transition) { + var tipTransition = transition + if tipNode.supernode == nil { + tipTransition = .immediate + self.addSubnode(tipNode) + } + + let tipAlpha: CGFloat = itemLayouts[i].alphaTransitionFraction + + tipTransition.updateFrame(node: tipNode, frame: CGRect(origin: CGPoint(x: navigationContainerFrame.minX, y: navigationContainerFrame.maxY + tipSpacing), size: CGSize(width: itemLayouts[i].size.width, height: tipHeight)), beginWithCurrentState: true) + tipTransition.updateAlpha(node: tipNode, alpha: tipAlpha, beginWithCurrentState: true) + + if i == self.itemContainers.count - 1 { + topItemSize.height += tipSpacing + tipHeight + } + } } for (itemContainer, isPopped) in self.dismissingItemContainers { @@ -1059,6 +1112,11 @@ final class ContextControllerActionsStackNode: ASDisplayNode { transition.updatePosition(node: itemContainer, position: position, completion: { [weak itemContainer] _ in itemContainer?.removeFromSupernode() }) + if let tipNode = itemContainer.tipNode { + transition.updateAlpha(node: tipNode, alpha: 0.0, completion: { [weak tipNode] _ in + tipNode?.removeFromSupernode() + }) + } } self.dismissingItemContainers.removeAll() @@ -1082,4 +1140,12 @@ final class ContextControllerActionsStackNode: ASDisplayNode { selectionPanGesture.isEnabled = isEnabled } } + + func animateIn() { + for itemContainer in self.itemContainers { + if let tipNode = itemContainer.tipNode { + tipNode.animateIn() + } + } + } } diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index bac0f86382..0ef886fe5d 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -503,6 +503,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo reactionContextNode.animateIn(from: currentContentScreenFrame) } + self.actionsStackNode.animateIn() + contentNode.containingNode.isExtractedToContextPreview = true contentNode.containingNode.isExtractedToContextPreviewUpdated?(true) contentNode.containingNode.willUpdateIsExtractedToContextPreview?(true, transition)