diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 1c3121229c..6a98e350f4 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -8622,3 +8622,5 @@ Sorry for the inconvenience."; "StorageManagement.OpenPhoto" = "Open Photo"; "StorageManagement.OpenVideo" = "Open Video"; "StorageManagement.OpenFile" = "Open File"; + +"ChatListFilter.AddChatsSearchPlaceholder" = "Search Chats"; diff --git a/submodules/AccountContext/Sources/ContactMultiselectionController.swift b/submodules/AccountContext/Sources/ContactMultiselectionController.swift index 31f58f369c..63e9957c5d 100644 --- a/submodules/AccountContext/Sources/ContactMultiselectionController.swift +++ b/submodules/AccountContext/Sources/ContactMultiselectionController.swift @@ -14,12 +14,14 @@ public struct ChatListNodeAdditionalCategory { public var id: Int public var icon: UIImage? + public var smallIcon: UIImage? public var title: String public var appearance: Appearance - public init(id: Int, icon: UIImage?, title: String, appearance: Appearance = .option) { + public init(id: Int, icon: UIImage?, smallIcon: UIImage?, title: String, appearance: Appearance = .option) { self.id = id self.icon = icon + self.smallIcon = smallIcon self.title = title self.appearance = appearance } diff --git a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift index 22e8301f4a..d80d2ad40b 100644 --- a/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift +++ b/submodules/AttachmentTextInputPanelNode/Sources/AttachmentTextInputPanelNode.swift @@ -1506,14 +1506,21 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS return UIMenu(children: actions) } + private var currentSpeechHolder: SpeechSynthesizerHolder? @objc func _accessibilitySpeak(_ sender: Any) { var text = "" self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in text = current.inputText.attributedSubstring(from: NSMakeRange(current.selectionRange.lowerBound, current.selectionRange.count)).string return (current, inputMode) } - let _ = speakText(context: self.context, text: text) - + if let speechHolder = speakText(context: self.context, text: text) { + speechHolder.completion = { [weak self, weak speechHolder] in + if let strongSelf = self, strongSelf.currentSpeechHolder == speechHolder { + strongSelf.currentSpeechHolder = nil + } + } + self.currentSpeechHolder = speechHolder + } if #available(iOS 13.0, *) { UIMenuController.shared.hideMenu() } else { diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 1da88a5d83..301cff0eed 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -751,12 +751,20 @@ public enum AvatarBackgroundColor { case violet } -public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat = 1.0, color: AvatarBackgroundColor) -> UIImage? { +public func generateAvatarImage(size: CGSize, icon: UIImage?, iconScale: CGFloat = 1.0, cornerRadius: CGFloat? = nil, circleCorners: Bool = false, color: AvatarBackgroundColor) -> UIImage? { return generateImage(size, rotatedContext: { size, context in context.clear(CGRect(origin: CGPoint(), size: size)) - context.beginPath() - context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: - size.height)) + if let cornerRadius { + if circleCorners { + let roundedRect = CGPath(roundedRect: CGRect(origin: .zero, size: size), cornerWidth: cornerRadius, cornerHeight: cornerRadius, transform: nil) + context.addPath(roundedRect) + } else { + let roundedRect = UIBezierPath(roundedRect: CGRect(origin: .zero, size: size), cornerRadius: cornerRadius) + context.addPath(roundedRect.cgPath) + } + } else { + context.addEllipse(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)) + } context.clip() let colorIndex: Int diff --git a/submodules/CallListUI/Sources/CallListController.swift b/submodules/CallListUI/Sources/CallListController.swift index c29f646cc4..23839ef1c3 100644 --- a/submodules/CallListUI/Sources/CallListController.swift +++ b/submodules/CallListUI/Sources/CallListController.swift @@ -237,32 +237,35 @@ public final class CallListController: TelegramBaseController { strongSelf.navigationItem.setRightBarButton(nil, animated: true) } } else { + var pressedImpl: (() -> Void)? + let buttonNode = DeleteAllButtonNode(presentationData: strongSelf.presentationData, pressed: { + pressedImpl?() + }) + pressedImpl = { [weak self, weak buttonNode] in + guard let strongSelf = self, let buttonNode = buttonNode else { + return + } + strongSelf.deleteAllPressed(buttonNode: buttonNode) + } + switch strongSelf.mode { case .tab: if strongSelf.editingMode { strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed)), animated: true) - var pressedImpl: (() -> Void)? - let buttonNode = DeleteAllButtonNode(presentationData: strongSelf.presentationData, pressed: { - pressedImpl?() - }) strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: true) strongSelf.navigationItem.rightBarButtonItem?.setCustomAction({ pressedImpl?() }) - pressedImpl = { [weak self, weak buttonNode] in - guard let strongSelf = self, let buttonNode = buttonNode else { - return - } - strongSelf.deleteAllPressed(buttonNode: buttonNode) - } - - //strongSelf.navigationItem.rightBarButtonItem = UIBarButtonItem(title: strongSelf.presentationData.strings.Notification_Exceptions_DeleteAll, style: .plain, target: strongSelf, action: #selector(strongSelf.deleteAllPressed)) } else { strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: true) strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(strongSelf.presentationData.theme), style: .plain, target: self, action: #selector(strongSelf.callPressed)), animated: true) } case .navigation: if strongSelf.editingMode { + strongSelf.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: true) + strongSelf.navigationItem.leftBarButtonItem?.setCustomAction({ + pressedImpl?() + }) strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Done, style: .done, target: strongSelf, action: #selector(strongSelf.donePressed)), animated: true) } else { strongSelf.navigationItem.setRightBarButton(UIBarButtonItem(title: strongSelf.presentationData.strings.Common_Edit, style: .plain, target: strongSelf, action: #selector(strongSelf.editPressed)), animated: true) @@ -412,25 +415,31 @@ public final class CallListController: TelegramBaseController { @objc func editPressed() { self.editingMode = true + var pressedImpl: (() -> Void)? + let buttonNode = DeleteAllButtonNode(presentationData: self.presentationData, pressed: { + pressedImpl?() + }) + pressedImpl = { [weak self, weak buttonNode] in + guard let strongSelf = self, let buttonNode = buttonNode else { + return + } + strongSelf.deleteAllPressed(buttonNode: buttonNode) + } + switch self.mode { case .tab: self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)), animated: true) - var pressedImpl: (() -> Void)? - let buttonNode = DeleteAllButtonNode(presentationData: self.presentationData, pressed: { - pressedImpl?() - }) + self.navigationItem.setRightBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: true) self.navigationItem.rightBarButtonItem?.setCustomAction({ pressedImpl?() }) - pressedImpl = { [weak self, weak buttonNode] in - guard let strongSelf = self, let buttonNode = buttonNode else { - return - } - strongSelf.deleteAllPressed(buttonNode: buttonNode) - } - //self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Notification_Exceptions_DeleteAll, style: .plain, target: self, action: #selector(self.deleteAllPressed)) case .navigation: + self.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: buttonNode), animated: true) + self.navigationItem.leftBarButtonItem?.setCustomAction({ + pressedImpl?() + }) + self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Done, style: .done, target: self, action: #selector(self.donePressed)), animated: true) } @@ -446,6 +455,7 @@ public final class CallListController: TelegramBaseController { self.navigationItem.setLeftBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true) self.navigationItem.setRightBarButton(UIBarButtonItem(image: PresentationResourcesRootController.navigationCallIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.callPressed)), animated: true) case .navigation: + self.navigationItem.setLeftBarButton(nil, animated: true) self.navigationItem.setRightBarButton(UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed)), animated: true) } diff --git a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift index e838fdcddc..e33cadd481 100644 --- a/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift +++ b/submodules/ChatListUI/Sources/ChatListFilterPresetController.swift @@ -561,27 +561,32 @@ private func internalChatListFilterAddChatsController(context: AccountContext, f let additionalCategories: [ChatListNodeAdditionalCategory] = [ ChatListNodeAdditionalCategory( id: AdditionalCategoryId.contacts.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/User"), color: .white), color: .blue), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), cornerRadius: 12.0, color: .blue), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Contact"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue), title: presentationData.strings.ChatListFolder_CategoryContacts ), ChatListNodeAdditionalCategory( id: AdditionalCategoryId.nonContacts.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/UnknownUser"), color: .white), color: .yellow), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/User"), color: .white), cornerRadius: 12.0, color: .yellow), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/User"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .yellow), title: presentationData.strings.ChatListFolder_CategoryNonContacts ), ChatListNodeAdditionalCategory( id: AdditionalCategoryId.groups.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Groups"), color: .white), color: .green), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Group"), color: .white), cornerRadius: 12.0, color: .green), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Group"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .green), title: presentationData.strings.ChatListFolder_CategoryGroups ), ChatListNodeAdditionalCategory( id: AdditionalCategoryId.channels.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Channels"), color: .white), color: .red), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white), cornerRadius: 12.0, color: .red), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Channel"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .red), title: presentationData.strings.ChatListFolder_CategoryChannels ), ChatListNodeAdditionalCategory( id: AdditionalCategoryId.bots.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Bots"), color: .white), color: .violet), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Bot"), color: .white), cornerRadius: 12.0, color: .violet), + smallIcon: generateAvatarImage(size: CGSize(width: 22.0, height: 22.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Bot"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .violet), title: presentationData.strings.ChatListFolder_CategoryBots ) ] @@ -603,7 +608,7 @@ private func internalChatListFilterAddChatsController(context: AccountContext, f let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(ContactMultiselectionControllerMode.ChatSelection( title: presentationData.strings.ChatListFolder_IncludeChatsTitle, - searchPlaceholder: presentationData.strings.ChatListFilter_AddChatsTitle, + searchPlaceholder: presentationData.strings.ChatListFilter_AddChatsSearchPlaceholder, selectedChats: Set(filterData.includePeers.peers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories), chatListFilters: allFilters @@ -704,17 +709,20 @@ private func internalChatListFilterExcludeChatsController(context: AccountContex let additionalCategories: [ChatListNodeAdditionalCategory] = [ ChatListNodeAdditionalCategory( id: AdditionalExcludeCategoryId.muted.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Muted"), color: .white), color: .red), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Muted"), color: .white), cornerRadius: 12.0, color: .red), + smallIcon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Muted"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .red), title: presentationData.strings.ChatListFolder_CategoryMuted ), ChatListNodeAdditionalCategory( id: AdditionalExcludeCategoryId.read.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/MarkAsRead"), color: .white), color: .blue), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Read"), color: .white), cornerRadius: 12.0, color: .blue), + smallIcon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Read"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .blue), title: presentationData.strings.ChatListFolder_CategoryRead ), ChatListNodeAdditionalCategory( id: AdditionalExcludeCategoryId.archived.rawValue, - icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Archive"), color: .white), color: .yellow), + icon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Archive"), color: .white), cornerRadius: 12.0, color: .yellow), + smallIcon: generateAvatarImage(size: CGSize(width: 40.0, height: 40.0), icon: generateTintedImage(image: UIImage(bundleImageName: "Chat List/Filters/Archive"), color: .white), iconScale: 0.6, cornerRadius: 6.0, circleCorners: true, color: .yellow), title: presentationData.strings.ChatListFolder_CategoryArchived ), ] @@ -731,7 +739,7 @@ private func internalChatListFilterExcludeChatsController(context: AccountContex let controller = context.sharedContext.makeContactMultiselectionController(ContactMultiselectionControllerParams(context: context, mode: .chatSelection(ContactMultiselectionControllerMode.ChatSelection( title: presentationData.strings.ChatListFolder_ExcludeChatsTitle, - searchPlaceholder: presentationData.strings.ChatListFilter_AddChatsTitle, + searchPlaceholder: presentationData.strings.ChatListFilter_AddChatsSearchPlaceholder, selectedChats: Set(filterData.excludePeers), additionalCategories: ContactMultiselectionControllerAdditionalCategories(categories: additionalCategories, selectedCategories: selectedCategories), chatListFilters: allFilters diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index b4e95c1dca..0e2e2b7ad0 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -411,7 +411,7 @@ private func forumGeneralRevealOptions(strings: PresentationStrings, theme: Pres if canOpenClose && !hiddenByDefault { if !isEditing { if !isClosed { -// options.append(ItemListRevealOption(key: RevealOptionKey.close.rawValue, title: strings.ChatList_CloseAction, icon: closeIcon, color: theme.list.itemDisclosureActions.inactive.fillColor, textColor: theme.list.itemDisclosureActions.inactive.foregroundColor)) + } else { options.append(ItemListRevealOption(key: RevealOptionKey.open.rawValue, title: strings.ChatList_StartAction, icon: startIcon, color: theme.list.itemDisclosureActions.constructive.fillColor, textColor: theme.list.itemDisclosureActions.constructive.foregroundColor)) } @@ -2533,8 +2533,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } strongSelf.currentOnline = online - if let currentHiddenOffset = currentItem?.hiddenOffset, item.hiddenOffset, currentHiddenOffset != item.hiddenOffset { - strongSelf.supernode?.insertSubnode(strongSelf, at: 0) + if item.hiddenOffset { + strongSelf.layer.zPosition = -1.0 } if case .groupReference = item.content { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift index 1689de42d1..d42a0046aa 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift @@ -975,6 +975,8 @@ public final class ChatListNode: ListView { private var visibleTopInset: CGFloat? private var originalTopInset: CGFloat? + public var passthroughPeerSelection = false + let hideArhiveIntro = ValuePromise(false, ignoreRepeated: true) public init(context: AccountContext, location: ChatListControllerLocation, chatListFilter: ChatListFilter? = nil, previewing: Bool, fillPreloadItems: Bool, mode: ChatListNodeMode, isPeerEnabled: ((EnginePeer) -> Bool)? = nil, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, disableAnimations: Bool, isInlineMode: Bool) { @@ -1020,12 +1022,12 @@ public final class ChatListNode: ListView { guard let strongSelf = self else { return } -// if case .peers = strongSelf.mode { -// if let strongSelf = self, let peerSelected = strongSelf.peerSelected { -// peerSelected(peer, nil, true, true, nil) -// } -// return -// } + if case .peers = strongSelf.mode, strongSelf.passthroughPeerSelection { + if let strongSelf = self, let peerSelected = strongSelf.peerSelected { + peerSelected(peer, nil, true, true, nil) + } + return + } var didBeginSelecting = false var count = 0 strongSelf.updateState { [weak self] state in diff --git a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift index d9de6b9510..4c9a2e2a5f 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNodeEntries.swift @@ -670,7 +670,7 @@ func chatListNodeEntriesForView(_ view: EngineChatList, state: ChatListNodeState if !view.hasLater, case let .peers(_, _, additionalCategories, _, _) = mode { var index = 0 - for category in additionalCategories.reversed(){ + for category in additionalCategories.reversed() { result.append(.AdditionalCategory(index: index, id: category.id, title: category.title, image: category.icon, appearance: category.appearance, selected: state.selectedAdditionalCategoryIds.contains(category.id), presentationData: state.presentationData)) index += 1 } diff --git a/submodules/Display/Source/DisplayLinkAnimator.swift b/submodules/Display/Source/DisplayLinkAnimator.swift index 2089800711..3297c2556a 100644 --- a/submodules/Display/Source/DisplayLinkAnimator.swift +++ b/submodules/Display/Source/DisplayLinkAnimator.swift @@ -58,11 +58,15 @@ public final class SharedDisplayLinkDriver { self.update() }) - switch UIApplication.shared.applicationState { - case .active: + if Bundle.main.bundlePath.hasSuffix(".appex") { self.isInForeground = true - default: - self.isInForeground = false + } else { + switch UIApplication.shared.applicationState { + case .active: + self.isInForeground = true + default: + self.isInForeground = false + } } self.update() diff --git a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift index 80c40276b4..b0454a7af2 100644 --- a/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift +++ b/submodules/GalleryUI/Sources/Items/ChatImageGalleryItem.swift @@ -227,6 +227,8 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { private let pagingEnabledPromise = ValuePromise(true) + private var currentSpeechHolder: SpeechSynthesizerHolder? + init(context: AccountContext, presentationData: PresentationData, performAction: @escaping (GalleryControllerInteractionTapAction) -> Void, openActionOptions: @escaping (GalleryControllerInteractionTapAction, Message) -> Void, present: @escaping (ViewController, Any?) -> Void) { self.context = context self.presentationData = presentationData @@ -378,7 +380,14 @@ final class ChatImageGalleryItemNode: ZoomableContentGalleryItemNode { window.rootViewController?.present(controller, animated: true) } case .speak: - let _ = speakText(context: strongSelf.context, text: string) + if let speechHolder = speakText(context: strongSelf.context, text: string) { + speechHolder.completion = { [weak self, weak speechHolder] in + if let strongSelf = self, strongSelf.currentSpeechHolder == speechHolder { + strongSelf.currentSpeechHolder = nil + } + } + strongSelf.currentSpeechHolder = speechHolder + } case .translate: if let parentController = strongSelf.baseNavigationController()?.topViewController as? ViewController { let controller = TranslateScreen(context: strongSelf.context, text: string, canCopy: true, fromLanguage: nil) diff --git a/submodules/GameUI/Sources/GameControllerNode.swift b/submodules/GameUI/Sources/GameControllerNode.swift index d54b1fec04..5ee44cd3fb 100644 --- a/submodules/GameUI/Sources/GameControllerNode.swift +++ b/submodules/GameUI/Sources/GameControllerNode.swift @@ -144,9 +144,9 @@ final class GameControllerNode: ViewControllerTracingNode { if eventName == "share_game" || eventName == "share_score" { if let (botPeer, gameName) = self.shareData(), let addressName = botPeer.addressName, !addressName.isEmpty, !gameName.isEmpty { if eventName == "share_score" { - self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, text, account, _ in + self.present(ShareController(context: self.context, subject: .fromExternal({ [weak self] peerIds, threadIds, text, account, _ in if let strongSelf = self, let message = strongSelf.message { - let signals = peerIds.map { TelegramEngine(account: account).messages.forwardGameWithScore(messageId: message.id, to: $0, as: nil) } + let signals = peerIds.map { TelegramEngine(account: account).messages.forwardGameWithScore(messageId: message.id, to: $0, threadId: threadIds[$0], as: nil) } return .single(.preparing(false)) |> castError(ShareControllerError.self) |> then( diff --git a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift index 6663053c28..dccb694f8a 100644 --- a/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift +++ b/submodules/ListMessageItem/Sources/ListMessageFileItemNode.swift @@ -1193,6 +1193,10 @@ public final class ListMessageFileItemNode: ListMessageNode { let _ = extensionTextApply() strongSelf.currentIconImage = iconImage + + if let updateIconImageSignal, let iconImage, case .albumArt = iconImage { + strongSelf.iconStatusNode.setBackgroundImage(updateIconImageSignal) + } if let iconImageApply = iconImageApply { if let updateImageSignal = updateIconImageSignal { diff --git a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift index e95bdd9d0b..a45e5ae06d 100644 --- a/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift +++ b/submodules/MediaPlayer/Sources/MediaPlayerScrubbingNode.swift @@ -722,7 +722,10 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode { self.updateProgressAnimations() } + private var isCollapsed = false public func setCollapsed(_ collapsed: Bool, animated: Bool) { + self.isCollapsed = collapsed + let alpha: CGFloat = collapsed ? 0.0 : 1.0 let backgroundScale: CGFloat = collapsed ? 0.4 : 1.0 let handleScale: CGFloat = collapsed ? 0.2 : 1.0 @@ -956,10 +959,12 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode { if let handleNodeContainer = node.handleNodeContainer { handleNodeContainer.bounds = bounds.offsetBy(dx: -floorToScreenPixels(bounds.size.width * progress), dy: 0.0) - if handleNodeContainer.alpha.isZero { - handleNodeContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + if !self.isCollapsed { + if handleNodeContainer.alpha.isZero { + handleNodeContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + handleNodeContainer.alpha = 1.0 } - handleNodeContainer.alpha = 1.0 } } else if let statusValue = self.statusValue { var actualTimestamp: Double @@ -987,10 +992,12 @@ public final class MediaPlayerScrubbingNode: ASDisplayNode { if let handleNodeContainer = node.handleNodeContainer { handleNodeContainer.bounds = bounds.offsetBy(dx: -floorToScreenPixels(bounds.size.width * progress), dy: 0.0) - if handleNodeContainer.alpha.isZero { - handleNodeContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + if !self.isCollapsed { + if handleNodeContainer.alpha.isZero { + handleNodeContainer.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + handleNodeContainer.alpha = 1.0 } - handleNodeContainer.alpha = 1.0 } } else { node.foregroundNode.frame = CGRect(origin: backgroundFrame.origin, size: CGSize(width: 0.0, height: backgroundFrame.size.height)) diff --git a/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift b/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift index c7906f79d1..9949f1a771 100644 --- a/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift +++ b/submodules/MusicAlbumArtResources/Sources/ExternalMusicAlbumArtResources.swift @@ -60,103 +60,4 @@ public class ExternalMusicAlbumArtResource: Equatable { public func fetchExternalMusicAlbumArtResource(engine: TelegramEngine, file: FileMediaReference?, resource: ExternalMusicAlbumArtResource) -> Signal { return engine.resources.fetchAlbumCover(file: file, title: resource.title, performer: resource.performer, isThumbnail: resource.isThumbnail) - - /*return Signal { subscriber in - if resource.performer.isEmpty || resource.performer.lowercased().trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) == "unknown artist" || resource.title.isEmpty { - subscriber.putError(.generic) - return EmptyDisposable - } else { - let excludeWords: [String] = [ - " vs. ", - " vs ", - " versus ", - " ft. ", - " ft ", - " featuring ", - " feat. ", - " feat ", - " presents ", - " pres. ", - " pres ", - " and ", - " & ", - " . " - ] - - var performer = resource.performer - - for word in excludeWords { - performer = performer.replacingOccurrences(of: word, with: " ") - } - - let metaUrl = "https://itunes.apple.com/search?term=\(urlEncodedStringFromString("\(performer) \(resource.title)"))&entity=song&limit=4" - - let title = resource.title.lowercased() - let isMix = title.contains("remix") || title.contains("mixed") - - let fetchDisposable = MetaDisposable() - - let disposable = fetchHttpResource(url: metaUrl).start(next: { result in - if case let .dataPart(_, data, _, complete) = result, complete { - guard let dict = (try? JSONSerialization.jsonObject(with: data, options: [])) as? [String: Any] else { - subscriber.putError(.generic) - return - } - - guard let results = dict["results"] as? [Any] else { - subscriber.putError(.generic) - return - } - - var matchingResult: Any? - for result in results { - if let result = result as? [String: Any] { - let title = ((result["trackCensoredName"] as? String) ?? (result["trackName"] as? String))?.lowercased() ?? "" - let resultIsMix = title.contains("remix") || title.contains("mixed") - if isMix == resultIsMix { - matchingResult = result - break - } - } - } - - if matchingResult == nil { - matchingResult = results.first - } - - guard let result = matchingResult as? [String: Any] else { - subscriber.putError(.generic) - return - } - - guard var artworkUrl = result["artworkUrl100"] as? String else { - subscriber.putError(.generic) - return - } - - if !resource.isThumbnail { - artworkUrl = artworkUrl.replacingOccurrences(of: "100x100", with: "600x600") - } - - if artworkUrl.isEmpty { - subscriber.putError(.generic) - return - } else { - fetchDisposable.set(engine.resources.httpData(url: artworkUrl).start(next: { next in - let file = EngineTempBox.shared.tempFile(fileName: "image.jpg") - let _ = try? next.write(to: URL(fileURLWithPath: file.path)) - subscriber.putNext(.moveTempFile(file: file)) - }, completed: { - subscriber.putCompletion() - })) - } - } - }) - - return ActionDisposable { - disposable.dispose() - fetchDisposable.dispose() - } - } - }*/ } diff --git a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift index 9852a8d08c..ca2d5d5a5d 100644 --- a/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift +++ b/submodules/SemanticStatusNode/Sources/SemanticStatusNode.swift @@ -857,6 +857,25 @@ public final class SemanticStatusNode: ASControlNode { self.setNeedsDisplay() } } + + public func setBackgroundImage(_ image: Signal<(TransformImageArguments) -> DrawingContext?, NoError>) { + let start = CACurrentMediaTime() + self.disposable = combineLatest(queue: Queue.mainQueue(), image, self.hasLayoutPromise.get()).start(next: { [weak self] transform, ready in + guard let strongSelf = self, ready else { + return + } + let context = transform(TransformImageArguments(corners: ImageCorners(radius: strongSelf.bounds.width / 2.0), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets())) + + let previousAppearanceContext = strongSelf.appearanceContext + strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage()) + + if CACurrentMediaTime() - start > 0.3 { + strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {}) + strongSelf.updateAnimations() + } + strongSelf.setNeedsDisplay() + }) + } private var animator: ConstantDisplayLinkAnimator? @@ -889,23 +908,8 @@ public final class SemanticStatusNode: ASControlNode { self.isOpaque = false self.displaysAsynchronously = true - if let image = image { - let start = CACurrentMediaTime() - self.disposable = combineLatest(queue: Queue.mainQueue(), image, self.hasLayoutPromise.get()).start(next: { [weak self] transform, ready in - guard let strongSelf = self, ready else { - return - } - let context = transform(TransformImageArguments(corners: ImageCorners(radius: strongSelf.bounds.width / 2.0), imageSize: strongSelf.bounds.size, boundingSize: strongSelf.bounds.size, intrinsicInsets: UIEdgeInsets())) - - let previousAppearanceContext = strongSelf.appearanceContext - strongSelf.appearanceContext = strongSelf.appearanceContext.withUpdatedBackgroundImage(context?.generateImage()) - - if CACurrentMediaTime() - start > 0.3 { - strongSelf.transitionContext = SemanticStatusNodeTransitionContext(startTime: CACurrentMediaTime(), duration: 0.18, previousStateContext: nil, previousAppearanceContext: previousAppearanceContext, completion: {}) - strongSelf.updateAnimations() - } - strongSelf.setNeedsDisplay() - }) + if let image { + self.setBackgroundImage(image) } } diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index ccb9c4f094..08a96013cd 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -65,7 +65,7 @@ public enum ShareControllerSubject { case image([ImageRepresentationWithReference]) case media(AnyMediaReference) case mapMedia(TelegramMediaMap) - case fromExternal(([PeerId], String, Account, Bool) -> Signal) + case fromExternal(([PeerId], [PeerId: Int64], String, Account, Bool) -> Signal) } private enum ExternalShareItem { @@ -695,7 +695,7 @@ public final class ShareController: ViewController { shareSignals.append(enqueueMessages(account: strongSelf.currentAccount, peerId: peerId, messages: messagesToEnqueue)) } case let .fromExternal(f): - return f(peerIds, text, strongSelf.currentAccount, silently) + return f(peerIds, topicIds, text, strongSelf.currentAccount, silently) |> map { state -> ShareState in switch state { case let .preparing(long): diff --git a/submodules/ShareController/Sources/ShareControllerNode.swift b/submodules/ShareController/Sources/ShareControllerNode.swift index 360ce83835..a1a3f1679b 100644 --- a/submodules/ShareController/Sources/ShareControllerNode.swift +++ b/submodules/ShareController/Sources/ShareControllerNode.swift @@ -236,7 +236,7 @@ final class ShareControllerNode: ViewControllerTracingNode, UIScrollViewDelegate ]) return ContextController.Items(content: .list(items), animationCache: nil) } - let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(ShareContextReferenceContentSource(sourceNode: node, customPosition: CGPoint(x: 0.0, y: -116.0))), items: items, gesture: gesture) + let contextController = ContextController(account: context.account, presentationData: presentationData, source: .reference(ShareContextReferenceContentSource(sourceNode: node, customPosition: CGPoint(x: 0.0, y: fromForeignApp ? -116.0 : 0.0))), items: items, gesture: gesture) contextController.immediateItemsTransitionAnimation = true strongSelf.present?(contextController) } diff --git a/submodules/ShareController/Sources/ShareSearchBarNode.swift b/submodules/ShareController/Sources/ShareSearchBarNode.swift index 7c5825a231..4a8fd9ae96 100644 --- a/submodules/ShareController/Sources/ShareSearchBarNode.swift +++ b/submodules/ShareController/Sources/ShareSearchBarNode.swift @@ -53,6 +53,8 @@ final class ShareSearchBarNode: ASDisplayNode, UITextFieldDelegate { self.textInputNode.textField.tintColor = theme.actionSheet.controlAccentColor self.textInputNode.textField.returnKeyType = .search self.textInputNode.textField.accessibilityTraits = .searchField + self.textInputNode.textField.spellCheckingType = .no + self.textInputNode.textField.autocorrectionType = .no super.init() diff --git a/submodules/ShareItems/Sources/ShareItems.swift b/submodules/ShareItems/Sources/ShareItems.swift index 9ceeaf925a..6d66c748a8 100644 --- a/submodules/ShareItems/Sources/ShareItems.swift +++ b/submodules/ShareItems/Sources/ShareItems.swift @@ -414,7 +414,7 @@ public func preparedShareItems(account: Account, to peerId: PeerId, dataItems: [ }) } -public func sentShareItems(account: Account, to peerIds: [PeerId], items: [PreparedShareItemContent], silently: Bool) -> Signal { +public func sentShareItems(account: Account, to peerIds: [PeerId], threadIds: [PeerId: Int64], items: [PreparedShareItemContent], silently: Bool) -> Signal { var messages: [EnqueueMessage] = [] var groupingKey: Int64? var mediaTypes: (photo: Int, video: Int, music: Int, other: Int) = (0, 0, 0, 0) @@ -474,7 +474,7 @@ public func sentShareItems(account: Account, to peerIds: [PeerId], items: [Prepa } } - return enqueueMessagesToMultiplePeers(account: account, peerIds: peerIds, messages: messages) + return enqueueMessagesToMultiplePeers(account: account, peerIds: peerIds, threadIds: threadIds, messages: messages) |> castError(Void.self) |> mapToSignal { messageIds -> Signal in return TelegramEngine(account: account).data.subscribe(EngineDataMap( diff --git a/submodules/Speak/Sources/Speak.swift b/submodules/Speak/Sources/Speak.swift index 845550e67b..7afa3306bf 100644 --- a/submodules/Speak/Sources/Speak.swift +++ b/submodules/Speak/Sources/Speak.swift @@ -19,6 +19,10 @@ public class SpeechSynthesizerHolder: NSObject, AVSpeechSynthesizerDelegate { self.speechSynthesizer.delegate = self } + deinit { + self.stop() + } + public func stop() { self.speechSynthesizer.stopSpeaking(at: .immediate) } diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index 7b9daf339a..48312014ef 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -244,7 +244,7 @@ public func enqueueMessages(account: Account, peerId: PeerId, messages: [Enqueue } } -public func enqueueMessagesToMultiplePeers(account: Account, peerIds: [PeerId], messages: [EnqueueMessage]) -> Signal<[MessageId], NoError> { +public func enqueueMessagesToMultiplePeers(account: Account, peerIds: [PeerId], threadIds: [PeerId: Int64], messages: [EnqueueMessage]) -> Signal<[MessageId], NoError> { let signal: Signal<[(Bool, EnqueueMessage)], NoError> if let transformOutgoingMessageMedia = account.transformOutgoingMessageMedia { signal = opportunisticallyTransformOutgoingMedia(network: account.network, postbox: account.postbox, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messages: messages, userInteractive: true) @@ -256,6 +256,14 @@ public func enqueueMessagesToMultiplePeers(account: Account, peerIds: [PeerId], return account.postbox.transaction { transaction -> [MessageId] in var messageIds: [MessageId] = [] for peerId in peerIds { + var replyToMessageId: MessageId? + if let threadIds = threadIds[peerId] { + replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadIds)) + } + var messages = messages + if let replyToMessageId { + messages = messages.map { ($0.0, $0.1.withUpdatedReplyToMessageId(replyToMessageId)) } + } for id in enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: messages, disableAutoremove: false, transformGroupingKeysWithPeerId: true) { if let id = id { messageIds.append(id) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift index 4067170ea3..b61557dec8 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/ForwardGame.swift @@ -3,7 +3,7 @@ import Postbox import TelegramApi import SwiftSignalKit -func _internal_forwardGameWithScore(account: Account, messageId: MessageId, to peerId: PeerId, as sendAsPeerId: PeerId?) -> Signal { +func _internal_forwardGameWithScore(account: Account, messageId: MessageId, to peerId: PeerId, threadId: Int64?, as sendAsPeerId: PeerId?) -> Signal { return account.postbox.transaction { transaction -> Signal in if let _ = transaction.getMessage(messageId), let fromPeer = transaction.getPeer(messageId.peerId), let fromInputPeer = apiInputPeer(fromPeer), let toPeer = transaction.getPeer(peerId), let toInputPeer = apiInputPeer(toPeer) { var flags: Int32 = 1 << 8 @@ -14,7 +14,7 @@ func _internal_forwardGameWithScore(account: Account, messageId: MessageId, to p flags |= (1 << 13) } - return account.network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: fromInputPeer, id: [messageId.id], randomId: [Int64.random(in: Int64.min ... Int64.max)], toPeer: toInputPeer, topMsgId: nil, scheduleDate: nil, sendAs: sendAsInputPeer)) + return account.network.request(Api.functions.messages.forwardMessages(flags: flags, fromPeer: fromInputPeer, id: [messageId.id], randomId: [Int64.random(in: Int64.min ... Int64.max)], toPeer: toInputPeer, topMsgId: threadId.flatMap { Int32(clamping: $0) }, scheduleDate: nil, sendAs: sendAsInputPeer)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift index f1c1afa03f..217b37fd35 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/TelegramEngineMessages.swift @@ -98,8 +98,8 @@ public extension TelegramEngine { |> ignoreValues } - public func forwardGameWithScore(messageId: MessageId, to peerId: PeerId, as senderPeerId: PeerId?) -> Signal { - return _internal_forwardGameWithScore(account: self.account, messageId: messageId, to: peerId, as: senderPeerId) + public func forwardGameWithScore(messageId: MessageId, to peerId: PeerId, threadId: Int64?, as senderPeerId: PeerId?) -> Signal { + return _internal_forwardGameWithScore(account: self.account, messageId: messageId, to: peerId, threadId: threadId, as: senderPeerId) } public func requestUpdatePinnedMessage(peerId: PeerId, update: PinnedMessageUpdate) -> Signal { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Archive.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Archive.imageset/Contents.json new file mode 100644 index 0000000000..35c3bbbeb1 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Archive.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_archived_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Archive.imageset/folder_archived_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Archive.imageset/folder_archived_40.pdf new file mode 100644 index 0000000000..5a505ad5ae --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Archive.imageset/folder_archived_40.pdf @@ -0,0 +1,101 @@ +%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 11.000000 25.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 2.000000 m +0.000000 3.104569 0.895431 4.000000 2.000000 4.000000 c +16.000000 4.000000 l +17.104568 4.000000 18.000000 3.104569 18.000000 2.000000 c +18.000000 2.000000 l +18.000000 0.895431 17.104568 0.000000 16.000000 0.000000 c +2.000000 0.000000 l +0.895430 0.000000 0.000000 0.895431 0.000000 2.000000 c +0.000000 2.000000 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 12.000000 11.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 6.400001 m +0.000000 13.000000 l +15.999990 13.000000 l +15.999995 6.400005 l +15.999997 4.159793 15.999998 3.039687 15.564024 2.184040 c +15.180531 1.431391 14.568608 0.819468 13.815960 0.435974 c +12.960313 0.000000 11.840207 0.000000 9.599995 0.000000 c +6.400000 0.000000 l +4.159790 0.000000 3.039685 0.000000 2.184038 0.435974 c +1.431390 0.819468 0.819467 1.431390 0.435974 2.184038 c +0.000000 3.039685 0.000000 4.159790 0.000000 6.400001 c +h +4.000000 10.669944 m +3.632731 10.669944 3.335000 10.372213 3.335000 10.004944 c +3.335000 9.637674 3.632731 9.339944 4.000000 9.339944 c +12.000000 9.339944 l +12.367270 9.339944 12.665000 9.637674 12.665000 10.004944 c +12.665000 10.372213 12.367270 10.669944 12.000000 10.669944 c +4.000000 10.669944 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1313 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000001403 00000 n +0000001426 00000 n +0000001599 00000 n +0000001673 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1732 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Bot.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Bot.imageset/Contents.json new file mode 100644 index 0000000000..26de2ec884 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Bot.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_bots_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Bot.imageset/folder_bots_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Bot.imageset/folder_bots_40.pdf new file mode 100644 index 0000000000..b0e480e386 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Bot.imageset/folder_bots_40.pdf @@ -0,0 +1,133 @@ +%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 9.170044 11.000000 cm +0.000000 0.000000 0.000000 scn +11.660001 20.500017 m +11.660001 20.958412 11.288398 21.330017 10.830000 21.330017 c +10.371605 21.330017 10.000001 20.958414 10.000001 20.500017 c +10.000000 18.999161 l +8.674637 18.994707 7.917117 18.967068 7.277086 18.795572 c +5.206533 18.240768 3.589248 16.623484 3.034445 14.552931 c +2.842555 13.836790 2.830771 12.973555 2.830047 11.332342 c +2.832777 11.296032 2.836228 11.266307 2.840583 11.238806 c +2.953750 10.524204 3.514198 9.963750 4.228799 9.850575 c +4.342342 9.832593 4.493809 9.830017 4.989999 9.830017 c +16.649998 9.830017 l +17.146219 9.830017 17.297695 9.832593 17.411245 9.850577 c +18.125826 9.963757 18.686260 10.524191 18.799438 11.238771 c +18.817423 11.352322 18.820000 11.503798 18.820000 12.000017 c +18.828419 12.000017 l +18.822008 13.221151 18.789581 13.940780 18.625555 14.552931 c +18.070751 16.623484 16.453466 18.240768 14.382915 18.795572 c +13.742884 18.967068 12.985363 18.994707 11.660000 18.999161 c +11.660001 20.500017 l +h +18.830000 8.624647 m +18.479866 8.419952 18.088602 8.277168 17.670925 8.211015 c +17.410572 8.169780 17.116264 8.169874 16.718634 8.170002 c +16.718611 8.170002 l +16.649998 8.170017 l +4.989999 8.170017 l +4.921389 8.170002 l +4.921366 8.170002 l +4.523762 8.169874 4.229471 8.169780 3.969133 8.211010 c +3.559370 8.275907 3.175025 8.414557 2.830000 8.613064 c +2.830000 6.400018 l +2.830000 4.159807 2.830000 3.039703 3.265974 2.184055 c +3.649467 1.431406 4.261390 0.819485 5.014038 0.435991 c +5.869685 0.000017 6.989790 0.000017 9.230000 0.000017 c +12.430000 0.000017 l +14.670210 0.000017 15.790315 0.000017 16.645962 0.435991 c +17.398609 0.819485 18.010532 1.431406 18.394026 2.184055 c +18.830000 3.039703 18.830000 4.159807 18.830000 6.400017 c +18.830000 8.624647 l +h +7.330000 12.000017 m +8.158427 12.000017 8.830000 12.671590 8.830000 13.500017 c +8.830000 14.328444 8.158427 15.000017 7.330000 15.000017 c +6.501573 15.000017 5.830000 14.328444 5.830000 13.500017 c +5.830000 12.671590 6.501573 12.000017 7.330000 12.000017 c +h +14.330000 12.000017 m +15.158427 12.000017 15.830000 12.671590 15.830000 13.500017 c +15.830000 14.328444 15.158427 15.000017 14.330000 15.000017 c +13.501573 15.000017 12.830000 14.328444 12.830000 13.500017 c +12.830000 12.671590 13.501573 12.000017 14.330000 12.000017 c +h +0.830000 9.830009 m +1.288396 9.830009 1.660000 9.458405 1.660000 9.000009 c +1.660000 4.000008 l +1.660000 3.541613 1.288396 3.170010 0.830000 3.170010 c +0.371604 3.170010 0.000000 3.541613 0.000000 4.000008 c +0.000000 9.000009 l +0.000000 9.458405 0.371604 9.830009 0.830000 9.830009 c +h +21.660000 9.000009 m +21.660000 9.458405 21.288397 9.830009 20.830000 9.830009 c +20.371603 9.830009 20.000000 9.458405 20.000000 9.000009 c +20.000000 4.000008 l +20.000000 3.541613 20.371603 3.170010 20.830000 3.170010 c +21.288397 3.170010 21.660000 3.541613 21.660000 4.000008 c +21.660000 9.000009 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2959 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000003049 00000 n +0000003072 00000 n +0000003245 00000 n +0000003319 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +3378 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Channel.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Channel.imageset/Contents.json new file mode 100644 index 0000000000..2b9d85b93f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Channel.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_channels_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Channel.imageset/folder_channels_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Channel.imageset/folder_channels_40.pdf new file mode 100644 index 0000000000..a8a538a0b0 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Channel.imageset/folder_channels_40.pdf @@ -0,0 +1,102 @@ +%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 11.000000 10.493469 cm +0.000000 0.000000 0.000000 scn +4.000000 6.005876 m +6.634244 6.005876 l +6.966668 6.005876 7.132880 6.005876 7.294138 5.988387 c +7.738360 5.940209 8.166211 5.793410 8.546436 5.558714 c +8.684463 5.473516 8.815662 5.371471 9.078063 5.167382 c +11.835391 3.022794 l +11.835403 3.022783 l +13.492669 1.733799 14.321304 1.089306 15.017017 1.095226 c +15.622235 1.100376 16.192566 1.379318 16.568199 1.853886 c +17.000000 2.399414 17.000000 3.449183 17.000000 5.548722 c +17.000000 14.463028 l +17.000000 16.562567 17.000000 17.612337 16.568199 18.157866 c +16.192566 18.632435 15.622235 18.911375 15.017017 18.916525 c +14.321305 18.922445 13.492673 18.277954 11.835412 16.988974 c +11.835398 16.988964 l +11.835389 16.988956 l +9.078062 14.844368 l +8.815662 14.640280 8.684463 14.538236 8.546436 14.453037 c +8.166211 14.218340 7.738360 14.071542 7.294138 14.023365 c +7.132880 14.005876 6.966668 14.005876 6.634244 14.005876 c +4.000000 14.005876 l +1.790861 14.005876 0.000000 12.215014 0.000000 10.005876 c +0.000000 7.796737 1.790861 6.005876 4.000000 6.005876 c +h +4.094136 4.100112 m +4.000000 3.898788 4.000000 3.639077 4.000000 3.119654 c +4.000000 2.005873 l +4.000000 0.901304 4.895431 0.005873 6.000000 0.005873 c +7.104569 0.005873 8.000000 0.901304 8.000000 2.005873 c +8.000000 3.119654 l +8.000000 3.639077 8.000000 3.898788 7.905864 4.100112 c +7.806588 4.312430 7.635887 4.483131 7.423568 4.582409 c +7.222244 4.676544 6.962533 4.676544 6.443110 4.676544 c +5.556890 4.676544 l +5.037467 4.676544 4.777756 4.676544 4.576432 4.582409 c +4.364113 4.483131 4.193412 4.312430 4.094136 4.100112 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1673 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000001763 00000 n +0000001786 00000 n +0000001959 00000 n +0000002033 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2092 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contact.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contact.imageset/Contents.json new file mode 100644 index 0000000000..e779807edb --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contact.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_contacts_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contact.imageset/folder_contacts_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contact.imageset/folder_contacts_40.pdf new file mode 100644 index 0000000000..a50ad0e283 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contact.imageset/folder_contacts_40.pdf @@ -0,0 +1,78 @@ +%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 11.730286 11.000000 cm +0.000000 0.000000 0.000000 scn +8.269731 10.500000 m +10.478869 10.500000 12.269731 12.290861 12.269731 14.500000 c +12.269731 16.709139 10.478869 18.500000 8.269731 18.500000 c +6.060592 18.500000 4.269731 16.709139 4.269731 14.500000 c +4.269731 12.290861 6.060592 10.500000 8.269731 10.500000 c +h +16.238016 3.829396 m +15.145898 5.884616 12.897719 8.000000 8.269734 8.000000 c +3.641746 8.000000 1.393565 5.884617 0.301445 3.829397 c +-0.735194 1.878584 1.060591 0.000000 3.269730 0.000000 c +13.269731 0.000000 l +15.478869 0.000000 17.274656 1.878582 16.238016 3.829396 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 666 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000000756 00000 n +0000000778 00000 n +0000000951 00000 n +0000001025 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1084 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Group.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Group.imageset/Contents.json new file mode 100644 index 0000000000..38f6f2d963 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Group.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_groups_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Group.imageset/folder_groups_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Group.imageset/folder_groups_40.pdf new file mode 100644 index 0000000000..abf1f87d32 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Group.imageset/folder_groups_40.pdf @@ -0,0 +1,96 @@ +%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 9.450012 12.000000 cm +0.000000 0.000000 0.000000 scn +7.798214 10.500000 m +9.731211 10.500000 11.298214 12.067003 11.298214 14.000000 c +11.298214 15.932997 9.731211 17.500000 7.798214 17.500000 c +5.865218 17.500000 4.298214 15.932997 4.298214 14.000000 c +4.298214 12.067003 5.865218 10.500000 7.798214 10.500000 c +h +15.800003 10.500000 m +17.456858 10.500000 18.800003 11.843145 18.800003 13.500000 c +18.800003 15.156855 17.456858 16.500000 15.800003 16.500000 c +14.143148 16.500000 12.800003 15.156855 12.800003 13.500000 c +12.800003 11.843145 14.143148 10.500000 15.800003 10.500000 c +h +12.332236 6.972357 m +13.807444 6.168993 14.733925 4.998592 15.315786 3.845357 c +15.642039 3.198733 15.667471 2.562775 15.476417 1.999998 c +15.084723 0.846209 13.783087 0.000000 12.298215 0.000000 c +3.298214 0.000000 l +1.089075 0.000000 -0.714483 1.873044 0.280643 3.845357 c +1.315064 5.895553 3.438652 8.000000 7.798214 8.000000 c +9.529486 8.000000 10.908134 7.668119 12.005980 7.139552 c +12.117610 7.085807 12.226336 7.030027 12.332236 6.972357 c +h +13.528140 7.807037 m +14.969814 6.871095 15.903218 5.633628 16.503208 4.444468 c +16.923948 3.610569 17.012365 2.769079 16.852047 1.999998 c +19.298889 1.999998 l +20.955744 1.999998 22.306608 3.401897 21.573879 4.887923 c +20.792742 6.472124 19.169998 8.117645 15.798890 8.117645 c +14.925295 8.117645 14.169111 8.007141 13.514557 7.815837 c +13.528140 7.807037 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1471 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000001561 00000 n +0000001584 00000 n +0000001757 00000 n +0000001831 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1890 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Muted.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Muted.imageset/Contents.json new file mode 100644 index 0000000000..9c408a9038 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Muted.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_muted_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Muted.imageset/folder_muted_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Muted.imageset/folder_muted_40.pdf new file mode 100644 index 0000000000..1af2209f91 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Muted.imageset/folder_muted_40.pdf @@ -0,0 +1,103 @@ +%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 9.335022 9.205139 cm +0.000000 0.000000 0.000000 scn +0.194774 21.265064 m +0.454473 21.524763 0.875527 21.524763 1.135226 21.265064 c +5.274762 17.125528 l +5.872045 17.835260 6.676024 18.369415 7.604840 18.634792 c +7.661423 18.650957 l +7.979070 18.741714 8.236824 18.974384 8.359516 19.281115 c +8.607920 19.902122 l +8.944381 20.743275 9.759058 21.294838 10.665007 21.294838 c +11.570954 21.294838 12.385632 20.743277 12.722093 19.902124 c +12.970496 19.281115 l +13.093188 18.974384 13.350943 18.741714 13.668590 18.650959 c +13.725172 18.634792 l +15.515708 18.123209 16.842316 16.612776 17.118553 14.771193 c +17.898428 9.572028 l +18.072489 8.411618 18.575701 7.325472 19.348385 6.442405 c +19.884527 5.829672 l +20.578663 5.036373 20.015293 3.794838 18.961184 3.794838 c +18.605453 3.794838 l +21.135227 1.265064 l +21.394924 1.005367 21.394924 0.584312 21.135227 0.324612 c +20.875526 0.064915 20.454472 0.064915 20.194775 0.324612 c +0.194774 20.324614 l +-0.064925 20.584312 -0.064925 21.005365 0.194774 21.265064 c +h +4.216347 14.803045 m +4.211460 14.771194 l +3.431585 9.572028 l +3.257523 8.411618 2.754311 7.325473 1.981627 6.442406 c +1.445486 5.829673 l +0.751350 5.036373 1.314719 3.794838 2.368829 3.794838 c +15.224553 3.794838 l +4.216347 14.803045 l +h +13.164993 2.794861 m +13.164993 1.414148 12.045706 0.294861 10.664993 0.294861 c +9.284282 0.294861 8.164993 1.414148 8.164993 2.794861 c +13.164993 2.794861 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1478 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000001568 00000 n +0000001591 00000 n +0000001764 00000 n +0000001838 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1897 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Read.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Read.imageset/Contents.json new file mode 100644 index 0000000000..1996183d3f --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Read.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_read_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Read.imageset/folder_read_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Read.imageset/folder_read_40.pdf new file mode 100644 index 0000000000..80adfba0fe --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/Read.imageset/folder_read_40.pdf @@ -0,0 +1,90 @@ +%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 10.000000 8.864380 cm +0.000000 0.000000 0.000000 scn +20.000000 11.044710 m +20.000000 16.065481 15.522848 20.135620 10.000000 20.135620 c +4.477152 20.135620 0.000000 16.065481 0.000000 11.044710 c +0.000000 8.181208 1.337573 5.834020 3.613619 4.167675 c +3.904685 3.954578 4.172771 2.770548 3.523984 1.775993 c +3.258681 1.369297 2.966608 1.052908 2.741760 0.809341 c +2.416753 0.457275 2.232201 0.257359 2.471971 0.156788 c +2.722059 0.051888 4.199766 -0.000004 5.266314 0.598129 c +6.101101 1.066288 6.606689 1.533300 6.947171 1.847805 c +7.228708 2.107861 7.397357 2.263641 7.545889 2.229572 c +8.331102 2.049471 9.153261 1.953800 10.000000 1.953800 c +15.522848 1.953800 20.000000 6.023939 20.000000 11.044710 c +h +15.019278 14.220198 m +15.248709 14.506987 15.202211 14.925467 14.915422 15.154898 c +14.628633 15.384329 14.210154 15.337831 13.980722 15.051043 c +8.482336 8.178061 l +6.004906 11.068397 l +5.765890 11.347248 5.346076 11.379541 5.067224 11.140526 c +4.788372 10.901510 4.756079 10.481695 4.995094 10.202844 c +7.995094 6.702844 l +8.123997 6.552459 8.313225 6.467360 8.511267 6.470716 c +8.709307 6.474072 8.895545 6.565532 9.019278 6.720198 c +15.019278 14.220198 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1243 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000001333 00000 n +0000001356 00000 n +0000001529 00000 n +0000001603 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1662 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/User.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/User.imageset/Contents.json new file mode 100644 index 0000000000..78ecb9ce43 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/User.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "folder_noncontacts_40.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/Filters/User.imageset/folder_noncontacts_40.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/User.imageset/folder_noncontacts_40.pdf new file mode 100644 index 0000000000..b2ba8fc92a --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/Filters/User.imageset/folder_noncontacts_40.pdf @@ -0,0 +1,107 @@ +%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 10.061523 8.500000 cm +0.000000 0.000000 0.000000 scn +10.938424 13.000000 m +13.147563 13.000000 14.938424 14.790861 14.938424 17.000000 c +14.938424 19.209139 13.147563 21.000000 10.938424 21.000000 c +8.729285 21.000000 6.938424 19.209139 6.938424 17.000000 c +6.938424 14.790861 8.729285 13.000000 10.938424 13.000000 c +h +18.906710 6.329396 m +17.814592 8.384616 15.566413 10.500000 10.938428 10.500000 c +9.942817 10.500000 9.057343 10.402101 8.269824 10.227960 c +8.445148 9.665609 8.536674 9.079668 8.536674 8.500021 c +8.536674 6.573775 7.599305 4.738405 5.980786 3.737831 c +5.955194 3.722008 5.931429 3.706717 5.909462 3.692053 c +5.858409 3.278175 5.705797 2.895748 5.477325 2.570473 c +5.487815 2.555046 5.498142 2.539518 5.508308 2.523895 c +5.649430 2.508137 5.793032 2.500000 5.938424 2.500000 c +15.938424 2.500000 l +18.147564 2.500000 19.943350 4.378582 18.906710 6.329396 c +h +3.438355 10.670017 m +2.561421 10.670017 1.660000 9.811304 1.660000 8.500017 c +1.660000 8.041620 1.288396 7.670017 0.830000 7.670017 c +0.371604 7.670017 0.000000 8.041620 0.000000 8.500017 c +0.000000 10.502439 1.434180 12.330017 3.438355 12.330017 c +5.442531 12.330017 6.876710 10.502439 6.876710 8.500017 c +6.876710 7.094978 6.192886 5.820520 5.107935 5.149799 c +4.843530 4.986343 4.616027 4.806322 4.462280 4.622707 c +4.311458 4.442589 4.268355 4.305183 4.268355 4.200016 c +4.268355 4.000017 l +4.268355 3.541620 3.896751 3.170017 3.438355 3.170017 c +2.979959 3.170017 2.608355 3.541620 2.608355 4.000017 c +2.608355 4.200016 l +2.608355 4.812820 2.876473 5.314534 3.189545 5.688422 c +3.499692 6.058817 3.888757 6.347694 4.235048 6.561773 c +4.787395 6.903235 5.216710 7.617404 5.216710 8.500017 c +5.216710 9.811304 4.315289 10.670017 3.438355 10.670017 c +h +4.307807 1.000017 m +4.307807 0.447731 3.918540 0.000017 3.438355 0.000017 c +2.958170 0.000017 2.568903 0.447731 2.568903 1.000017 c +2.568903 1.552303 2.958170 2.000017 3.438355 2.000017 c +3.918540 2.000017 4.307807 1.552303 4.307807 1.000017 c +h +f* +n +Q + +endstream +endobj + +3 0 obj + 2054 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 40.000000 40.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 +0000002144 00000 n +0000002167 00000 n +0000002340 00000 n +0000002414 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +2473 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 9b460cf5ef..63268c0783 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -539,6 +539,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return title } + private var currentSpeechHolder: SpeechSynthesizerHolder? + public init(context: AccountContext, chatLocation: ChatLocation, chatLocationContextHolder: Atomic = Atomic(value: nil), subject: ChatControllerSubject? = nil, botStart: ChatControllerInitialBotStart? = nil, attachBotStart: ChatControllerInitialAttachBotStart? = nil, mode: ChatControllerPresentationMode = .standard(previewing: false), peekData: ChatPeekTimeout? = nil, peerNearbyData: ChatPeerNearbyData? = nil, chatListFilter: Int32? = nil, chatNavigationStack: [ChatNavigationStackItem] = []) { let _ = ChatControllerCount.modify { value in return value + 1 @@ -3519,7 +3521,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G window.rootViewController?.present(controller, animated: true) } case .speak: - let _ = speakText(context: strongSelf.context, text: text.string) + if let speechHolder = speakText(context: strongSelf.context, text: text.string) { + speechHolder.completion = { [weak self, weak speechHolder] in + if let strongSelf = self, strongSelf.currentSpeechHolder == speechHolder { + strongSelf.currentSpeechHolder = nil + } + } + strongSelf.currentSpeechHolder = speechHolder + } case .translate: strongSelf.chatDisplayNode.dismissInput() let f = { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index b6d75323bc..7122316778 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -1486,7 +1486,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } private func transcribe() { - guard let item = self.item, self.statusNode == nil else { + guard let item = self.item, item.message.id.namespace == Namespaces.Message.Cloud else { return } diff --git a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift index 2f34774565..d1fab167b3 100644 --- a/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatReportPeerTitlePanelNode.swift @@ -439,17 +439,17 @@ final class ChatReportPeerTitlePanelNode: ChatTitleAccessoryPanelNode { } } + let additionalRightInset: CGFloat = 36.0 if !self.buttons.isEmpty { let maxInset = max(contentRightInset, leftInset) if self.buttons.count == 1 { - let buttonWidth = floor((width - maxInset * 2.0) / CGFloat(self.buttons.count)) + let buttonWidth = floor((width - maxInset * 2.0 - additionalRightInset) / CGFloat(self.buttons.count)) var nextButtonOrigin: CGFloat = maxInset for (_, view) in self.buttons { view.frame = CGRect(origin: CGPoint(x: nextButtonOrigin, y: 0.0), size: CGSize(width: buttonWidth, height: panelHeight)) nextButtonOrigin += buttonWidth } } else { - let additionalRightInset: CGFloat = 36.0 var areaWidth = width - maxInset * 2.0 - additionalRightInset let maxButtonWidth = floor(areaWidth / CGFloat(self.buttons.count)) let buttonSizes = self.buttons.map { button -> CGFloat in diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 82aaefbf80..17c279aa47 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -3059,6 +3059,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { return UIMenu(children: actions) } + private var currentSpeechHolder: SpeechSynthesizerHolder? @objc func _accessibilitySpeak(_ sender: Any) { var text = "" self.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in @@ -3066,9 +3067,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate { return (current, inputMode) } if let context = self.context { - let _ = speakText(context: context, text: text) + if let speechHolder = speakText(context: context, text: text) { + speechHolder.completion = { [weak self, weak speechHolder] in + if let strongSelf = self, strongSelf.currentSpeechHolder == speechHolder { + strongSelf.currentSpeechHolder = nil + } + } + self.currentSpeechHolder = speechHolder + } } - if #available(iOS 13.0, *) { UIMenuController.shared.hideMenu() } else { diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift index bf260b5240..61e9109124 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionController.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionController.swift @@ -147,12 +147,12 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection if let additionalCategories = additionalCategories { for i in 0 ..< additionalCategories.categories.count { if additionalCategories.selectedCategories.contains(additionalCategories.categories[i].id) { - strongSelf.contactsNode.editableTokens.append(EditableTokenListToken(id: additionalCategories.categories[i].id, title: additionalCategories.categories[i].title, fixedPosition: i)) + strongSelf.contactsNode.editableTokens.append(EditableTokenListToken(id: additionalCategories.categories[i].id, title: additionalCategories.categories[i].title, fixedPosition: i, subject: .category(additionalCategories.categories[i].smallIcon))) } } } strongSelf.contactsNode.editableTokens.append(contentsOf: peers.map { peer -> EditableTokenListToken in - return EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: params.context.account.peerId, peer: peer._asPeer(), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) + return EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: params.context.account.peerId, peer: peer._asPeer(), strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil, subject: .peer(peer)) }) strongSelf._peersReady.set(.single(true)) if strongSelf.isNodeLoaded { @@ -261,7 +261,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection displayCountAlert = true updatedState = updatedState.withToggledPeerId(.peer(peer.id)) } else { - addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) + addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil, subject: .peer(EnginePeer(peer))) } } updatedCount = updatedState.selectedPeerIndices.count @@ -279,7 +279,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection state.selectedPeerIds.remove(peer.id) removedTokenId = peer.id } else { - addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil) + addedToken = EditableTokenListToken(id: peer.id, title: peerTokenTitle(accountPeerId: accountPeerId, peer: peer, strings: strongSelf.presentationData.strings, nameDisplayOrder: strongSelf.presentationData.nameDisplayOrder), fixedPosition: nil, subject: .peer(EnginePeer(peer))) state.selectedPeerIds.insert(peer.id) } updatedCount = state.selectedPeerIds.count @@ -450,7 +450,7 @@ class ContactMultiselectionControllerImpl: ViewController, ContactMultiselection if let additionalCategories = chatSelection.additionalCategories { for i in 0 ..< additionalCategories.categories.count { if additionalCategories.categories[i].id == id { - categoryToken = EditableTokenListToken(id: id, title: additionalCategories.categories[i].title, fixedPosition: i) + categoryToken = EditableTokenListToken(id: id, title: additionalCategories.categories[i].title, fixedPosition: i, subject: .category(additionalCategories.categories[i].smallIcon)) break } } diff --git a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift index f6f7d3fb74..da2b4d17bc 100644 --- a/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/ContactMultiselectionControllerNode.swift @@ -106,6 +106,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { placeholder = placeholderValue let chatListNode = ChatListNode(context: context, location: .chatList(groupId: .root), previewing: false, fillPreloadItems: false, mode: .peers(filter: [.excludeSecretChats], isSelecting: true, additionalCategories: additionalCategories?.categories ?? [], chatListFilters: chatListFilters, displayAutoremoveTimeout: chatSelection.displayAutoremoveTimeout), isPeerEnabled: isPeerEnabled, theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false) + chatListNode.passthroughPeerSelection = true chatListNode.disabledPeerSelected = { peer, _ in attemptDisabledItemSelection?(peer) } @@ -133,7 +134,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode { self.contentNode = .contacts(ContactListNode(context: context, presentation: .single(.natural(options: options, includeChatList: includeChatList)), filters: filters, selectionState: ContactListNodeGroupSelectionState())) } - self.tokenListNode = EditableTokenListNode(theme: EditableTokenListNodeTheme(backgroundColor: .clear, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, selectedTextColor: self.presentationData.theme.list.itemCheckColors.foregroundColor, selectedBackgroundColor: self.presentationData.theme.list.itemCheckColors.fillColor, accentColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.rootController.keyboardColor), placeholder: placeholder) + self.tokenListNode = EditableTokenListNode(context: self.context, presentationTheme: self.presentationData.theme, theme: EditableTokenListNodeTheme(backgroundColor: .clear, separatorColor: self.presentationData.theme.rootController.navigationBar.separatorColor, placeholderTextColor: self.presentationData.theme.list.itemPlaceholderTextColor, primaryTextColor: self.presentationData.theme.list.itemPrimaryTextColor, tokenBackgroundColor: self.presentationData.theme.list.itemCheckColors.strokeColor.withAlphaComponent(0.25), selectedTextColor: self.presentationData.theme.list.itemCheckColors.foregroundColor, selectedBackgroundColor: self.presentationData.theme.list.itemCheckColors.fillColor, accentColor: self.presentationData.theme.list.itemAccentColor, keyboardColor: self.presentationData.theme.rootController.keyboardColor), placeholder: placeholder) super.init() diff --git a/submodules/TelegramUI/Sources/EditableTokenListNode.swift b/submodules/TelegramUI/Sources/EditableTokenListNode.swift index 440a6a7e3f..6d5dbd06a9 100644 --- a/submodules/TelegramUI/Sources/EditableTokenListNode.swift +++ b/submodules/TelegramUI/Sources/EditableTokenListNode.swift @@ -2,12 +2,21 @@ import Foundation import UIKit import AsyncDisplayKit import Display +import TelegramCore import TelegramPresentationData +import AvatarNode +import AccountContext struct EditableTokenListToken { + enum Subject { + case peer(EnginePeer) + case category(UIImage?) + } + let id: AnyHashable let title: String let fixedPosition: Int? + let subject: Subject } private let caretIndicatorImage = generateVerticallyStretchableFilledCircleImage(radius: 1.0, color: UIColor(rgb: 0x3350ee)) @@ -27,21 +36,41 @@ private func caretAnimation() -> CAAnimation { return animation } +private func generateRemoveIcon(_ color: UIColor) -> UIImage? { + return generateImage(CGSize(width: 22.0, height: 22.0), rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + context.setStrokeColor(color.cgColor) + context.setLineWidth(2.0 - UIScreenPixel) + context.setLineCap(.round) + + let length: CGFloat = 8.0 + context.move(to: CGPoint(x: 7.0, y: 7.0)) + context.addLine(to: CGPoint(x: 7.0 + length, y: 7.0 + length)) + context.strokePath() + + context.move(to: CGPoint(x: 7.0 + length, y: 7.0)) + context.addLine(to: CGPoint(x: 7.0, y: 7.0 + length)) + context.strokePath() + }) +} + final class EditableTokenListNodeTheme { let backgroundColor: UIColor let separatorColor: UIColor let placeholderTextColor: UIColor let primaryTextColor: UIColor + let tokenBackgroundColor: UIColor let selectedTextColor: UIColor let selectedBackgroundColor: UIColor let accentColor: UIColor let keyboardColor: PresentationThemeKeyboardColor - init(backgroundColor: UIColor, separatorColor: UIColor, placeholderTextColor: UIColor, primaryTextColor: UIColor, selectedTextColor: UIColor, selectedBackgroundColor: UIColor, accentColor: UIColor, keyboardColor: PresentationThemeKeyboardColor) { + init(backgroundColor: UIColor, separatorColor: UIColor, placeholderTextColor: UIColor, primaryTextColor: UIColor, tokenBackgroundColor: UIColor, selectedTextColor: UIColor, selectedBackgroundColor: UIColor, accentColor: UIColor, keyboardColor: PresentationThemeKeyboardColor) { self.backgroundColor = backgroundColor self.separatorColor = separatorColor self.placeholderTextColor = placeholderTextColor self.primaryTextColor = primaryTextColor + self.tokenBackgroundColor = tokenBackgroundColor self.selectedTextColor = selectedTextColor self.selectedBackgroundColor = selectedBackgroundColor self.accentColor = accentColor @@ -50,44 +79,82 @@ final class EditableTokenListNodeTheme { } private final class TokenNode: ASDisplayNode { + private let context: AccountContext + private let presentationTheme: PresentationTheme + let theme: EditableTokenListNodeTheme let token: EditableTokenListToken + let avatarNode: AvatarNode + let categoryAvatarNode: ASImageNode + let removeIconNode: ASImageNode let titleNode: ASTextNode + let backgroundNode: ASImageNode let selectedBackgroundNode: ASImageNode - var isSelected: Bool { - didSet { - if self.isSelected != oldValue { - self.titleNode.attributedText = NSAttributedString(string: token.title + ",", font: Font.regular(15.0), textColor: self.isSelected ? self.theme.selectedTextColor : self.theme.primaryTextColor) - self.titleNode.redrawIfPossible() - self.selectedBackgroundNode.isHidden = !self.isSelected - } - } - } + var isSelected: Bool = false - init(theme: EditableTokenListNodeTheme, token: EditableTokenListToken, isSelected: Bool) { + init(context: AccountContext, presentationTheme: PresentationTheme, theme: EditableTokenListNodeTheme, token: EditableTokenListToken, isSelected: Bool) { + self.context = context + self.presentationTheme = presentationTheme self.theme = theme self.token = token self.titleNode = ASTextNode() self.titleNode.isUserInteractionEnabled = false self.titleNode.displaysAsynchronously = false self.titleNode.maximumNumberOfLines = 1 + + self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 13.0)) + self.categoryAvatarNode = ASImageNode() + self.categoryAvatarNode.displaysAsynchronously = false + self.categoryAvatarNode.displayWithoutProcessing = true + + self.removeIconNode = ASImageNode() + self.removeIconNode.alpha = 0.0 + self.removeIconNode.displaysAsynchronously = false + self.removeIconNode.displayWithoutProcessing = true + self.removeIconNode.image = generateRemoveIcon(theme.selectedTextColor) + + let cornerRadius: CGFloat + switch token.subject { + case .peer: + cornerRadius = 24.0 + case .category: + cornerRadius = 14.0 + } + + self.backgroundNode = ASImageNode() + self.backgroundNode.displaysAsynchronously = false + self.backgroundNode.displayWithoutProcessing = true + self.backgroundNode.image = generateStretchableFilledCircleImage(diameter: cornerRadius, color: theme.tokenBackgroundColor) + self.selectedBackgroundNode = ASImageNode() + self.selectedBackgroundNode.alpha = 0.0 self.selectedBackgroundNode.displaysAsynchronously = false self.selectedBackgroundNode.displayWithoutProcessing = true - self.selectedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: 8.0, color: theme.selectedBackgroundColor) - self.selectedBackgroundNode.isHidden = !isSelected - self.isSelected = isSelected - + self.selectedBackgroundNode.image = generateStretchableFilledCircleImage(diameter: cornerRadius, color: theme.selectedBackgroundColor) + super.init() + self.addSubnode(self.backgroundNode) self.addSubnode(self.selectedBackgroundNode) - self.titleNode.attributedText = NSAttributedString(string: token.title + ",", font: Font.regular(15.0), textColor: self.isSelected ? self.theme.selectedTextColor : self.theme.primaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(14.0), textColor: self.isSelected ? self.theme.selectedTextColor : self.theme.primaryTextColor) self.addSubnode(self.titleNode) + self.addSubnode(self.removeIconNode) + + switch token.subject { + case let .peer(peer): + self.addSubnode(self.avatarNode) + self.avatarNode.setPeer(context: context, theme: presentationTheme, peer: peer) + case let .category(image): + self.addSubnode(self.categoryAvatarNode) + self.categoryAvatarNode.image = image + } + + self.updateIsSelected(isSelected, animated: false) } override func calculateSizeThatFits(_ constrainedSize: CGSize) -> CGSize { let titleSize = self.titleNode.measure(CGSize(width: constrainedSize.width - 8.0, height: constrainedSize.height)) - return CGSize(width: titleSize.width + 8.0, height: 28.0) + return CGSize(width: 22.0 + titleSize.width + 16.0, height: 28.0) } override func layout() { @@ -95,8 +162,63 @@ private final class TokenNode: ASDisplayNode { if titleSize.width.isZero { return } + self.backgroundNode.frame = self.bounds.insetBy(dx: 2.0, dy: 2.0) self.selectedBackgroundNode.frame = self.bounds.insetBy(dx: 2.0, dy: 2.0) - self.titleNode.frame = CGRect(origin: CGPoint(x: 4.0, y: floor((self.bounds.size.height - titleSize.height) / 2.0)), size: titleSize) + self.avatarNode.frame = CGRect(origin: CGPoint(x: 3.0, y: 3.0), size: CGSize(width: 22.0, height: 22.0)) + self.categoryAvatarNode.frame = self.avatarNode.frame + self.removeIconNode.frame = self.avatarNode.frame + + self.titleNode.frame = CGRect(origin: CGPoint(x: 29.0, y: floor((self.bounds.size.height - titleSize.height) / 2.0)), size: titleSize) + } + + func updateIsSelected(_ isSelected: Bool, animated: Bool) { + guard self.isSelected != isSelected else { + return + } + self.isSelected = isSelected + + self.avatarNode.alpha = isSelected ? 0.0 : 1.0 + self.categoryAvatarNode.alpha = isSelected ? 0.0 : 1.0 + self.removeIconNode.alpha = isSelected ? 1.0 : 0.0 + + if animated { + if isSelected { + self.selectedBackgroundNode.alpha = 1.0 + self.selectedBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + + self.avatarNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + self.avatarNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2) + + self.categoryAvatarNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + self.categoryAvatarNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2) + + self.removeIconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.removeIconNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + } else { + self.selectedBackgroundNode.alpha = 0.0 + self.selectedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + + self.avatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.avatarNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + + self.categoryAvatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.categoryAvatarNode.layer.animateScale(from: 0.01, to: 1.0, duration: 0.2) + + self.removeIconNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2) + self.removeIconNode.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2) + } + + if let snapshotView = self.titleNode.view.snapshotContentTree() { + self.titleNode.view.superview?.addSubview(snapshotView) + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + } + self.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } + + self.titleNode.attributedText = NSAttributedString(string: token.title, font: Font.regular(14.0), textColor: self.isSelected ? self.theme.selectedTextColor : self.theme.primaryTextColor) + self.titleNode.redrawIfPossible() } } @@ -111,6 +233,9 @@ private final class CaretIndicatorNode: ASImageNode { } final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { + private let context: AccountContext + private let presentationTheme: PresentationTheme + private let theme: EditableTokenListNodeTheme private let backgroundNode: NavigationBackgroundNode private let scrollNode: ASScrollNode @@ -126,7 +251,9 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { var deleteToken: ((AnyHashable) -> Void)? var textReturned: (() -> Void)? - init(theme: EditableTokenListNodeTheme, placeholder: String) { + init(context: AccountContext, presentationTheme: PresentationTheme, theme: EditableTokenListNodeTheme, placeholder: String) { + self.context = context + self.presentationTheme = presentationTheme self.theme = theme self.backgroundNode = NavigationBackgroundNode(color: theme.backgroundColor) @@ -178,7 +305,7 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { strongSelf.deleteToken?(selectedTokenId) strongSelf.updateSelectedTokenId(nil) } else if let tokenNode = strongSelf.tokenNodes.last { - strongSelf.updateSelectedTokenId(tokenNode.token.id) + strongSelf.updateSelectedTokenId(tokenNode.token.id, animated: true) } } } @@ -211,10 +338,6 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { let sideInset: CGFloat = 12.0 + leftInset let verticalInset: CGFloat = 6.0 - let placeholderSize = self.placeholderNode.measure(CGSize(width: max(1.0, width - sideInset - sideInset), height: CGFloat.greatestFiniteMagnitude)) - self.placeholderNode.frame = CGRect(origin: CGPoint(x: sideInset + 4.0, y: verticalInset + floor((28.0 - placeholderSize.height) / 2.0)), size: placeholderSize) - - transition.updateAlpha(node: self.placeholderNode, alpha: tokens.isEmpty ? 1.0 : 0.0) var animationDelay = 0.0 var currentOffset = CGPoint(x: sideInset, y: verticalInset) @@ -231,7 +354,7 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { if let currentNode = currentNode { tokenNode = currentNode } else { - tokenNode = TokenNode(theme: self.theme, token: token, isSelected: self.selectedTokenId != nil && token.id == self.selectedTokenId!) + tokenNode = TokenNode(context: self.context, presentationTheme: self.presentationTheme, theme: self.theme, token: token, isSelected: self.selectedTokenId != nil && token.id == self.selectedTokenId!) self.tokenNodes.append(tokenNode) self.scrollNode.addSubnode(tokenNode) animateIn = true @@ -287,6 +410,9 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { currentOffset.x = sideInset } + let placeholderSize = self.placeholderNode.measure(CGSize(width: max(1.0, width - sideInset - sideInset), height: CGFloat.greatestFiniteMagnitude)) + transition.updateFrame(node: self.placeholderNode, frame: CGRect(origin: CGPoint(x: currentOffset.x + 4.0, y: currentOffset.y + floor((28.0 - placeholderSize.height) / 2.0)), size: placeholderSize)) + let textNodeFrame = CGRect(origin: CGPoint(x: currentOffset.x + 4.0, y: currentOffset.y + UIScreenPixel), size: CGSize(width: width - currentOffset.x - sideInset - 8.0, height: 28.0)) let caretNodeFrame = CGRect(origin: CGPoint(x: textNodeFrame.minX, y: textNodeFrame.minY + 4.0 - UIScreenPixel), size: CGSize(width: 2.0, height: 19.0 + UIScreenPixel)) if case .immediate = transition { @@ -363,10 +489,10 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { self.textFieldChanged(self.textFieldNode.textField) } - private func updateSelectedTokenId(_ id: AnyHashable?) { + private func updateSelectedTokenId(_ id: AnyHashable?, animated: Bool = false) { self.selectedTokenId = id for tokenNode in self.tokenNodes { - tokenNode.isSelected = id == tokenNode.token.id + tokenNode.updateIsSelected(id == tokenNode.token.id, animated: animated) } if id != nil && !self.textFieldNode.textField.isFirstResponder { self.textFieldNode.textField.becomeFirstResponder() @@ -377,8 +503,13 @@ final class EditableTokenListNode: ASDisplayNode, UITextFieldDelegate { if case .ended = recognizer.state { let point = recognizer.location(in: self.view) for tokenNode in self.tokenNodes { - if tokenNode.bounds.contains(self.view.convert(point, to: tokenNode.view)) { - self.updateSelectedTokenId(tokenNode.token.id) + let convertedPoint = self.view.convert(point, to: tokenNode.view) + if tokenNode.bounds.contains(convertedPoint) { + if tokenNode.isSelected && convertedPoint.x < 24.0 { + self.deleteToken?(tokenNode.token.id) + } else { + self.updateSelectedTokenId(tokenNode.token.id, animated: true) + } break } } diff --git a/submodules/TelegramUI/Sources/LegacyCamera.swift b/submodules/TelegramUI/Sources/LegacyCamera.swift index 23c6bf543b..1efc0e4076 100644 --- a/submodules/TelegramUI/Sources/LegacyCamera.swift +++ b/submodules/TelegramUI/Sources/LegacyCamera.swift @@ -234,7 +234,7 @@ func presentedLegacyShortcutCamera(context: AccountContext, saveCapturedMedia: B nativeGenerator(_1, _2, _3, nil) }) if let parentController = parentController { - parentController.present(ShareController(context: context, subject: .fromExternal({ peerIds, text, account, silently in + parentController.present(ShareController(context: context, subject: .fromExternal({ peerIds, _, text, account, silently in return legacyAssetPickerEnqueueMessages(account: account, signals: signals!) |> `catch` { _ -> Signal<[LegacyAssetPickerEnqueueMessage], ShareControllerError> in return .single([]) diff --git a/submodules/TelegramUI/Sources/MediaManager.swift b/submodules/TelegramUI/Sources/MediaManager.swift index d44ce03b6e..33939a11cf 100644 --- a/submodules/TelegramUI/Sources/MediaManager.swift +++ b/submodules/TelegramUI/Sources/MediaManager.swift @@ -2,6 +2,7 @@ import Foundation import SwiftSignalKit import AVFoundation import MobileCoreServices +import Display import Postbox import TelegramCore import MediaPlayer @@ -325,23 +326,11 @@ public final class MediaManagerImpl: NSObject, MediaManager { |> distinctUntilChanged(isEqual: { $0?.0 === $1?.0 && $0?.1 == $1?.1 }) |> mapToSignal { value -> Signal in if let (account, value) = value { - return albumArtThumbnailData(engine: TelegramEngine(account: account), thumbnail: value.fullSizeResource) - |> map { data -> UIImage? in - return data.flatMap(UIImage.init(data:)) + return playerAlbumArt(postbox: account.postbox, engine: TelegramEngine(account: account), fileReference: value.fullSizeResource.file, albumArt: value, thumbnail: false) + |> map { generator -> UIImage? in + let arguments = TransformImageArguments(corners: ImageCorners(), imageSize: CGSize(width: 640, height: 640), boundingSize: CGSize(width: 640, height: 640), intrinsicInsets: .zero) + return generator(arguments)?.generateImage() } - /*return Signal { subscriber in - let fetched = account.postbox.mediaBox.fetchedResource(value.fullSizeResource, parameters: nil).start() - let data = account.postbox.mediaBox.resourceData(value.fullSizeResource, pathExtension: nil, option: .complete(waitUntilFetchStatus: false)).start(next: { data in - if data.complete, let value = try? Data(contentsOf: URL(fileURLWithPath: data.path)) { - subscriber.putNext(UIImage(data: value)) - subscriber.putCompletion() - } - }) - return ActionDisposable { - fetched.dispose() - data.dispose() - } - }*/ } else { return .single(nil) } diff --git a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift index c8d76905af..c29cfded5b 100644 --- a/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Sources/PeerSelectionControllerNode.swift @@ -127,7 +127,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { var chatListCategories: [ChatListNodeAdditionalCategory] = [] if let _ = createNewGroup { - chatListCategories.append(ChatListNodeAdditionalCategory(id: 0, icon: PresentationResourcesItemList.createGroupIcon(self.presentationData.theme), title: self.presentationData.strings.PeerSelection_ImportIntoNewGroup, appearance: .action)) + chatListCategories.append(ChatListNodeAdditionalCategory(id: 0, icon: PresentationResourcesItemList.createGroupIcon(self.presentationData.theme), smallIcon: nil, title: self.presentationData.strings.PeerSelection_ImportIntoNewGroup, appearance: .action)) } let chatListLocation: ChatListControllerLocation diff --git a/submodules/TelegramUI/Sources/ShareExtensionContext.swift b/submodules/TelegramUI/Sources/ShareExtensionContext.swift index 12fb7471d7..ed67e9a955 100644 --- a/submodules/TelegramUI/Sources/ShareExtensionContext.swift +++ b/submodules/TelegramUI/Sources/ShareExtensionContext.swift @@ -353,8 +353,8 @@ public class ShareRootControllerImpl { } |> runOn(Queue.mainQueue()) } - let sentItems: ([PeerId], [PreparedShareItemContent], Account, Bool) -> Signal = { peerIds, contents, account, silently in - let sentItems = sentShareItems(account: account, to: peerIds, items: contents, silently: silently) + let sentItems: ([PeerId], [PeerId: Int64], [PreparedShareItemContent], Account, Bool) -> Signal = { peerIds, threadIds, contents, account, silently in + let sentItems = sentShareItems(account: account, to: peerIds, threadIds: threadIds, items: contents, silently: silently) |> `catch` { _ -> Signal< Float, NoError> in return .complete() @@ -366,7 +366,7 @@ public class ShareRootControllerImpl { |> then(.single(.done)) } - let shareController = ShareController(context: context, subject: .fromExternal({ peerIds, additionalText, account, silently in + let shareController = ShareController(context: context, subject: .fromExternal({ peerIds, threadIds, additionalText, account, silently in if let strongSelf = self, let inputItems = strongSelf.getExtensionContext()?.inputItems, !inputItems.isEmpty, !peerIds.isEmpty { let rawSignals = TGItemProviderSignals.itemSignals(forInputItems: inputItems)! return preparedShareItems(account: account, to: peerIds[0], dataItems: rawSignals, additionalText: additionalText) @@ -392,11 +392,11 @@ public class ShareRootControllerImpl { return requestUserInteraction(value) |> castError(ShareControllerError.self) |> mapToSignal { contents -> Signal in - return sentItems(peerIds, contents, account, silently) + return sentItems(peerIds, threadIds, contents, account, silently) |> castError(ShareControllerError.self) } case let .done(contents): - return sentItems(peerIds, contents, account, silently) + return sentItems(peerIds, threadIds, contents, account, silently) |> castError(ShareControllerError.self) } }